fix and & or

This commit is contained in:
Scott Richmond 2025-06-22 20:26:08 -04:00
parent e4a948ba94
commit c00e1275fd
5 changed files with 221 additions and 236 deletions

View File

@ -1,115 +1,115 @@
& the very base: know something's type & the very base: know something's type
& fn type { fn type {
& "Returns a keyword representing the type of the value passed in." "Returns a keyword representing the type of the value passed in."
& (x) -> base :type (x) (x) -> base :type (x)
& } }
& & some helper type functions & & some helper type functions
& fn coll? { fn coll? {
& "Returns true if a value is a collection: dict, list, tuple, or set." "Returns true if a value is a collection: dict, list, tuple, or set."
& (coll as :dict) -> true (coll as :dict) -> true
& (coll as :list) -> true (coll as :list) -> true
& (coll as :tuple) -> true (coll as :tuple) -> true
& & (coll as :set) -> true & (coll as :set) -> true
& (_) -> false (_) -> false
& } }
& fn ordered? { fn ordered? {
& "Returns true if a value is an indexed collection: list or tuple." "Returns true if a value is an indexed collection: list or tuple."
& (coll as :list) -> true (coll as :list) -> true
& (coll as :tuple) -> true (coll as :tuple) -> true
& (coll as :string) -> true (coll as :string) -> true
& (_) -> false (_) -> false
& } }
& fn assoc? { fn assoc? {
& "Returns true if a value is an associative collection: a dict or a pkg." "Returns true if a value is an associative collection: a dict or a pkg."
& (d as :dict) -> true (d as :dict) -> true
& (_) -> false (_) -> false
& } }
& &&& nil: working with nothing & &&& nil: working with nothing
& fn nil? { fn nil? {
& "Returns true if a value is nil." "Returns true if a value is nil."
& (nil) -> true (nil) -> true
& (_) -> false (_) -> false
& } }
& fn some? { fn some? {
& "Returns true if a value is not nil." "Returns true if a value is not nil."
& (nil) -> false (nil) -> false
& (_) -> true (_) -> true
& } }
& fn some { 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." "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 (nil, default) -> default
& (value, _) -> value (value, _) -> value
& } }
& ...and if two things are the same & ...and if two things are the same
fn eq? { fn eq? {
"Returns true if all arguments have the same value." "Returns true if all arguments have the same value."
& (x) -> true & (x) -> true
(x, y) -> base :eq? (x, y) (x, y) -> base :eq? (x, y)
& (x, y, ...zs) -> if eq? (x, y) (x, y, ...zs) -> if eq? (x, y)
& then loop (y, zs) with { then loop (y, zs) with {
& (a, []) -> eq? (a, x) (a, []) -> eq? (a, x)
& (a, [b, ...cs]) -> if eq? (a, x) (a, [b, ...cs]) -> if eq? (a, x)
& then recur (b, cs) then recur (b, cs)
& else false else false
& } }
& else false else false
} }
& &&& true & false: boolean logic (part the first) & &&& true & false: boolean logic (part the first)
& fn bool? { fn bool? {
& "Returns true if a value is of type :boolean." "Returns true if a value is of type :boolean."
& (false) -> true (false) -> true
& (true) -> true (true) -> true
& (_) -> false (_) -> false
& } }
& fn true? { fn true? {
& "Returns true if a value is boolean `true`. Useful to distinguish between `true` and anything else." "Returns true if a value is boolean `true`. Useful to distinguish between `true` and anything else."
& (true) -> true (true) -> true
& (_) -> false (_) -> false
& } }
& fn false? { fn false? {
& "Returns `true` if a value is `false`, otherwise returns `false`. Useful to distinguish between `false` and `nil`." "Returns `true` if a value is `false`, otherwise returns `false`. Useful to distinguish between `false` and `nil`."
& (false) -> true (false) -> true
& (_) -> false (_) -> false
& } }
& fn bool { fn bool {
& "Returns false if a value is nil or false, otherwise returns true." "Returns false if a value is nil or false, otherwise returns true."
& (nil) -> false (nil) -> false
& (false) -> false (false) -> false
& (_) -> true (_) -> true
& } }
& fn not { fn not {
& "Returns false if a value is truthy, true if a value is falsy." "Returns false if a value is truthy, true if a value is falsy."
& (nil) -> true (nil) -> true
& (false) -> true (false) -> true
& (_) -> false (_) -> false
& } }
& & tuples: not a lot you can do with them functionally & & tuples: not a lot you can do with them functionally
& fn tuple? { fn tuple? {
& "Returns true if a value is a tuple." "Returns true if a value is a tuple."
& (tuple as :tuple) -> true (tuple as :tuple) -> true
& (_) -> false (_) -> false
& } }
& &&& functions: getting things done & &&& functions: getting things done
& fn fn? { fn fn? {
& "Returns true if an argument is a function." "Returns true if an argument is a function."
& (f as :fn) -> true (f as :fn) -> true
& (_) -> false (_) -> false
& } }
& what we need for some very basic list manipulation & what we need for some very basic list manipulation
fn first { fn first {
@ -136,50 +136,50 @@ fn inc {
(x as :number) -> base :inc (x) (x as :number) -> base :inc (x)
} }
& fn dec { fn dec {
& "Decrements a number." "Decrements a number."
& (x as :number) -> base :dec (x) (x as :number) -> base :dec (x)
& } }
& fn count { fn count {
& "Returns the number of elements in a collection (including string)." "Returns the number of elements in a collection (including string)."
& (xs as :list) -> base :count (xs) (xs as :list) -> base :count (xs)
& (xs as :tuple) -> base :count (xs) (xs as :tuple) -> base :count (xs)
& (xs as :dict) -> base :count (xs) (xs as :dict) -> base :count (xs)
& (xs as :string) -> base :count (xs) (xs as :string) -> base :count (xs)
& & (xs as :set) -> base :count (xs) & (xs as :set) -> base :count (xs)
& } }
& fn empty? { fn empty? {
& "Returns true if something is empty. Otherwise returns false (including for things that can't logically be empty, like numbers)." "Returns true if something is empty. Otherwise returns false (including for things that can't logically be empty, like numbers)."
& ([]) -> true ([]) -> true
& (#{}) -> true (#{}) -> true
& & (s as :set) -> eq? (s, ${}) & (s as :set) -> eq? (s, ${})
& (()) -> true (()) -> true
& ("") -> true ("") -> true
& (_) -> false (_) -> false
& } }
& fn any? { fn any? {
& "Returns true if something is not empty, otherwise returns false (including for things that can't be logically full, like numbers)." "Returns true if something is not empty, otherwise returns false (including for things that can't be logically full, like numbers)."
& ([...]) -> true ([...]) -> true
& (#{...}) -> true (#{...}) -> true
& & (s as :set) -> not (empty? (s)) & (s as :set) -> not (empty? (s))
& ((...)) -> true ((...)) -> true
& (s as :string) -> not (empty? (s)) (s as :string) -> not (empty? (s))
& (_) -> false (_) -> false
& } }
& fn list? { fn list? {
& "Returns true if the value is a list." "Returns true if the value is a list."
& (l as :list) -> true (l as :list) -> true
& (_) -> false (_) -> false
& } }
& fn list { 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." "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) (x) -> base :list (x)
& } }
fn append { fn append {
"Adds an element to a list." "Adds an element to a list."
@ -203,43 +203,43 @@ fn fold {
} }
} }
& fn map { 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." "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, _) & (f as :fn) -> map (f, _)
& & (kw as :keyword) -> map (kw, _) & (kw as :keyword) -> map (kw, _)
& (f as :fn, xs) -> { (f as :fn, xs) -> {
& fn mapper (prev, curr) -> append (prev, f (curr)) fn mapper (prev, curr) -> append (prev, f (curr))
& fold (mapper, xs, []) fold (mapper, xs, [])
& } }
& & (kw as :keyword, xs) -> { & (kw as :keyword, xs) -> {
& & fn mapper (prev, curr) -> append (prev, kw (curr)) & fn mapper (prev, curr) -> append (prev, kw (curr))
& & fold (mapper, xs, []) & fold (mapper, xs, [])
& & } & }
& } }
& fn filter { 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]`." "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) -> filter (p?, _)
& (p? as :fn, xs) -> { (p? as :fn, xs) -> {
& fn filterer (filtered, x) -> if p? (x) fn filterer (filtered, x) -> if p? (x)
& then append (filtered, x) then append (filtered, x)
& else filtered else filtered
& fold (filterer, xs, []) fold (filterer, xs, [])
& } }
& } }
& fn keep { fn keep {
& "Takes a list and returns a new list with any `nil` values omitted." "Takes a list and returns a new list with any `nil` values omitted."
& (xs) -> filter (some?, xs) (xs) -> filter (some?, xs)
& } }
& fn concat { fn concat {
& "Combines two lists, strings, or sets." "Combines two lists, strings, or sets."
& (x as :string, y as :string) -> "{x}{y}" (x as :string, y as :string) -> "{x}{y}"
& (xs as :list, ys as :list) -> base :concat (xs, ys) (xs as :list, ys as :list) -> base :concat (xs, ys)
& & (xs as :set, ys as :set) -> base :concat (xs, ys) & (xs as :set, ys as :set) -> base :concat (xs, ys)
& (xs, ys, ...zs) -> fold (concat, zs, concat (xs, ys)) (xs, ys, ...zs) -> fold (concat, zs, concat (xs, ys))
& } }
fn contains? { fn contains? {
"Returns true if a set or list contains a value." "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 type
& coll? coll?
& ordered? ordered?
& assoc? assoc?
& nil? nil?
& some? some?
& some some
eq? eq?
& bool? bool?
& true? true?
& false? false?
& bool bool
& not not
& tuple? tuple?
& fn? fn?
first first
rest rest
& inc inc
& dec dec
& count count
& empty? empty?
& any? any?
& list? list?
& list list
& first first
& fold fold
& append append
& map map
& filter filter
& keep keep
& concat concat
& contains? contains?
add
} }

View File

@ -1,31 +1,15 @@
let test = 3 fn my_eq? {
(x, y, ...zs) -> if eq? (x, y)
let quux = loop ([1, 2]) with { then loop (y, zs) with {
([]) -> false (a, [b]) -> and (eq? (a, x), eq? (b, x))
([x]) -> eq? (x, test) (a, [b, ...cs]) -> if eq? (a, x)
([x, ...xs]) -> if eq? (x, test) then recur (b, cs)
then :yes else false
else recur (xs) }
else false
} }
my_eq? (1, 1, 3)
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]

View File

@ -310,6 +310,11 @@ impl<'a> Compiler<'a> {
self.stack_depth += 1; self.stack_depth += 1;
} }
fn duplicate(&mut self) {
self.emit_op(Op::Duplicate);
self.stack_depth += 1;
}
fn pop(&mut self) { fn pop(&mut self) {
self.emit_op(Op::Pop); self.emit_op(Op::Pop);
self.stack_depth -= 1; self.stack_depth -= 1;
@ -863,42 +868,44 @@ impl<'a> Compiler<'a> {
self.stack_depth -= 1; self.stack_depth -= 1;
} }
(Or, Arguments(args)) => { (Or, Arguments(args)) => {
let stack_depth = self.stack_depth;
let mut jump_idxes = vec![]; let mut jump_idxes = vec![];
if !args.is_empty() { 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.visit(arg);
self.emit_op(Op::Stash); self.duplicate();
jump_idxes.push(self.stub_jump(Op::JumpIfTrue)); jump_idxes.push(self.stub_jump(Op::JumpIfTrue));
self.pop();
} }
self.visit(last);
for idx in jump_idxes { 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 { } else {
self.emit_op(Op::False); self.emit_op(Op::False);
self.stack_depth += 1;
} }
self.stack_depth = stack_depth + 1;
} }
(And, Arguments(args)) => { (And, Arguments(args)) => {
let stack_depth = self.stack_depth;
let mut jump_idxes = vec![]; let mut jump_idxes = vec![];
if !args.is_empty() { 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.visit(arg);
self.emit_op(Op::Stash); self.duplicate();
jump_idxes.push(self.stub_jump(Op::JumpIfFalse)); jump_idxes.push(self.stub_jump(Op::JumpIfFalse));
self.pop();
} }
self.visit(last);
for idx in jump_idxes { 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 { } else {
self.emit_op(Op::True); self.emit_op(Op::True);
self.stack_depth += 1;
} }
self.stack_depth = stack_depth + 1;
} }
(Word(fn_name), Arguments(args)) => { (Word(fn_name), Arguments(args)) => {
self.report_depth_str(format!("calling function {fn_name}")); self.report_depth_str(format!("calling function {fn_name}"));

View File

@ -112,8 +112,8 @@ pub fn run(src: &'static str) {
// in any event, the AST should live forever // in any event, the AST should live forever
let parsed: &'static Spanned<Ast> = Box::leak(Box::new(parse_result.unwrap())); let parsed: &'static Spanned<Ast> = Box::leak(Box::new(parse_result.unwrap()));
let prelude = prelude(); // let prelude = prelude();
// let prelude = imbl::HashMap::new(); let prelude = imbl::HashMap::new();
let mut validator = Validator::new(&parsed.0, &parsed.1, "user input", src, prelude.clone()); let mut validator = Validator::new(&parsed.0, &parsed.1, "user input", src, prelude.clone());
validator.validate(); validator.validate();

View File

@ -284,7 +284,7 @@ impl<'a> Validator<'a> {
// check arity against fn info if first term is word and second term is args // check arity against fn info if first term is word and second term is args
Synthetic(first, second, rest) => { Synthetic(first, second, rest) => {
match (&first.0, &second.0) { 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()) self.visit(second.as_ref())
} }
(Ast::Word(_), Ast::Keyword(_)) => self.visit(first.as_ref()), (Ast::Word(_), Ast::Keyword(_)) => self.visit(first.as_ref()),