keep grinding on loop/recur/jif stack mismatch; add ast->code printer

This commit is contained in:
Scott Richmond 2025-06-22 14:04:43 -04:00
parent 398b140d79
commit 2f60de79a2
8 changed files with 431 additions and 80 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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::*;

View File

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

View File

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