diff --git a/assets/test_prelude.ld b/assets/test_prelude.ld index 51e4236..4455377 100644 --- a/assets/test_prelude.ld +++ b/assets/test_prelude.ld @@ -1,115 +1,115 @@ & the very base: know something's type -& fn type { -& "Returns a keyword representing the type of the value passed in." -& (x) -> base :type (x) -& } +fn type { + "Returns a keyword representing the type of the value passed in." + (x) -> base :type (x) +} & & some helper type functions -& fn coll? { -& "Returns true if a value is a collection: dict, list, tuple, or set." -& (coll as :dict) -> true -& (coll as :list) -> true -& (coll as :tuple) -> true -& & (coll as :set) -> true -& (_) -> false -& } +fn coll? { + "Returns true if a value is a collection: dict, list, tuple, or set." + (coll as :dict) -> true + (coll as :list) -> true + (coll as :tuple) -> true + & (coll as :set) -> true + (_) -> false +} -& fn ordered? { -& "Returns true if a value is an indexed collection: list or tuple." -& (coll as :list) -> true -& (coll as :tuple) -> true -& (coll as :string) -> true -& (_) -> false -& } +fn ordered? { + "Returns true if a value is an indexed collection: list or tuple." + (coll as :list) -> true + (coll as :tuple) -> true + (coll as :string) -> true + (_) -> false +} -& fn assoc? { -& "Returns true if a value is an associative collection: a dict or a pkg." -& (d as :dict) -> true -& (_) -> false -& } +fn assoc? { + "Returns true if a value is an associative collection: a dict or a pkg." + (d as :dict) -> true + (_) -> false +} & &&& nil: working with nothing -& fn nil? { -& "Returns true if a value is nil." -& (nil) -> true -& (_) -> false -& } +fn nil? { + "Returns true if a value is nil." + (nil) -> true + (_) -> false +} -& fn some? { -& "Returns true if a value is not nil." -& (nil) -> false -& (_) -> true -& } +fn some? { + "Returns true if a value is not nil." + (nil) -> false + (_) -> true +} -& fn some { -& "Takes a possibly nil value and a default value. Returns the value if it's not nil, returns the default if it's nil." -& (nil, default) -> default -& (value, _) -> value -& } +fn some { + "Takes a possibly nil value and a default value. Returns the value if it's not nil, returns the default if it's nil." + (nil, default) -> default + (value, _) -> value +} & ...and if two things are the same fn eq? { "Returns true if all arguments have the same value." & (x) -> true (x, y) -> base :eq? (x, y) - & (x, y, ...zs) -> if eq? (x, y) - & then loop (y, zs) with { - & (a, []) -> eq? (a, x) - & (a, [b, ...cs]) -> if eq? (a, x) - & then recur (b, cs) - & else false - & } - & else false + (x, y, ...zs) -> if eq? (x, y) + then loop (y, zs) with { + (a, []) -> eq? (a, x) + (a, [b, ...cs]) -> if eq? (a, x) + then recur (b, cs) + else false + } + else false } & &&& true & false: boolean logic (part the first) -& fn bool? { -& "Returns true if a value is of type :boolean." -& (false) -> true -& (true) -> true -& (_) -> false -& } +fn bool? { + "Returns true if a value is of type :boolean." + (false) -> true + (true) -> true + (_) -> false +} -& fn true? { -& "Returns true if a value is boolean `true`. Useful to distinguish between `true` and anything else." -& (true) -> true -& (_) -> false -& } +fn true? { + "Returns true if a value is boolean `true`. Useful to distinguish between `true` and anything else." + (true) -> true + (_) -> false +} -& fn false? { -& "Returns `true` if a value is `false`, otherwise returns `false`. Useful to distinguish between `false` and `nil`." -& (false) -> true -& (_) -> false -& } +fn false? { + "Returns `true` if a value is `false`, otherwise returns `false`. Useful to distinguish between `false` and `nil`." + (false) -> true + (_) -> false +} -& fn bool { -& "Returns false if a value is nil or false, otherwise returns true." -& (nil) -> false -& (false) -> false -& (_) -> true -& } +fn bool { + "Returns false if a value is nil or false, otherwise returns true." + (nil) -> false + (false) -> false + (_) -> true +} -& fn not { -& "Returns false if a value is truthy, true if a value is falsy." -& (nil) -> true -& (false) -> true -& (_) -> false -& } +fn not { + "Returns false if a value is truthy, true if a value is falsy." + (nil) -> true + (false) -> true + (_) -> false +} & & tuples: not a lot you can do with them functionally -& fn tuple? { -& "Returns true if a value is a tuple." -& (tuple as :tuple) -> true -& (_) -> false -& } +fn tuple? { + "Returns true if a value is a tuple." + (tuple as :tuple) -> true + (_) -> false +} & &&& functions: getting things done -& fn fn? { -& "Returns true if an argument is a function." -& (f as :fn) -> true -& (_) -> false -& } +fn fn? { + "Returns true if an argument is a function." + (f as :fn) -> true + (_) -> false +} & what we need for some very basic list manipulation fn first { @@ -136,50 +136,50 @@ fn inc { (x as :number) -> base :inc (x) } -& fn dec { -& "Decrements a number." -& (x as :number) -> base :dec (x) -& } +fn dec { + "Decrements a number." + (x as :number) -> base :dec (x) +} -& fn count { -& "Returns the number of elements in a collection (including string)." -& (xs as :list) -> base :count (xs) -& (xs as :tuple) -> base :count (xs) -& (xs as :dict) -> base :count (xs) -& (xs as :string) -> base :count (xs) -& & (xs as :set) -> base :count (xs) -& } +fn count { + "Returns the number of elements in a collection (including string)." + (xs as :list) -> base :count (xs) + (xs as :tuple) -> base :count (xs) + (xs as :dict) -> base :count (xs) + (xs as :string) -> base :count (xs) + & (xs as :set) -> base :count (xs) +} -& fn empty? { -& "Returns true if something is empty. Otherwise returns false (including for things that can't logically be empty, like numbers)." -& ([]) -> true -& (#{}) -> true -& & (s as :set) -> eq? (s, ${}) -& (()) -> true -& ("") -> true -& (_) -> false -& } +fn empty? { + "Returns true if something is empty. Otherwise returns false (including for things that can't logically be empty, like numbers)." + ([]) -> true + (#{}) -> true + & (s as :set) -> eq? (s, ${}) + (()) -> true + ("") -> true + (_) -> false +} -& fn any? { -& "Returns true if something is not empty, otherwise returns false (including for things that can't be logically full, like numbers)." -& ([...]) -> true -& (#{...}) -> true -& & (s as :set) -> not (empty? (s)) -& ((...)) -> true -& (s as :string) -> not (empty? (s)) -& (_) -> false -& } +fn any? { + "Returns true if something is not empty, otherwise returns false (including for things that can't be logically full, like numbers)." + ([...]) -> true + (#{...}) -> true + & (s as :set) -> not (empty? (s)) + ((...)) -> true + (s as :string) -> not (empty? (s)) + (_) -> false +} -& fn list? { -& "Returns true if the value is a list." -& (l as :list) -> true -& (_) -> false -& } +fn list? { + "Returns true if the value is a list." + (l as :list) -> true + (_) -> false +} -& fn list { -& "Takes a value and returns it as a list. For values, it simply wraps them in a list. For collections, conversions are as follows. A tuple->list conversion preservers order and length. Unordered collections do not preserve order: sets and dicts don't have predictable or stable ordering in output. Dicts return lists of (key, value) tuples." -& (x) -> base :list (x) -& } +fn list { + "Takes a value and returns it as a list. For values, it simply wraps them in a list. For collections, conversions are as follows. A tuple->list conversion preservers order and length. Unordered collections do not preserve order: sets and dicts don't have predictable or stable ordering in output. Dicts return lists of (key, value) tuples." + (x) -> base :list (x) +} fn append { "Adds an element to a list." @@ -203,43 +203,43 @@ fn fold { } } -& fn map { -& "Maps a function over a list: returns a new list with elements that are the result of applying the function to each element in the original list. E.g., `map ([1, 2, 3], inc) &=> [2, 3, 4]`. With one argument, returns a function that is a mapper over lists; with two, it executes the mapping function right away." -& & (f as :fn) -> map (f, _) -& & (kw as :keyword) -> map (kw, _) -& (f as :fn, xs) -> { -& fn mapper (prev, curr) -> append (prev, f (curr)) -& fold (mapper, xs, []) -& } -& & (kw as :keyword, xs) -> { -& & fn mapper (prev, curr) -> append (prev, kw (curr)) -& & fold (mapper, xs, []) -& & } -& } +fn map { + "Maps a function over a list: returns a new list with elements that are the result of applying the function to each element in the original list. E.g., `map ([1, 2, 3], inc) &=> [2, 3, 4]`. With one argument, returns a function that is a mapper over lists; with two, it executes the mapping function right away." + & (f as :fn) -> map (f, _) + & (kw as :keyword) -> map (kw, _) + (f as :fn, xs) -> { + fn mapper (prev, curr) -> append (prev, f (curr)) + fold (mapper, xs, []) + } + & (kw as :keyword, xs) -> { + & fn mapper (prev, curr) -> append (prev, kw (curr)) + & fold (mapper, xs, []) + & } +} -& fn filter { -& "Takes a list and a predicate function, and returns a new list with only the items that produce truthy values when the function is called on them. E.g., `filter ([1, 2, 3, 4], odd?) &=> [1, 3]`." -& (p? as :fn) -> filter (p?, _) -& (p? as :fn, xs) -> { -& fn filterer (filtered, x) -> if p? (x) -& then append (filtered, x) -& else filtered -& fold (filterer, xs, []) -& } -& } +fn filter { + "Takes a list and a predicate function, and returns a new list with only the items that produce truthy values when the function is called on them. E.g., `filter ([1, 2, 3, 4], odd?) &=> [1, 3]`." + (p? as :fn) -> filter (p?, _) + (p? as :fn, xs) -> { + fn filterer (filtered, x) -> if p? (x) + then append (filtered, x) + else filtered + fold (filterer, xs, []) + } +} -& fn keep { -& "Takes a list and returns a new list with any `nil` values omitted." -& (xs) -> filter (some?, xs) -& } +fn keep { + "Takes a list and returns a new list with any `nil` values omitted." + (xs) -> filter (some?, xs) +} -& fn concat { -& "Combines two lists, strings, or sets." -& (x as :string, y as :string) -> "{x}{y}" -& (xs as :list, ys as :list) -> base :concat (xs, ys) -& & (xs as :set, ys as :set) -> base :concat (xs, ys) -& (xs, ys, ...zs) -> fold (concat, zs, concat (xs, ys)) -& } +fn concat { + "Combines two lists, strings, or sets." + (x as :string, y as :string) -> "{x}{y}" + (xs as :list, ys as :list) -> base :concat (xs, ys) + & (xs as :set, ys as :set) -> base :concat (xs, ys) + (xs, ys, ...zs) -> fold (concat, zs, concat (xs, ys)) +} fn contains? { "Returns true if a set or list contains a value." @@ -252,44 +252,38 @@ fn contains? { } } -fn add { - () -> 0 - (x) -> x - (x, y) -> base :add (x, y) -} #{ - & type - & coll? - & ordered? - & assoc? - & nil? - & some? - & some + type + coll? + ordered? + assoc? + nil? + some? + some eq? - & bool? - & true? - & false? - & bool - & not - & tuple? - & fn? + bool? + true? + false? + bool + not + tuple? + fn? first rest - & inc - & dec - & count - & empty? - & any? - & list? - & list - & first - & fold - & append - & map - & filter - & keep - & concat - & contains? - add + inc + dec + count + empty? + any? + list? + list + first + fold + append + map + filter + keep + concat + contains? } diff --git a/sandbox.ld b/sandbox.ld index 69b5ec9..d123411 100644 --- a/sandbox.ld +++ b/sandbox.ld @@ -1,31 +1,15 @@ -let test = 3 - -let quux = loop ([1, 2]) with { - ([]) -> false - ([x]) -> eq? (x, test) - ([x, ...xs]) -> if eq? (x, test) - then :yes - else recur (xs) +fn my_eq? { + (x, y, ...zs) -> if eq? (x, y) + then loop (y, zs) with { + (a, [b]) -> and (eq? (a, x), eq? (b, x)) + (a, [b, ...cs]) -> if eq? (a, x) + then recur (b, cs) + else false + } + else false } - -let foo = :bar - -fn not { - (false) -> true - (nil) -> true - (_) -> false -} - -let frob = loop ([1, 2, 3]) with { - ([]) -> false - ([y]) -> eq? (y, test) - ([y, ...ys]) -> if not (eq? (y, test)) - then recur (ys) - else true -} - -[quux, frob] +my_eq? (1, 1, 3) diff --git a/src/compiler.rs b/src/compiler.rs index 12e1db6..468bcc2 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -310,6 +310,11 @@ impl<'a> Compiler<'a> { self.stack_depth += 1; } + fn duplicate(&mut self) { + self.emit_op(Op::Duplicate); + self.stack_depth += 1; + } + fn pop(&mut self) { self.emit_op(Op::Pop); self.stack_depth -= 1; @@ -863,42 +868,44 @@ impl<'a> Compiler<'a> { self.stack_depth -= 1; } (Or, Arguments(args)) => { - let stack_depth = self.stack_depth; let mut jump_idxes = vec![]; if !args.is_empty() { - for arg in args { + let mut args = args.iter().rev(); + let last = args.next().unwrap(); + for arg in args.rev() { self.visit(arg); - self.emit_op(Op::Stash); + self.duplicate(); jump_idxes.push(self.stub_jump(Op::JumpIfTrue)); + self.pop(); } + self.visit(last); for idx in jump_idxes { - self.patch_jump(idx, self.len() - idx); + self.patch_jump(idx, self.len() - idx - 3); } - // self.emit_op(Op::Load); - self.load(); } else { self.emit_op(Op::False); + self.stack_depth += 1; } - self.stack_depth = stack_depth + 1; } (And, Arguments(args)) => { - let stack_depth = self.stack_depth; let mut jump_idxes = vec![]; if !args.is_empty() { - for arg in args { + let mut args = args.iter().rev(); + let last = args.next().unwrap(); + for arg in args.rev() { self.visit(arg); - self.emit_op(Op::Stash); + self.duplicate(); jump_idxes.push(self.stub_jump(Op::JumpIfFalse)); + self.pop(); } + self.visit(last); for idx in jump_idxes { - self.patch_jump(idx, self.len() - idx); + self.patch_jump(idx, self.len() - idx - 3); } - // self.emit_op(Op::Load); - self.load(); } else { self.emit_op(Op::True); + self.stack_depth += 1; } - self.stack_depth = stack_depth + 1; } (Word(fn_name), Arguments(args)) => { self.report_depth_str(format!("calling function {fn_name}")); 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(); diff --git a/src/validator.rs b/src/validator.rs index 455012b..c8467e9 100644 --- a/src/validator.rs +++ b/src/validator.rs @@ -284,7 +284,7 @@ impl<'a> Validator<'a> { // check arity against fn info if first term is word and second term is args Synthetic(first, second, rest) => { match (&first.0, &second.0) { - (Ast::And, Ast::Arguments(_)) | (Ast::Or, Ast::Tuple(_)) => { + (Ast::And, Ast::Arguments(_)) | (Ast::Or, Ast::Arguments(_)) => { self.visit(second.as_ref()) } (Ast::Word(_), Ast::Keyword(_)) => self.visit(first.as_ref()),