keep grinding on loop/recur/jif stack mismatch; add ast->code printer
This commit is contained in:
parent
398b140d79
commit
2f60de79a2
|
@ -1,4 +1,4 @@
|
|||
& & the very base: know something's type
|
||||
& the very base: know something's type
|
||||
& fn type {
|
||||
& "Returns a keyword representing the type of the value passed in."
|
||||
& (x) -> base :type (x)
|
||||
|
@ -48,11 +48,11 @@
|
|||
& (value, _) -> value
|
||||
& }
|
||||
|
||||
& & ...and if two things are the same
|
||||
& fn eq? {
|
||||
& "Returns true if all arguments have the same 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) -> base :eq? (x, y)
|
||||
& (x, y, ...zs) -> if eq? (x, y)
|
||||
& then loop (y, zs) with {
|
||||
& (a, []) -> eq? (a, x)
|
||||
|
@ -61,7 +61,7 @@
|
|||
& else false
|
||||
& }
|
||||
& else false
|
||||
& }
|
||||
}
|
||||
|
||||
& &&& true & false: boolean logic (part the first)
|
||||
& fn bool? {
|
||||
|
@ -115,20 +115,20 @@
|
|||
fn first {
|
||||
"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
|
||||
(xs as :list) -> base :first (xs)
|
||||
(xs as :tuple) -> base :first (xs)
|
||||
(str as :string) -> base :slice (str, 0, 1)
|
||||
& (xs as :tuple) -> base :first (xs)
|
||||
& (str as :string) -> base :slice (str, 0, 1)
|
||||
}
|
||||
|
||||
fn rest {
|
||||
"Returns all but the first element of a list or tuple, as a list."
|
||||
([]) -> []
|
||||
(()) -> ()
|
||||
& (()) -> ()
|
||||
(xs as :list) -> base :rest (xs)
|
||||
(xs as :tuple) -> base :rest (xs)
|
||||
(str as :string) -> base :rest (str)
|
||||
& (xs as :tuple) -> base :rest (xs)
|
||||
& (str as :string) -> base :rest (str)
|
||||
}
|
||||
|
||||
fn inc {
|
||||
|
@ -203,18 +203,53 @@ 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))
|
||||
& 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 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 contains? {
|
||||
"Returns true if a set or list contains a value."
|
||||
& (value, s as :set) -> bool (base :get (s, value))
|
||||
(value, l as :list) -> loop (l) with {
|
||||
([]) -> false
|
||||
([...xs]) -> if eq? (first(xs), value)
|
||||
then true
|
||||
else recur (rest (xs))
|
||||
}
|
||||
}
|
||||
|
||||
fn add {
|
||||
|
@ -231,7 +266,7 @@ fn add {
|
|||
& nil?
|
||||
& some?
|
||||
& some
|
||||
& eq?
|
||||
eq?
|
||||
& bool?
|
||||
& true?
|
||||
& false?
|
||||
|
@ -241,7 +276,7 @@ fn add {
|
|||
& fn?
|
||||
first
|
||||
rest
|
||||
inc
|
||||
& inc
|
||||
& dec
|
||||
& count
|
||||
& empty?
|
||||
|
@ -249,8 +284,12 @@ fn add {
|
|||
& list?
|
||||
& list
|
||||
& first
|
||||
fold
|
||||
append
|
||||
map
|
||||
& fold
|
||||
& append
|
||||
& map
|
||||
& filter
|
||||
& keep
|
||||
& concat
|
||||
& contains?
|
||||
add
|
||||
}
|
||||
|
|
|
@ -434,3 +434,45 @@ And the stack positions on stuff are also totally weird.
|
|||
One thing: the way we are now resolving upvalues means that nothing should ever reach back further in the stack than the stack base in the vm.
|
||||
So now we'll do all bindings relative to the stack base.
|
||||
UGH.
|
||||
|
||||
***
|
||||
|
||||
So now I have a bug that I'm dying to figure out.
|
||||
But it's time to go to bed.
|
||||
|
||||
When `recur` is in the alternatve branch of an if, it thinks there's one more value on the stack than there really is.
|
||||
To the best of my ability to tell, `if` has proper stack behaviour.
|
||||
So the question is what's happening in the interaction between the `jump_if_false` instruction and `recur`.
|
||||
|
||||
To wit, the following code works just fine:
|
||||
```
|
||||
fn not {
|
||||
(false) -> true
|
||||
(nil) -> true
|
||||
(_) -> false
|
||||
}
|
||||
|
||||
let test = 2
|
||||
loop ([1, 2, 3]) with {
|
||||
([]) -> false
|
||||
([x]) -> eq? (x, test)
|
||||
([x, ...xs]) -> if not(eq? (x, test))
|
||||
then recur (xs)
|
||||
else true
|
||||
}
|
||||
```
|
||||
|
||||
But the following code does not:
|
||||
```
|
||||
let test = 2
|
||||
loop ([1, 2, 3]) with {
|
||||
([]) -> false
|
||||
([x]) -> eq? (x, test)
|
||||
([x, ...xs]) -> if eq? (x, test)
|
||||
then true
|
||||
else recur (xs)
|
||||
}
|
||||
```
|
||||
|
||||
Meanwhile, other `loop`/`recur` forms seem to work okay for me.
|
||||
So: ugh.
|
||||
|
|
|
@ -22,7 +22,7 @@ pub fn eq(x: &Value, y: &Value) -> Value {
|
|||
pub fn add(x: &Value, y: &Value) -> Value {
|
||||
match (x, y) {
|
||||
(Value::Number(x), Value::Number(y)) => Value::Number(x + y),
|
||||
_ => unreachable!("internal Ludus error"),
|
||||
_ => unreachable!("internal Ludus error: wrong arguments to base add: {x}, {y}"),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -259,6 +259,7 @@ pub struct Chunk {
|
|||
pub keywords: Vec<&'static str>,
|
||||
pub string_patterns: Vec<StrPattern>,
|
||||
pub env: imbl::HashMap<&'static str, Value>,
|
||||
pub msgs: Vec<String>,
|
||||
}
|
||||
|
||||
impl Chunk {
|
||||
|
@ -272,7 +273,7 @@ impl Chunk {
|
|||
| Mult | Div | Unbox | BoxStore | Assert | Get | At | Not | Panic | EmptyString
|
||||
| ConcatStrings | Stringify | MatchType | Return | Match | Print | AppendList
|
||||
| ConcatList | PushList | PushDict | AppendDict | ConcatDict | Nothing | PushGlobal
|
||||
| SetUpvalue | Msg => {
|
||||
| SetUpvalue => {
|
||||
println!("{i:04}: {op}")
|
||||
}
|
||||
Constant | MatchConstant => {
|
||||
|
@ -283,6 +284,12 @@ impl Chunk {
|
|||
println!("{i:04}: {:16} {idx:05}: {value}", op.to_string());
|
||||
*i += 2;
|
||||
}
|
||||
Msg => {
|
||||
let msg_idx = self.bytecode[*i + 1];
|
||||
let msg = &self.msgs[msg_idx as usize];
|
||||
println!("{i:04}: {msg}");
|
||||
*i += 1;
|
||||
}
|
||||
PushBinding | MatchTuple | MatchSplattedTuple | LoadSplattedTuple | MatchList
|
||||
| MatchSplattedList | LoadSplattedList | MatchDict | MatchSplattedDict
|
||||
| DropDictEntry | LoadDictValue | PushTuple | PushBox | MatchDepth | PopN | StoreN
|
||||
|
@ -370,6 +377,7 @@ pub struct Compiler<'a> {
|
|||
pub upvalues: Vec<&'static str>,
|
||||
loop_info: Vec<LoopInfo>,
|
||||
tail_pos: bool,
|
||||
debug: bool,
|
||||
}
|
||||
|
||||
fn is_binding(expr: &Spanned<Ast>) -> bool {
|
||||
|
@ -393,6 +401,7 @@ impl<'a> Compiler<'a> {
|
|||
src: &'static str,
|
||||
enclosing: Option<&'a Compiler>,
|
||||
env: imbl::HashMap<&'static str, Value>,
|
||||
debug: bool,
|
||||
) -> Compiler<'a> {
|
||||
let chunk = Chunk {
|
||||
constants: vec![],
|
||||
|
@ -400,6 +409,7 @@ impl<'a> Compiler<'a> {
|
|||
keywords: vec![],
|
||||
string_patterns: vec![],
|
||||
env,
|
||||
msgs: vec![],
|
||||
};
|
||||
Compiler {
|
||||
chunk,
|
||||
|
@ -417,6 +427,7 @@ impl<'a> Compiler<'a> {
|
|||
src,
|
||||
name,
|
||||
tail_pos: false,
|
||||
debug,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -512,13 +523,14 @@ impl<'a> Compiler<'a> {
|
|||
}
|
||||
|
||||
pub fn bind(&mut self, name: &'static str) {
|
||||
self.msg(format!("binding `{name}` in {}", self.name));
|
||||
println!("stack: {}; match: {}", self.stack_depth, self.match_depth);
|
||||
let binding = Binding {
|
||||
name,
|
||||
depth: self.scope_depth,
|
||||
stack_pos: self.stack_depth - self.match_depth - 1,
|
||||
};
|
||||
println!("{:?}", binding);
|
||||
println!("stack: {}; match: {}", self.stack_depth, self.match_depth);
|
||||
self.bindings.push(binding);
|
||||
}
|
||||
|
||||
|
@ -536,6 +548,7 @@ impl<'a> Compiler<'a> {
|
|||
}
|
||||
|
||||
fn resolve_binding(&mut self, name: &'static str) {
|
||||
self.msg(format!("resolving binding `{name}` in {}", self.name));
|
||||
if let Some(pos) = self.resolve_local(name) {
|
||||
self.emit_op(Op::PushBinding);
|
||||
self.emit_byte(pos);
|
||||
|
@ -598,10 +611,17 @@ impl<'a> Compiler<'a> {
|
|||
}
|
||||
|
||||
fn msg(&mut self, str: String) {
|
||||
let leaked = Box::leak(str.into_boxed_str());
|
||||
self.emit_constant(Value::Interned(leaked));
|
||||
self.emit_op(Op::Msg);
|
||||
self.stack_depth -= 1;
|
||||
self.emit_byte(self.chunk.msgs.len());
|
||||
self.chunk.msgs.push(str);
|
||||
}
|
||||
|
||||
fn report_depth(&mut self, label: &'static str) {
|
||||
self.msg(format!("***{label} stack depth: {}", self.stack_depth));
|
||||
}
|
||||
|
||||
fn report_depth_str(&mut self, label: String) {
|
||||
self.msg(format!("***{label} stack depth: {}", self.stack_depth));
|
||||
}
|
||||
|
||||
pub fn compile(&mut self) {
|
||||
|
@ -686,13 +706,17 @@ impl<'a> Compiler<'a> {
|
|||
let tail_pos = self.tail_pos;
|
||||
self.tail_pos = false;
|
||||
self.visit(cond);
|
||||
self.report_depth("after condition");
|
||||
let jif_idx = self.stub_jump(Op::JumpIfFalse);
|
||||
self.stack_depth -= 1;
|
||||
self.tail_pos = tail_pos;
|
||||
self.visit(then);
|
||||
self.report_depth("after consequent");
|
||||
let jump_idx = self.stub_jump(Op::Jump);
|
||||
self.visit(r#else);
|
||||
self.stack_depth -= 1;
|
||||
self.visit(r#else);
|
||||
self.report_depth("after alternative");
|
||||
// self.stack_depth += 1;
|
||||
let end_idx = self.len();
|
||||
let jif_offset = jump_idx - jif_idx;
|
||||
let jump_offset = end_idx - jump_idx - 3;
|
||||
|
@ -1031,11 +1055,13 @@ impl<'a> Compiler<'a> {
|
|||
let tail_pos = self.tail_pos;
|
||||
self.tail_pos = false;
|
||||
match (&first.0, &second.0) {
|
||||
(Word(_), Keyword(_)) => {
|
||||
(Word(name), Keyword(key)) => {
|
||||
self.report_depth_str(format!("accessing keyword: {name} :{key}"));
|
||||
self.visit(first);
|
||||
self.visit(second);
|
||||
self.emit_op(Op::GetKey);
|
||||
self.stack_depth -= 1;
|
||||
self.report_depth("after keyword access");
|
||||
}
|
||||
(Keyword(_), Arguments(args)) => {
|
||||
self.visit(&args[0]);
|
||||
|
@ -1080,6 +1106,7 @@ impl<'a> Compiler<'a> {
|
|||
self.stack_depth = stack_depth + 1;
|
||||
}
|
||||
(Word(fn_name), Arguments(args)) => {
|
||||
self.report_depth_str(format!("calling function {fn_name}"));
|
||||
if has_placeholder(args) {
|
||||
let arity = args.len();
|
||||
for arg in args {
|
||||
|
@ -1105,6 +1132,7 @@ impl<'a> Compiler<'a> {
|
|||
}
|
||||
self.resolve_binding(fn_name);
|
||||
// if we're in tail position AND there aren't any rest args, this should be a tail call (I think)
|
||||
self.report_depth_str(format!("after {arity} args"));
|
||||
if rest.is_empty() && tail_pos {
|
||||
self.emit_op(Op::TailCall);
|
||||
} else {
|
||||
|
@ -1275,6 +1303,7 @@ impl<'a> Compiler<'a> {
|
|||
self.src,
|
||||
Some(self),
|
||||
self.chunk.env.clone(),
|
||||
self.debug,
|
||||
);
|
||||
compiler.emit_op(Op::ResetMatch);
|
||||
compilers.insert(arity, compiler);
|
||||
|
@ -1347,7 +1376,7 @@ impl<'a> Compiler<'a> {
|
|||
for (arity, mut compiler) in compilers {
|
||||
compiler.emit_op(Op::PanicNoMatch);
|
||||
let chunk = compiler.chunk;
|
||||
if crate::DEBUG_COMPILE {
|
||||
if self.debug {
|
||||
println!("=== function chuncktion: {name}/{arity} ===");
|
||||
chunk.dissasemble();
|
||||
}
|
||||
|
@ -1444,18 +1473,27 @@ impl<'a> Compiler<'a> {
|
|||
self.visit(member);
|
||||
}
|
||||
self.msg(format!(
|
||||
"entering loop with stack depth of {}",
|
||||
"***entering loop with stack depth of {}",
|
||||
self.stack_depth
|
||||
));
|
||||
self.emit_op(Op::StoreN);
|
||||
self.emit_byte(members.len());
|
||||
let arity = members.len();
|
||||
// self.pop();
|
||||
let stack_depth = self.stack_depth;
|
||||
self.msg(format!(
|
||||
"***after store, stack depth is now {}",
|
||||
self.stack_depth
|
||||
));
|
||||
//then, save the beginning of the loop
|
||||
self.emit_op(Op::Load);
|
||||
self.enter_loop();
|
||||
// self.stack_depth += arity;
|
||||
//next, compile each clause:
|
||||
self.msg(format!(
|
||||
"***after load, stack depth is now {}",
|
||||
self.stack_depth
|
||||
));
|
||||
let mut clauses = clauses.iter();
|
||||
let mut jump_idxes = vec![];
|
||||
while let Some((Ast::MatchClause(pattern, guard, body), _)) = clauses.next() {
|
||||
|
@ -1482,9 +1520,18 @@ impl<'a> Compiler<'a> {
|
|||
self.stack_depth -= 1;
|
||||
}
|
||||
self.tail_pos = tail_pos;
|
||||
self.msg(format!(
|
||||
"***before visiting body, the stack depth is {}",
|
||||
self.stack_depth
|
||||
));
|
||||
self.visit(body);
|
||||
self.msg(format!(
|
||||
"***after visiting loop body, the stack depth is {}",
|
||||
self.stack_depth
|
||||
));
|
||||
self.emit_op(Op::Store);
|
||||
// self.scope_depth -= 1;
|
||||
self.scope_depth -= 1;
|
||||
self.msg("releasing loop bindings".to_string());
|
||||
while let Some(binding) = self.bindings.last() {
|
||||
if binding.depth > self.scope_depth {
|
||||
self.bindings.pop();
|
||||
|
@ -1492,6 +1539,9 @@ impl<'a> Compiler<'a> {
|
|||
break;
|
||||
}
|
||||
}
|
||||
self.msg("resetting the stack after loop".to_string());
|
||||
// self.emit_op(Op::PopN);
|
||||
// self.emit_byte(self.stack_depth - stack_depth);
|
||||
while self.stack_depth > stack_depth {
|
||||
self.pop();
|
||||
}
|
||||
|
@ -1511,6 +1561,11 @@ impl<'a> Compiler<'a> {
|
|||
self.leave_loop();
|
||||
}
|
||||
Recur(args) => {
|
||||
// self.emit_op(Op::Nothing);
|
||||
self.msg(format!(
|
||||
"before visiting recur args the compiler thinks the stack depth is {}",
|
||||
self.stack_depth
|
||||
));
|
||||
let tail_pos = self.tail_pos;
|
||||
self.tail_pos = false;
|
||||
let mut argnum = 0;
|
||||
|
@ -1519,6 +1574,10 @@ impl<'a> Compiler<'a> {
|
|||
argnum += 1;
|
||||
self.visit(arg);
|
||||
}
|
||||
self.msg(format!(
|
||||
"after visiting recur args the compiler thinks the stack depth is {}",
|
||||
self.stack_depth,
|
||||
));
|
||||
self.emit_op(Op::StoreN);
|
||||
self.emit_byte(args.len());
|
||||
self.emit_op(Op::PopN);
|
||||
|
|
59
src/main.rs
59
src/main.rs
|
@ -1,9 +1,12 @@
|
|||
use chumsky::{input::Stream, prelude::*};
|
||||
use imbl::HashMap;
|
||||
use std::env;
|
||||
use std::fs;
|
||||
|
||||
const DEBUG_COMPILE: bool = true;
|
||||
const DEBUG_RUN: bool = true;
|
||||
const DEBUG_SCRIPT_COMPILE: bool = true;
|
||||
const DEBUG_SCRIPT_RUN: bool = true;
|
||||
const DEBUG_PRELUDE_COMPILE: bool = false;
|
||||
const DEBUG_PRELUDE_RUN: bool = false;
|
||||
|
||||
mod base;
|
||||
|
||||
|
@ -63,13 +66,20 @@ pub fn prelude() -> HashMap<&'static str, Value> {
|
|||
}
|
||||
|
||||
let parsed: &'static Spanned<Ast> = Box::leak(Box::new(parsed));
|
||||
let mut compiler = Compiler::new(parsed, "prelude", PRELUDE, None, HashMap::new());
|
||||
let mut compiler = Compiler::new(
|
||||
parsed,
|
||||
"prelude",
|
||||
PRELUDE,
|
||||
None,
|
||||
HashMap::new(),
|
||||
DEBUG_PRELUDE_COMPILE,
|
||||
);
|
||||
compiler.emit_constant(base);
|
||||
compiler.bind("base");
|
||||
compiler.compile();
|
||||
|
||||
let chunk = compiler.chunk;
|
||||
let mut vm = Vm::new(chunk);
|
||||
let mut vm = Vm::new(chunk, DEBUG_PRELUDE_RUN);
|
||||
let prelude = vm.run().clone().unwrap();
|
||||
match prelude {
|
||||
Value::Dict(hashmap) => *hashmap,
|
||||
|
@ -111,26 +121,26 @@ pub fn run(src: &'static str) {
|
|||
return;
|
||||
}
|
||||
|
||||
let mut compiler = Compiler::new(parsed, "test", src, None, prelude);
|
||||
let mut compiler = Compiler::new(parsed, "test", src, None, prelude, DEBUG_SCRIPT_COMPILE);
|
||||
// let base = base::make_base();
|
||||
// compiler.emit_constant(base);
|
||||
// compiler.bind("base");
|
||||
|
||||
compiler.compile();
|
||||
if DEBUG_COMPILE {
|
||||
if DEBUG_SCRIPT_COMPILE {
|
||||
println!("=== source code ===");
|
||||
println!("{src}");
|
||||
compiler.disassemble();
|
||||
println!("\n\n")
|
||||
}
|
||||
|
||||
if DEBUG_RUN {
|
||||
if DEBUG_SCRIPT_RUN {
|
||||
println!("=== vm run: test ===");
|
||||
}
|
||||
|
||||
let vm_chunk = compiler.chunk;
|
||||
|
||||
let mut vm = Vm::new(vm_chunk);
|
||||
let mut vm = Vm::new(vm_chunk, DEBUG_SCRIPT_RUN);
|
||||
let result = vm.run();
|
||||
let output = match result {
|
||||
Ok(val) => val.show(),
|
||||
|
@ -140,10 +150,35 @@ pub fn run(src: &'static str) {
|
|||
println!("{output}");
|
||||
}
|
||||
|
||||
pub fn ld_fmt(src: &'static str) -> Result<String, String> {
|
||||
let (tokens, lex_errs) = lexer().parse(src).into_output_errors();
|
||||
if !lex_errs.is_empty() {
|
||||
println!("{:?}", lex_errs);
|
||||
return Err(format!("{:?}", lex_errs));
|
||||
}
|
||||
|
||||
let tokens = tokens.unwrap();
|
||||
|
||||
let (parse_result, parse_errors) = parser()
|
||||
.parse(Stream::from_iter(tokens).map((0..src.len()).into(), |(t, s)| (t, s)))
|
||||
.into_output_errors();
|
||||
if !parse_errors.is_empty() {
|
||||
return Err(format!("{:?}", parse_errors));
|
||||
}
|
||||
|
||||
// ::sigh:: The AST should be 'static
|
||||
// This simplifies lifetimes, and
|
||||
// in any event, the AST should live forever
|
||||
let parsed: &'static Spanned<Ast> = Box::leak(Box::new(parse_result.unwrap()));
|
||||
|
||||
Ok(parsed.0.show())
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
env::set_var("RUST_BACKTRACE", "1");
|
||||
let src = r#"
|
||||
map(inc, [1, 2, 3])
|
||||
"#;
|
||||
run(src);
|
||||
let src: &'static str = fs::read_to_string("sandbox.ld").unwrap().leak();
|
||||
match ld_fmt(src) {
|
||||
Ok(src) => println!("{}", src),
|
||||
Err(msg) => println!("Could not format source with errors:\n{}", msg),
|
||||
}
|
||||
}
|
||||
|
|
158
src/parser.rs
158
src/parser.rs
|
@ -120,6 +120,164 @@ pub enum Ast {
|
|||
DictPattern(Vec<Spanned<Self>>),
|
||||
}
|
||||
|
||||
impl Ast {
|
||||
pub fn show(&self) -> String {
|
||||
use Ast::*;
|
||||
match self {
|
||||
And => "and".to_string(),
|
||||
Or => "or".to_string(),
|
||||
Error => unreachable!(),
|
||||
Nil | NilPattern => "nil".to_string(),
|
||||
String(s) | StringPattern(s) => format!("\"{s}\""),
|
||||
Interpolated(strs) | InterpolatedPattern(strs, _) => {
|
||||
let mut out = "".to_string();
|
||||
out = format!("\"{out}");
|
||||
for (part, _) in strs {
|
||||
out = format!("{out}{part}");
|
||||
}
|
||||
format!("{out}\"")
|
||||
}
|
||||
Boolean(b) | BooleanPattern(b) => b.to_string(),
|
||||
Number(n) | NumberPattern(n) => n.to_string(),
|
||||
Keyword(k) | KeywordPattern(k) => format!(":{k}"),
|
||||
Word(w) | WordPattern(w) => w.to_string(),
|
||||
Block(lines) => {
|
||||
let mut out = "{\n".to_string();
|
||||
for (line, _) in lines {
|
||||
out = format!("{out}\n {}", line.show());
|
||||
}
|
||||
format!("{out}\n}}")
|
||||
}
|
||||
If(cond, then, r#else) => format!(
|
||||
"if {}\n then {}\n else {}",
|
||||
cond.0.show(),
|
||||
then.0.show(),
|
||||
r#else.0.show()
|
||||
),
|
||||
Let(pattern, expression) => {
|
||||
format!("let {} = {}", pattern.0.show(), expression.0.show())
|
||||
}
|
||||
Dict(entries) | DictPattern(entries) => {
|
||||
format!(
|
||||
"#{{{}}}",
|
||||
entries
|
||||
.iter()
|
||||
.map(|(pair, _)| pair.show())
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ")
|
||||
)
|
||||
}
|
||||
List(members) | ListPattern(members) => format!(
|
||||
"[{}]",
|
||||
members
|
||||
.iter()
|
||||
.map(|(member, _)| member.show())
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ")
|
||||
),
|
||||
Arguments(members) => format!(
|
||||
"({})",
|
||||
members
|
||||
.iter()
|
||||
.map(|(member, _)| member.show())
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ")
|
||||
),
|
||||
Tuple(members) | TuplePattern(members) => format!(
|
||||
"({})",
|
||||
members
|
||||
.iter()
|
||||
.map(|(member, _)| member.show())
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ")
|
||||
),
|
||||
Synthetic(root, first, rest) => format!(
|
||||
"{} {} {}",
|
||||
root.0.show(),
|
||||
first.0.show(),
|
||||
rest.iter()
|
||||
.map(|(term, _)| term.show())
|
||||
.collect::<Vec<_>>()
|
||||
.join(" ")
|
||||
),
|
||||
When(clauses) => format!(
|
||||
"when {{\n {}\n}}",
|
||||
clauses
|
||||
.iter()
|
||||
.map(|(clause, _)| clause.show())
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n ")
|
||||
),
|
||||
Placeholder | PlaceholderPattern => "_".to_string(),
|
||||
LBox(name, rhs) => format!("box {name} = {}", rhs.0.show()),
|
||||
Match(scrutinee, clauses) => format!(
|
||||
"match {} with {{\n {}\n}}",
|
||||
scrutinee.0.show(),
|
||||
clauses
|
||||
.iter()
|
||||
.map(|(clause, _)| clause.show())
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n ")
|
||||
),
|
||||
FnBody(clauses) => format!(
|
||||
"{}",
|
||||
clauses
|
||||
.iter()
|
||||
.map(|(clause, _)| clause.show())
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n ")
|
||||
),
|
||||
Fn(name, body, doc) => {
|
||||
let mut out = format!("fn {name} {{\n");
|
||||
if let Some(doc) = doc {
|
||||
out = format!("{out} {doc}\n");
|
||||
}
|
||||
format!("{out} {}\n}}", body.0.show())
|
||||
}
|
||||
FnDeclaration(name) => format!("fn {name}"),
|
||||
Panic(expr) => format!("panic! {}", expr.0.show()),
|
||||
Do(terms) => {
|
||||
format!(
|
||||
"do {}",
|
||||
terms
|
||||
.iter()
|
||||
.map(|(term, _)| term.show())
|
||||
.collect::<Vec<_>>()
|
||||
.join(" > ")
|
||||
)
|
||||
}
|
||||
Repeat(times, body) => format!("repeat {} {{\n{}\n}}", times.0.show(), body.0.show()),
|
||||
Splat(word) => format!("...{}", word),
|
||||
Splattern(pattern) => format!("...{}", pattern.0.show()),
|
||||
AsPattern(word, type_keyword) => format!("{word} as :{type_keyword}"),
|
||||
Pair(key, value) | PairPattern(key, value) => format!(":{key} {}", value.0.show()),
|
||||
Loop(init, body) => format!(
|
||||
"loop {} with {{\n {}\n}}",
|
||||
init.0.show(),
|
||||
body.iter()
|
||||
.map(|(clause, _)| clause.show())
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n ")
|
||||
),
|
||||
Recur(args) => format!(
|
||||
"recur ({})",
|
||||
args.iter()
|
||||
.map(|(arg, _)| arg.show())
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ")
|
||||
),
|
||||
MatchClause(pattern, guard, body) => {
|
||||
let mut out = format!("{}", pattern.0.show());
|
||||
if let Some(guard) = guard.as_ref() {
|
||||
out = format!("{out} if {}", guard.0.show());
|
||||
}
|
||||
format!("{out} -> {}", body.0.show())
|
||||
}
|
||||
WhenClause(cond, body) => format!("{} -> {}", cond.0.show(), body.0.show()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Ast {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
use Ast::*;
|
||||
|
|
12
src/value.rs
12
src/value.rs
|
@ -26,7 +26,7 @@ impl LFn {
|
|||
match self {
|
||||
LFn::Declared { .. } => unreachable!(),
|
||||
LFn::Defined { closed, .. } => {
|
||||
println!("closing over in {}: {value}", self.name());
|
||||
println!("closing over in {}: {}", self.name(), value.show());
|
||||
closed.borrow_mut().push(value);
|
||||
}
|
||||
}
|
||||
|
@ -209,7 +209,7 @@ impl std::fmt::Display for Value {
|
|||
impl Value {
|
||||
pub fn show(&self) -> String {
|
||||
use Value::*;
|
||||
match &self {
|
||||
let mut out = match &self {
|
||||
Nil => "nil".to_string(),
|
||||
True => "true".to_string(),
|
||||
False => "false".to_string(),
|
||||
|
@ -244,7 +244,13 @@ impl Value {
|
|||
Fn(lfn) => format!("fn {}", lfn.name()),
|
||||
Partial(partial) => format!("fn {}/partial", partial.name),
|
||||
BaseFn(_) => format!("{self}"),
|
||||
Nothing => unreachable!(),
|
||||
Nothing => "_".to_string(),
|
||||
};
|
||||
if out.len() > 20 {
|
||||
out.truncate(20);
|
||||
format!("{out}...")
|
||||
} else {
|
||||
out
|
||||
}
|
||||
}
|
||||
|
||||
|
|
44
src/vm.rs
44
src/vm.rs
|
@ -88,10 +88,11 @@ pub struct Vm {
|
|||
pub matches: bool,
|
||||
pub match_depth: u8,
|
||||
pub result: Option<Result<Value, Panic>>,
|
||||
debug: bool,
|
||||
}
|
||||
|
||||
impl Vm {
|
||||
pub fn new(chunk: Chunk) -> Vm {
|
||||
pub fn new(chunk: Chunk, debug: bool) -> Vm {
|
||||
let lfn = LFn::Defined {
|
||||
name: "user script",
|
||||
doc: None,
|
||||
|
@ -116,6 +117,7 @@ impl Vm {
|
|||
matches: false,
|
||||
match_depth: 0,
|
||||
result: None,
|
||||
debug,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -136,12 +138,21 @@ impl Vm {
|
|||
}
|
||||
|
||||
pub fn print_stack(&self) {
|
||||
let inner = self
|
||||
.stack
|
||||
.iter()
|
||||
.map(|val| val.to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.join("|");
|
||||
let mut inner = vec![];
|
||||
for (i, value) in self.stack.iter().enumerate() {
|
||||
if i == self.frame.stack_base {
|
||||
inner.push(format!("->{}<-", value.show()))
|
||||
} else {
|
||||
inner.push(value.show())
|
||||
}
|
||||
}
|
||||
let inner = inner.join("|");
|
||||
// let inner = self
|
||||
// .stack
|
||||
// .iter()
|
||||
// .map(|val| val.show())
|
||||
// .collect::<Vec<_>>()
|
||||
// .join("|");
|
||||
let register = self
|
||||
.return_register
|
||||
.iter()
|
||||
|
@ -194,7 +205,7 @@ impl Vm {
|
|||
self.result = Some(Ok(self.stack.pop().unwrap()));
|
||||
return;
|
||||
};
|
||||
if crate::DEBUG_RUN {
|
||||
if self.debug {
|
||||
self.print_debug();
|
||||
}
|
||||
let op = Op::from_u8(*byte).unwrap();
|
||||
|
@ -875,8 +886,11 @@ impl Vm {
|
|||
|
||||
let val = self.pop();
|
||||
|
||||
if crate::DEBUG_RUN {
|
||||
println!("=== tail call into {val}/{arity} ===");
|
||||
if self.debug {
|
||||
println!(
|
||||
"=== tail call into {val}/{arity} from {} ===",
|
||||
self.frame.function.as_fn().name()
|
||||
);
|
||||
}
|
||||
|
||||
match val {
|
||||
|
@ -930,7 +944,7 @@ impl Vm {
|
|||
|
||||
self.ip = 0;
|
||||
|
||||
if crate::DEBUG_RUN {}
|
||||
if crate::DEBUG_SCRIPT_RUN {}
|
||||
}
|
||||
Value::BaseFn(base_fn) => {
|
||||
let value = match (arity, base_fn) {
|
||||
|
@ -991,7 +1005,7 @@ impl Vm {
|
|||
|
||||
let val = self.pop();
|
||||
|
||||
if crate::DEBUG_RUN {
|
||||
if crate::DEBUG_SCRIPT_RUN {
|
||||
println!("=== calling into {val}/{arity} ===");
|
||||
}
|
||||
|
||||
|
@ -1076,7 +1090,7 @@ impl Vm {
|
|||
}
|
||||
}
|
||||
Return => {
|
||||
if crate::DEBUG_RUN {
|
||||
if crate::DEBUG_SCRIPT_RUN {
|
||||
println!("== returning from {} ==", self.frame.function.show())
|
||||
}
|
||||
self.frame = self.call_stack.pop().unwrap();
|
||||
|
@ -1108,9 +1122,7 @@ impl Vm {
|
|||
}
|
||||
}
|
||||
Msg => {
|
||||
let msg = self.pop();
|
||||
println!("{msg}");
|
||||
self.ip += 1;
|
||||
self.ip += 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user