diff --git a/assets/test_prelude.ld b/assets/test_prelude.ld index 7c4a5c5..8325121 100644 --- a/assets/test_prelude.ld +++ b/assets/test_prelude.ld @@ -315,6 +315,633 @@ fn string { } } +fn join { + "Takes a list of strings, and joins them into a single string, interposing an optional separator." + ([]) -> "" + ([str as :string]) -> str + (strs as :list) -> join (strs, "") + ([], separator as :string) -> "" + ([str as :string], separator as :string) -> str + ([str, ...strs], separator as :string) -> fold ( + fn (joined, to_join) -> concat (joined, separator, to_join) + strs + str + ) +} + +fn split { + "Takes a string, and turns it into a list of strings, breaking on the separator." + (str as :string, splitter as :string) -> base :split (str, splitter) +} + +fn trim { + "Trims whitespace from a string. Takes an optional argument, `:left` or `:right`, to trim only on the left or right." + (str as :string) -> base :trim (str) + (str as :string, :left) -> base :triml (str) + (str as :string, :right) -> base :trimr (str) +} + +fn upcase { + "Takes a string and returns it in all uppercase. Works only for ascii characters." + (str as :string) -> base :upcase (str) +} + +fn downcase { + "Takes a string and returns it in all lowercase. Works only for ascii characters." + (str as :string) -> base :downcase (str) +} + +fn chars { + "Takes a string and returns its characters as a list. Works only for strings with only ascii characters. Panics on any non-ascii characters." + (str as :string) -> match base :chars (str) with { + (:ok, chrs) -> chrs + (:err, msg) -> panic! msg + } +} + +fn chars/safe { + "Takes a string and returns its characters as a list, wrapped in a result tuple. Works only for strings with only ascii characters. Returns an error tuple on any non-ascii characters." + (str as :string) -> base :chars (str) +} + +fn ws? { + "Tells if a string is a whitespace character." + (" ") -> true + ("\n") -> true + ("\t") -> true + (_) -> false +} + +fn strip { + "Removes punctuation from a string, removing all instances of ,.;:?!" + ("{x},{y}") -> strip ("{x}{y}") + ("{x}.{y}") -> strip ("{x}{y}") + ("{x};{y}") -> strip ("{x}{y}") + ("{x}:{y}") -> strip ("{x}{y}") + ("{x}?{y}") -> strip ("{x}{y}") + ("{x}!{y}") -> strip ("{x}{y}") + (x) -> x +} + +fn words { + "Takes a string and returns a list of the words in the string. Strips all whitespace." + (str as :string) -> { + let no_punct = strip (str) + let strs = split (no_punct, " ") + fn worder (l, s) -> if empty? (s) + then l + else append (l, s) + fold (worder, strs, []) + } +} + +fn sentence { + "Takes a list of words and turns it into a sentence." + (strs as :list) -> join (strs, " ") +} + +fn to_number { + "Takes a string that presumably contains a representation of a number, and tries to give you back the number represented. Returns a result tuple." + (num as :string) -> base :number (num) +} + +&&& boxes: mutable state and state changes + +fn box? { + "Returns true if a value is a box." + (b as :box) -> true + (_) -> false +} + +fn unbox { + "Returns the value that is stored in a box." + (b as :box) -> base :unbox (b) +} + +fn store! { + "Stores a value in a box, replacing the value that was previously there. Returns the value." + (b as :box, value) -> { + base :store! (b, value) + value + } +} + +fn update! { + "Updates a box by applying a function to its value. Returns the new value." + (b as :box, f as :fn) -> { + let current = unbox (b) + let new = f (current) + store! (b, new) + } +} + +&&& numbers, basically: arithmetic and not much else, yet +& TODO: add nan?, +fn number? { + "Returns true if a value is a number." + (x as :number) -> true + (_) -> false +} + +fn add { + "Adds numbers or vectors." + () -> 0 + (x as :number) -> x + (x as :number, y as :number) -> base :add (x, y) + (x, y, ...zs) -> fold (add, zs, base :add (x, y)) + & add vectors + ((x1, y1), (x2, y2)) -> (add (x1, x2), add (y1, y2)) +} + +fn sub { + "Subtracts numbers or vectors." + () -> 0 + (x as :number) -> x + (x as :number, y as :number) -> base :sub (x, y) + (x, y, ...zs) -> fold (sub, zs, base :sub (x, y)) + ((x1, y1), (x2, y2)) -> (base :sub (x1, x2), base :sub (y1, y2)) +} + +fn mult { + "Multiplies numbers or vectors." + () -> 1 + (x as :number) -> x + (x as :number, y as :number) -> base :mult (x, y) + (x, y, ...zs) -> fold (mult, zs, mult (x, y)) + (scalar as :number, (x, y)) -> (mult (x, scalar), mult (y, scalar)) + ((x, y), scalar as :number) -> mult (scalar, (x, y)) +} + +fn div { + "Divides numbers. Panics on division by zero." + (x as :number) -> x + (_, 0) -> panic! "Division by zero." + (x as :number, y as :number) -> base :div (x, y) + (x, y, ...zs) -> { + let divisor = fold (mult, zs, y) + div (x, divisor) + } +} + +fn div/0 { + "Divides numbers. Returns 0 on division by zero." + (x as :number) -> x + (_, 0) -> 0 + (x as :number, y as :number) -> base :div (x, y) + (x, y, ...zs) -> { + let divisor = fold (mult, zs, y) + div/0 (x, divisor) + } +} + +fn div/safe { + "Divides a number. Returns a result tuple." + (x as :number) -> (:ok, x) + (_, 0) -> (:err, "Division by zero") + (x, y) -> (:ok, div (x, y)) + (x, y, ...zs) -> { + let divisor = fold (mult, zs, y) + div/safe (x, divisor) + } +} + +fn inv { + "Returns the inverse of a number: 1/n or `div (1, n)`. Panics on division by zero." + (x as :number) -> div (1, x) +} + +fn inv/0 { + "Returns the inverse of a number: 1/n or `div/0 (1, n)`. Returns 0 on division by zero." + (x as :number) -> div/0 (1, x) +} + +fn inv/safe { + "Returns the inverse of a number: 1/n or `div/safe (1, n)`. Returns a result tuple." + (x as :number) -> div/safe (1, x) +} + +fn neg { + "Multiplies a number by -1, negating it." + (n as :number) -> mult (n, -1) +} + +fn zero? { + "Returns true if a number is 0." + (0) -> true + (_) -> false +} + +fn gt? { + "Returns true if numbers are in decreasing order." + (x as :number) -> true + (x as :number, y as :number) -> base :gt? (x, y) + (x, y, ...zs) -> loop (y, zs) with { + (a, [b]) -> base :gt? (a, b) + (a, [b, ...cs]) -> if base :gt? (a, b) + then recur (b, cs) + else false + } +} + +fn gte? { + "Returns true if numbers are in decreasing or flat order." + (x as :number) -> true + (x as :number, y as :number) -> base :gte? (x, y) + (x, y, ...zs) -> loop (y, zs) with { + (a, [b]) -> base :gte? (a, b) + (a, [b, ...cs]) -> if base :gte? (a, b) + then recur (b, cs) + else false + } +} + +fn lt? { + "Returns true if numbers are in increasing order." + (x as :number) -> true + (x as :number, y as :number) -> base :lt? (x, y) + (x, y, ...zs) -> loop (y, zs) with { + (a, [b]) -> base :lt? (a, b) + (a, [b, ...cs]) -> if base :lt? (a, b) + then recur (b, cs) + else false + } +} + +fn lte? { + "Returns true if numbers are in increasing or flat order." + (x as :number) -> true + (x as :number, y as :number) -> base :lte? (x, y) + (x, y, ...zs) -> loop (y, zs) with { + (a, [b]) -> base :lte? (a, b) + (a, [b, ...cs]) -> if base :lte? (a, b) + then recur (b, cs) + else false + } +} + +fn between? { + "Returns true if a number is in the range [lower, higher): greater than or equal to the lower number, less than the higher." + (lower as :number, higher as :number, x as :number) -> and ( + gte? (x, lower) + lt? (x, higher) + ) +} + +fn neg? { + "Returns true if a value is a negative number, otherwise returns false." + (x as :number) if lt? (x, 0) -> true + (_) -> false +} + +fn pos? { + "Returns true if a value is a positive number, otherwise returns false." + (x as :number) if gt? (x, 0) -> true + (_) -> false +} + +fn abs { + "Returns the absolute value of a number." + (0) -> 0 + (n as :number) -> if neg? (n) then mult (-1, n) else n +} + +&&& Trigonometry functions + +& Ludus uses turns as its default unit to measure angles +& However, anything that takes an angle can also take a +& units argument, that's a keyword of :turns, :degrees, or :radians + +let pi = base :pi + +let tau = mult (2, pi) + +fn turn/deg { + "Converts an angle in turns to an angle in degrees." + (a as :number) -> mult (a, 360) +} + +fn deg/turn { + "Converts an angle in degrees to an angle in turns." + (a as :number) -> div (a, 360) +} + +fn turn/rad { + "Converts an angle in turns to an angle in radians." + (a as :number) -> mult (a, tau) +} + +fn rad/turn { + "Converts an angle in radians to an angle in turns." + (a as :number) -> div (a, tau) +} + +fn deg/rad { + "Converts an angle in degrees to an angle in radians." + (a as :number) -> mult (tau, div (a, 360)) +} + +fn rad/deg { + "Converts an angle in radians to an angle in degrees." + (a as :number) -> mult (360, div (a, tau)) +} + +fn sin { + "Returns the sine of an angle. Default angle measure is turns. An optional keyword argument specifies the units of the angle passed in." + (a as :number) -> do a > turn/rad > base :sin + (a as :number, :turns) -> do a > turn/rad > base :sin + (a as :number, :degrees) -> do a > deg/rad > base :sin + (a as :number, :radians) -> base :sin (a) +} + +fn cos { + "Returns the cosine of an angle. Default angle measure is turns. An optional keyword argument specifies the units of the angle passed in." + (a as :number) -> do a > turn/rad > base :cos + (a as :number, :turns) -> do a > turn/rad > base :cos + (a as :number, :degrees) -> do a > deg/rad > base :cos + (a as :number, :radians) -> base :cos (a) +} + +fn tan { + "Returns the sine of an angle. Default angle measure is turns. An optional keyword argument specifies the units of the angle passed in." + (a as :number) -> do a > turn/rad > base :tan + (a as :number, :turns) -> do a > turn/rad > base :tan + (a as :number, :degrees) -> do a > deg/rad > base :tan + (a as :number, :radians) -> base :tan (a) +} + +fn rotate { + "Rotates a vector by an angle. Default angle measure is turns. An optional keyword argument specifies the units of the angle passed in." + ((x, y), a) -> rotate ((x, y), a, :turns) + ((x, y), a, units as :keyword) -> ( + sub (mult (x, cos (a, units)), mult (y, sin (a, units))) + add (mult (x, sin (a, units)), mult (y, cos (a, units))) + ) +} + + +fn atan/2 { + "Returns an angle from a slope. Takes an optional keyword argument to specify units. Takes either two numbers or a vector tuple." + (x as :number, y as :number) -> do base :atan_2 (x, y) > rad/turn + (x, y, :turns) -> atan/2 (x, y) + (x, y, :radians) -> base :atan_2 (x, y) + (x, y, :degrees) -> do base :atan_2 (x, y) > rad/deg + ((x, y)) -> atan/2 (x, y) + ((x, y), units as :keyword) -> atan/2 (x, y, units) +} + +fn angle { + "Calculates the angle between two vectors." + (v1, v2) -> sub (atan/2 (v2), atan/2 (v1)) +} + +fn mod { + "Returns the modulus of x and y. Truncates towards negative infinity. Panics if y is 0." + (x as :number, 0) -> panic! "Division by zero." + (x as :number, y as :number) -> base :mod (x, y) +} + +fn mod/0 { + "Returns the modulus of x and y. Truncates towards negative infinity. Returns 0 if y is 0." + (x as :number, 0) -> 0 + (x as :number, y as :number) -> base :mod (x, y) +} + +fn mod/safe { + "Returns the modulus of x and y in a result tuple, or an error if y is 0. Truncates towards negative infinity." + (x as :number, 0) -> (:err, "Division by zero.") + (x as :number, y as :number) -> (:ok, base :mod (x, y)) +} + +fn even? { + "Returns true if a value is an even number, otherwise returns false." + (x as :number) if eq? (0, mod (x, 2)) -> true + (_) -> false +} + +fn odd? { + "Returns true if a value is an odd number, otherwise returns false." + (x as :number) if eq? (1, mod (x, 2)) -> true + (_) -> false +} + +fn square { + "Squares a number." + (x as :number) -> mult (x, x) +} + +fn sqrt { + "Returns the square root of a number. Panics if the number is negative." + (x as :number) if not (neg? (x)) -> base :sqrt (x) +} + +fn sqrt/safe { + "Returns a result containing the square root of a number, or an error if the number is negative." + (x as :number) -> if not (neg? (x)) + then (:ok, base :sqrt (x)) + else (:err, "sqrt of negative number") +} + +fn sum_of_squares { + "Returns the sum of squares of numbers." + () -> 0 + (x as :number) -> square (x) + (x as :number, y as :number) -> add (square (x), square (y)) + (x, y, ...zs) -> fold ( + fn (sum, z) -> add (sum, square (z)) + zs + sum_of_squares (x, y)) +} + +fn dist { + "Returns the distance from the origin to a point described by x and y, or by the vector (x, y)." + (x as :number, y as :number) -> sqrt (sum_of_squares (x, y)) + ((x, y)) -> dist (x, y) +} + +fn heading/vector { + "Takes a turtle heading, and returns a unit vector of that heading." + (heading) -> { + & 0 is 90º/0.25T, 0.25 is 180º/0.5T, 0.5 is 270º/0.75T, 0.75 is 0º/0T + let a = add (neg (heading), 0.25) + (cos (a), sin (a)) + } +} + +fn floor { + "Truncates a number towards negative infinity. With positive numbers, it returns the integer part. With negative numbers, returns the next more-negative integer." + (n as :number) -> base :floor (n) +} + +fn ceil { + "Truncates a number towards positive infinity. With negative numbers, it returns the integer part. With positive numbers, returns the next more-positive integer." + (n as :number) -> base :ceil (n) +} + +fn round { + "Rounds a number to the nearest integer." + (n as :number) -> base :round (n) +} + +fn range { + "Returns the set of integers between start (inclusive) and end (exclusive) as a list: [start, end). With one argument, starts at 0. If end is less than start, returns an empty list." + (end as :number) -> base :range (0, end) + (start as :number, end as :number) -> base :range (start, end) +} + +& additional list operations now that we have comparitors +fn at { + "Returns the element at index n of a list or tuple, or the byte at index n of a string. Zero-indexed: the first element is at index 0. Returns nil if nothing is found in a list or tuple; returns an empty string if nothing is found in a string." + (xs as :list, n as :number) -> base :nth (n, xs) + (xs as :tuple, n as :number) -> base :nth (n, xs) + (str as :string, n as :number) -> { + let raw = base :nth (n, str) + when { + nil? (raw) -> nil + gte? (raw, 128) -> panic! "not an ASCII char" + true -> base :str_slice (str, n, inc (n)) + } + } + (_) -> nil +} + +& fn first { +& "Returns the first element of a list or tuple." +& (xs) if ordered? -> at (xs, 0) +& } + +fn second { + "Returns the second element of a list or tuple." + (xs) if ordered? (xs) -> at (xs, 1) +} + +fn last { + "Returns the last element of a list or tuple." + (xs) if ordered? (xs) -> at (xs, dec (count (xs))) +} + +fn butlast { + "Returns a list, omitting the last element." + (xs as :list) -> base :slice (xs, 0, dec (count (xs))) +} + +fn slice { + "Returns a slice of a list or a string, representing a sub-list or sub-string." + (xs as :list, end as :number) -> slice (xs, 0, end) + (xs as :list, start as :number, end as :number) -> when { + gte? (start, end) -> [] + gt? (end, count (xs)) -> slice (xs, start, count (xs)) + neg? (start) -> slice (xs, 0, end) + true -> base :slice (xs, start, end) + } + (str as :string, end as :number) -> base :str_slice (str, 0, end) + (str as :string, start as :number, end as :number) -> base :str_slice (str, start, end) +} + +&&& keywords: funny names +fn keyword? { + "Returns true if a value is a keyword, otherwise returns false." + (kw as :keyword) -> true + (_) -> false +} + +& TODO: determine if Ludus should have a `keyword` function that takes a string and returns a keyword. Too many panics, it has weird memory consequences, etc. + +fn assoc { + "Takes a dict, key, and value, and returns a new dict with the key set to value." + () -> #{} + (d as :dict) -> d + (d as :dict, k as :keyword, val) -> base :assoc (d, k, val) + (d as :dict, (k as :keyword, val)) -> base :assoc (d, k, val) +} + +fn dissoc { + "Takes a dict and a key, and returns a new dict with the key and associated value omitted." + (d as :dict) -> d + (d as :dict, k as :keyword) -> base :dissoc (d, k) +} + +fn update { + "Takes a dict, key, and function, and returns a new dict with the key set to the result of applying the function to original value held at the key." + (d as :dict) -> d + (d as :dict, k as :keyword, updater as :fn) -> base :assoc (d, k, updater (k (d))) +} + +fn keys { + "Takes a dict and returns a list of keys in that dict." + (d as :dict) -> do d > list > map (first, _) +} + +fn values { + "Takes a dict and returns a list of values in that dict." + (d as :dict) -> do d > list > map (second, _) +} + +& TODO: consider merging `get` and `at` +fn get { + "Takes a key, dict, and optional default value; returns the value at key. If the value is not found, returns nil or the default value." + (k as :keyword) -> get (k, _) + (k as :keyword, d as :dict) -> get (k, d, nil) + (k as :keyword, d as :dict, default) -> base :get (k, d, default) +} + +& TODO: add sets to this? +fn has? { + "Takes a key and a dict, and returns true if there is a non-`nil` value stored at the key." + (k as :keyword) -> has? (k, _) + (k as :keyword, d as :dict) -> do d > k > some? +} + +fn dict { + "Takes a list or tuple of (key, value) tuples and returns it as a dict. Returns dicts unharmed." + (d as :dict) -> d + (l as :list) -> fold (assoc, l) + (t as :tuple) -> do t > list > dict +} + +fn dict? { + "Returns true if a value is a dict." + (d as :dict) -> true + (_) -> false +} + +fn each! { + "Takes a list and applies a function, presumably with side effects, to each element in the list. Returns nil." + (f! as :fn, []) -> nil + (f! as :fn, [x]) -> { f! (x); nil } + (f! as :fn, [x, ...xs]) -> { f! (x); each! (f!, xs) } +} + +fn random { + "Returns a random something. With zero arguments, returns a random number between 0 (inclusive) and 1 (exclusive). With one argument, returns a random number between 0 and n. With two arguments, returns a random number between m and n. Alternately, given a collection (tuple, list, dict, set), it returns a random member of that collection." + () -> base :random () + (n as :number) -> mult (n, random ()) + (m as :number, n as :number) -> add (m, random (sub (n, m))) + (l as :list) -> { + let i = do l > count > random > floor + at (l, i) + } + (t as :tuple) -> { + let i = do t > count > random > floor + at (t, i) + } + (d as :dict) -> { + let key = do d > keys > random + get (key, d) + } + & (s as :set) -> do s > list > random +} + +fn random_int { + "Returns a random integer. With one argument, returns a random integer between 0 and that number. With two arguments, returns a random integer between them." + (n as :number) -> do n > random > floor + (m as :number, n as :number) -> floor (random (m, n)) +} + + + + + + #{ type coll? @@ -355,4 +982,87 @@ fn string { doc! string string? + join + split + trim + upcase + downcase + chars + chars/safe + ws? + strip + words + sentence + to_number + box? + unbox + store! + update! + add + sub + mult + div + div/0 + div/safe + inv + inv/0 + inv/safe + abs + neg + zero? + gt? + gte? + lt? + lte? + between? + neg? + pos? + abs + pi + tau + turn/deg + deg/turn + turn/rad + rad/turn + deg/rad + rad/deg + sin + cos + tan + rotate + atan/2 + angle + mod + mod/0 + mod/safe + even? + odd? + square + sqrt + sqrt/safe + dist + heading/vector + floor + ceil + round + range + at + first + second + last + butlast + slice + keyword? + assoc + dissoc + update + keys + values + get + has? + dict + dict? + each! + random + random_int } diff --git a/may_2025_thoughts.md b/may_2025_thoughts.md index 5f5ab17..8e12ac9 100644 --- a/may_2025_thoughts.md +++ b/may_2025_thoughts.md @@ -476,3 +476,16 @@ loop ([1, 2, 3]) with { Meanwhile, other `loop`/`recur` forms seem to work okay for me. So: ugh. + +### Grinding and grinding +#### 2025-06-22 +Got 'er done. +Fixed `loop`/`recur` and many other stack shananigans. +I don't believe I've fixed everything yet. +I may be surprised, though. + +Currently fixing little bugs in prelude. +Here's a list of things that need doing: +* [ ] Escape characters in strings: \n, \t, and \{, \}. +* [ ] `doc!` needs to print the patterns of a function. + diff --git a/sandbox.ld b/sandbox.ld index fd40910..0a54d57 100644 --- a/sandbox.ld +++ b/sandbox.ld @@ -1,4 +1,7 @@ - - +when { + false -> :no + nil -> :nope + :else -> :yup +} diff --git a/src/base.rs b/src/base.rs index 7498a44..9a579b1 100644 --- a/src/base.rs +++ b/src/base.rs @@ -389,6 +389,7 @@ pub fn r#type(x: &Value) -> Value { pub fn split(source: &Value, splitter: &Value) -> Value { match (source, splitter) { (Value::String(source), Value::String(splitter)) => { + println!("splitting {source} with {splitter}"); let parts = source.split_terminator(splitter.as_str()); let mut list = vector![]; for part in parts { @@ -396,29 +397,16 @@ pub fn split(source: &Value, splitter: &Value) -> Value { } Value::List(Box::new(list)) } - (Value::String(source), Value::Interned(splitter)) => { - let parts = source.split_terminator(splitter); - let mut list = vector![]; - for part in parts { - list.push_back(Value::String(Rc::new(part.to_string()))); - } - Value::List(Box::new(list)) + (Value::String(_), Value::Interned(splitter)) => { + split(source, &Value::String(Rc::new(splitter.to_string()))) } - (Value::Interned(source), Value::String(splitter)) => { - let parts = source.split_terminator(splitter.as_str()); - let mut list = vector![]; - for part in parts { - list.push_back(Value::String(Rc::new(part.to_string()))); - } - Value::List(Box::new(list)) + (Value::Interned(source), Value::String(_)) => { + split(&Value::String(Rc::new(source.to_string())), splitter) } (Value::Interned(source), Value::Interned(splitter)) => { - let parts = source.split_terminator(splitter); - let mut list = vector![]; - for part in parts { - list.push_back(Value::String(Rc::new(part.to_string()))); - } - Value::List(Box::new(list)) + let source = Value::String(Rc::new(source.to_string())); + let splitter = Value::String(Rc::new(splitter.to_string())); + split(&source, &splitter) } _ => unreachable!("internal Ludus error"), } diff --git a/src/compiler.rs b/src/compiler.rs index 64cd1ab..1f9eb15 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -975,7 +975,6 @@ impl<'a> Compiler<'a> { self.tail_pos = false; self.visit(cond.as_ref()); let jif_jump_idx = self.stub_jump(Op::JumpIfFalse); - self.stack_depth -= 1; self.tail_pos = tail_pos; self.visit(body); self.stack_depth -= 1; @@ -1010,21 +1009,11 @@ impl<'a> Compiler<'a> { Box::leak(Box::new(guard.clone().unwrap())); self.visit(guard_expr); no_match_jumps.push(self.stub_jump(Op::JumpIfFalse)); - self.stack_depth -= 1; } self.tail_pos = tail_pos; self.visit(body); - // self.emit_op(Op::Store); self.store(); self.leave_scope(); - // self.scope_depth -= 1; - // while let Some(binding) = self.bindings.last() { - // if binding.depth > self.scope_depth { - // self.bindings.pop(); - // } else { - // break; - // } - // } self.pop_n(self.stack_depth - stack_depth); jump_idxes.push(self.stub_jump(Op::Jump)); for idx in no_match_jumps { diff --git a/src/main.rs b/src/main.rs index 70eb87e..52d8542 100644 --- a/src/main.rs +++ b/src/main.rs @@ -112,8 +112,8 @@ pub fn run(src: &'static str) { // in any event, the AST should live forever let parsed: &'static Spanned = Box::leak(Box::new(parse_result.unwrap())); - let prelude = prelude(); - // let prelude = imbl::HashMap::new(); + // let prelude = prelude(); + let prelude = imbl::HashMap::new(); let mut validator = Validator::new(&parsed.0, &parsed.1, "user input", src, prelude.clone()); validator.validate();