Compare commits

..

No commits in common. "9d798e5e586a99fbe8787d6d1160b72919e304df" and "e4a948ba947da01b95e329cd797f3e13975faf08" have entirely different histories.

4 changed files with 261 additions and 292 deletions

View File

@ -1,133 +1,133 @@
& 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, [b]) -> and (eq? (a, x), eq? (b, 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 {
"Retrieves the first element of an ordered collection--a tuple or a list. If the collection is empty, returns nil." "Retrieves the first element of an ordered collection--a tuple or a list. If the collection is empty, returns nil."
([]) -> nil ([]) -> nil
(()) -> nil & (()) -> nil
& ("") -> nil & ("") -> nil
(xs as :list) -> base :first (xs) (xs as :list) -> base :first (xs)
(xs as :tuple) -> base :first (xs) & (xs as :tuple) -> base :first (xs)
& (str as :string) -> base :slice (str, 0, 1) & (str as :string) -> base :slice (str, 0, 1)
} }
fn rest { fn rest {
"Returns all but the first element of a list or tuple, as a list." "Returns all but the first element of a list or tuple, as a list."
([]) -> [] ([]) -> []
(()) -> () & (()) -> ()
(xs as :list) -> base :rest (xs) (xs as :list) -> base :rest (xs)
(xs as :tuple) -> base :rest (xs) & (xs as :tuple) -> base :rest (xs)
& (str as :string) -> base :rest (str) & (str as :string) -> base :rest (str)
} }
@ -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,58 +203,43 @@ fn fold {
} }
} }
fn foldr { & fn map {
"Folds a list, right-associatively." & "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, []) -> [] & & (f as :fn) -> map (f, _)
(f as :fn, xs as :list) -> foldr(f, xs, f ()) & & (kw as :keyword) -> map (kw, _)
(f as :fn, [], root) -> [] & (f as :fn, xs) -> {
(f as :fn, xs as :list, root) -> loop (root, first (xs), rest (xs)) with { & fn mapper (prev, curr) -> append (prev, f (curr))
(prev, curr, []) -> f (curr, prev) & fold (mapper, xs, [])
(prev, curr, remaining) -> recur ( & }
f (curr, prev) & & (kw as :keyword, xs) -> {
first (remaining) & & fn mapper (prev, curr) -> append (prev, kw (curr))
rest (remaining) & & fold (mapper, xs, [])
) & & }
} & }
}
fn map { & fn filter {
"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." & "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]`."
(f as :fn) -> map (f, _) & (p? as :fn) -> filter (p?, _)
(kw as :keyword) -> map (kw, _) & (p? as :fn, xs) -> {
(f as :fn, xs) -> { & fn filterer (filtered, x) -> if p? (x)
fn mapper (prev, curr) -> append (prev, f (curr)) & then append (filtered, x)
fold (mapper, xs, []) & else filtered
} & fold (filterer, xs, [])
(kw as :keyword, xs) -> { & }
fn mapper (prev, curr) -> append (prev, kw (curr)) & }
fold (mapper, xs, [])
}
}
fn filter { & fn keep {
"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 returns a new list with any `nil` values omitted."
(p? as :fn) -> filter (p?, _) & (xs) -> filter (some?, xs)
(p? as :fn, xs) -> { & }
fn filterer (filtered, x) -> if p? (x)
then append (filtered, x)
else filtered
fold (filterer, xs, [])
}
}
fn keep { & fn concat {
"Takes a list and returns a new list with any `nil` values omitted." & "Combines two lists, strings, or sets."
(xs) -> filter (some?, xs) & (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)
fn concat { & (xs, ys, ...zs) -> fold (concat, zs, concat (xs, ys))
"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? { fn contains? {
"Returns true if a set or list contains a value." "Returns true if a set or list contains a value."
@ -267,92 +252,44 @@ fn contains? {
} }
} }
fn print! { fn add {
"Sends a text representation of Ludus values to the console." () -> 0
(...args) -> { (x) -> x
base :print! (args) (x, y) -> base :add (x, y)
:ok
}
}
fn show {
"Returns a text representation of a Ludus value as a string."
(x) -> base :show (x)
}
fn report! {
"Prints a value, then returns it."
(x) -> {
print! (x)
x
}
(msg as :string, x) -> {
print! (concat ("{msg} ", show (x)))
x
}
}
fn doc! {
"Prints the documentation of a function to the console."
(f as :fn) -> do f > base :doc! > print!
(_) -> :none
}
&&& strings: harder than they look!
fn string? {
"Returns true if a value is a string."
(x as :string) -> true
(_) -> false
}
fn string {
"Converts a value to a string by using `show`. If it is a string, returns it unharmed. Use this to build up strings of different kinds of values."
(x as :string) -> x
(x) -> show (x)
(x, ...xs) -> loop (string (x), xs) with {
(out, [y]) -> concat (out, show (y))
(out, [y, ...ys]) -> recur (concat (out, show (y)), ys)
}
} }
#{ #{
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
foldr & append
append & map
map & filter
filter & keep
keep & concat
concat & contains?
contains? add
print!
show
report!
doc!
string
string?
} }

