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
& 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?
}

View File

@ -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)

View File

@ -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}"));

View File

@ -112,8 +112,8 @@ pub fn run(src: &'static str) {
// in any event, the AST should live forever
let parsed: &'static Spanned<Ast> = 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();

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
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()),