diff --git a/src/main.rs b/src/main.rs index 4dbf529..3ef0eec 100644 --- a/src/main.rs +++ b/src/main.rs @@ -57,26 +57,28 @@ pub fn run(src: &'static str) { println!("\n\n") } - // if DEBUG_RUN { - // println!("=== vm run: test ==="); - // } + if DEBUG_RUN { + println!("=== vm run: test ==="); + } - // let mut vm = Vm::new(&compiler.chunk); - // let result = vm.run(); - // let output = match result { - // Ok(val) => val.show(&compiler.chunk), - // Err(panic) => format!("Ludus panicked! {panic}"), - // }; - // vm.print_stack(); - // println!("{output}"); + // TODO: investigate lifeteims and remove this clone + let show_chunk = compiler.chunk.clone(); + let vm_chunk = compiler.chunk; + + let mut vm = Vm::new(vm_chunk); + let result = vm.run(); + let output = match result { + Ok(val) => val.show(&show_chunk), + Err(panic) => format!("Ludus panicked! {panic}"), + }; + vm.print_stack(); + println!("{output}"); } pub fn main() { env::set_var("RUST_BACKTRACE", "1"); let src = " -fn foo () -> :bar - -foo () +(1, 2, 3) "; run(src); } diff --git a/src/vm.rs b/src/vm.rs index b5d32df..79bcb10 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -1,10 +1,11 @@ use crate::compiler::{Chunk, Op}; use crate::parser::Ast; use crate::spans::Spanned; -use crate::value::Value; +use crate::value::{LFn, Value}; use chumsky::prelude::SimpleSpan; use imbl::{HashMap, Vector}; use num_traits::FromPrimitive; +use std::cell::OnceCell; use std::cell::RefCell; use std::fmt; use std::mem::swap; @@ -43,9 +44,44 @@ pub struct Trace { pub src: &'static str, } -pub struct Vm<'a> { +pub struct CallFrame { + pub function: Value, + pub arity: u8, + pub stack_base: usize, + pub ip: usize, +} + +impl CallFrame { + pub fn chunk(&self) -> &Chunk { + let Value::Fn(ref function) = self.function else { + unreachable!() + }; + &function + .get() + .unwrap() + .chunks + .iter() + .find(|(arity, _)| *arity == self.arity) + .unwrap() + .1 + } +} + +impl fmt::Display for CallFrame { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let Value::Fn(ref function) = self.function else { + unreachable!() + }; + let inner_fn = function.get().unwrap(); + let name = inner_fn.name; + write!(f, "CallFrame: {name}/{} @ {}", self.arity, self.ip) + } +} + +pub struct Vm { pub stack: Vec, - pub chunk: &'a Chunk, + pub call_stack: Vec, + pub frame: CallFrame, pub ip: usize, pub return_register: [Value; 7], pub matches: bool, @@ -53,11 +89,27 @@ pub struct Vm<'a> { pub result: Option>, } -impl<'a> Vm<'a> { - pub fn new(chunk: &'a Chunk) -> Vm<'a> { +impl Vm { + pub fn new(chunk: Chunk) -> Vm { + let cell = OnceCell::new(); + let lfn = LFn { + name: "user script", + doc: None, + chunks: vec![(0, chunk)], + closed: vec![], + }; + let _ = cell.set(lfn); + let base_fn = Value::Fn(Rc::new(cell)); + let base_frame = CallFrame { + function: base_fn.clone(), + stack_base: 1, + ip: 0, + arity: 0, + }; Vm { - chunk, stack: vec![], + call_stack: Vec::with_capacity(64), + frame: base_frame, ip: 0, return_register: [ Value::Nothing, @@ -74,6 +126,10 @@ impl<'a> Vm<'a> { } } + pub fn chunk(&self) -> &Chunk { + self.frame.chunk() + } + pub fn push(&mut self, value: Value) { self.stack.push(value); } @@ -105,7 +161,7 @@ impl<'a> Vm<'a> { fn print_debug(&self) { self.print_stack(); let mut ip = self.ip; - self.chunk.dissasemble_instr(&mut ip); + self.chunk().dissasemble_instr(&mut ip); } pub fn run(&mut self) -> &Result { @@ -124,7 +180,7 @@ impl<'a> Vm<'a> { } pub fn interpret(&mut self) { - let Some(byte) = self.chunk.bytecode.get(self.ip) else { + let Some(byte) = self.chunk().bytecode.get(self.ip) else { self.result = Some(Ok(self.stack.pop().unwrap())); return; }; @@ -150,21 +206,21 @@ impl<'a> Vm<'a> { self.ip += 1; } Constant => { - let const_idx = self.chunk.bytecode[self.ip + 1]; - let value = self.chunk.constants[const_idx as usize].clone(); + let const_idx = self.chunk().bytecode[self.ip + 1]; + let value = self.chunk().constants[const_idx as usize].clone(); self.push(value); self.ip += 2; } Jump => { - let jump_len = self.chunk.bytecode[self.ip + 1]; + let jump_len = self.chunk().bytecode[self.ip + 1]; self.ip += jump_len as usize + 2; } JumpBack => { - let jump_len = self.chunk.bytecode[self.ip + 1]; + let jump_len = self.chunk().bytecode[self.ip + 1]; self.ip -= jump_len as usize; } JumpIfFalse => { - let jump_len = self.chunk.bytecode[self.ip + 1]; + let jump_len = self.chunk().bytecode[self.ip + 1]; let cond = self.pop(); match cond { Value::Nil | Value::False => { @@ -176,7 +232,7 @@ impl<'a> Vm<'a> { } } JumpIfTrue => { - let jump_len = self.chunk.bytecode[self.ip + 1]; + let jump_len = self.chunk().bytecode[self.ip + 1]; let cond = self.pop(); match cond { Value::Nil | Value::False => { @@ -188,7 +244,7 @@ impl<'a> Vm<'a> { } } JumpIfZero => { - let jump_len = self.chunk.bytecode[self.ip + 1]; + let jump_len = self.chunk().bytecode[self.ip + 1]; let cond = self.pop(); match cond { Value::Number(0.0) => { @@ -207,12 +263,12 @@ impl<'a> Vm<'a> { self.ip += 1; } PopN => { - let n = self.chunk.bytecode[self.ip + 1] as usize; + let n = self.chunk().bytecode[self.ip + 1] as usize; self.stack.truncate(self.stack.len() - n); self.ip += 2; } PushBinding => { - let binding_idx = self.chunk.bytecode[self.ip + 1] as usize; + let binding_idx = self.chunk().bytecode[self.ip + 1] as usize; let binding_value = self.stack[binding_idx].clone(); self.push(binding_value); self.ip += 2; @@ -223,7 +279,7 @@ impl<'a> Vm<'a> { self.ip += 1; } StoreAt => { - let i = self.chunk.bytecode[self.ip + 1] as usize; + let i = self.chunk().bytecode[self.ip + 1] as usize; self.return_register[i] = self.pop(); self.ip += 2; } @@ -254,7 +310,7 @@ impl<'a> Vm<'a> { let as_type = self.pop(); let idx = self.stack.len() - self.match_depth as usize - 1; let val_type = self.stack[idx].type_of(); - let val_type = self.chunk.kw_from(val_type).unwrap(); + let val_type = self.chunk().kw_from(val_type).unwrap(); self.matches = val_type == as_type; self.ip += 1; self.interpret() @@ -289,14 +345,14 @@ impl<'a> Vm<'a> { } } MatchConstant => { - let const_idx = self.chunk.bytecode[self.ip + 1]; + let const_idx = self.chunk().bytecode[self.ip + 1]; let idx = self.stack.len() - self.match_depth as usize - 1; - self.matches = self.stack[idx] == self.chunk.constants[const_idx as usize]; + self.matches = self.stack[idx] == self.chunk().constants[const_idx as usize]; self.ip += 2; } MatchTuple => { let idx = self.stack.len() - self.match_depth as usize - 1; - let tuple_len = self.chunk.bytecode[self.ip + 1]; + let tuple_len = self.chunk().bytecode[self.ip + 1]; let scrutinee = self.stack[idx].clone(); match scrutinee { Value::Tuple(members) => self.matches = members.len() == tuple_len as usize, @@ -305,7 +361,7 @@ impl<'a> Vm<'a> { self.ip += 2; } PushTuple => { - let tuple_len = self.chunk.bytecode[self.ip + 1]; + let tuple_len = self.chunk().bytecode[self.ip + 1]; let tuple_members = self.stack.split_off(self.stack.len() - tuple_len as usize); let tuple = Value::Tuple(Rc::new(tuple_members)); self.stack.push(tuple); @@ -325,7 +381,7 @@ impl<'a> Vm<'a> { self.ip += 1; } PushList => { - let list_len = self.chunk.bytecode[self.ip + 1]; + let list_len = self.chunk().bytecode[self.ip + 1]; let list_members = self.stack.split_off(self.stack.len() - list_len as usize); let list = Value::List(Box::new(Vector::from(list_members))); self.stack.push(list); @@ -333,7 +389,7 @@ impl<'a> Vm<'a> { } MatchList => { let idx = self.stack.len() - self.match_depth as usize - 1; - let tuple_len = self.chunk.bytecode[self.ip + 1]; + let tuple_len = self.chunk().bytecode[self.ip + 1]; let scrutinee = self.stack[idx].clone(); match scrutinee { Value::List(members) => self.matches = members.len() == tuple_len as usize, @@ -355,7 +411,7 @@ impl<'a> Vm<'a> { self.ip += 1; } PushDict => { - let dict_len = self.chunk.bytecode[self.ip + 1] as usize * 2; + let dict_len = self.chunk().bytecode[self.ip + 1] as usize * 2; let dict_members = self.stack.split_off(self.stack.len() - dict_len); let mut dict = HashMap::new(); let mut dict_iter = dict_members.iter(); @@ -370,7 +426,7 @@ impl<'a> Vm<'a> { self.ip += 2; } LoadDictValue => { - let dict_idx = self.chunk.bytecode[self.ip + 1] as usize; + let dict_idx = self.chunk().bytecode[self.ip + 1] as usize; let Value::Dict(dict) = self.stack[dict_idx].clone() else { unreachable!("expected dict, got something else") }; @@ -383,7 +439,7 @@ impl<'a> Vm<'a> { } MatchDict => { let idx = self.stack.len() - self.match_depth as usize - 1; - let dict_len = self.chunk.bytecode[self.ip + 1]; + let dict_len = self.chunk().bytecode[self.ip + 1]; let scrutinee = self.stack[idx].clone(); match scrutinee { Value::Dict(members) => self.matches = members.len() == dict_len as usize, @@ -410,7 +466,7 @@ impl<'a> Vm<'a> { self.ip += 1; } JumpIfNoMatch => { - let jump_len = self.chunk.bytecode[self.ip + 1] as usize; + let jump_len = self.chunk().bytecode[self.ip + 1] as usize; if !self.matches { self.ip += jump_len + 2; } else { @@ -418,7 +474,7 @@ impl<'a> Vm<'a> { } } JumpIfMatch => { - let jump_len = self.chunk.bytecode[self.ip + 1] as usize; + let jump_len = self.chunk().bytecode[self.ip + 1] as usize; if self.matches { self.ip += jump_len + 2; } else { @@ -427,7 +483,7 @@ impl<'a> Vm<'a> { } TypeOf => { let val = self.pop(); - let type_of = self.chunk.kw_from(val.type_of()).unwrap(); + let type_of = self.chunk().kw_from(val.type_of()).unwrap(); self.push(type_of); self.ip += 1; } @@ -456,7 +512,7 @@ impl<'a> Vm<'a> { self.interpret() } MatchDepth => { - self.match_depth = self.chunk.bytecode[self.ip + 1]; + self.match_depth = self.chunk().bytecode[self.ip + 1]; self.ip += 2; self.interpret() } @@ -582,7 +638,7 @@ impl<'a> Vm<'a> { self.ip += 1; } Panic => { - let msg = self.pop().show(self.chunk); + let msg = self.pop().show(self.chunk()); self.panic_with(msg); } EmptyString => { @@ -606,7 +662,7 @@ impl<'a> Vm<'a> { } Stringify => { let to_stringify = self.pop(); - let the_string = to_stringify.stringify(self.chunk); + let the_string = to_stringify.stringify(self.chunk()); let stringified = Value::String(Rc::new(the_string)); self.push(stringified); self.ip += 1;