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 {
|
& 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)
|
||||||
|
@ -48,20 +48,20 @@
|
||||||
& (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? {
|
||||||
|
@ -115,20 +115,20 @@
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn inc {
|
fn inc {
|
||||||
|
@ -203,18 +203,53 @@ 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) -> {
|
||||||
|
& & 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))
|
||||||
}
|
}
|
||||||
& (kw as :keyword, xs) -> {
|
|
||||||
& fn mapper (prev, curr) -> append (prev, kw (curr))
|
|
||||||
& fold (mapper, xs, [])
|
|
||||||
& }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add {
|
fn add {
|
||||||
|
@ -231,7 +266,7 @@ fn add {
|
||||||
& nil?
|
& nil?
|
||||||
& some?
|
& some?
|
||||||
& some
|
& some
|
||||||
& eq?
|
eq?
|
||||||
& bool?
|
& bool?
|
||||||
& true?
|
& true?
|
||||||
& false?
|
& false?
|
||||||
|
@ -241,7 +276,7 @@ fn add {
|
||||||
& fn?
|
& fn?
|
||||||
first
|
first
|
||||||
rest
|
rest
|
||||||
inc
|
& inc
|
||||||
& dec
|
& dec
|
||||||
& count
|
& count
|
||||||
& empty?
|
& empty?
|
||||||
|
@ -249,8 +284,12 @@ fn add {
|
||||||
& list?
|
& list?
|
||||||
& list
|
& list
|
||||||
& first
|
& first
|
||||||
fold
|
& fold
|
||||||
append
|
& append
|
||||||
map
|
& map
|
||||||
|
& filter
|
||||||
|
& keep
|
||||||
|
& concat
|
||||||
|
& contains?
|
||||||
add
|
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.
|
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.
|
So now we'll do all bindings relative to the stack base.
|
||||||
UGH.
|
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 {
|
pub fn add(x: &Value, y: &Value) -> Value {
|
||||||
match (x, y) {
|
match (x, y) {
|
||||||
(Value::Number(x), Value::Number(y)) => Value::Number(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 keywords: Vec<&'static str>,
|
||||||
pub string_patterns: Vec<StrPattern>,
|
pub string_patterns: Vec<StrPattern>,
|
||||||
pub env: imbl::HashMap<&'static str, Value>,
|
pub env: imbl::HashMap<&'static str, Value>,
|
||||||
|
pub msgs: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Chunk {
|
impl Chunk {
|
||||||
|
@ -272,7 +273,7 @@ impl Chunk {
|
||||||
| Mult | Div | Unbox | BoxStore | Assert | Get | At | Not | Panic | EmptyString
|
| Mult | Div | Unbox | BoxStore | Assert | Get | At | Not | Panic | EmptyString
|
||||||
| ConcatStrings | Stringify | MatchType | Return | Match | Print | AppendList
|
| ConcatStrings | Stringify | MatchType | Return | Match | Print | AppendList
|
||||||
| ConcatList | PushList | PushDict | AppendDict | ConcatDict | Nothing | PushGlobal
|
| ConcatList | PushList | PushDict | AppendDict | ConcatDict | Nothing | PushGlobal
|
||||||
| SetUpvalue | Msg => {
|
| SetUpvalue => {
|
||||||
println!("{i:04}: {op}")
|
println!("{i:04}: {op}")
|
||||||
}
|
}
|
||||||
Constant | MatchConstant => {
|
Constant | MatchConstant => {
|
||||||
|
@ -283,6 +284,12 @@ impl Chunk {
|
||||||
println!("{i:04}: {:16} {idx:05}: {value}", op.to_string());
|
println!("{i:04}: {:16} {idx:05}: {value}", op.to_string());
|
||||||
*i += 2;
|
*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
|
PushBinding | MatchTuple | MatchSplattedTuple | LoadSplattedTuple | MatchList
|
||||||
| MatchSplattedList | LoadSplattedList | MatchDict | MatchSplattedDict
|
| MatchSplattedList | LoadSplattedList | MatchDict | MatchSplattedDict
|
||||||
| DropDictEntry | LoadDictValue | PushTuple | PushBox | MatchDepth | PopN | StoreN
|
| DropDictEntry | LoadDictValue | PushTuple | PushBox | MatchDepth | PopN | StoreN
|
||||||
|
@ -370,6 +377,7 @@ pub struct Compiler<'a> {
|
||||||
pub upvalues: Vec<&'static str>,
|
pub upvalues: Vec<&'static str>,
|
||||||
loop_info: Vec<LoopInfo>,
|
loop_info: Vec<LoopInfo>,
|
||||||
tail_pos: bool,
|
tail_pos: bool,
|
||||||
|
debug: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_binding(expr: &Spanned<Ast>) -> bool {
|
fn is_binding(expr: &Spanned<Ast>) -> bool {
|
||||||
|
@ -393,6 +401,7 @@ impl<'a> Compiler<'a> {
|
||||||
src: &'static str,
|
src: &'static str,
|
||||||
enclosing: Option<&'a Compiler>,
|
enclosing: Option<&'a Compiler>,
|
||||||
env: imbl::HashMap<&'static str, Value>,
|
env: imbl::HashMap<&'static str, Value>,
|
||||||
|
debug: bool,
|
||||||
) -> Compiler<'a> {
|
) -> Compiler<'a> {
|
||||||
let chunk = Chunk {
|
let chunk = Chunk {
|
||||||
constants: vec![],
|
constants: vec![],
|
||||||
|
@ -400,6 +409,7 @@ impl<'a> Compiler<'a> {
|
||||||
keywords: vec![],
|
keywords: vec![],
|
||||||
string_patterns: vec![],
|
string_patterns: vec![],
|
||||||
env,
|
env,
|
||||||
|
msgs: vec![],
|
||||||
};
|
};
|
||||||
Compiler {
|
Compiler {
|
||||||
chunk,
|
chunk,
|
||||||
|
@ -417,6 +427,7 @@ impl<'a> Compiler<'a> {
|
||||||
src,
|
src,
|
||||||
name,
|
name,
|
||||||
tail_pos: false,
|
tail_pos: false,
|
||||||
|
debug,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -512,13 +523,14 @@ impl<'a> Compiler<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bind(&mut self, name: &'static str) {
|
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 {
|
let binding = Binding {
|
||||||
name,
|
name,
|
||||||
depth: self.scope_depth,
|
depth: self.scope_depth,
|
||||||
stack_pos: self.stack_depth - self.match_depth - 1,
|
stack_pos: self.stack_depth - self.match_depth - 1,
|
||||||
};
|
};
|
||||||
println!("{:?}", binding);
|
println!("{:?}", binding);
|
||||||
println!("stack: {}; match: {}", self.stack_depth, self.match_depth);
|
|
||||||
self.bindings.push(binding);
|
self.bindings.push(binding);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -536,6 +548,7 @@ impl<'a> Compiler<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_binding(&mut self, name: &'static str) {
|
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) {
|
if let Some(pos) = self.resolve_local(name) {
|
||||||
self.emit_op(Op::PushBinding);
|
self.emit_op(Op::PushBinding);
|
||||||
self.emit_byte(pos);
|
self.emit_byte(pos);
|
||||||
|
@ -598,10 +611,17 @@ impl<'a> Compiler<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn msg(&mut self, str: String) {
|
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.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) {
|
pub fn compile(&mut self) {
|
||||||
|
@ -686,13 +706,17 @@ impl<'a> Compiler<'a> {
|
||||||
let tail_pos = self.tail_pos;
|
let tail_pos = self.tail_pos;
|
||||||
self.tail_pos = false;
|
self.tail_pos = false;
|
||||||
self.visit(cond);
|
self.visit(cond);
|
||||||
|
self.report_depth("after condition");
|
||||||
let jif_idx = self.stub_jump(Op::JumpIfFalse);
|
let jif_idx = self.stub_jump(Op::JumpIfFalse);
|
||||||
self.stack_depth -= 1;
|
self.stack_depth -= 1;
|
||||||
self.tail_pos = tail_pos;
|
self.tail_pos = tail_pos;
|
||||||
self.visit(then);
|
self.visit(then);
|
||||||
|
self.report_depth("after consequent");
|
||||||
let jump_idx = self.stub_jump(Op::Jump);
|
let jump_idx = self.stub_jump(Op::Jump);
|
||||||
self.visit(r#else);
|
|
||||||
self.stack_depth -= 1;
|
self.stack_depth -= 1;
|
||||||
|
self.visit(r#else);
|
||||||
|
self.report_depth("after alternative");
|
||||||
|
// self.stack_depth += 1;
|
||||||
let end_idx = self.len();
|
let end_idx = self.len();
|
||||||
let jif_offset = jump_idx - jif_idx;
|
let jif_offset = jump_idx - jif_idx;
|
||||||
let jump_offset = end_idx - jump_idx - 3;
|
let jump_offset = end_idx - jump_idx - 3;
|
||||||
|
@ -1031,11 +1055,13 @@ impl<'a> Compiler<'a> {
|
||||||
let tail_pos = self.tail_pos;
|
let tail_pos = self.tail_pos;
|
||||||
self.tail_pos = false;
|
self.tail_pos = false;
|
||||||
match (&first.0, &second.0) {
|
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(first);
|
||||||
self.visit(second);
|
self.visit(second);
|
||||||
self.emit_op(Op::GetKey);
|
self.emit_op(Op::GetKey);
|
||||||
self.stack_depth -= 1;
|
self.stack_depth -= 1;
|
||||||
|
self.report_depth("after keyword access");
|
||||||
}
|
}
|
||||||
(Keyword(_), Arguments(args)) => {
|
(Keyword(_), Arguments(args)) => {
|
||||||
self.visit(&args[0]);
|
self.visit(&args[0]);
|
||||||
|
@ -1080,6 +1106,7 @@ impl<'a> Compiler<'a> {
|
||||||
self.stack_depth = 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}"));
|
||||||
if has_placeholder(args) {
|
if has_placeholder(args) {
|
||||||
let arity = args.len();
|
let arity = args.len();
|
||||||
for arg in args {
|
for arg in args {
|
||||||
|
@ -1105,6 +1132,7 @@ impl<'a> Compiler<'a> {
|
||||||
}
|
}
|
||||||
self.resolve_binding(fn_name);
|
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)
|
// 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 {
|
if rest.is_empty() && tail_pos {
|
||||||
self.emit_op(Op::TailCall);
|
self.emit_op(Op::TailCall);
|
||||||
} else {
|
} else {
|
||||||
|
@ -1275,6 +1303,7 @@ impl<'a> Compiler<'a> {
|
||||||
self.src,
|
self.src,
|
||||||
Some(self),
|
Some(self),
|
||||||
self.chunk.env.clone(),
|
self.chunk.env.clone(),
|
||||||
|
self.debug,
|
||||||
);
|
);
|
||||||
compiler.emit_op(Op::ResetMatch);
|
compiler.emit_op(Op::ResetMatch);
|
||||||
compilers.insert(arity, compiler);
|
compilers.insert(arity, compiler);
|
||||||
|
@ -1347,7 +1376,7 @@ impl<'a> Compiler<'a> {
|
||||||
for (arity, mut compiler) in compilers {
|
for (arity, mut compiler) in compilers {
|
||||||
compiler.emit_op(Op::PanicNoMatch);
|
compiler.emit_op(Op::PanicNoMatch);
|
||||||
let chunk = compiler.chunk;
|
let chunk = compiler.chunk;
|
||||||
if crate::DEBUG_COMPILE {
|
if self.debug {
|
||||||
println!("=== function chuncktion: {name}/{arity} ===");
|
println!("=== function chuncktion: {name}/{arity} ===");
|
||||||
chunk.dissasemble();
|
chunk.dissasemble();
|
||||||
}
|
}
|
||||||
|
@ -1444,18 +1473,27 @@ impl<'a> Compiler<'a> {
|
||||||
self.visit(member);
|
self.visit(member);
|
||||||
}
|
}
|
||||||
self.msg(format!(
|
self.msg(format!(
|
||||||
"entering loop with stack depth of {}",
|
"***entering loop with stack depth of {}",
|
||||||
self.stack_depth
|
self.stack_depth
|
||||||
));
|
));
|
||||||
self.emit_op(Op::StoreN);
|
self.emit_op(Op::StoreN);
|
||||||
self.emit_byte(members.len());
|
self.emit_byte(members.len());
|
||||||
let arity = members.len();
|
let arity = members.len();
|
||||||
|
// self.pop();
|
||||||
let stack_depth = self.stack_depth;
|
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
|
//then, save the beginning of the loop
|
||||||
self.emit_op(Op::Load);
|
self.emit_op(Op::Load);
|
||||||
self.enter_loop();
|
self.enter_loop();
|
||||||
// self.stack_depth += arity;
|
// self.stack_depth += arity;
|
||||||
//next, compile each clause:
|
//next, compile each clause:
|
||||||
|
self.msg(format!(
|
||||||
|
"***after load, stack depth is now {}",
|
||||||
|
self.stack_depth
|
||||||
|
));
|
||||||
let mut clauses = clauses.iter();
|
let mut clauses = clauses.iter();
|
||||||
let mut jump_idxes = vec![];
|
let mut jump_idxes = vec![];
|
||||||
while let Some((Ast::MatchClause(pattern, guard, body), _)) = clauses.next() {
|
while let Some((Ast::MatchClause(pattern, guard, body), _)) = clauses.next() {
|
||||||
|
@ -1482,9 +1520,18 @@ impl<'a> Compiler<'a> {
|
||||||
self.stack_depth -= 1;
|
self.stack_depth -= 1;
|
||||||
}
|
}
|
||||||
self.tail_pos = tail_pos;
|
self.tail_pos = tail_pos;
|
||||||
|
self.msg(format!(
|
||||||
|
"***before visiting body, the stack depth is {}",
|
||||||
|
self.stack_depth
|
||||||
|
));
|
||||||
self.visit(body);
|
self.visit(body);
|
||||||
|
self.msg(format!(
|
||||||
|
"***after visiting loop body, the stack depth is {}",
|
||||||
|
self.stack_depth
|
||||||
|
));
|
||||||
self.emit_op(Op::Store);
|
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() {
|
while let Some(binding) = self.bindings.last() {
|
||||||
if binding.depth > self.scope_depth {
|
if binding.depth > self.scope_depth {
|
||||||
self.bindings.pop();
|
self.bindings.pop();
|
||||||
|
@ -1492,6 +1539,9 @@ impl<'a> Compiler<'a> {
|
||||||
break;
|
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 {
|
while self.stack_depth > stack_depth {
|
||||||
self.pop();
|
self.pop();
|
||||||
}
|
}
|
||||||
|
@ -1511,6 +1561,11 @@ impl<'a> Compiler<'a> {
|
||||||
self.leave_loop();
|
self.leave_loop();
|
||||||
}
|
}
|
||||||
Recur(args) => {
|
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;
|
let tail_pos = self.tail_pos;
|
||||||
self.tail_pos = false;
|
self.tail_pos = false;
|
||||||
let mut argnum = 0;
|
let mut argnum = 0;
|
||||||
|
@ -1519,6 +1574,10 @@ impl<'a> Compiler<'a> {
|
||||||
argnum += 1;
|
argnum += 1;
|
||||||
self.visit(arg);
|
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_op(Op::StoreN);
|
||||||
self.emit_byte(args.len());
|
self.emit_byte(args.len());
|
||||||
self.emit_op(Op::PopN);
|
self.emit_op(Op::PopN);
|
||||||
|
|
59
src/main.rs
59
src/main.rs
|
@ -1,9 +1,12 @@
|
||||||
use chumsky::{input::Stream, prelude::*};
|
use chumsky::{input::Stream, prelude::*};
|
||||||
use imbl::HashMap;
|
use imbl::HashMap;
|
||||||
use std::env;
|
use std::env;
|
||||||
|
use std::fs;
|
||||||
|
|
||||||
const DEBUG_COMPILE: bool = true;
|
const DEBUG_SCRIPT_COMPILE: bool = true;
|
||||||
const DEBUG_RUN: bool = true;
|
const DEBUG_SCRIPT_RUN: bool = true;
|
||||||
|
const DEBUG_PRELUDE_COMPILE: bool = false;
|
||||||
|
const DEBUG_PRELUDE_RUN: bool = false;
|
||||||
|
|
||||||
mod base;
|
mod base;
|
||||||
|
|
||||||
|
@ -63,13 +66,20 @@ pub fn prelude() -> HashMap<&'static str, Value> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let parsed: &'static Spanned<Ast> = Box::leak(Box::new(parsed));
|
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.emit_constant(base);
|
||||||
compiler.bind("base");
|
compiler.bind("base");
|
||||||
compiler.compile();
|
compiler.compile();
|
||||||
|
|
||||||
let chunk = compiler.chunk;
|
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();
|
let prelude = vm.run().clone().unwrap();
|
||||||
match prelude {
|
match prelude {
|
||||||
Value::Dict(hashmap) => *hashmap,
|
Value::Dict(hashmap) => *hashmap,
|
||||||
|
@ -111,26 +121,26 @@ pub fn run(src: &'static str) {
|
||||||
return;
|
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();
|
// let base = base::make_base();
|
||||||
// compiler.emit_constant(base);
|
// compiler.emit_constant(base);
|
||||||
// compiler.bind("base");
|
// compiler.bind("base");
|
||||||
|
|
||||||
compiler.compile();
|
compiler.compile();
|
||||||
if DEBUG_COMPILE {
|
if DEBUG_SCRIPT_COMPILE {
|
||||||
println!("=== source code ===");
|
println!("=== source code ===");
|
||||||
println!("{src}");
|
println!("{src}");
|
||||||
compiler.disassemble();
|
compiler.disassemble();
|
||||||
println!("\n\n")
|
println!("\n\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
if DEBUG_RUN {
|
if DEBUG_SCRIPT_RUN {
|
||||||
println!("=== vm run: test ===");
|
println!("=== vm run: test ===");
|
||||||
}
|
}
|
||||||
|
|
||||||
let vm_chunk = compiler.chunk;
|
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 result = vm.run();
|
||||||
let output = match result {
|
let output = match result {
|
||||||
Ok(val) => val.show(),
|
Ok(val) => val.show(),
|
||||||
|
@ -140,10 +150,35 @@ pub fn run(src: &'static str) {
|
||||||
println!("{output}");
|
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() {
|
pub fn main() {
|
||||||
env::set_var("RUST_BACKTRACE", "1");
|
env::set_var("RUST_BACKTRACE", "1");
|
||||||
let src = r#"
|
let src: &'static str = fs::read_to_string("sandbox.ld").unwrap().leak();
|
||||||
map(inc, [1, 2, 3])
|
match ld_fmt(src) {
|
||||||
"#;
|
Ok(src) => println!("{}", src),
|
||||||
run(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>>),
|
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 {
|
impl fmt::Display for Ast {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
use Ast::*;
|
use Ast::*;
|
||||||
|
|
12
src/value.rs
12
src/value.rs
|
@ -26,7 +26,7 @@ impl LFn {
|
||||||
match self {
|
match self {
|
||||||
LFn::Declared { .. } => unreachable!(),
|
LFn::Declared { .. } => unreachable!(),
|
||||||
LFn::Defined { closed, .. } => {
|
LFn::Defined { closed, .. } => {
|
||||||
println!("closing over in {}: {value}", self.name());
|
println!("closing over in {}: {}", self.name(), value.show());
|
||||||
closed.borrow_mut().push(value);
|
closed.borrow_mut().push(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -209,7 +209,7 @@ impl std::fmt::Display for Value {
|
||||||
impl Value {
|
impl Value {
|
||||||
pub fn show(&self) -> String {
|
pub fn show(&self) -> String {
|
||||||
use Value::*;
|
use Value::*;
|
||||||
match &self {
|
let mut out = match &self {
|
||||||
Nil => "nil".to_string(),
|
Nil => "nil".to_string(),
|
||||||
True => "true".to_string(),
|
True => "true".to_string(),
|
||||||
False => "false".to_string(),
|
False => "false".to_string(),
|
||||||
|
@ -244,7 +244,13 @@ impl Value {
|
||||||
Fn(lfn) => format!("fn {}", lfn.name()),
|
Fn(lfn) => format!("fn {}", lfn.name()),
|
||||||
Partial(partial) => format!("fn {}/partial", partial.name),
|
Partial(partial) => format!("fn {}/partial", partial.name),
|
||||||
BaseFn(_) => format!("{self}"),
|
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 matches: bool,
|
||||||
pub match_depth: u8,
|
pub match_depth: u8,
|
||||||
pub result: Option<Result<Value, Panic>>,
|
pub result: Option<Result<Value, Panic>>,
|
||||||
|
debug: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Vm {
|
impl Vm {
|
||||||
pub fn new(chunk: Chunk) -> Vm {
|
pub fn new(chunk: Chunk, debug: bool) -> Vm {
|
||||||
let lfn = LFn::Defined {
|
let lfn = LFn::Defined {
|
||||||
name: "user script",
|
name: "user script",
|
||||||
doc: None,
|
doc: None,
|
||||||
|
@ -116,6 +117,7 @@ impl Vm {
|
||||||
matches: false,
|
matches: false,
|
||||||
match_depth: 0,
|
match_depth: 0,
|
||||||
result: None,
|
result: None,
|
||||||
|
debug,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,12 +138,21 @@ impl Vm {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn print_stack(&self) {
|
pub fn print_stack(&self) {
|
||||||
let inner = self
|
let mut inner = vec![];
|
||||||
.stack
|
for (i, value) in self.stack.iter().enumerate() {
|
||||||
.iter()
|
if i == self.frame.stack_base {
|
||||||
.map(|val| val.to_string())
|
inner.push(format!("->{}<-", value.show()))
|
||||||
.collect::<Vec<_>>()
|
} else {
|
||||||
.join("|");
|
inner.push(value.show())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let inner = inner.join("|");
|
||||||
|
// let inner = self
|
||||||
|
// .stack
|
||||||
|
// .iter()
|
||||||
|
// .map(|val| val.show())
|
||||||
|
// .collect::<Vec<_>>()
|
||||||
|
// .join("|");
|
||||||
let register = self
|
let register = self
|
||||||
.return_register
|
.return_register
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -194,7 +205,7 @@ impl Vm {
|
||||||
self.result = Some(Ok(self.stack.pop().unwrap()));
|
self.result = Some(Ok(self.stack.pop().unwrap()));
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if crate::DEBUG_RUN {
|
if self.debug {
|
||||||
self.print_debug();
|
self.print_debug();
|
||||||
}
|
}
|
||||||
let op = Op::from_u8(*byte).unwrap();
|
let op = Op::from_u8(*byte).unwrap();
|
||||||
|
@ -875,8 +886,11 @@ impl Vm {
|
||||||
|
|
||||||
let val = self.pop();
|
let val = self.pop();
|
||||||
|
|
||||||
if crate::DEBUG_RUN {
|
if self.debug {
|
||||||
println!("=== tail call into {val}/{arity} ===");
|
println!(
|
||||||
|
"=== tail call into {val}/{arity} from {} ===",
|
||||||
|
self.frame.function.as_fn().name()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
match val {
|
match val {
|
||||||
|
@ -930,7 +944,7 @@ impl Vm {
|
||||||
|
|
||||||
self.ip = 0;
|
self.ip = 0;
|
||||||
|
|
||||||
if crate::DEBUG_RUN {}
|
if crate::DEBUG_SCRIPT_RUN {}
|
||||||
}
|
}
|
||||||
Value::BaseFn(base_fn) => {
|
Value::BaseFn(base_fn) => {
|
||||||
let value = match (arity, base_fn) {
|
let value = match (arity, base_fn) {
|
||||||
|
@ -991,7 +1005,7 @@ impl Vm {
|
||||||
|
|
||||||
let val = self.pop();
|
let val = self.pop();
|
||||||
|
|
||||||
if crate::DEBUG_RUN {
|
if crate::DEBUG_SCRIPT_RUN {
|
||||||
println!("=== calling into {val}/{arity} ===");
|
println!("=== calling into {val}/{arity} ===");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1076,7 +1090,7 @@ impl Vm {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Return => {
|
Return => {
|
||||||
if crate::DEBUG_RUN {
|
if crate::DEBUG_SCRIPT_RUN {
|
||||||
println!("== returning from {} ==", self.frame.function.show())
|
println!("== returning from {} ==", self.frame.function.show())
|
||||||
}
|
}
|
||||||
self.frame = self.call_stack.pop().unwrap();
|
self.frame = self.call_stack.pop().unwrap();
|
||||||
|
@ -1108,9 +1122,7 @@ impl Vm {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Msg => {
|
Msg => {
|
||||||
let msg = self.pop();
|
self.ip += 2;
|
||||||
println!("{msg}");
|
|
||||||
self.ip += 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user