From 86de66c4d26df266d999386453740360f918dffd Mon Sep 17 00:00:00 2001 From: Scott Richmond Date: Sun, 22 Jun 2025 17:03:50 -0400 Subject: [PATCH] keep grinding; quality of life improvements to aid in grinding --- sandbox.ld | 35 ++-- src/compiler.rs | 432 +++++++++--------------------------------------- src/main.rs | 7 +- src/value.rs | 2 +- src/vm.rs | 3 +- 5 files changed, 103 insertions(+), 376 deletions(-) diff --git a/sandbox.ld b/sandbox.ld index f30aeaa..20417b4 100644 --- a/sandbox.ld +++ b/sandbox.ld @@ -1,16 +1,14 @@ let test = 2 -& loop ([1, 2, 3]) with { -& ([]) -> false -& ([x]) -> eq? (x, test) -& ([x, ...xs]) -> { -& let foo = :bar -& when { -& eq? (x, test) -> true -& :else -> recur (xs) -& } -& } -& } +loop ([1, 2, 3]) with { + ([]) -> false + ([x]) -> eq? (x, test) + ([x, ...xs]) -> if eq? (x, test) + then true + else recur (xs) +} + +let foo = :bar fn not { (false) -> true @@ -18,12 +16,11 @@ fn not { (_) -> false } -loop ([1, 2, 3]) with { - ([]) -> false - ([x]) -> eq? (x, test) - ([x, ...xs]) -> if not (eq? (x, test)) - then recur (xs) - else true - -} +& loop ([1, 2, 3]) with { +& ([]) -> false +& ([y]) -> eq? (y, test) +& ([y, ...ys]) -> if not (eq? (y, test)) +& then recur (ys) +& else true +& } diff --git a/src/compiler.rs b/src/compiler.rs index 493c140..39c09b7 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -1,238 +1,15 @@ +use crate::chunk::{Chunk, StrPattern}; +use crate::op::Op; use crate::parser::Ast; use crate::parser::StringPart; use crate::spans::Spanned; use crate::value::*; use chumsky::prelude::SimpleSpan; -use num_derive::{FromPrimitive, ToPrimitive}; -use num_traits::FromPrimitive; use regex::Regex; use std::cell::RefCell; use std::collections::HashMap; use std::rc::Rc; -#[derive(Copy, Clone, Debug, PartialEq, Eq, FromPrimitive, ToPrimitive)] -pub enum Op { - Noop, - Nothing, - Nil, - True, - False, - Constant, - Jump, - JumpIfFalse, - JumpIfTrue, - Pop, - PopN, - PushBinding, - PushGlobal, - Store, - StoreN, - Stash, - Load, - ResetMatch, - Match, - MatchNil, - MatchTrue, - MatchFalse, - PanicIfNoMatch, - MatchConstant, - MatchString, - PushStringMatches, - MatchType, - MatchTuple, - MatchSplattedTuple, - PushTuple, - LoadTuple, - LoadSplattedTuple, - MatchList, - MatchSplattedList, - LoadList, - LoadSplattedList, - PushList, - AppendList, - ConcatList, - PushDict, - AppendDict, - ConcatDict, - LoadDictValue, - MatchDict, - MatchSplattedDict, - DropDictEntry, - PushBox, - GetKey, - PanicNoWhen, - JumpIfNoMatch, - JumpIfMatch, - PanicNoMatch, - TypeOf, - JumpBack, - JumpIfZero, - Duplicate, - Decrement, - Truncate, - MatchDepth, - Panic, - EmptyString, - ConcatStrings, - Stringify, - - Call, - TailCall, - Return, - Partial, - - Eq, - Add, - Sub, - Mult, - Div, - Unbox, - BoxStore, - Assert, - Get, - At, - - Not, - Print, - SetUpvalue, - GetUpvalue, - - Msg, - // Inc, - // Dec, - // Gt, - // Gte, - // Lt, - // Lte, - // Mod, - // Round, - // Ceil, - // Floor, - // Random, - // Sqrt, - - // Assoc, - // Concat, - // Conj, - // Count, - // Disj, - // Dissoc, - // Range, - // Rest, - // Slice, - - // "atan_2" math/atan2 - // "chars" chars - // "cos" math/cos - // "doc" doc - // "downcase" string/ascii-lower - // "pi" math/pi - // "show" show - // "sin" math/sin - // "split" string/split - // "str_slice" string/slice - // "tan" math/tan - // "trim" string/trim - // "triml" string/triml - // "trimr" string/trimr - // "upcase" string/ascii-upper -} - -impl std::fmt::Display for Op { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - use Op::*; - let rep = match self { - Msg => "msg", - Noop => "noop", - Nothing => "nothing", - Nil => "nil", - True => "true", - False => "false", - Constant => "constant", - Jump => "jump", - JumpIfFalse => "jump_if_false", - JumpIfTrue => "jump_if_true", - Pop => "pop", - PopN => "pop_n", - PushBinding => "push_binding", - PushGlobal => "push_global", - Store => "store", - StoreN => "store_n", - Stash => "stash", - Load => "load", - Match => "match", - MatchNil => "match_nil", - MatchTrue => "match_true", - MatchFalse => "match_false", - ResetMatch => "reset_match", - PanicIfNoMatch => "panic_if_no_match", - MatchConstant => "match_constant", - MatchString => "match_string", - PushStringMatches => "push_string_matches", - MatchType => "match_type", - MatchTuple => "match_tuple", - MatchSplattedTuple => "match_splatted_tuple", - PushTuple => "push_tuple", - LoadTuple => "load_tuple", - LoadSplattedTuple => "load_splatted_tuple", - MatchList => "match_list", - MatchSplattedList => "match_splatted_list", - LoadList => "load_list", - LoadSplattedList => "load_splatted_list", - PushList => "push_list", - AppendList => "append_list", - ConcatList => "concat_list", - PushDict => "push_dict", - AppendDict => "append_dict", - ConcatDict => "concat_dict", - LoadDictValue => "load_dict_value", - MatchDict => "match_dict", - MatchSplattedDict => "match_splatted_dict", - DropDictEntry => "drop_dict_entry", - PushBox => "push_box", - GetKey => "get_key", - PanicNoWhen => "panic_no_when", - JumpIfNoMatch => "jump_if_no_match", - JumpIfMatch => "jump_if_match", - PanicNoMatch => "panic_no_match", - TypeOf => "type_of", - JumpBack => "jump_back", - JumpIfZero => "jump_if_zero", - Decrement => "decrement", - Truncate => "truncate", - Duplicate => "duplicate", - MatchDepth => "match_depth", - Panic => "panic", - EmptyString => "empty_string", - ConcatStrings => "concat_strings", - Stringify => "stringify", - Print => "print", - - Eq => "eq", - Add => "add", - Sub => "sub", - Mult => "mult", - Div => "div", - Unbox => "unbox", - BoxStore => "box_store", - Assert => "assert", - Get => "get", - At => "at", - - Not => "not", - - Call => "call", - Return => "return", - Partial => "partial", - TailCall => "tail_call", - - SetUpvalue => "set_upvalue", - GetUpvalue => "get_upvalue", - }; - write!(f, "{rep}") - } -} - #[derive(Clone, Debug, PartialEq)] pub struct Binding { name: &'static str, @@ -246,87 +23,6 @@ pub struct Upvalue { stack_pos: usize, } -#[derive(Clone, Debug)] -pub struct StrPattern { - pub words: Vec, - pub re: Regex, -} - -#[derive(Clone, Debug)] -pub struct Chunk { - pub constants: Vec, - pub bytecode: Vec, - pub keywords: Vec<&'static str>, - pub string_patterns: Vec, - pub env: imbl::HashMap<&'static str, Value>, - pub msgs: Vec, -} - -impl Chunk { - pub fn dissasemble_instr(&self, i: &mut usize) { - let op = Op::from_u8(self.bytecode[*i]).unwrap(); - use Op::*; - match op { - Pop | Store | Stash | Load | Nil | True | False | MatchNil | MatchTrue | MatchFalse - | PanicIfNoMatch | ResetMatch | GetKey | PanicNoWhen | PanicNoMatch | TypeOf - | Duplicate | Decrement | Truncate | Noop | LoadTuple | LoadList | Eq | Add | Sub - | 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 => { - println!("{i:04}: {op}") - } - Constant | MatchConstant => { - let high = self.bytecode[*i + 1]; - let low = self.bytecode[*i + 2]; - let idx = ((high as usize) << 8) + low as usize; - let value = &self.constants[idx].show(); - 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 - | Call | GetUpvalue | Partial | MatchString | PushStringMatches | TailCall => { - let next = self.bytecode[*i + 1]; - println!("{i:04}: {:16} {next:03}", op.to_string()); - *i += 1; - } - Jump | JumpIfFalse | JumpIfTrue | JumpIfNoMatch | JumpIfMatch | JumpBack - | JumpIfZero => { - let high = self.bytecode[*i + 1]; - let low = self.bytecode[*i + 2]; - let len = ((high as u16) << 8) + low as u16; - println!("{i:04}: {:16} {len:05}", op.to_string()); - *i += 2; - } - } - } - - pub fn dissasemble(&self) { - println!("IDX | CODE | INFO"); - let mut i = 0; - while i < self.bytecode.len() { - self.dissasemble_instr(&mut i); - i += 1; - } - } - - // pub fn kw_from(&self, kw: &str) -> Option { - // self.kw_index_from(kw).map(Value::Keyword) - // } - - // pub fn kw_index_from(&self, kw: &str) -> Option { - // self.keywords.iter().position(|s| *s == kw) - // } -} - #[derive(Debug, Clone, PartialEq)] struct LoopInfo { start: usize, @@ -524,14 +220,21 @@ 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); + self.msg(format!( + "stack depth: {}; match depth: {}", + self.stack_depth, self.match_depth + )); + self.msg(format!( + "at stack index: {}", + self.stack_depth - self.match_depth - 1 + )); let binding = Binding { name, depth: self.scope_depth, stack_pos: self.stack_depth - self.match_depth - 1, }; - println!("{:?}", binding); self.bindings.push(binding); + self.msg(format!("locals are now: {:?}", self.bindings)); } fn resolve_local(&self, name: &'static str) -> Option { @@ -548,24 +251,31 @@ impl<'a> Compiler<'a> { } fn resolve_binding(&mut self, name: &'static str) { - self.msg(format!("resolving binding `{name}` in {}", self.name)); + self.msg(format!( + "resolving binding `{name}` in {}\nlocals: {:?}", + self.name, self.bindings + )); if let Some(pos) = self.resolve_local(name) { + self.msg(format!("at locals position {pos}")); self.emit_op(Op::PushBinding); self.emit_byte(pos); self.stack_depth += 1; return; } if let Some(pos) = self.resolve_upvalue(name) { + self.msg(format!("as upvalue {pos}")); self.emit_op(Op::GetUpvalue); self.emit_byte(pos); self.stack_depth += 1; return; } if self.chunk.env.contains_key(name) { + self.msg("as global".to_string()); self.emit_constant(Value::Keyword(name)); self.emit_op(Op::PushGlobal); return; } + self.msg(format!("as enclosing upvalue {}", self.upvalues.len())); self.emit_op(Op::GetUpvalue); self.emit_byte(self.upvalues.len()); self.upvalues.push(name); @@ -589,6 +299,23 @@ impl<'a> Compiler<'a> { } } + fn enter_scope(&mut self) { + self.scope_depth += 1; + } + + fn leave_scope(&mut self) { + self.msg(format!("leaving scope {}", self.scope_depth)); + while let Some(binding) = self.bindings.last() { + if binding.depth == self.scope_depth { + let unbound = self.bindings.pop(); + self.msg(format!("releasing binding {:?}", unbound)); + } else { + break; + } + } + self.scope_depth -= 1; + } + fn enter_loop(&mut self) { self.loop_info .push(LoopInfo::new(self.len(), self.stack_depth)); @@ -649,7 +376,8 @@ impl<'a> Compiler<'a> { let tail_pos = self.tail_pos; self.tail_pos = false; // increase the scope - self.scope_depth += 1; + self.enter_scope(); + // self.scope_depth += 1; // stash the stack depth let stack_depth = self.stack_depth; // evaluate all the lines but the last @@ -694,14 +422,15 @@ impl<'a> Compiler<'a> { self.emit_op(Op::Store); // reset the scope - self.scope_depth -= 1; - while let Some(binding) = self.bindings.last() { - if binding.depth > self.scope_depth { - self.bindings.pop(); - } else { - break; - } - } + 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 self.pop_n(self.stack_depth - stack_depth - 1); // load the value from the return register @@ -729,12 +458,15 @@ impl<'a> Compiler<'a> { self.patch_jump(jump_idx, jump_offset); } Let(patt, expr) => { + self.report_depth("before let binding"); self.match_depth = 0; self.emit_op(Op::ResetMatch); self.visit(expr); + self.report_depth("after let expr"); self.report_ast("let binding: matching".to_string(), patt); self.visit(patt); self.emit_op(Op::PanicIfNoMatch); + self.report_depth("after let binding"); } WordPattern(name) => { self.emit_op(Op::Match); @@ -1215,7 +947,8 @@ impl<'a> Compiler<'a> { self.tail_pos = false; let mut no_match_jumps = vec![]; self.report_ast("match clause: ".to_string(), pattern); - self.scope_depth += 1; + // self.scope_depth += 1; + self.enter_scope(); self.match_depth = 0; self.visit(pattern); no_match_jumps.push(self.stub_jump(Op::JumpIfNoMatch)); @@ -1229,14 +962,15 @@ impl<'a> Compiler<'a> { self.tail_pos = tail_pos; self.visit(body); self.emit_op(Op::Store); - self.scope_depth -= 1; - while let Some(binding) = self.bindings.last() { - if binding.depth > self.scope_depth { - self.bindings.pop(); - } else { - break; - } - } + 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; + // } + // } self.pop_n(self.stack_depth - stack_depth); jump_idxes.push(self.stub_jump(Op::Jump)); for idx in no_match_jumps { @@ -1471,6 +1205,7 @@ impl<'a> Compiler<'a> { self.emit_constant(Value::Nil); } Loop(value, clauses) => { + self.report_depth("entering loop"); let tail_pos = self.tail_pos; self.tail_pos = false; //algo: @@ -1481,14 +1216,10 @@ impl<'a> Compiler<'a> { for member in members { self.visit(member); } - self.msg(format!( - "***entering loop with stack depth of {}", - self.stack_depth - )); + self.report_depth("after loop args"); 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 {}", @@ -1508,7 +1239,8 @@ impl<'a> Compiler<'a> { while let Some((Ast::MatchClause(pattern, guard, body), _)) = clauses.next() { self.tail_pos = false; self.emit_op(Op::ResetMatch); - self.scope_depth += 1; + self.enter_scope(); + // self.scope_depth += 1; let (Ast::TuplePattern(members), _) = pattern.as_ref() else { unreachable!() }; @@ -1535,37 +1267,31 @@ impl<'a> Compiler<'a> { self.stack_depth )); self.visit(body); - self.msg(format!( - "***after visiting loop body, the stack depth is {}", - self.stack_depth - )); + self.report_depth("after loop body, before store"); self.emit_op(Op::Store); - 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(); - } else { - 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(); - } + self.leave_scope(); + self.report_depth_str(format!( + "resetting the stack after loop from {stack_depth} to {}", + self.stack_depth, + )); + self.pop_n(self.stack_depth - stack_depth); + // while self.stack_depth > stack_depth { + // self.pop(); + // } jump_idxes.push(self.stub_jump(Op::Jump)); for idx in jnm_idxes { self.patch_jump(idx, self.len() - idx - 3); } - self.scope_depth -= 1; + // self.scope_depth -= 1; } self.emit_op(Op::PanicNoMatch); for idx in jump_idxes { self.patch_jump(idx, self.len() - idx - 3); } self.stack_depth -= arity; + // pop back to the original depth before load + // i.e. clear loop args + self.pop(); self.emit_op(Op::Load); self.stack_depth += 1; self.leave_loop(); diff --git a/src/main.rs b/src/main.rs index c6946d2..70eb87e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -25,6 +25,9 @@ use crate::validator::Validator; mod errors; use crate::errors::report_invalidation; +mod chunk; +mod op; + mod compiler; use crate::compiler::Compiler; @@ -121,7 +124,7 @@ pub fn run(src: &'static str) { return; } - let mut compiler = Compiler::new(parsed, "test", src, None, prelude, DEBUG_SCRIPT_COMPILE); + let mut compiler = Compiler::new(parsed, "sandbox", src, None, prelude, DEBUG_SCRIPT_COMPILE); // let base = base::make_base(); // compiler.emit_constant(base); // compiler.bind("base"); @@ -135,7 +138,7 @@ pub fn run(src: &'static str) { } if DEBUG_SCRIPT_RUN { - println!("=== vm run: test ==="); + println!("=== vm run ==="); } let vm_chunk = compiler.chunk; diff --git a/src/value.rs b/src/value.rs index ed85546..12d94cb 100644 --- a/src/value.rs +++ b/src/value.rs @@ -1,5 +1,5 @@ use crate::base::BaseFn; -use crate::compiler::Chunk; +use crate::chunk::Chunk; // use crate::parser::Ast; // use crate::spans::Spanned; use imbl::{HashMap, Vector}; diff --git a/src/vm.rs b/src/vm.rs index 1d4bb99..843b489 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -1,5 +1,6 @@ use crate::base::BaseFn; -use crate::compiler::{Chunk, Op}; +use crate::chunk::Chunk; +use crate::op::Op; use crate::parser::Ast; use crate::spans::Spanned; use crate::value::{LFn, Value};