diff --git a/Cargo.lock b/Cargo.lock index 39e863c..755861a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -88,7 +88,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn", + "syn 2.0.77", ] [[package]] @@ -109,6 +109,27 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3f6d59c71e7dc3af60f0af9db32364d96a16e9310f3f5db2b55ed642162dd35" +[[package]] +name = "gc" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f73a03797d58caede765f4b19522d1a63d737088bbef81de06d2dd117d7bc8c3" +dependencies = [ + "gc_derive", +] + +[[package]] +name = "gc_derive" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb6f1e0d69658b2806f60864bad1a80a9634fc0bc17f7ed55828e3b9c45bf61e" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", + "synstructure", +] + [[package]] name = "heck" version = "0.5.0" @@ -128,6 +149,7 @@ dependencies = [ "assert_matches", "clap", "common_macros", + "gc", "thiserror", ] @@ -155,6 +177,17 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "syn" version = "2.0.77" @@ -166,6 +199,18 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "synstructure" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", + "unicode-xid", +] + [[package]] name = "thiserror" version = "1.0.63" @@ -183,7 +228,7 @@ checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.77", ] [[package]] @@ -192,6 +237,12 @@ version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + [[package]] name = "utf8parse" version = "0.2.2" diff --git a/Cargo.toml b/Cargo.toml index 36c1228..631438c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,3 +8,4 @@ assert_matches = "1.5.0" clap = { version = "4.5.8", features = ["derive"] } common_macros = "0.1.1" thiserror = "1.0.63" +gc = { version = "0.5", features = ["derive"] } diff --git a/src/builtins.rs b/src/builtins.rs index c88596d..6d32d09 100644 --- a/src/builtins.rs +++ b/src/builtins.rs @@ -1,13 +1,34 @@ //! Builtin functions. -use crate::obj::{NilInst, ObjP}; +use std::collections::HashMap; + +use crate::obj::*; use crate::vm::Vm; -pub(crate) fn println(_vm: &mut Vm, args: Vec) -> ObjP { - println!("{}", args[0]); - NilInst::create() +pub(crate) fn println(vm: &mut Vm, args: Vec) -> ObjP { + println!("{}", args[0].borrow()); + vm.create_nil() } -pub(crate) fn print(_vm: &mut Vm, args: Vec) -> ObjP { - print!("{}", args[0]); - NilInst::create() +pub(crate) fn print(vm: &mut Vm, args: Vec) -> ObjP { + print!("{}", args[0].borrow()); + vm.create_nil() +} + +pub fn init_builtins(builtins: &mut HashMap) { + macro_rules! builtins { + ($($builtin:ident / $argc:expr),* $(,)?) => { + $({ + let builtin_function = builtins.create_builtin_function(stringify!($builtin), $builtin, $argc); + builtins.insert( + stringify!($builtin).to_string(), + builtin_function + ); + })* + } + } + + builtins! { + print/1, + println/1, + } } diff --git a/src/compiler.rs b/src/compiler.rs index acbe82c..1717374 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -1,6 +1,7 @@ use std::collections::{HashMap, HashSet}; use std::fmt::{self, Display}; -use std::sync::{Arc, LazyLock}; +use std::rc::Rc; +use std::sync::LazyLock; use assert_matches::assert_matches; use common_macros::hash_map; @@ -405,25 +406,25 @@ impl Display for CompileError { //////////////////////////////////////////////////////////////////////////////// #[derive(Debug)] -pub struct Compiler { +pub struct Compiler<'b> { chunks: Vec, scopes: Vec, constants: Vec, globals: Vec, + builtins: &'b HashMap, } -impl Default for Compiler { - fn default() -> Self { +impl<'b> Compiler<'b> { + pub fn new(builtins: &'b HashMap) -> Self { Compiler { chunks: Default::default(), scopes: Default::default(), constants: Default::default(), - globals: vec!["print".to_string(), "println".to_string()], + globals: builtins.keys().map(ToString::to_string).collect(), + builtins, } } -} -impl Compiler { fn chunk(&self) -> &Chunk { self.chunks.last().expect("no chunk") } @@ -478,7 +479,7 @@ impl Compiler { // simple interning - try to find a constant that is exactly equal to this one and just // return its value instead for (index, interned) in self.constants.iter().enumerate() { - if constant.equals(interned.as_ref()) { + if constant.borrow().equals(&*interned.borrow()) { return Ok(index as ConstantId); } } @@ -491,6 +492,8 @@ impl Compiler { } .into()); } + + // convert this to a pointer, upcast, and then re-GC self.constants.push(constant); Ok(index as ConstantId) } @@ -629,7 +632,7 @@ impl Compiler { } } -impl StmtVisitor for Compiler { +impl StmtVisitor for Compiler<'_> { fn visit_expr_stmt(&mut self, stmt: &ExprStmt) -> Result<()> { self.compile_expr(&stmt.expr)?; self.emit(stmt_line_number(stmt), Op::Pop); @@ -661,9 +664,13 @@ impl StmtVisitor for Compiler { // TODO - maybe this would be smarter to set up in the AST. I'm 99% sure that the last // object created, if it were a function object, will be what we're assigning it to, but I // want to be 100% sure instead of 99%. - let obj = Arc::get_mut(self.constants.last_mut().unwrap()).unwrap(); - if let Some(fun) = obj.as_any_mut().downcast_mut::() { - fun.set_name(Arc::new(name.to_string())); + let obj = self.constants.last().unwrap().as_ref(); + if let Some(fun) = obj + .borrow_mut() + .as_any_mut() + .downcast_mut::() + { + fun.set_name(Rc::new(name.to_string())); } Ok(()) @@ -671,7 +678,7 @@ impl StmtVisitor for Compiler { fn visit_set_stmt(&mut self, stmt: &SetStmt) -> Result<()> { self.compile_expr(&stmt.expr)?; - let name = self.insert_constant(StrInst::create(&stmt.name.text))?; + let name = self.insert_constant(self.create_str(&stmt.name.text))?; self.compile_expr(&stmt.rhs)?; self.emit(stmt_line_number(stmt), Op::SetAttr(name)); Ok(()) @@ -690,7 +697,7 @@ impl StmtVisitor for Compiler { if let Some(expr) = &stmt.expr { self.compile_expr(expr)?; } else { - let nil = self.insert_constant(NilInst::create())?; + let nil = self.insert_constant(self.create_nil())?; self.emit(stmt_line_number(stmt), Op::PushConstant(nil)); } Ok(()) @@ -699,7 +706,7 @@ impl StmtVisitor for Compiler { // condition self.compile_expr(&stmt.condition)?; // call obj.__bool__() - let bool_attr = self.insert_constant(StrInst::create("__bool__"))?; + let bool_attr = self.insert_constant(self.create_str("__bool__"))?; self.emit(expr_line_number(&*stmt.condition), Op::GetAttr(bool_attr)); self.emit(expr_line_number(&*stmt.condition), Op::Call(0)); let condition_patch_index = self.chunk().code.len(); @@ -746,7 +753,7 @@ impl StmtVisitor for Compiler { } } -impl ExprVisitor for Compiler { +impl ExprVisitor for Compiler<'_> { fn visit_binary_expr(&mut self, expr: &BinaryExpr) -> Result<()> { static OP_NAMES: LazyLock> = LazyLock::new(|| { hash_map! { @@ -771,7 +778,7 @@ impl ExprVisitor for Compiler { let mut exit_patch_index = 0; if let TokenKind::And | TokenKind::Or = expr.op.kind { - let constant_id = self.insert_constant(StrInst::create("__bool__"))?; + let constant_id = self.insert_constant(self.create_str("__bool__"))?; self.emit(expr_line_number(&*expr.lhs), Op::GetAttr(constant_id)); self.emit(expr_line_number(&*expr.lhs), Op::Call(0)); exit_patch_index = self.chunk().code.len(); @@ -785,12 +792,12 @@ impl ExprVisitor for Compiler { let name = OP_NAMES .get(&expr.op.kind) .expect("invalid binary operator"); - let constant_id = self.insert_constant(StrInst::create(name))?; + let constant_id = self.insert_constant(self.create_str(name))?; self.emit(expr_line_number(expr), Op::GetAttr(constant_id)); // convert RHS to a bool if we're doing AND or OR if let TokenKind::And | TokenKind::Or = expr.op.kind { - let constant_id = self.insert_constant(StrInst::create("__bool__"))?; + let constant_id = self.insert_constant(self.create_str("__bool__"))?; self.emit(expr_line_number(&*expr.rhs), Op::GetAttr(constant_id)); self.emit(expr_line_number(&*expr.rhs), Op::Call(0)); } @@ -828,7 +835,7 @@ impl ExprVisitor for Compiler { }); self.compile_expr(&expr.expr)?; let name = OP_NAMES.get(&expr.op.kind).expect("invalid unary operator"); - let constant_id = self.insert_constant(StrInst::create(name))?; + let constant_id = self.insert_constant(self.create_str(name))?; self.emit(expr_line_number(expr), Op::GetAttr(constant_id)); self.emit(expr_line_number(expr), Op::Call(0)); Ok(()) @@ -852,7 +859,7 @@ impl ExprVisitor for Compiler { fn visit_get_expr(&mut self, expr: &GetExpr) -> Result<()> { self.compile_expr(&expr.expr)?; - let constant_id = self.insert_constant(StrInst::create(&expr.name.text))?; + let constant_id = self.insert_constant(self.create_str(&expr.name.text))?; self.emit(expr_line_number(expr), Op::GetAttr(constant_id)); Ok(()) } @@ -878,25 +885,25 @@ impl ExprVisitor for Compiler { } TokenKind::Number => { let obj = if expr.token.text.contains('.') { - FloatInst::create(expr.token.text.parse().unwrap()) as ObjP + self.create_float(expr.token.text.parse().unwrap()) } else { - IntInst::create(expr.token.text.parse().unwrap()) as ObjP + self.create_int(expr.token.text.parse().unwrap()) }; let constant_id = self.insert_constant(obj)?; self.emit(expr_line_number(expr), Op::PushConstant(constant_id)); } TokenKind::String => { let constant_id = - self.insert_constant(StrInst::create(unescape(&expr.token.text)))?; + self.insert_constant(self.create_str(unescape(&expr.token.text)))?; self.emit(expr_line_number(expr), Op::PushConstant(constant_id)); } TokenKind::True | TokenKind::False => { let constant_id = - self.insert_constant(BoolInst::create(expr.token.kind == TokenKind::True))?; + self.insert_constant(self.create_bool(expr.token.kind == TokenKind::True))?; self.emit(expr_line_number(expr), Op::PushConstant(constant_id)); } TokenKind::Nil => { - let constant_id = self.insert_constant(NilInst::create())?; + let constant_id = self.insert_constant(self.create_nil())?; self.emit(expr_line_number(expr), Op::PushConstant(constant_id)); } _ => unreachable!(), @@ -954,7 +961,7 @@ impl ExprVisitor for Compiler { } // always end with a "return nil" - let nil = self.insert_constant(NilInst::create())?; + let nil = self.insert_constant(self.create_nil())?; self.emit(end_line, Op::PushConstant(nil)); self.emit(end_line, Op::Return); @@ -962,7 +969,7 @@ impl ExprVisitor for Compiler { // create the function let chunk = self.chunks.pop().unwrap(); - let fun = UserFunctionInst::create(chunk, expr.params.len() as Argc); + let fun = self.create_user_function(chunk, expr.params.len() as Argc); // register the function as a constant let fun_constant = self.insert_constant(fun)?; @@ -982,3 +989,9 @@ impl ExprVisitor for Compiler { Ok(()) } } + +impl ObjFactory for Compiler<'_> { + fn builtins(&self) -> &HashMap { + &self.builtins + } +} diff --git a/src/disassemble.rs b/src/disassemble.rs index 55fd212..864d10d 100644 --- a/src/disassemble.rs +++ b/src/disassemble.rs @@ -33,7 +33,7 @@ fn disassemble_chunk(chunk: &Chunk, constants: &Vec, globals: &Vec } Op::PushConstant(constant_id) => { op_str = "PUSH_CONSTANT"; - arg = format!("{}", &constants[*constant_id as usize]); + arg = format!("{}", &constants[*constant_id as usize].borrow()); info = format!("(constant ID {constant_id})"); } Op::GetLocal(local_id) => { @@ -60,12 +60,12 @@ fn disassemble_chunk(chunk: &Chunk, constants: &Vec, globals: &Vec } Op::GetAttr(constant_id) => { op_str = "GET_ATTR"; - arg = format!("{}", &constants[*constant_id as usize]); + arg = format!("{}", &constants[*constant_id as usize].borrow()); info = format!("(constant ID {constant_id})"); } Op::SetAttr(constant_id) => { op_str = "SET_ATTR"; - arg = format!("{}", &constants[*constant_id as usize]); + arg = format!("{}", &constants[*constant_id as usize].borrow()); info = format!("(constant ID {constant_id})"); } Op::Jump(jump_offset) => { @@ -145,7 +145,11 @@ pub fn disassemble(chunk: &Chunk, constants: &Vec, globals: &Vec) disassemble_chunk(chunk, constants, globals); for constant in constants { - if let Some(fun) = constant.as_any().downcast_ref::() { + if let Some(fun) = constant + .borrow() + .as_any() + .downcast_ref::() + { println!(); println!( "== {} starting on line {}", diff --git a/src/main.rs b/src/main.rs index 819c9c8..73e05a8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,7 @@ // trait_upcasting - https://github.com/rust-lang/rust/issues/65991 // stabilization in progress #![feature(trait_upcasting)] +#![feature(coerce_unsized)] mod ast; mod builtins; @@ -11,6 +12,7 @@ mod parser; mod token; mod vm; +use std::collections::HashMap; use std::fmt; use std::fs::File; use std::io::prelude::*; @@ -52,10 +54,12 @@ fn main() -> Result<(), Box> { } // initialize type system - obj::init_types(); + let mut builtins = HashMap::new(); + obj::init_types(&mut builtins); + crate::builtins::init_builtins(&mut builtins); // compile - let (chunk, constants, globals) = compiler::Compiler::default().compile(&ast)?; + let (chunk, constants, globals) = compiler::Compiler::new(&builtins).compile(&ast)?; if args.disassemble { disassemble::disassemble(&chunk, &constants, &globals); @@ -63,7 +67,7 @@ fn main() -> Result<(), Box> { } // run - let mut vm = vm::Vm::new(chunk.into(), constants, globals); + let mut vm = vm::Vm::new(chunk.into(), constants, globals, builtins); vm.run(); Ok(()) diff --git a/src/obj.rs b/src/obj.rs index f562e5f..b797f8e 100644 --- a/src/obj.rs +++ b/src/obj.rs @@ -5,22 +5,23 @@ use std::any::Any; use std::collections::HashMap; use std::fmt::{self, Debug, Display}; use std::ptr; -use std::sync::{Arc, LazyLock, Mutex, RwLock}; +use std::rc::Rc; -use common_macros::hash_map; +use gc::{Finalize, Gc, GcCell, Trace}; use crate::vm::{Argc, Chunk, Frame, Vm}; -pub type Ptr = Arc; -pub type ObjP = Ptr; -pub type Attrs = RwLock>; +pub type Ptr = Gc>; +pub type ObjP = Ptr; +pub type Attrs = HashMap; /// Downcast an object pointer to a concrete type, and do something with that object. pub fn with_obj_downcast(ptr: ObjP, closure: impl FnOnce(&T) -> Out) -> Out where T: Obj + 'static, { - if let Some(obj) = ptr.as_any().downcast_ref::() { + let borrowed = ptr.borrow(); + if let Some(obj) = borrowed.as_any().downcast_ref::() { closure(obj) } else { panic!( @@ -35,115 +36,136 @@ pub fn obj_is_inst(ptr: &ObjP) -> bool where T: Obj + 'static, { - ptr.as_any().downcast_ref::().is_some() + let borrowed = ptr.borrow(); + borrowed.as_any().downcast_ref::().is_some() } -/// Builtin types macro -macro_rules! builtin_types { - ( - $( - $type_name:ident => { $( $vtable_name:ident => $vtable_value:expr ),* $(,)? } - ),+ $(,)? - ) => { - pub static TYPES: LazyLock>>> = LazyLock::new(|| { - RwLock::new(hash_map! { - $( - stringify!($type_name).to_string() => make_ptr(TypeInst::new(stringify!($type_name))) - ),+ - }) - }); - - static TYPE_SYSTEM_INIT: LazyLock> = LazyLock::new(|| Mutex::new(false)); - - /// Initialize types. - /// - /// This should only be called once. - fn init_base_types() { - // instantiate - $( - if stringify!($type_name) != "Type" { - let mut types_ptr = TYPES.try_write().unwrap(); - let mut ty = types_ptr.get_mut(stringify!($type_name)).unwrap(); - Arc::get_mut(&mut ty).unwrap().instantiate(); - } - )+ - - // __name__ - $( - { - let name = StrInst::create(stringify!($type_name)); - let ty = Ptr::clone(&TYPES.try_read().unwrap()[stringify!($type_name)]); - ty.set_attr("__name__", name); - } - )+ - - // vtable - $( - { - let ptr = Ptr::clone(&TYPES.try_write().unwrap()[stringify!($type_name)]); - $( - ptr.vtable.insert($vtable_name.into(), $vtable_value); - )* - } - )+ - } - }; +pub fn upcast_obj(ptr: Ptr) -> ObjP { + unsafe { + let ptr = Ptr::into_raw(ptr) as *const GcCell; + Ptr::from_raw(ptr) + } } -pub(crate) fn init_types() { - // Taking the lock here will lock the entire function from being run twice - // simulataneously and prevent race conditions. - // - // Race conditions really can only happen during testing so this is just a precaution. - let mut lock_guard = TYPE_SYSTEM_INIT.lock().unwrap(); - if *lock_guard { - if cfg!(test) { - return; - } else { - panic!("do not initialize type system twice"); - } +pub fn init_types(builtins: &mut HashMap) { + #![allow(non_snake_case)] + macro_rules! types { + (base_type: $base_type:ident, $($name:ident),* $(,)?) => { + $( + let $name = make_ptr(TypeInst::new(stringify!($name))); + )* + + $( + $name.borrow_mut().instantiate(upcast_obj(Gc::clone(&$base_type))); + )* + + $( + builtins.insert(stringify!($name).to_string(), upcast_obj($name)); + )* + }; + } + types! { + // base type + base_type: Type, + // type definitions + Type, + Obj, + Str, + Int, + Float, + Bool, + Nil, + BuiltinFunction, + UserFunction, + Method, + } +} + +pub trait ObjFactory { + fn builtins(&self) -> &HashMap; + + fn create_obj(&self) -> ObjP { + upcast_obj(ObjInst::create(ObjP::clone( + self.builtins().get("Obj").unwrap(), + ))) } - // Init type_type here - { - let types_ptr = TYPES.try_read().unwrap(); - let type_ptr = types_ptr.get("Type").unwrap(); - type_ptr.set_attr( - "__type__", - Ptr::clone(&TYPES.try_read().unwrap()["Type"]) as ObjP, - ); - drop(types_ptr); - //let mut types_ptr = TYPES.try_write().unwrap(); - //let mut type_ptr = types_ptr.get_mut("Type").unwrap(); - //Arc::get_mut(&mut type_ptr).unwrap().base.is_instantiated = true; + fn create_type(&self, name: impl ToString) -> ObjP { + upcast_obj(TypeInst::create( + ObjP::clone(self.builtins().get("Type").unwrap()), + name, + )) } - // Init the rest of the types - init_base_types(); + fn create_str(&self, str_value: impl ToString) -> ObjP { + upcast_obj(StrInst::create( + ObjP::clone(self.builtins().get("Str").unwrap()), + str_value, + )) + } - *lock_guard = true; + fn create_int(&self, int_value: i64) -> ObjP { + upcast_obj(IntInst::create( + ObjP::clone(self.builtins().get("Int").unwrap()), + int_value, + )) + } + + fn create_float(&self, float_value: f64) -> ObjP { + upcast_obj(FloatInst::create( + ObjP::clone(self.builtins().get("Float").unwrap()), + float_value, + )) + } + + fn create_bool(&self, bool_value: bool) -> ObjP { + upcast_obj(BoolInst::create( + ObjP::clone(self.builtins().get("Bool").unwrap()), + bool_value, + )) + } + + fn create_nil(&self) -> ObjP { + upcast_obj(NilInst::create(ObjP::clone( + self.builtins().get("Nil").unwrap(), + ))) + } + + fn create_builtin_function( + &self, + name: impl ToString, + function: BuiltinFunctionPtr, + arity: Argc, + ) -> ObjP { + upcast_obj(BuiltinFunctionInst::create( + ObjP::clone(self.builtins().get("BuiltinFunction").unwrap()), + name, + function, + arity, + )) + } + + fn create_user_function(&self, chunk: Chunk, arity: Argc) -> ObjP { + upcast_obj(UserFunctionInst::create( + ObjP::clone(self.builtins().get("UserFunction").unwrap()), + chunk, + arity, + )) + } + + fn create_method(&self, self_binding: ObjP, function: ObjP) -> ObjP { + upcast_obj(MethodInst::create( + ObjP::clone(self.builtins().get("Method").unwrap()), + self_binding, + function, + )) + } } -fn placeholder(_: &mut Vm, _: Vec) -> ObjP { - NilInst::create() -} - -fn to_string(_: &mut Vm, args: Vec) -> ObjP { - let str_value = format!("{}", args[0]); - StrInst::create(str_value) -} - -builtin_types! { - Type => {}, - Obj => {}, - Str => {}, - Int => {}, - Float => {}, - Bool => {}, - Nil => {}, - BuiltinFunction => {}, - UserFunction => {}, - Method => {}, +impl ObjFactory for HashMap { + fn builtins(&self) -> &Self { + self + } } /// Convenience function for creating pointers, in case the `Arc>` pointer type has to @@ -152,36 +174,35 @@ builtin_types! { /// I would implement this as a `From` but it doesn't seem to work for a foreign type, and I'm /// not sure why. pub fn make_ptr(obj: T) -> Ptr { - Arc::new(obj) + Ptr::new(GcCell::new(obj)) } //////////////////////////////////////////////////////////////////////////////// // Obj //////////////////////////////////////////////////////////////////////////////// -pub trait Obj: Debug + Display + Any + Send + Sync { - fn instantiate(&mut self); +pub trait Obj: Debug + Display + Any + Trace { + fn instantiate(&mut self, ty: ObjP); fn is_instantiated(&self) -> bool; fn attrs(&self) -> &Attrs; + fn attrs_mut(&mut self) -> &mut Attrs; - fn set_attr(&self, name: &str, value: ObjP) { - let mut borrowed = self.attrs().try_write().unwrap(); - borrowed.insert(name.to_string(), value); + fn set_attr(&mut self, name: &str, value: ObjP) { + self.attrs_mut().insert(name.to_string(), value); } fn get_attr(&self, name: &str) -> Option { - let borrowed = self.attrs().try_read().unwrap(); - borrowed.get(name).map(Arc::clone) + self.attrs().get(name).map(Ptr::clone) } fn type_inst(&self) -> ObjP { self.get_attr("__type__").unwrap() } - fn type_name(&self) -> Arc { + fn type_name(&self) -> Rc { with_obj_downcast(self.type_inst(), |type_inst: &TypeInst| { - Arc::clone(&type_inst.name) + Rc::clone(&type_inst.name) }) } @@ -210,16 +231,20 @@ pub trait Obj: Debug + Display + Any + Send + Sync { // BaseObjInst //////////////////////////////////////////////////////////////////////////////// -#[derive(Debug, Default)] +#[derive(Debug, Default, Trace)] struct BaseObjInst { - attrs: RwLock>, + attrs: Attrs, is_instantiated: bool, } +impl Finalize for BaseObjInst { + fn finalize(&self) {} +} + impl Clone for BaseObjInst { fn clone(&self) -> Self { Self { - attrs: RwLock::new(self.attrs.try_read().unwrap().clone()), + attrs: self.attrs.clone(), is_instantiated: self.is_instantiated, } } @@ -232,13 +257,8 @@ impl Display for BaseObjInst { } impl Obj for BaseObjInst { - fn instantiate(&mut self) { - if self.get_attr("__type__").is_none() { - self.set_attr( - "__type__", - Ptr::clone(&TYPES.try_read().unwrap()["Obj"]) as ObjP, - ); - } + fn instantiate(&mut self, ty: ObjP) { + self.set_attr("__type__", ty); // TODO BaseObjInst::instantiate - instantiate VTable // Okay, we are running into a little snag here: @@ -275,15 +295,18 @@ impl Obj for BaseObjInst { &self.attrs } + fn attrs_mut(&mut self) -> &mut Attrs { + &mut self.attrs + } + fn equals(&self, other: &dyn Obj) -> bool { if let Some(other) = other.as_any().downcast_ref::() { // compare all attrs - let borrowed = self.attrs.try_read().unwrap(); - borrowed.iter().all(|(k1, v1)| { - let borrowed = other.attrs.try_read().unwrap(); - borrowed + self.attrs.iter().all(|(k1, v1)| { + other + .attrs .get(k1) - .map(|v2| v2.equals(v1.as_ref())) + .map(|v2| v2.borrow().equals(&*v1.borrow())) .unwrap_or(false) }) && self.is_instantiated == other.is_instantiated } else { @@ -310,6 +333,10 @@ macro_rules! impl_base_obj { self.$base_name.attrs() } + fn attrs_mut(&mut self) -> &mut Attrs { + self.$base_name.attrs_mut() + } + fn as_any(&self) -> &dyn Any { self } @@ -327,7 +354,7 @@ macro_rules! impl_base_obj { // ObjInst //////////////////////////////////////////////////////////////////////////////// -#[derive(Debug)] +#[derive(Debug, Trace)] pub struct ObjInst { base: BaseObjInst, } @@ -339,13 +366,17 @@ impl ObjInst { } } - pub fn create() -> Ptr { + pub fn create(ty: ObjP) -> Ptr { let mut new = Self::new(); - new.instantiate(); + new.instantiate(ty); make_ptr(new) } } +impl Finalize for ObjInst { + fn finalize(&self) {} +} + impl Display for ObjInst { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { write!(fmt, "", (self as *const _ as usize)) @@ -353,8 +384,8 @@ impl Display for ObjInst { } impl Obj for ObjInst { - fn instantiate(&mut self) { - self.base.instantiate(); + fn instantiate(&mut self, ty: ObjP) { + self.base.instantiate(ty); } fn equals(&self, other: &dyn Obj) -> bool { @@ -372,28 +403,34 @@ impl Obj for ObjInst { // TypeInst //////////////////////////////////////////////////////////////////////////////// +#[derive(Trace)] pub struct TypeInst { - name: Arc, + #[unsafe_ignore_trace] + name: Rc, base: BaseObjInst, vtable: HashMap, } +impl Finalize for TypeInst { + fn finalize(&self) {} +} + impl TypeInst { pub fn new(name: impl ToString) -> Self { Self { - name: Arc::new(name.to_string()), + name: Rc::new(name.to_string()), base: Default::default(), vtable: Default::default(), } } - pub fn create(name: impl ToString) -> Ptr { + pub fn create(ty: ObjP, name: impl ToString) -> Ptr { let mut new = Self::new(name); - new.instantiate(); + new.instantiate(ty); make_ptr(new) } - pub fn name(&self) -> &Arc { + pub fn name(&self) -> &Rc { &self.name } } @@ -421,12 +458,8 @@ impl Display for TypeInst { } impl Obj for TypeInst { - fn instantiate(&mut self) { - self.set_attr( - "__type__", - Ptr::clone(&TYPES.try_read().unwrap()["Type"]) as ObjP, - ); - self.base.instantiate(); + fn instantiate(&mut self, ty: ObjP) { + self.base.instantiate(ty); } fn equals(&self, other: &dyn Obj) -> bool { @@ -450,44 +483,45 @@ impl Obj for TypeInst { // StrInst //////////////////////////////////////////////////////////////////////////////// -#[derive(Debug)] +#[derive(Debug, Trace)] pub struct StrInst { - str_value: Arc, + #[unsafe_ignore_trace] + str_value: Rc, base: BaseObjInst, } +impl StrInst { + pub fn new(str_value: impl ToString) -> Self { + Self { + str_value: Rc::new(str_value.to_string()), + base: Default::default(), + } + } + + pub fn create(ty: ObjP, str_value: impl ToString) -> Ptr { + let mut new = Self::new(str_value); + new.instantiate(ty); + make_ptr(new) + } + + pub fn str_value(&self) -> &Rc { + &self.str_value + } +} + +impl Finalize for StrInst { + fn finalize(&self) {} +} + impl Display for StrInst { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { write!(fmt, "{}", self.str_value) } } -impl StrInst { - pub fn new(str_value: impl ToString) -> Self { - Self { - str_value: Arc::new(str_value.to_string()), - base: Default::default(), - } - } - - pub fn create(str_value: impl ToString) -> Ptr { - let mut new = Self::new(str_value); - new.instantiate(); - make_ptr(new) - } - - pub fn str_value(&self) -> &Arc { - &self.str_value - } -} - impl Obj for StrInst { - fn instantiate(&mut self) { - self.set_attr( - "__type__", - Ptr::clone(&TYPES.try_read().unwrap()["Str"]) as ObjP, - ); - self.base.instantiate(); + fn instantiate(&mut self, ty: ObjP) { + self.base.instantiate(ty); } fn is_truthy(&self) -> bool { @@ -509,7 +543,7 @@ impl Obj for StrInst { // IntInst //////////////////////////////////////////////////////////////////////////////// -#[derive(Debug)] +#[derive(Debug, Trace)] pub struct IntInst { int_value: i64, base: BaseObjInst, @@ -523,9 +557,9 @@ impl IntInst { } } - pub fn create(int_value: i64) -> Ptr { + pub fn create(ty: ObjP, int_value: i64) -> Ptr { let mut new = Self::new(int_value); - new.instantiate(); + new.instantiate(ty); make_ptr(new) } @@ -534,6 +568,10 @@ impl IntInst { } } +impl Finalize for IntInst { + fn finalize(&self) {} +} + impl Display for IntInst { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { write!(fmt, "{}", self.int_value) @@ -541,12 +579,8 @@ impl Display for IntInst { } impl Obj for IntInst { - fn instantiate(&mut self) { - self.set_attr( - "__type__", - Ptr::clone(&TYPES.try_read().unwrap()["Int"]) as ObjP, - ); - self.base.instantiate(); + fn instantiate(&mut self, ty: ObjP) { + self.base.instantiate(ty); } fn is_truthy(&self) -> bool { @@ -570,7 +604,7 @@ impl Obj for IntInst { // FloatInst //////////////////////////////////////////////////////////////////////////////// -#[derive(Debug)] +#[derive(Debug, Trace)] pub struct FloatInst { float_value: f64, base: BaseObjInst, @@ -584,9 +618,9 @@ impl FloatInst { } } - pub fn create(float_value: f64) -> Ptr { + pub fn create(ty: ObjP, float_value: f64) -> Ptr { let mut new = Self::new(float_value); - new.instantiate(); + new.instantiate(ty); make_ptr(new) } @@ -595,6 +629,10 @@ impl FloatInst { } } +impl Finalize for FloatInst { + fn finalize(&self) {} +} + impl Display for FloatInst { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { write!(fmt, "{}", self.float_value) @@ -602,12 +640,8 @@ impl Display for FloatInst { } impl Obj for FloatInst { - fn instantiate(&mut self) { - self.set_attr( - "__type__", - Ptr::clone(&TYPES.try_read().unwrap()["Float"]) as ObjP, - ); - self.base.instantiate(); + fn instantiate(&mut self, ty: ObjP) { + self.base.instantiate(ty); } fn is_truthy(&self) -> bool { @@ -631,7 +665,7 @@ impl Obj for FloatInst { // BoolInst //////////////////////////////////////////////////////////////////////////////// -#[derive(Debug)] +#[derive(Debug, Trace)] pub struct BoolInst { bool_value: bool, base: BaseObjInst, @@ -645,10 +679,10 @@ impl BoolInst { } } - pub fn create(bool_value: bool) -> Ptr { + pub fn create(ty: ObjP, bool_value: bool) -> Ptr { // TODO BoolInst::create : interning let mut new = Self::new(bool_value); - new.instantiate(); + new.instantiate(ty); make_ptr(new) } @@ -657,6 +691,10 @@ impl BoolInst { } } +impl Finalize for BoolInst { + fn finalize(&self) {} +} + impl Display for BoolInst { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { write!(fmt, "{}", self.bool_value) @@ -664,12 +702,8 @@ impl Display for BoolInst { } impl Obj for BoolInst { - fn instantiate(&mut self) { - self.set_attr( - "__type__", - Ptr::clone(&TYPES.try_read().unwrap()["Bool"]) as ObjP, - ); - self.base.instantiate(); + fn instantiate(&mut self, ty: ObjP) { + self.base.instantiate(ty); } fn is_truthy(&self) -> bool { @@ -691,7 +725,7 @@ impl Obj for BoolInst { // NilInst //////////////////////////////////////////////////////////////////////////////// -#[derive(Debug, Default)] +#[derive(Debug, Default, Trace)] pub struct NilInst { base: BaseObjInst, } @@ -701,14 +735,18 @@ impl NilInst { Default::default() } - pub fn create() -> Ptr { + pub fn create(ty: ObjP) -> Ptr { // TODO NilInst::create : interning let mut new = Self::new(); - new.instantiate(); + new.instantiate(ty); make_ptr(new) } } +impl Finalize for NilInst { + fn finalize(&self) {} +} + impl Display for NilInst { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { write!(fmt, "nil") @@ -716,12 +754,8 @@ impl Display for NilInst { } impl Obj for NilInst { - fn instantiate(&mut self) { - self.set_attr( - "__type__", - Ptr::clone(&TYPES.try_read().unwrap()["Nil"]) as ObjP, - ); - self.base.instantiate(); + fn instantiate(&mut self, ty: ObjP) { + self.base.instantiate(ty); } fn is_truthy(&self) -> bool { @@ -741,27 +775,33 @@ impl Obj for NilInst { pub type BuiltinFunctionPtr = fn(vm: &mut Vm, args: Vec) -> ObjP; -#[derive(Debug)] +#[derive(Debug, Trace)] pub struct BuiltinFunctionInst { base: BaseObjInst, name: String, + #[unsafe_ignore_trace] function: BuiltinFunctionPtr, arity: Argc, } impl BuiltinFunctionInst { - pub fn new(name: String, function: BuiltinFunctionPtr, arity: Argc) -> Self { + pub fn new(name: impl ToString, function: BuiltinFunctionPtr, arity: Argc) -> Self { Self { base: Default::default(), - name, + name: name.to_string(), function, arity, } } - pub fn create(name: String, function: BuiltinFunctionPtr, arity: Argc) -> Ptr { + pub fn create( + ty: ObjP, + name: impl ToString, + function: BuiltinFunctionPtr, + arity: Argc, + ) -> Ptr { let mut new = Self::new(name, function, arity); - new.instantiate(); + new.instantiate(ty); make_ptr(new) } @@ -770,6 +810,10 @@ impl BuiltinFunctionInst { } } +impl Finalize for BuiltinFunctionInst { + fn finalize(&self) {} +} + impl Display for BuiltinFunctionInst { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { write!( @@ -783,12 +827,8 @@ impl Display for BuiltinFunctionInst { } impl Obj for BuiltinFunctionInst { - fn instantiate(&mut self) { - self.set_attr( - "__type__", - Ptr::clone(&TYPES.try_read().unwrap()["UserFunction"]) as ObjP, - ); - self.base.instantiate(); + fn instantiate(&mut self, ty: ObjP) { + self.base.instantiate(ty); } fn arity(&self) -> Option { @@ -825,11 +865,13 @@ impl Obj for BuiltinFunctionInst { // UserFunctionInst //////////////////////////////////////////////////////////////////////////////// -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Trace)] pub struct UserFunctionInst { base: BaseObjInst, - name: Arc, - chunk: Arc, + #[unsafe_ignore_trace] + name: Rc, + #[unsafe_ignore_trace] + chunk: Rc, arity: Argc, captures: Vec, } @@ -838,16 +880,16 @@ impl UserFunctionInst { pub fn new(chunk: Chunk, arity: Argc) -> Self { Self { base: Default::default(), - name: Arc::new("(anonymous)".to_string()), - chunk: Arc::new(chunk), + name: Rc::new("(anonymous)".to_string()), + chunk: Rc::new(chunk), arity, captures: Default::default(), } } - pub fn create(chunk: Chunk, arity: Argc) -> Ptr { + pub fn create(ty: ObjP, chunk: Chunk, arity: Argc) -> Ptr { let mut new = Self::new(chunk, arity); - new.instantiate(); + new.instantiate(ty); make_ptr(new) } @@ -855,7 +897,7 @@ impl UserFunctionInst { &self.name } - pub fn set_name(&mut self, name: Arc) { + pub fn set_name(&mut self, name: Rc) { self.name = name; } @@ -868,6 +910,10 @@ impl UserFunctionInst { } } +impl Finalize for UserFunctionInst { + fn finalize(&self) {} +} + impl Display for UserFunctionInst { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { write!( @@ -881,12 +927,8 @@ impl Display for UserFunctionInst { } impl Obj for UserFunctionInst { - fn instantiate(&mut self) { - self.set_attr( - "__type__", - Ptr::clone(&TYPES.try_read().unwrap()["UserFunction"]) as ObjP, - ); - self.base.instantiate(); + fn instantiate(&mut self, ty: ObjP) { + self.base.instantiate(ty); } fn arity(&self) -> Option { @@ -896,8 +938,8 @@ impl Obj for UserFunctionInst { fn call(&self, vm: &mut Vm, argc: Argc) { assert_eq!(argc, self.arity, "argc must match arity"); let new_frame = Frame { - name: Arc::clone(&self.name), - chunk: Arc::clone(&self.chunk), + name: Rc::clone(&self.name), + chunk: Rc::clone(&self.chunk), ip: 0, stack_base: vm.stack().len() - (argc as usize), }; @@ -923,7 +965,7 @@ impl Obj for UserFunctionInst { // MethodInst //////////////////////////////////////////////////////////////////////////////// -#[derive(Debug)] +#[derive(Debug, Trace)] pub struct MethodInst { base: BaseObjInst, self_binding: ObjP, @@ -939,9 +981,9 @@ impl MethodInst { } } - pub fn create(self_binding: ObjP, function: ObjP) -> Ptr { + pub fn create(ty: ObjP, self_binding: ObjP, function: ObjP) -> Ptr { let mut new = Self::new(self_binding, function); - new.instantiate(); + new.instantiate(ty); make_ptr(new) } @@ -950,27 +992,27 @@ impl MethodInst { } } +impl Finalize for MethodInst { + fn finalize(&self) {} +} + impl Display for MethodInst { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - write!(fmt, "{}", self.function) + write!(fmt, "{}", self.function.borrow()) } } impl Obj for MethodInst { - fn instantiate(&mut self) { - self.set_attr( - "__type__", - Ptr::clone(&TYPES.try_read().unwrap()["Method"]) as ObjP, - ); - self.base.instantiate(); + fn instantiate(&mut self, ty: ObjP) { + self.base.instantiate(ty); } fn arity(&self) -> Option { - self.function.arity() + self.function.borrow().arity() } fn call(&self, vm: &mut Vm, argc: Argc) { - self.function.call(vm, argc) + self.function.borrow().call(vm, argc) } fn equals(&self, other: &dyn Obj) -> bool { @@ -991,58 +1033,60 @@ impl Obj for MethodInst { #[test] fn test_new_objects() { - init_types(); + let mut builtins = HashMap::new(); + init_types(&mut builtins); - let type_value = TypeInst::create("Type"); - assert_eq!(&*type_value.type_name(), "Type"); + let type_value = builtins.create_type("Type"); + assert_eq!(&*type_value.borrow().type_name(), "Type"); - let str_value = StrInst::create("asdfasdfasdfasdfasdf"); - assert_eq!(&*str_value.type_name(), "Str"); + let str_value = builtins.create_str("asdfasdfasdfasdfasdf"); + assert_eq!(&*str_value.borrow().type_name(), "Str"); - let int_value = IntInst::create(1234); - assert_eq!(&*int_value.type_name(), "Int"); + let int_value = builtins.create_int(1234); + assert_eq!(&*int_value.borrow().type_name(), "Int"); - let float_value = FloatInst::create(1234.5678); - assert_eq!(&*float_value.type_name(), "Float"); + let float_value = builtins.create_float(1234.5678); + assert_eq!(&*float_value.borrow().type_name(), "Float"); - let nil_value = NilInst::create(); - assert_eq!(&*nil_value.type_name(), "Nil"); + let nil_value = builtins.create_nil(); + assert_eq!(&*nil_value.borrow().type_name(), "Nil"); } #[test] fn test_obj_equals() { - init_types(); + let mut builtins = HashMap::new(); + init_types(&mut builtins); - let int1 = IntInst::create(1234); - let int2 = IntInst::create(1234); + let int1 = builtins.create_int(1234); + let int2 = builtins.create_int(1234); - assert!(int1.equals(int2.as_ref())); - assert!(int2.equals(int1.as_ref())); + assert!(int1.borrow().equals(&*int2.borrow())); + assert!(int2.borrow().equals(&*int1.borrow())); - let float1 = FloatInst::create(1234.0); - assert!(int1.equals(float1.as_ref())); - assert!(float1.equals(int2.as_ref())); + let float1 = builtins.create_float(1234.0); + assert!(int1.borrow().equals(&*float1.borrow())); + assert!(float1.borrow().equals(&*int2.borrow())); // self-equality - let str1 = StrInst::create("1234"); - assert!(str1.equals(str1.as_ref())); + let str1 = builtins.create_str("1234"); + assert!(str1.borrow().equals(&*str1.borrow())); - let str2 = StrInst::create("1234"); - assert!(str1.equals(str2.as_ref())); - assert!(str2.equals(str1.as_ref())); + let str2 = builtins.create_str("1234"); + assert!(str1.borrow().equals(&*str2.borrow())); + assert!(str2.borrow().equals(&*str1.borrow())); - assert!(!str1.equals(float1.as_ref())); - assert!(!str1.equals(int1.as_ref())); + assert!(!str1.borrow().equals(&*float1.borrow())); + assert!(!str1.borrow().equals(&*int1.borrow())); - let obj1 = ObjInst::create(); - let obj2 = ObjInst::create(); - assert!(obj1.equals(obj2.as_ref())); + let obj1 = builtins.create_obj(); + let obj2 = builtins.create_obj(); + assert!(obj1.borrow().equals(&*obj2.borrow())); // these objects aren't equal anymore - obj1.set_attr("my_attr", Ptr::clone(&str2) as ObjP); - assert!(!obj1.equals(obj2.as_ref())); + obj1.borrow_mut().set_attr("my_attr", ObjP::clone(&str2)); + assert!(!obj1.borrow().equals(&*obj2.borrow())); // but now they are! - obj2.set_attr("my_attr", Ptr::clone(&str2) as ObjP); - assert!(obj2.equals(obj1.as_ref())); + obj2.borrow_mut().set_attr("my_attr", ObjP::clone(&str2)); + assert!(obj2.borrow().equals(&*obj1.borrow())); } diff --git a/src/vm.rs b/src/vm.rs index e51dff1..49d8a6e 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -1,6 +1,6 @@ -use std::sync::Arc; +use std::collections::HashMap; +use std::rc::Rc; -use crate::builtins; use crate::obj::*; #[derive(Debug, Clone, Copy, PartialEq)] @@ -62,14 +62,14 @@ pub struct Chunk { #[derive(Debug)] pub struct Frame { - pub(crate) name: Arc, - pub(crate) chunk: Arc, + pub(crate) name: Rc, + pub(crate) chunk: Rc, pub(crate) ip: usize, pub(crate) stack_base: usize, } impl Frame { - pub fn new(name: Arc, chunk: Arc, stack_base: usize) -> Self { + pub fn new(name: Rc, chunk: Rc, stack_base: usize) -> Self { Self { name, chunk, @@ -85,17 +85,20 @@ pub struct Vm { globals: Vec, stack: Vec, frames: Vec, + builtins: HashMap, } impl Vm { /// Create a new virtual machine with the given chunk, constants, and global names. - pub fn new(chunk: Arc, constants: Vec, global_names: Vec) -> Self { + pub fn new( + chunk: Rc, + constants: Vec, + global_names: Vec, + builtins: HashMap, + ) -> Self { // set up globals - let nil = NilInst::create(); - let mut globals: Vec<_> = global_names - .iter() - .map(|_| Ptr::clone(&nil) as ObjP) - .collect(); + let nil = builtins.create_nil(); + let mut globals: Vec<_> = global_names.iter().map(|_| ObjP::clone(&nil)).collect(); let mut register_global = |name: &str, value: ObjP| { let index = global_names @@ -105,14 +108,9 @@ impl Vm { globals[index] = value; }; - register_global( - "print", - BuiltinFunctionInst::create("print".to_string(), builtins::print, 1), - ); - register_global( - "println", - BuiltinFunctionInst::create("println".to_string(), builtins::println, 1), - ); + for (name, builtin) in builtins.iter() { + register_global(&name, ObjP::clone(&builtin)); + } // stack and frames let stack = Vec::new(); @@ -124,6 +122,7 @@ impl Vm { globals, stack, frames, + builtins, } } @@ -231,9 +230,9 @@ impl Vm { // need both declarations to borrow cell value let name_obj = Ptr::clone(&self.constants[constant_id as usize]); let name = - with_obj_downcast(name_obj, |name: &StrInst| Arc::clone(&name.str_value())); + with_obj_downcast(name_obj, |name: &StrInst| Rc::clone(&name.str_value())); let owner = self.pop(); - let value = owner.get_attr(&name); + let value = owner.borrow().get_attr(&name); if let Some(value) = value { self.push(value); } else { @@ -243,18 +242,18 @@ impl Vm { todo!( "throw an error because we couldn't read attr '{}' on '{}'", name, - owner, + owner.borrow(), ); } } Op::SetAttr(constant_id) => { let name_obj = Ptr::clone(&self.constants[constant_id as usize]); let name = - with_obj_downcast(name_obj, |name: &StrInst| Arc::clone(&name.str_value())); + with_obj_downcast(name_obj, |name: &StrInst| Rc::clone(&name.str_value())); let value = self.pop(); let target = self.pop(); - target.set_attr(&name, value); + target.borrow_mut().set_attr(&name, value); } Op::Jump(offset) => { let base = (self.ip() - 1) as JumpOpArg; @@ -264,14 +263,14 @@ impl Vm { Op::JumpFalse(offset) => { let base = (self.ip() - 1) as JumpOpArg; let value = self.peek(); - if !value.is_truthy() { + if !value.borrow().is_truthy() { self.set_ip((base + offset) as usize); } } Op::JumpTrue(offset) => { let base = (self.ip() - 1) as JumpOpArg; let value = self.peek(); - if value.is_truthy() { + if value.borrow().is_truthy() { self.set_ip((base + offset) as usize); } } @@ -280,19 +279,22 @@ impl Vm { let index = self.stack.len() - argc - 1; let fun_ptr = Ptr::clone(&self.stack[index]); - let arity = if let Some(arity) = fun_ptr.arity() { + let arity = if let Some(arity) = fun_ptr.borrow().arity() { arity as usize } else { // TODO Vm::run, Op::Call - throw an exception when the value isn't // callable // BLOCKED-ON: exceptions - todo!("throw an error because we couldn't call {}", fun_ptr); + todo!( + "throw an error because we couldn't call {}", + fun_ptr.borrow() + ); }; // Methods with bound "self" parameter // argc may be mutated let mut argc = argc; - if let Some(method) = fun_ptr.as_any().downcast_ref::() { + if let Some(method) = fun_ptr.borrow().as_any().downcast_ref::() { // shift all of the arguments over by one // (duplicate the last item on the stack and then shift everyone else over) self.stack @@ -309,10 +311,10 @@ impl Vm { // BLOCKED-ON: exceptions todo!( "throw an error because we passed the wrong number of arguments to {}", - fun_ptr + fun_ptr.borrow() ); } - fun_ptr.call(self, argc as Argc); + fun_ptr.borrow().call(self, argc as Argc); } Op::Return => { let return_value = self.pop(); @@ -337,7 +339,7 @@ impl Vm { let stack_base = self.frames[frame_index].stack_base; let value = Ptr::clone(&self.stack[stack_base + (slot as usize)]); fun.push_capture(value); - self.push(make_ptr(fun)); + self.push(upcast_obj(make_ptr(fun))); } Op::Halt => { break; @@ -346,3 +348,9 @@ impl Vm { } } } + +impl ObjFactory for Vm { + fn builtins(&self) -> &HashMap { + &self.builtins + } +}