fix block compilation; compile & run repeat

This commit is contained in:
Scott Richmond 2024-12-26 23:33:57 -05:00
parent 4fa2ce5e78
commit cfe0b83192
4 changed files with 160 additions and 20 deletions

View File

@ -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<Value> {
self.kw_index_from(kw).map(Value::Keyword)
}
pub fn kw_index_from(&self, kw: &str) -> Option<usize> {
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());
}

View File

@ -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);

View File

@ -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",
}
}
}

View File

@ -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<Value, Panic> {
@ -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")),
}
}