From cfe0b831921e9dcbfe3afca0630594ff6fd5cf68 Mon Sep 17 00:00:00 2001 From: Scott Richmond Date: Thu, 26 Dec 2024 23:33:57 -0500 Subject: [PATCH] fix block compilation; compile & run repeat --- src/compiler.rs | 88 ++++++++++++++++++++++++++++++++++++++++++++----- src/main.rs | 13 ++------ src/value.rs | 19 +++++++++++ src/vm.rs | 60 +++++++++++++++++++++++++++++++-- 4 files changed, 160 insertions(+), 20 deletions(-) diff --git a/src/compiler.rs b/src/compiler.rs index 15502cc..72518d5 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -35,6 +35,12 @@ pub enum Op { PanicNoWhen, JumpIfNoMatch, PanicNoMatch, + TypeOf, + JumpBack, + JumpIfZero, + Duplicate, + Decrement, + Truncate, } impl std::fmt::Display for Op { @@ -67,6 +73,12 @@ impl std::fmt::Display for Op { PanicNoWhen => "panic_no_when", JumpIfNoMatch => "jump_if_no_match", PanicNoMatch => "panic_no_match", + TypeOf => "type_of", + JumpBack => "jump_back", + JumpIfZero => "jump_if_zero", + Decrement => "decrement", + Truncate => "truncate", + Duplicate => "duplicate", }; write!(f, "{rep}") } @@ -92,7 +104,8 @@ impl Chunk { use Op::*; match op { Pop | Store | Load | Nil | True | False | MatchNil | MatchTrue | MatchFalse - | PanicIfNoMatch | MatchWord | ResetMatch | GetKey | PanicNoWhen | PanicNoMatch => { + | PanicIfNoMatch | MatchWord | ResetMatch | GetKey | PanicNoWhen | PanicNoMatch + | TypeOf | Duplicate | Decrement | Truncate => { println!("{i:04}: {op}") } Constant | MatchConstant => { @@ -101,12 +114,20 @@ impl Chunk { println!("{i:04}: {:16} {next:04}: {value}", op.to_string()); } PushBinding | MatchTuple | PushTuple | PushDict | PushList | PushBox | Jump - | JumpIfFalse | JumpIfNoMatch => { + | JumpIfFalse | JumpIfNoMatch | JumpBack | JumpIfZero => { let next = self.bytecode[i + 1]; println!("{i:04}: {:16} {next:04}", op.to_string()); } } } + + 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) + } } pub struct Compiler { @@ -226,6 +247,7 @@ impl Compiler { pub fn compile(&mut self) { use Ast::*; match self.ast { + Error => unreachable!(), Nil => self.emit_op(Op::Nil), Number(n) => self.emit_constant(Value::Number(*n)), Boolean(b) => self.emit_op(if *b { Op::True } else { Op::False }), @@ -253,13 +275,19 @@ impl Compiler { self.scope_depth += 1; for expr in lines.iter().take(lines.len() - 1) { if is_binding(expr) { - self.visit(expr) + self.visit(expr); } else { self.visit(expr); self.emit_op(Op::Pop); } } - self.visit(lines.last().unwrap()); + let last_expr = lines.last().unwrap(); + if is_binding(last_expr) { + self.visit(last_expr); + self.emit_op(Op::Duplicate); + } else { + self.visit(last_expr); + } self.emit_op(Op::Store); self.scope_depth -= 1; while let Some(binding) = self.bindings.last() { @@ -270,6 +298,7 @@ impl Compiler { break; } } + self.emit_op(Op::Pop); self.emit_op(Op::Load); } If(cond, then, r#else) => { @@ -284,7 +313,7 @@ impl Compiler { self.visit(r#else); let end_idx = self.chunk.bytecode.len(); let jif_offset = jump_idx - jif_idx; - let jump_offset = end_idx - jump_idx; + let jump_offset = end_idx - jump_idx - 2; self.chunk.bytecode[jif_idx + 1] = jif_offset as u8; self.chunk.bytecode[jump_idx + 1] = jump_offset as u8; } @@ -430,6 +459,7 @@ impl Compiler { self.chunk.bytecode[idx] = self.chunk.bytecode.len() as u8 - idx as u8 + 1; } } + WhenClause(..) => unreachable!(), Match(scrutinee, clauses) => { self.visit(scrutinee.as_ref()); let mut jump_idxes = vec![]; @@ -464,6 +494,7 @@ impl Compiler { self.chunk.bytecode[idx] = self.chunk.bytecode.len() as u8 - idx as u8 + 2; } } + MatchClause(..) => unreachable!(), Fn(name, body, doc) => { // first, declare the function // TODO: or, check if the function has already been declared! @@ -498,7 +529,47 @@ impl Compiler { FnBody(clauses) => { self.emit_op(Op::ResetMatch); } - _ => todo!(), + Repeat(times, body) => { + self.visit(times); + self.emit_op(Op::Truncate); + // skip the decrement the first time + self.emit_op(Op::Jump); + self.chunk.bytecode.push(1); + // begin repeat + self.emit_op(Op::Decrement); + let repeat_begin = self.chunk.bytecode.len(); + self.emit_op(Op::Duplicate); + self.emit_op(Op::JumpIfZero); + self.chunk.bytecode.push(0xff); + // compile the body + self.visit(body); + // pop whatever value the body returns + self.emit_op(Op::Pop); + self.emit_op(Op::JumpBack); + // set jump points + let repeat_end = self.chunk.bytecode.len(); + self.chunk.bytecode.push((repeat_end - repeat_begin) as u8); + self.chunk.bytecode[repeat_begin + 2] = (repeat_end - repeat_begin - 2) as u8; + // pop the counter + self.emit_op(Op::Pop); + // and emit nil + self.emit_constant(Value::Nil); + } + Interpolated(..) + | Arguments(..) + | Placeholder + | Panic(..) + | Do(..) + | Splat(..) + | Loop(..) + | Recur(..) + | InterpolatedPattern(..) + | AsPattern(..) + | Splattern(..) + | TuplePattern(..) + | ListPattern(..) + | PairPattern(..) + | DictPattern(..) => todo!(), } } @@ -511,7 +582,8 @@ impl Compiler { use Op::*; match op { Pop | Store | Load | Nil | True | False | MatchNil | MatchTrue | MatchFalse - | MatchWord | ResetMatch | PanicIfNoMatch | GetKey | PanicNoWhen | PanicNoMatch => { + | MatchWord | ResetMatch | PanicIfNoMatch | GetKey | PanicNoWhen | PanicNoMatch + | TypeOf | Duplicate | Truncate | Decrement => { println!("{i:04}: {op}") } Constant | MatchConstant => { @@ -520,7 +592,7 @@ impl Compiler { println!("{i:04}: {:16} {next:04}: {value}", op.to_string()); } PushBinding | MatchTuple | PushTuple | PushDict | PushList | PushBox | Jump - | JumpIfFalse | JumpIfNoMatch => { + | JumpIfFalse | JumpIfNoMatch | JumpBack | JumpIfZero => { 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 14c0b3c..b763e90 100644 --- a/src/main.rs +++ b/src/main.rs @@ -68,16 +68,9 @@ pub fn run(src: &'static str) { pub fn main() { let src = " -let foo = 42 -match foo with { - :foo -> { - 1 - 2 - x - } - 2 -> :two - 42 -> :everything - _ -> :something_else +let foo = 4 +repeat foo { + :foo } "; run(src); diff --git a/src/value.rs b/src/value.rs index 2b5ca14..6bcc1ae 100644 --- a/src/value.rs +++ b/src/value.rs @@ -126,4 +126,23 @@ impl Value { _ => todo!(), } } + + pub fn type_of(&self) -> &'static str { + use Value::*; + match self { + Nil => "nil", + True => "bool", + False => "bool", + Keyword(..) => "keyword", + Interned(..) => "string", + FnDecl(..) => "fn", + String(..) => "string", + Number(..) => "number", + Tuple(..) => "tuple", + List(..) => "list", + Dict(..) => "dict", + Box(..) => "box", + Fn(..) => "fn", + } + } } diff --git a/src/vm.rs b/src/vm.rs index 347c237..16b4843 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -57,6 +57,10 @@ impl<'a> Vm<'a> { self.stack.pop().unwrap() } + pub fn peek(&self) -> &Value { + self.stack.last().unwrap() + } + fn print_stack(&self) { let inner = self .stack @@ -68,8 +72,8 @@ impl<'a> Vm<'a> { } fn print_debug(&self) { - self.chunk.dissasemble_instr(self.ip); self.print_stack(); + self.chunk.dissasemble_instr(self.ip); } pub fn interpret(&mut self) -> Result { @@ -106,7 +110,12 @@ impl<'a> Vm<'a> { } Jump => { let jump_len = self.chunk.bytecode[self.ip + 1]; - self.ip += jump_len as usize; + self.ip += jump_len as usize + 2; + self.interpret() + } + JumpBack => { + let jump_len = self.chunk.bytecode[self.ip + 1]; + self.ip -= jump_len as usize; self.interpret() } JumpIfFalse => { @@ -123,6 +132,21 @@ impl<'a> Vm<'a> { } } } + JumpIfZero => { + let jump_len = self.chunk.bytecode[self.ip + 1]; + let cond = self.pop(); + match cond { + Value::Number(0.0) => { + self.ip += jump_len as usize + 2; + self.interpret() + } + Value::Number(..) => { + self.ip += 2; + self.interpret() + } + _ => Err(Panic("repeat requires a number")), + } + } Pop => { self.pop(); self.ip += 1; @@ -258,6 +282,38 @@ impl<'a> Vm<'a> { } self.interpret() } + TypeOf => { + let val = self.pop(); + let type_of = self.chunk.kw_from(val.type_of()).unwrap(); + self.push(type_of); + self.ip += 1; + self.interpret() + } + Truncate => { + let val = self.pop(); + if let Value::Number(x) = val { + self.push(Value::Number(x as usize as f64)); + self.ip += 1; + self.interpret() + } else { + Err(Panic("repeat requires a number")) + } + } + Decrement => { + let val = self.pop(); + if let Value::Number(x) = val { + self.push(Value::Number(x - 1.0)); + self.ip += 1; + self.interpret() + } else { + Err(Panic("you may only decrement a number")) + } + } + Duplicate => { + self.push(self.peek().clone()); + self.ip += 1; + self.interpret() + } PanicNoWhen | PanicNoMatch => Err(Panic("no match")), } }