first draft loop/recur, seems to work?

This commit is contained in:
Scott Richmond 2025-05-26 11:03:37 -04:00
parent db7eb5965d
commit 74cc0025d6
3 changed files with 80 additions and 26 deletions

View File

@ -20,6 +20,7 @@ pub enum Op {
PopN, PopN,
PushBinding, PushBinding,
Store, Store,
StoreAt,
Load, Load,
ResetMatch, ResetMatch,
MatchNil, MatchNil,
@ -67,6 +68,7 @@ impl std::fmt::Display for Op {
PopN => "pop_n", PopN => "pop_n",
PushBinding => "push_binding", PushBinding => "push_binding",
Store => "store", Store => "store",
StoreAt => "store_at",
Load => "load", Load => "load",
MatchNil => "match_nil", MatchNil => "match_nil",
MatchTrue => "match_true", MatchTrue => "match_true",
@ -134,7 +136,7 @@ impl Chunk {
} }
PushBinding | MatchTuple | MatchList | MatchDict | LoadDictValue | PushTuple PushBinding | MatchTuple | MatchList | MatchDict | LoadDictValue | PushTuple
| PushDict | PushList | PushBox | Jump | JumpIfFalse | JumpIfNoMatch | JumpIfMatch | PushDict | PushList | PushBox | Jump | JumpIfFalse | JumpIfNoMatch | JumpIfMatch
| JumpBack | JumpIfZero | MatchDepth | PopN => { | JumpBack | JumpIfZero | MatchDepth | PopN | StoreAt => {
let next = self.bytecode[i + 1]; let next = self.bytecode[i + 1];
println!("{i:04}: {:16} {next:04}", op.to_string()); println!("{i:04}: {:16} {next:04}", op.to_string());
} }
@ -319,22 +321,22 @@ impl Compiler {
fn enter_loop(&mut self) { fn enter_loop(&mut self) {
self.loop_info self.loop_info
.push(LoopInfo::new(self.len(), self.bindings.len())); .push(LoopInfo::new(self.len(), self.stack_depth));
} }
fn leave_loop(&mut self) { fn leave_loop(&mut self) {
self.loop_info.pop(); self.loop_info.pop();
} }
fn loop_info(&mut self) -> LoopInfo { fn loop_info(&self) -> LoopInfo {
self.loop_info.last().unwrap().clone() self.loop_info.last().unwrap().clone()
} }
fn loop_idx(&mut self) -> usize { fn loop_idx(&self) -> usize {
self.loop_info.last().unwrap().start self.loop_info.last().unwrap().start
} }
fn loop_root(&mut self) -> usize { fn loop_root(&self) -> usize {
self.loop_info.last().unwrap().stack_root self.loop_info.last().unwrap().stack_root
} }
@ -825,58 +827,96 @@ impl Compiler {
self.emit_constant(Value::Nil); self.emit_constant(Value::Nil);
} }
Loop(value, clauses) => { Loop(value, clauses) => {
todo!();
//algo: //algo:
//first, put the values on the stack //first, put the values on the stack
let (Ast::Tuple(members), _) = value.as_ref() else { let (Ast::Tuple(members), _) = value.as_ref() else {
unreachable!() unreachable!()
}; };
for member in members { for (i, member) in members.iter().enumerate() {
self.visit(member); self.visit(member);
self.emit_op(Op::StoreAt);
self.emit_byte(i);
} }
let arity = members.len(); let arity = members.len();
let stack_depth = self.stack_depth;
//then, save the beginning of the loop //then, save the beginning of the loop
self.emit_op(Op::Load);
self.enter_loop(); self.enter_loop();
self.emit_op(Op::ResetMatch); self.stack_depth += arity;
//next, compile each clause: //next, compile each clause:
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, _, body), _)) = clauses.next() { while let Some((Ast::MatchClause(pattern, _, body), _)) = clauses.next() {
self.emit_op(Op::ResetMatch);
self.scope_depth += 1; self.scope_depth += 1;
let (Ast::TuplePattern(members), _) = pattern.as_ref() else { let (Ast::TuplePattern(members), _) = pattern.as_ref() else {
unreachable!() unreachable!()
}; };
let mut match_depth = arity; self.match_depth = arity;
let mut members = members.iter(); let mut tup_jump_idxes = vec![];
while match_depth > 0 { for member in members {
self.match_depth -= 1;
self.emit_op(Op::MatchDepth); self.emit_op(Op::MatchDepth);
self.emit_byte(match_depth - 1); self.emit_byte(self.match_depth);
self.visit(members.next().unwrap()); self.visit(member);
match_depth -= 1; 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); self.emit_op(Op::JumpIfNoMatch);
let jnm_idx = self.len(); let jnm_idx = self.len();
self.emit_byte(0xff); self.emit_byte(0xff);
self.visit(body); 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_op(Op::Jump);
self.emit_byte(0xff);
jump_idxes.push(self.len()); jump_idxes.push(self.len());
self.emit_byte(0xff);
self.chunk.bytecode[jnm_idx] = self.len() as u8 - jnm_idx as u8; self.chunk.bytecode[jnm_idx] = self.len() as u8 - jnm_idx as u8;
self.scope_depth -= 1; self.scope_depth -= 1;
} }
self.emit_op(Op::PanicNoMatch); self.emit_op(Op::PanicNoMatch);
for idx in jump_idxes {
// TODO: fix up jump indexes a la match 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(); self.leave_loop();
} }
Recur(args) => { Recur(args) => {
// algo for (i, arg) in args.iter().enumerate() {
// visit each member of the arguments self.visit(arg);
self.emit_op(Op::StoreAt);
// then store those in the return register self.emit_byte(i);
// then pop back to loop stack root }
// then jump to loop start 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(..) Interpolated(..)
| Arguments(..) | Arguments(..)
@ -911,7 +951,7 @@ impl Compiler {
} }
PushBinding | MatchTuple | MatchList | PushTuple | PushDict | PushList PushBinding | MatchTuple | MatchList | PushTuple | PushDict | PushList
| MatchDict | LoadDictValue | PushBox | Jump | JumpIfFalse | JumpIfNoMatch | MatchDict | LoadDictValue | PushBox | Jump | JumpIfFalse | JumpIfNoMatch
| JumpIfMatch | JumpBack | JumpIfZero | MatchDepth | PopN => { | JumpIfMatch | JumpBack | JumpIfZero | MatchDepth | PopN | StoreAt => {
let (_, next) = codes.next().unwrap(); let (_, next) = codes.next().unwrap();
println!("{i:04}: {:16} {next:04}", op.to_string()); println!("{i:04}: {:16} {next:04}", op.to_string());
} }

View File

@ -74,7 +74,16 @@ pub fn run(src: &'static str) {
pub fn main() { pub fn main() {
env::set_var("RUST_BACKTRACE", "1"); env::set_var("RUST_BACKTRACE", "1");
let src = " let src = "
123 loop (true) with {
(false) -> :done
(true) -> recur (42)
(42) -> recur (:thingie)
(:thingie) -> {
let x = false
12
recur (x)
}
}
"; ";
run(src); run(src);
} }

View File

@ -193,6 +193,11 @@ impl<'a> Vm<'a> {
self.push(Value::Nothing); self.push(Value::Nothing);
self.ip += 1; self.ip += 1;
} }
StoreAt => {
let i = self.chunk.bytecode[self.ip + 1] as usize;
self.return_register[i] = self.pop();
self.ip += 2;
}
Load => { Load => {
let mut i = 0; let mut i = 0;
while i < 8 && self.return_register[i] != Value::Nothing { while i < 8 && self.return_register[i] != Value::Nothing {