View File

@ -1,4 +1,31 @@
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)
}
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,11 +310,6 @@ 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;
@ -433,6 +428,7 @@ impl<'a> Compiler<'a> {
self.tail_pos = false; self.tail_pos = false;
// increase the scope // increase the scope
self.enter_scope(); self.enter_scope();
// self.scope_depth += 1;
// stash the stack depth // stash the stack depth
let stack_depth = self.stack_depth; let stack_depth = self.stack_depth;
// evaluate all the lines but the last // evaluate all the lines but the last
@ -453,6 +449,7 @@ impl<'a> Compiler<'a> {
// we do this by pretending it's a binding // we do this by pretending it's a binding
(Let(patt, expr), _) => { (Let(patt, expr), _) => {
// self.match_depth = 0; // self.match_depth = 0;
// self.emit_op(Op::ResetMatch);
self.visit(expr); self.visit(expr);
let expr_pos = self.stack_depth - 1; let expr_pos = self.stack_depth - 1;
self.report_ast("let binding: matching".to_string(), patt); self.report_ast("let binding: matching".to_string(), patt);
@ -470,20 +467,28 @@ impl<'a> Compiler<'a> {
} }
} }
// we've made a new value, so increase the stack level in the compiler
self.stack_depth += 1;
// store the value in the return register // store the value in the return register
// self.emit_op(Op::Store);
self.store(); self.store();
// reset the scope // reset the scope
self.leave_scope(); 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;
// }
// }
// reset the stack // reset the stack
self.report_depth("leaving block before pop"); self.pop_n(self.stack_depth - stack_depth - 1);
self.msg(format!(
"popping back from {} to {}",
self.stack_depth, stack_depth,
));
self.pop_n(self.stack_depth - stack_depth);
// load the value from the return register // load the value from the return register
self.load(); self.load();
// self.emit_op(Op::Load);
} }
If(cond, then, r#else) => { If(cond, then, r#else) => {
let tail_pos = self.tail_pos; let tail_pos = self.tail_pos;
@ -858,44 +863,42 @@ 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() {
let mut args = args.iter().rev(); for arg in args {
let last = args.next().unwrap();
for arg in args.rev() {
self.visit(arg); self.visit(arg);
self.duplicate(); self.emit_op(Op::Stash);
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 - 3); self.patch_jump(idx, self.len() - idx);
} }
// 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() {
let mut args = args.iter().rev(); for arg in args {
let last = args.next().unwrap();
for arg in args.rev() {
self.visit(arg); self.visit(arg);
self.duplicate(); self.emit_op(Op::Stash);
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 - 3); self.patch_jump(idx, self.len() - idx);
} }
// 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}"));
@ -948,12 +951,13 @@ impl<'a> Compiler<'a> {
self.stack_depth -= 1; self.stack_depth -= 1;
} }
Arguments(args) => { Arguments(args) => {
self.store(); self.emit_op(Op::Stash);
self.pop();
let arity = args.len(); let arity = args.len();
for arg in args { for arg in args {
self.visit(arg); self.visit(arg);
} }
self.load(); self.emit_op(Op::Load);
if tail_pos && i == num_rest_terms - 1 { if tail_pos && i == num_rest_terms - 1 {
self.emit_op(Op::TailCall) self.emit_op(Op::TailCall)
} else { } else {
@ -1145,6 +1149,7 @@ impl<'a> Compiler<'a> {
} }
compiler.tail_pos = true; compiler.tail_pos = true;
compiler.visit(clause_body); compiler.visit(clause_body);
// compiler.emit_op(Op::Store);
compiler.store(); compiler.store();
compiler.scope_depth -= 1; compiler.scope_depth -= 1;
while let Some(binding) = compiler.bindings.last() { while let Some(binding) = compiler.bindings.last() {
@ -1386,6 +1391,7 @@ impl<'a> Compiler<'a> {
let allocated = Value::String(Rc::new(str.clone())); let allocated = Value::String(Rc::new(str.clone()));
self.emit_constant(allocated); self.emit_constant(allocated);
self.emit_op(Op::ConcatStrings); self.emit_op(Op::ConcatStrings);
self.stack_depth -= 1;
} }
StringPart::Word(word) => { StringPart::Word(word) => {
self.resolve_binding(word); self.resolve_binding(word);
@ -1393,7 +1399,6 @@ impl<'a> Compiler<'a> {
self.emit_op(Op::ConcatStrings); self.emit_op(Op::ConcatStrings);
} }
} }
self.stack_depth -= 1;
} }
} }
Do(terms) => { Do(terms) => {

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::Arguments(_)) => { (Ast::And, Ast::Arguments(_)) | (Ast::Or, Ast::Tuple(_)) => {
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()),