From 74cc0025d6ec380914c8ec49b17c138685fa3a35 Mon Sep 17 00:00:00 2001 From: Scott Richmond Date: Mon, 26 May 2025 11:03:37 -0400 Subject: [PATCH] first draft loop/recur, seems to work? --- src/compiler.rs | 90 +++++++++++++++++++++++++++++++++++-------------- src/main.rs | 11 +++++- src/vm.rs | 5 +++ 3 files changed, 80 insertions(+), 26 deletions(-) diff --git a/src/compiler.rs b/src/compiler.rs index ea9429f..533af5f 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -20,6 +20,7 @@ pub enum Op { PopN, PushBinding, Store, + StoreAt, Load, ResetMatch, MatchNil, @@ -67,6 +68,7 @@ impl std::fmt::Display for Op { PopN => "pop_n", PushBinding => "push_binding", Store => "store", + StoreAt => "store_at", Load => "load", MatchNil => "match_nil", MatchTrue => "match_true", @@ -134,7 +136,7 @@ impl Chunk { } PushBinding | MatchTuple | MatchList | MatchDict | LoadDictValue | PushTuple | PushDict | PushList | PushBox | Jump | JumpIfFalse | JumpIfNoMatch | JumpIfMatch - | JumpBack | JumpIfZero | MatchDepth | PopN => { + | JumpBack | JumpIfZero | MatchDepth | PopN | StoreAt => { let next = self.bytecode[i + 1]; println!("{i:04}: {:16} {next:04}", op.to_string()); } @@ -319,22 +321,22 @@ impl Compiler { fn enter_loop(&mut self) { self.loop_info - .push(LoopInfo::new(self.len(), self.bindings.len())); + .push(LoopInfo::new(self.len(), self.stack_depth)); } fn leave_loop(&mut self) { self.loop_info.pop(); } - fn loop_info(&mut self) -> LoopInfo { + fn loop_info(&self) -> LoopInfo { self.loop_info.last().unwrap().clone() } - fn loop_idx(&mut self) -> usize { + fn loop_idx(&self) -> usize { self.loop_info.last().unwrap().start } - fn loop_root(&mut self) -> usize { + fn loop_root(&self) -> usize { self.loop_info.last().unwrap().stack_root } @@ -825,58 +827,96 @@ impl Compiler { self.emit_constant(Value::Nil); } Loop(value, clauses) => { - todo!(); //algo: //first, put the values on the stack let (Ast::Tuple(members), _) = value.as_ref() else { unreachable!() }; - for member in members { + for (i, member) in members.iter().enumerate() { self.visit(member); + self.emit_op(Op::StoreAt); + self.emit_byte(i); } let arity = members.len(); + let stack_depth = self.stack_depth; //then, save the beginning of the loop + self.emit_op(Op::Load); self.enter_loop(); - self.emit_op(Op::ResetMatch); + self.stack_depth += arity; //next, compile each clause: let mut clauses = clauses.iter(); let mut jump_idxes = vec![]; while let Some((Ast::MatchClause(pattern, _, body), _)) = clauses.next() { + self.emit_op(Op::ResetMatch); self.scope_depth += 1; let (Ast::TuplePattern(members), _) = pattern.as_ref() else { unreachable!() }; - let mut match_depth = arity; - let mut members = members.iter(); - while match_depth > 0 { + self.match_depth = arity; + let mut tup_jump_idxes = vec![]; + for member in members { + self.match_depth -= 1; self.emit_op(Op::MatchDepth); - self.emit_byte(match_depth - 1); - self.visit(members.next().unwrap()); - match_depth -= 1; + self.emit_byte(self.match_depth); + self.visit(member); + self.emit_op(Op::JumpIfNoMatch); + tup_jump_idxes.push(self.len()); + self.emit_byte(0xff); } + self.emit_op(Op::Jump); + let jump_idx = self.len(); + self.emit_byte(0xff); + for idx in tup_jump_idxes { + self.chunk.bytecode[idx] = self.len() as u8 - idx as u8 - 2; + } + for _ in 0..arity { + self.emit_op(Op::Pop); + } + self.chunk.bytecode[jump_idx] = self.len() as u8 - jump_idx as u8 - 1; self.emit_op(Op::JumpIfNoMatch); let jnm_idx = self.len(); self.emit_byte(0xff); 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; + } + } + while self.stack_depth > stack_depth + arity { + self.pop(); + } + self.stack_depth -= arity; self.emit_op(Op::Jump); - self.emit_byte(0xff); jump_idxes.push(self.len()); + self.emit_byte(0xff); self.chunk.bytecode[jnm_idx] = self.len() as u8 - jnm_idx as u8; self.scope_depth -= 1; } self.emit_op(Op::PanicNoMatch); - - // TODO: fix up jump indexes a la match - + for idx in jump_idxes { + self.chunk.bytecode[idx] = self.len() as u8 - idx as u8 - 1; + } + self.emit_op(Op::PopN); + self.emit_byte(arity); + self.stack_depth -= arity; + self.emit_op(Op::Load); + self.stack_depth += 1; self.leave_loop(); } Recur(args) => { - // algo - // visit each member of the arguments - - // then store those in the return register - // then pop back to loop stack root - // then jump to loop start + for (i, arg) in args.iter().enumerate() { + self.visit(arg); + self.emit_op(Op::StoreAt); + self.emit_byte(i); + } + self.emit_op(Op::PopN); + self.emit_byte(self.loop_root()); + self.emit_op(Op::JumpBack); + self.emit_byte(self.len() - self.loop_idx()); } Interpolated(..) | Arguments(..) @@ -911,7 +951,7 @@ impl Compiler { } PushBinding | MatchTuple | MatchList | PushTuple | PushDict | PushList | MatchDict | LoadDictValue | PushBox | Jump | JumpIfFalse | JumpIfNoMatch - | JumpIfMatch | JumpBack | JumpIfZero | MatchDepth | PopN => { + | JumpIfMatch | JumpBack | JumpIfZero | MatchDepth | PopN | StoreAt => { let (_, next) = codes.next().unwrap(); println!("{i:04}: {:16} {next:04}", op.to_string()); } diff --git a/src/main.rs b/src/main.rs index 0b27be9..81ce8c8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -74,7 +74,16 @@ pub fn run(src: &'static str) { pub fn main() { env::set_var("RUST_BACKTRACE", "1"); let src = " -123 +loop (true) with { + (false) -> :done + (true) -> recur (42) + (42) -> recur (:thingie) + (:thingie) -> { + let x = false + 12 + recur (x) + } +} "; run(src); } diff --git a/src/vm.rs b/src/vm.rs index ed95580..a24b5e1 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -193,6 +193,11 @@ impl<'a> Vm<'a> { self.push(Value::Nothing); self.ip += 1; } + StoreAt => { + let i = self.chunk.bytecode[self.ip + 1] as usize; + self.return_register[i] = self.pop(); + self.ip += 2; + } Load => { let mut i = 0; while i < 8 && self.return_register[i] != Value::Nothing {