Compare commits

...

2 Commits

Author SHA1 Message Date
Scott Richmond
74cc0025d6 first draft loop/recur, seems to work? 2025-05-26 11:03:37 -04:00
Scott Richmond
db7eb5965d return register now an 8-member array 2025-05-26 09:16:47 -04:00
4 changed files with 110 additions and 35 deletions

View File

@ -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,57 +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(_) => {
// 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
Recur(args) => {
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(..)
@ -910,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());
}

View File

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

View File

@ -25,6 +25,7 @@ impl LFn {
#[derive(Clone, Debug, PartialEq)]
pub enum Value {
Nothing,
Nil,
True,
False,
@ -44,6 +45,7 @@ impl std::fmt::Display for Value {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
use Value::*;
match self {
Nothing => write!(f, "_"),
Nil => write!(f, "nil"),
True => write!(f, "true"),
False => write!(f, "false"),
@ -130,6 +132,7 @@ impl Value {
pub fn type_of(&self) -> &'static str {
use Value::*;
match self {
Nothing => unreachable!(),
Nil => "nil",
True => "bool",
False => "bool",

View File

@ -34,7 +34,7 @@ pub struct Vm<'a> {
pub stack: Vec<Value>,
pub chunk: &'a Chunk,
pub ip: usize,
pub return_register: Value,
pub return_register: [Value; 8],
pub matches: bool,
pub match_depth: u8,
pub result: Option<Result<Value, Panic>>,
@ -46,7 +46,16 @@ impl<'a> Vm<'a> {
chunk,
stack: vec![],
ip: 0,
return_register: Value::Nil,
return_register: [
Value::Nothing,
Value::Nothing,
Value::Nothing,
Value::Nothing,
Value::Nothing,
Value::Nothing,
Value::Nothing,
Value::Nothing,
],
matches: false,
match_depth: 0,
result: None,
@ -72,7 +81,13 @@ impl<'a> Vm<'a> {
.map(|val| val.to_string())
.collect::<Vec<_>>()
.join("|");
println!("{:04}: [{inner}] {}", self.ip, self.return_register);
let register = self
.return_register
.iter()
.map(|val| val.to_string())
.collect::<Vec<_>>()
.join(",");
println!("{:04}: [{inner}] ({register})", self.ip);
}
fn print_debug(&self) {
@ -174,14 +189,23 @@ impl<'a> Vm<'a> {
self.ip += 2;
}
Store => {
self.return_register = self.pop();
self.push(Value::Nil);
self.return_register[0] = self.pop();
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 value = Value::Nil;
swap(&mut self.return_register, &mut value);
let mut i = 0;
while i < 8 && self.return_register[i] != Value::Nothing {
let mut value = Value::Nothing;
swap(&mut self.return_register[i], &mut value);
self.push(value);
i += 1;
}
self.ip += 1;
}
ResetMatch => {