From 2203957ebb900a05bd1155e3f5dfd8320b5f3817 Mon Sep 17 00:00:00 2001 From: Alek Ratzloff Date: Wed, 25 Sep 2024 10:22:03 -0700 Subject: [PATCH] Typesystem global instance churn, again I don't know if I'm ever going to get this right. It's a massive pain having to pass around the base "Method" type everywhere. It really makes a lot more sense to have it already defined someplace statically available. It makes doing like getting an attribute or vtable entry a lot more ergonomic. Previously we'd have to pass in the Method type every time, which was silly. Now we can just let the MethodInst::instantiate() function query it directly. Like, this is 100000% better. Also, I got rid of get_attr_lazy in favor of get_vtable_attr. I think that I want to unify get_attr and get_vtable_attr, but that would require a GC pointer to the "self" object on every object that you create. That's a bit iffy. But for now, things are feeling a little better and all the tests are passing, so that's good at least. Signed-off-by: Alek Ratzloff --- src/builtins.rs | 109 +++++++++---------- src/compiler.rs | 51 ++++----- src/main.rs | 10 +- src/obj.rs | 251 +++++++++++++++----------------------------- src/obj/function.rs | 10 +- src/obj/macros.rs | 25 +++-- src/vm.rs | 34 ++---- 7 files changed, 194 insertions(+), 296 deletions(-) diff --git a/src/builtins.rs b/src/builtins.rs index c85873c..f7d217b 100644 --- a/src/builtins.rs +++ b/src/builtins.rs @@ -1,19 +1,18 @@ //! Builtin functions. -use std::collections::HashMap; - -use crate::obj::function::{FunctionResult, FunctionState}; +use crate::obj::function::{BuiltinFunctionInst, FunctionResult, FunctionState}; use crate::obj::*; use crate::vm::Vm; -pub fn init_global_builtins(builtins: &mut HashMap) { +pub fn init_global_builtins() { 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 - ); + let builtin_function = BuiltinFunctionInst::create(stringify!($builtin), $builtin, $argc); + $crate::obj::BUILTINS.with_borrow_mut(|builtins| + builtins.insert( + stringify!($builtin).to_string(), + builtin_function + )); })* } } @@ -42,17 +41,16 @@ pub(crate) fn println(vm: &mut Vm, state: FunctionState) -> FunctionResult { match state { FunctionState::Begin => { let obj = vm.peek(); - let method_type = vm.builtins().get("Method").unwrap().clone(); - let to_repr = obj - .borrow_mut() - .get_attr_lazy(obj.clone(), method_type, "to_str") + let method = obj + .borrow() + .get_vtable_attr(obj.clone(), "to_str") .expect("no to_str"); - to_repr.borrow().call(vm, 0); + method.borrow().call(vm, 0); FunctionResult::Yield(0) } FunctionState::Resume(0) => { println!("{}", vm.frame_stack()[0].borrow()); - vm.create_nil().into() + NilInst::create().into() } _ => unreachable!(), } @@ -62,17 +60,16 @@ pub(crate) fn print(vm: &mut Vm, state: FunctionState) -> FunctionResult { match state { FunctionState::Begin => { let obj = vm.peek(); - let method_type = vm.builtins().get("Method").unwrap().clone(); - let to_repr = obj - .borrow_mut() - .get_attr_lazy(obj.clone(), method_type, "to_str") + let method = obj + .borrow() + .get_vtable_attr(obj.clone(), "to_str") .expect("no to_str"); - to_repr.borrow().call(vm, 0); + method.borrow().call(vm, 0); FunctionResult::Yield(0) } FunctionState::Resume(0) => { print!("{}", vm.frame_stack()[0].borrow()); - vm.create_nil().into() + NilInst::create().into() } _ => unreachable!(), } @@ -91,12 +88,12 @@ impl BaseObjInst { match state { FunctionState::Begin => { let this = vm.frame_stack()[0].clone(); - let method_type = vm.builtins().get("Method").unwrap().clone(); - let to_repr = this - .borrow_mut() - .get_attr_lazy(this.clone(), method_type, "to_repr") + let method = this + .borrow() + .get_vtable_attr(this.clone(), "to_repr") + .clone() .expect("no to_repr"); - to_repr.borrow().call(vm, 0); + method.borrow().call(vm, 0); FunctionResult::Yield(0) } FunctionState::Resume(0) => FunctionResult::Return, @@ -106,12 +103,11 @@ impl BaseObjInst { pub(crate) fn to_repr(vm: &mut Vm, _state: FunctionState) -> FunctionResult { let str_value = format!("{}", vm.frame_stack()[0].borrow()); - vm.create_str(str_value).into() + StrInst::create(str_value).into() } pub(crate) fn to_bool(vm: &mut Vm, _state: FunctionState) -> FunctionResult { - vm.create_bool(vm.frame_stack()[0].borrow().is_truthy()) - .into() + BoolInst::create(vm.frame_stack()[0].borrow().is_truthy()).into() } // @@ -122,14 +118,14 @@ impl BaseObjInst { let lhs = vm.frame_stack()[0].borrow(); let rhs = vm.frame_stack()[1].borrow(); let result = lhs.is_truthy() && rhs.is_truthy(); - vm.create_bool(result).into() + BoolInst::create(result).into() } pub(crate) fn or(vm: &mut Vm, _state: FunctionState) -> FunctionResult { let lhs = vm.frame_stack()[0].borrow(); let rhs = vm.frame_stack()[1].borrow(); let result = lhs.is_truthy() || rhs.is_truthy(); - vm.create_bool(result).into() + BoolInst::create(result).into() } pub(crate) fn ne(vm: &mut Vm, state: FunctionState) -> FunctionResult { @@ -137,19 +133,19 @@ impl BaseObjInst { FunctionState::Begin => { // we actually want to be calling the lhs's eq function and then negate it let lhs = vm.frame_stack()[0].clone(); - let method_type = vm.builtins().get("Method").unwrap().clone(); - let eq_method = lhs - .borrow_mut() - .get_attr_lazy(lhs.clone(), method_type, "__eq__") + let method = lhs + .borrow() + .get_vtable_attr(lhs.clone(), "__eq__") .expect("no __eq__"); + let rhs = vm.frame_stack()[1].clone(); vm.push(rhs); - eq_method.borrow().call(vm, 1); + method.borrow().call(vm, 1); FunctionResult::Yield(0) } FunctionState::Resume(0) => { let result = !vm.peek().borrow().is_truthy(); - vm.create_bool(result).into() + BoolInst::create(result).into() } _ => { unreachable!() @@ -161,24 +157,23 @@ impl BaseObjInst { let lhs = vm.frame_stack()[0].borrow(); let rhs = vm.frame_stack()[1].borrow(); let equals = lhs.equals(&*rhs); - vm.create_bool(equals).into() + BoolInst::create(equals).into() } pub(crate) fn not(vm: &mut Vm, state: FunctionState) -> FunctionResult { match state { FunctionState::Begin => { let obj = vm.peek(); - let method_type = vm.builtins().get("Method").unwrap().clone(); - let to_bool = obj - .borrow_mut() - .get_attr_lazy(obj.clone(), method_type, "to_bool") + let method = obj + .borrow() + .get_vtable_attr(obj.clone(), "to_bool") .expect("no to_bool"); - to_bool.borrow().call(vm, 0); + method.borrow().call(vm, 0); FunctionResult::Yield(0) } FunctionState::Resume(0) => { let value = vm.peek().borrow().is_truthy(); - vm.create_bool(!value).into() + BoolInst::create(!value).into() } _ => unreachable!(), } @@ -223,14 +218,14 @@ impl StrInst { with_obj_downcast(vm.frame_stack()[0].clone(), |str_inst: &StrInst| { str_inst.str_value().as_str().escape_default().collect() }); - vm.create_str(format!("'{}'", escaped)).into() + StrInst::create(format!("'{}'", escaped)).into() } pub(crate) fn len(vm: &mut Vm, _state: FunctionState) -> FunctionResult { let len = with_obj_downcast(vm.frame_stack()[0].clone(), |str_inst: &StrInst| { str_inst.str_value().len() as i64 }); - vm.create_int(len).into() + IntInst::create(len).into() } pub(crate) fn add(vm: &mut Vm, _state: FunctionState) -> FunctionResult { @@ -246,7 +241,7 @@ impl StrInst { } let new = format!("{}{}", lhs.borrow(), rhs.borrow()); - vm.create_str(new).into() + StrInst::create(new).into() } pub(crate) fn mul(vm: &mut Vm, _state: FunctionState) -> FunctionResult { @@ -264,7 +259,7 @@ impl StrInst { }; let repeat_count = repeat_count.max(0) as usize; let new = format!("{}", lhs.borrow()).repeat(repeat_count); - vm.create_str(new).into() + StrInst::create(new).into() } } @@ -281,9 +276,9 @@ macro_rules! bin_op_math { let lhs_value = with_obj_downcast(lhs, IntInst::int_value); let result = if let Some(int_inst) = rhs.borrow().as_any().downcast_ref::() { - vm.create_int(lhs_value $op int_inst.int_value()) + IntInst::create(lhs_value $op int_inst.int_value()) } else if let Some(float_inst) = rhs.borrow().as_any().downcast_ref::() { - vm.create_float(lhs_value as f64 $op float_inst.float_value()) + FloatInst::create(lhs_value as f64 $op float_inst.float_value()) } else { // TODO IntInst arithmetic operator - throw an exception when RHS is not Int, Float // BLOCKED-ON: exceptions @@ -306,9 +301,9 @@ macro_rules! bin_op_logical { let lhs_value = with_obj_downcast(lhs, IntInst::int_value); let result = if let Some(int_inst) = rhs.borrow().as_any().downcast_ref::() { - vm.create_bool(lhs_value $op int_inst.int_value()) + BoolInst::create(lhs_value $op int_inst.int_value()) } else if let Some(float_inst) = rhs.borrow().as_any().downcast_ref::() { - vm.create_bool((lhs_value as f64) $op float_inst.float_value()) + BoolInst::create((lhs_value as f64) $op float_inst.float_value()) } else { // TODO IntInst logical operator - throw an exception when RHS is not Int, Float // BLOCKED-ON: exceptions @@ -334,13 +329,13 @@ impl IntInst { let lhs_value = with_obj_downcast(lhs, IntInst::int_value); let result = if let Some(int_inst) = rhs.borrow().as_any().downcast_ref::() { - vm.create_int(lhs_value * int_inst.int_value()) + IntInst::create(lhs_value * int_inst.int_value()) } else if let Some(float_inst) = rhs.borrow().as_any().downcast_ref::() { - vm.create_float(lhs_value as f64 * float_inst.float_value()) + FloatInst::create(lhs_value as f64 * float_inst.float_value()) } else if let Some(str_inst) = rhs.borrow().as_any().downcast_ref::() { // TODO IntInst::mul - maybe convert this to just call Str.mul with arguments reversed? // Just so we have the same logic here - vm.create_str(str_inst.str_value().repeat(lhs_value as usize)) + StrInst::create(str_inst.str_value().repeat(lhs_value as usize)) } else { // TODO IntInst::mul - throw an exception when RHS is not Int, Float, Str // BLOCKED-ON: exceptions @@ -365,12 +360,12 @@ impl IntInst { pub(crate) fn pos(vm: &mut Vm, _state: FunctionState) -> FunctionResult { let lhs = vm.frame_stack()[0].clone(); let value = with_obj_downcast(lhs, IntInst::int_value); - vm.create_int(value.abs()).into() + IntInst::create(value.abs()).into() } pub(crate) fn neg(vm: &mut Vm, _state: FunctionState) -> FunctionResult { let lhs = vm.frame_stack()[0].clone(); let value = with_obj_downcast(lhs, IntInst::int_value); - vm.create_int(-value).into() + IntInst::create(-value).into() } } diff --git a/src/compiler.rs b/src/compiler.rs index f185437..cd3e96c 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -407,22 +407,21 @@ impl Display for CompileError { //////////////////////////////////////////////////////////////////////////////// #[derive(Debug)] -pub struct Compiler<'b> { +pub struct Compiler { chunks: Vec, scopes: Vec, constants: Vec, globals: Vec, - builtins: &'b HashMap, } -impl<'b> Compiler<'b> { - pub fn new(builtins: &'b HashMap) -> Self { +impl Compiler { + pub fn new() -> Self { Compiler { chunks: Default::default(), scopes: Default::default(), constants: Default::default(), - globals: builtins.keys().map(ToString::to_string).collect(), - builtins, + globals: BUILTINS + .with_borrow(|builtins| builtins.keys().map(ToString::to_string).collect()), } } @@ -633,7 +632,7 @@ impl<'b> Compiler<'b> { } } -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); @@ -679,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(self.create_str(&stmt.name.text))?; + let name = self.insert_constant(StrInst::create(&stmt.name.text))?; self.compile_expr(&stmt.rhs)?; self.emit(stmt_line_number(stmt), Op::SetAttr(name)); Ok(()) @@ -698,7 +697,7 @@ impl StmtVisitor for Compiler<'_> { if let Some(expr) = &stmt.expr { self.compile_expr(expr)?; } else { - let nil = self.insert_constant(self.create_nil())?; + let nil = self.insert_constant(NilInst::create())?; self.emit(stmt_line_number(stmt), Op::PushConstant(nil)); } self.emit(stmt_line_number(stmt), Op::Return); @@ -708,7 +707,7 @@ impl StmtVisitor for Compiler<'_> { // condition self.compile_expr(&stmt.condition)?; // call obj.to_bool() - let bool_attr = self.insert_constant(self.create_str("to_bool"))?; + let bool_attr = self.insert_constant(StrInst::create("to_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(); @@ -755,7 +754,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! { @@ -780,7 +779,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(self.create_str("to_bool"))?; + let constant_id = self.insert_constant(StrInst::create("to_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(); @@ -794,14 +793,14 @@ impl ExprVisitor for Compiler<'_> { let name = OP_NAMES .get(&expr.op.kind) .expect("invalid binary operator"); - let constant_id = self.insert_constant(self.create_str(name))?; + let constant_id = self.insert_constant(StrInst::create(name))?; self.emit(expr_line_number(expr), Op::GetAttr(constant_id)); self.compile_expr(&expr.rhs)?; // 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(self.create_str("to_bool"))?; + let constant_id = self.insert_constant(StrInst::create("to_bool"))?; self.emit(expr_line_number(&*expr.rhs), Op::GetAttr(constant_id)); self.emit(expr_line_number(&*expr.rhs), Op::Call(0)); } @@ -839,7 +838,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(self.create_str(name))?; + let constant_id = self.insert_constant(StrInst::create(name))?; self.emit(expr_line_number(expr), Op::GetAttr(constant_id)); self.emit(expr_line_number(expr), Op::Call(0)); Ok(()) @@ -863,7 +862,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(self.create_str(&expr.name.text))?; + let constant_id = self.insert_constant(StrInst::create(&expr.name.text))?; self.emit(expr_line_number(expr), Op::GetAttr(constant_id)); Ok(()) } @@ -889,25 +888,25 @@ impl ExprVisitor for Compiler<'_> { } TokenKind::Number => { let obj = if expr.token.text.contains('.') { - self.create_float(expr.token.text.parse().unwrap()) + FloatInst::create(expr.token.text.parse().unwrap()) } else { - self.create_int(expr.token.text.parse().unwrap()) + IntInst::create(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(self.create_str(unescape(&expr.token.text)))?; + self.insert_constant(StrInst::create(unescape(&expr.token.text)))?; self.emit(expr_line_number(expr), Op::PushConstant(constant_id)); } TokenKind::True | TokenKind::False => { let constant_id = - self.insert_constant(self.create_bool(expr.token.kind == TokenKind::True))?; + self.insert_constant(BoolInst::create(expr.token.kind == TokenKind::True))?; self.emit(expr_line_number(expr), Op::PushConstant(constant_id)); } TokenKind::Nil => { - let constant_id = self.insert_constant(self.create_nil())?; + let constant_id = self.insert_constant(NilInst::create())?; self.emit(expr_line_number(expr), Op::PushConstant(constant_id)); } _ => unreachable!(), @@ -965,7 +964,7 @@ impl ExprVisitor for Compiler<'_> { } // always end with a "return nil" - let nil = self.insert_constant(self.create_nil())?; + let nil = self.insert_constant(NilInst::create())?; self.emit(end_line, Op::PushConstant(nil)); self.emit(end_line, Op::Return); @@ -973,7 +972,7 @@ impl ExprVisitor for Compiler<'_> { // create the function let chunk = self.chunks.pop().unwrap(); - let fun = self.create_user_function(chunk, expr.params.len() as Argc); + let fun = UserFunctionInst::create(chunk, expr.params.len() as Argc); // register the function as a constant let fun_constant = self.insert_constant(fun)?; @@ -993,9 +992,3 @@ impl ExprVisitor for Compiler<'_> { Ok(()) } } - -impl ObjFactory for Compiler<'_> { - fn builtins(&self) -> &HashMap { - &self.builtins - } -} diff --git a/src/main.rs b/src/main.rs index 33e504a..9118d39 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,7 +12,6 @@ mod parser; mod token; mod vm; -use std::collections::HashMap; use std::fmt; use std::fs::File; use std::io::prelude::*; @@ -54,12 +53,11 @@ fn main() -> Result<(), Box> { } // initialize type system - let mut builtins = HashMap::new(); - obj::init_types(&mut builtins); - crate::builtins::init_global_builtins(&mut builtins); + obj::init_types(); + crate::builtins::init_global_builtins(); // compile - let (chunk, constants, globals) = compiler::Compiler::new(&builtins).compile(&ast)?; + let (chunk, constants, globals) = compiler::Compiler::new().compile(&ast)?; if args.disassemble { disassemble::disassemble(&chunk, &constants, &globals); @@ -67,7 +65,7 @@ fn main() -> Result<(), Box> { } // run - let mut vm = vm::Vm::new(chunk.into(), constants, globals, builtins); + let mut vm = vm::Vm::new(chunk.into(), constants, globals); vm.run(); Ok(()) diff --git a/src/obj.rs b/src/obj.rs index a6422f3..4bc669c 100644 --- a/src/obj.rs +++ b/src/obj.rs @@ -6,6 +6,7 @@ mod macros; pub mod function; use std::any::Any; +use std::cell::RefCell; use std::collections::HashMap; use std::fmt::{self, Debug, Display}; use std::rc::Rc; @@ -14,7 +15,7 @@ use gc::{Finalize, Gc, GcCell, Trace}; use crate::obj::function::*; use crate::obj::macros::*; -use crate::vm::{Argc, Chunk, Vm}; +use crate::vm::{Argc, Vm}; pub type Ptr = Gc>; pub type ObjP = Ptr; @@ -71,7 +72,11 @@ pub fn upcast_obj(ptr: Ptr) -> ObjP { } } -pub fn init_types(builtins: &mut HashMap) { +thread_local! { + pub static BUILTINS: RefCell> = RefCell::new(HashMap::default()); +} + +pub fn init_types() { #![allow(non_snake_case)] macro_rules! types { ( @@ -84,6 +89,7 @@ pub fn init_types(builtins: &mut HashMap) { ) => {{ $( let $name = make_ptr(TypeInst::new(stringify!($name))); + BUILTINS.with_borrow_mut(|builtins| builtins.insert(stringify!($name).to_string(), $name.clone())); )* // We have to instantiate these objects all by hand. This is because the `instantiate` @@ -93,13 +99,8 @@ pub fn init_types(builtins: &mut HashMap) { let base_type = $base_type.clone(); $name.borrow_mut().set_attr("__type__", base_type); with_obj_downcast_mut($name.clone(), |type_inst: &mut TypeInst| { type_inst.base.is_instantiated = true; }); - //$name.borrow_mut().base.is_instantiated = true; })* - $( - builtins.insert(stringify!($name).to_string(), $name.clone()); - )* - $({ $( let vtable_name = stringify!($vtable_name); @@ -118,49 +119,49 @@ pub fn init_types(builtins: &mut HashMap) { // type definitions Type { // Method conversion - to_str => builtins.create_builtin_function("to_str", BaseObjInst::to_str, 1), - to_repr => builtins.create_builtin_function("to_repr", BaseObjInst::to_repr, 1), - to_bool => builtins.create_builtin_function("to_bool", BaseObjInst::to_bool, 1), - len => builtins.create_builtin_function("len", BaseObjInst::not_implemented_un, 1), + to_str => BuiltinFunctionInst::create("to_str", BaseObjInst::to_str, 1), + to_repr => BuiltinFunctionInst::create("to_repr", BaseObjInst::to_repr, 1), + to_bool => BuiltinFunctionInst::create("to_bool", BaseObjInst::to_bool, 1), + len => BuiltinFunctionInst::create("len", BaseObjInst::not_implemented_un, 1), // Operators - __add__ => builtins.create_builtin_function("__add__", BaseObjInst::not_implemented_bin, 2), - __sub__ => builtins.create_builtin_function("__sub__", BaseObjInst::not_implemented_bin, 2), - __mul__ => builtins.create_builtin_function("__mul__", BaseObjInst::not_implemented_bin, 2), - __div__ => builtins.create_builtin_function("__div__", BaseObjInst::not_implemented_bin, 2), - __and__ => builtins.create_builtin_function("__and__", BaseObjInst::and, 2), - __or__ => builtins.create_builtin_function("__or__", BaseObjInst::or, 2), - __ne__ => builtins.create_builtin_function("__ne__", BaseObjInst::ne, 2), - __eq__ => builtins.create_builtin_function("__eq__", BaseObjInst::eq, 2), - __gt__ => builtins.create_builtin_function("__gt__", BaseObjInst::not_implemented_bin, 2), - __ge__ => builtins.create_builtin_function("__ge__", BaseObjInst::not_implemented_bin, 2), - __lt__ => builtins.create_builtin_function("__lt__", BaseObjInst::not_implemented_bin, 2), - __le__ => builtins.create_builtin_function("__le__", BaseObjInst::not_implemented_bin, 2), - __pos__ => builtins.create_builtin_function("__pos__", BaseObjInst::not_implemented_un, 1), - __neg__ => builtins.create_builtin_function("__neg__", BaseObjInst::not_implemented_un, 1), - __not__ => builtins.create_builtin_function("__not__", BaseObjInst::not, 1), + __add__ => BuiltinFunctionInst::create("__add__", BaseObjInst::not_implemented_bin, 2), + __sub__ => BuiltinFunctionInst::create("__sub__", BaseObjInst::not_implemented_bin, 2), + __mul__ => BuiltinFunctionInst::create("__mul__", BaseObjInst::not_implemented_bin, 2), + __div__ => BuiltinFunctionInst::create("__div__", BaseObjInst::not_implemented_bin, 2), + __and__ => BuiltinFunctionInst::create("__and__", BaseObjInst::and, 2), + __or__ => BuiltinFunctionInst::create("__or__", BaseObjInst::or, 2), + __ne__ => BuiltinFunctionInst::create("__ne__", BaseObjInst::ne, 2), + __eq__ => BuiltinFunctionInst::create("__eq__", BaseObjInst::eq, 2), + __gt__ => BuiltinFunctionInst::create("__gt__", BaseObjInst::not_implemented_bin, 2), + __ge__ => BuiltinFunctionInst::create("__ge__", BaseObjInst::not_implemented_bin, 2), + __lt__ => BuiltinFunctionInst::create("__lt__", BaseObjInst::not_implemented_bin, 2), + __le__ => BuiltinFunctionInst::create("__le__", BaseObjInst::not_implemented_bin, 2), + __pos__ => BuiltinFunctionInst::create("__pos__", BaseObjInst::not_implemented_un, 1), + __neg__ => BuiltinFunctionInst::create("__neg__", BaseObjInst::not_implemented_un, 1), + __not__ => BuiltinFunctionInst::create("__not__", BaseObjInst::not, 1), }, Obj { }, Str { - to_str => builtins.create_builtin_function("to_str", StrInst::to_str, 1), - to_repr => builtins.create_builtin_function("to_repr", StrInst::to_repr, 1), - len => builtins.create_builtin_function("len", StrInst::len, 1), + to_str => BuiltinFunctionInst::create("to_str", StrInst::to_str, 1), + to_repr => BuiltinFunctionInst::create("to_repr", StrInst::to_repr, 1), + len => BuiltinFunctionInst::create("len", StrInst::len, 1), // Operators - __add__ => builtins.create_builtin_function("__add__", StrInst::add, 2), - __mul__ => builtins.create_builtin_function("__mul__", StrInst::mul, 2), + __add__ => BuiltinFunctionInst::create("__add__", StrInst::add, 2), + __mul__ => BuiltinFunctionInst::create("__mul__", StrInst::mul, 2), // .lower, .upper, .slice, etc }, Int { // Operators - __add__ => builtins.create_builtin_function("__add__", IntInst::add, 2), - __sub__ => builtins.create_builtin_function("__sub__", IntInst::sub, 2), - __mul__ => builtins.create_builtin_function("__mul__", IntInst::mul, 2), - __div__ => builtins.create_builtin_function("__div__", IntInst::div, 2), - __gt__ => builtins.create_builtin_function("__gt__", IntInst::gt, 2), - __ge__ => builtins.create_builtin_function("__ge__", IntInst::ge, 2), - __lt__ => builtins.create_builtin_function("__lt__", IntInst::lt, 2), - __le__ => builtins.create_builtin_function("__le__", IntInst::le, 2), - __pos__ => builtins.create_builtin_function("__pos__", IntInst::pos, 1), - __neg__ => builtins.create_builtin_function("__neg__", IntInst::neg, 1), + __add__ => BuiltinFunctionInst::create("__add__", IntInst::add, 2), + __sub__ => BuiltinFunctionInst::create("__sub__", IntInst::sub, 2), + __mul__ => BuiltinFunctionInst::create("__mul__", IntInst::mul, 2), + __div__ => BuiltinFunctionInst::create("__div__", IntInst::div, 2), + __gt__ => BuiltinFunctionInst::create("__gt__", IntInst::gt, 2), + __ge__ => BuiltinFunctionInst::create("__ge__", IntInst::ge, 2), + __lt__ => BuiltinFunctionInst::create("__lt__", IntInst::lt, 2), + __le__ => BuiltinFunctionInst::create("__le__", IntInst::le, 2), + __pos__ => BuiltinFunctionInst::create("__pos__", IntInst::pos, 1), + __neg__ => BuiltinFunctionInst::create("__neg__", IntInst::neg, 1), }, Float { }, Bool { }, @@ -171,74 +172,6 @@ pub fn init_types(builtins: &mut HashMap) { } } -pub trait ObjFactory { - fn builtins(&self) -> &HashMap; - - fn create_obj(&self) -> ObjP { - ObjInst::create(self.builtins().get("Obj").unwrap().clone()) - } - - fn create_type(&self, name: impl ToString) -> ObjP { - TypeInst::create(self.builtins().get("Type").unwrap().clone(), name) - } - - fn create_str(&self, str_value: impl ToString) -> ObjP { - StrInst::create(self.builtins().get("Str").unwrap().clone(), str_value) - } - - fn create_int(&self, int_value: i64) -> ObjP { - IntInst::create(self.builtins().get("Int").unwrap().clone(), int_value) - } - - fn create_float(&self, float_value: f64) -> ObjP { - FloatInst::create(self.builtins().get("Float").unwrap().clone(), float_value) - } - - fn create_bool(&self, bool_value: bool) -> ObjP { - BoolInst::create(self.builtins().get("Bool").unwrap().clone(), bool_value) - } - - fn create_nil(&self) -> ObjP { - NilInst::create(self.builtins().get("Nil").unwrap().clone()) - } - - fn create_builtin_function( - &self, - name: impl ToString, - function: BuiltinFunctionPtr, - arity: Argc, - ) -> ObjP { - BuiltinFunctionInst::create( - self.builtins().get("BuiltinFunction").unwrap().clone(), - name, - function, - arity, - ) - } - - fn create_user_function(&self, chunk: Chunk, arity: Argc) -> ObjP { - UserFunctionInst::create( - self.builtins().get("UserFunction").unwrap().clone(), - chunk, - arity, - ) - } - - fn create_method(&self, self_binding: ObjP, function: ObjP) -> ObjP { - MethodInst::create( - self.builtins().get("Method").unwrap().clone(), - self_binding, - function, - ) - } -} - -impl ObjFactory for HashMap { - fn builtins(&self) -> &Self { - self - } -} - /// Convenience function for creating pointers, in case the `Arc>` pointer type has to /// change. /// @@ -253,7 +186,7 @@ pub fn make_ptr(obj: T) -> ObjP { //////////////////////////////////////////////////////////////////////////////// pub trait Obj: Debug + Display + Any + Trace { - fn instantiate(&mut self, ty: ObjP); + fn instantiate(&mut self); fn is_instantiated(&self) -> bool; fn attrs(&self) -> &Attrs; @@ -264,21 +197,12 @@ pub trait Obj: Debug + Display + Any + Trace { } fn get_attr(&self, name: &str) -> Option { - // check attrs, then check vtable - self.attrs().get(name).map(Ptr::clone) + self.attrs().get(name).map(ObjP::clone) } - /// This tries to get an attribute using these sources: - /// * `self.attrs()` - /// * `self.type_inst().vtable` - /// * `self.type_inst().type_inst().vtable` - our type's type's vtable (search up the type - /// inheritance tree) - /// - /// If the value is found in a vtable, then it is inserted as an attribute. If it is a - /// function (BuiltinFunctionInst, UserFunctionInst), then it is wrapped in a `MethodInst` - /// first. - fn get_attr_lazy(&mut self, self_ptr: ObjP, method_ty: ObjP, name: &str) -> Option { - let attr = self.get_attr(name); + fn get_vtable_attr(&self, self_ptr: ObjP, name: &str) -> Option { + // check attrs, then check vtable + let attr = self.attrs().get(name).map(ObjP::clone); if attr.is_some() { return attr; } @@ -293,11 +217,14 @@ pub trait Obj: Debug + Display + Any + Trace { let ptr = if obj_is_inst::(&vtable_entry) || obj_is_inst::(&vtable_entry) { - MethodInst::create(method_ty.clone(), self_ptr.clone(), vtable_entry) + MethodInst::create(self_ptr.clone(), vtable_entry) } else { vtable_entry }; - self.set_attr(name, ptr.clone()); + // TODO Obj::get_attr - cache the vtable result somehow? we aren't caching for + // speed, but rather so we don't have a million different method objects + // floating around. + //self.set_attr(name, ptr.clone()); ptr }); if vtable_entry.is_some() { @@ -311,9 +238,7 @@ pub trait Obj: Debug + Display + Any + Trace { } } - fn type_inst(&self) -> ObjP { - self.get_attr("__type__").unwrap() - } + fn type_inst(&self) -> ObjP; fn type_name(&self) -> Rc { with_obj_downcast(self.type_inst(), |type_inst: &TypeInst| { @@ -372,8 +297,7 @@ impl Display for BaseObjInst { } impl Obj for BaseObjInst { - fn instantiate(&mut self, ty: ObjP) { - self.set_attr("__type__", ty); + fn instantiate(&mut self) { self.is_instantiated = true; } @@ -389,6 +313,10 @@ impl Obj for BaseObjInst { &mut self.attrs } + fn type_inst(&self) -> ObjP { + self.get_attr("__type__").expect("no __type__") + } + fn equals(&self, other: &dyn Obj) -> bool { if let Some(other) = other.as_any().downcast_ref::() { // compare all attrs @@ -451,7 +379,7 @@ impl Obj for ObjInst { } } - impl_base_obj!(); + impl_base_obj!(Obj); } //////////////////////////////////////////////////////////////////////////////// @@ -523,7 +451,7 @@ impl Obj for TypeInst { } } - impl_base_obj!(); + impl_base_obj!(Type); } //////////////////////////////////////////////////////////////////////////////// @@ -575,7 +503,7 @@ impl Obj for StrInst { } } - impl_base_obj!(); + impl_base_obj!(Str); } //////////////////////////////////////////////////////////////////////////////// @@ -628,7 +556,7 @@ impl Obj for IntInst { } } - impl_base_obj!(); + impl_base_obj!(Int); } //////////////////////////////////////////////////////////////////////////////// @@ -681,7 +609,7 @@ impl Obj for FloatInst { } } - impl_base_obj!(); + impl_base_obj!(Float); } //////////////////////////////////////////////////////////////////////////////// @@ -732,7 +660,7 @@ impl Obj for BoolInst { } } - impl_base_obj!(); + impl_base_obj!(Bool); } //////////////////////////////////////////////////////////////////////////////// @@ -771,7 +699,7 @@ impl Obj for NilInst { other.as_any().downcast_ref::().is_some() } - impl_base_obj!(); + impl_base_obj!(Nil); } //////////////////////////////////////////////////////////////////////////////// @@ -780,53 +708,51 @@ impl Obj for NilInst { #[test] fn test_new_objects() { - let mut builtins = HashMap::new(); - init_types(&mut builtins); + init_types(); - let type_value = builtins.create_type("Type"); + let type_value = TypeInst::create("Type"); assert_eq!(&*type_value.borrow().type_name(), "Type"); - let str_value = builtins.create_str("asdfasdfasdfasdfasdf"); + let str_value = StrInst::create("asdfasdfasdfasdfasdf"); assert_eq!(&*str_value.borrow().type_name(), "Str"); - let int_value = builtins.create_int(1234); + let int_value = IntInst::create(1234); assert_eq!(&*int_value.borrow().type_name(), "Int"); - let float_value = builtins.create_float(1234.5678); + let float_value = FloatInst::create(1234.5678); assert_eq!(&*float_value.borrow().type_name(), "Float"); - let nil_value = builtins.create_nil(); + let nil_value = NilInst::create(); assert_eq!(&*nil_value.borrow().type_name(), "Nil"); } #[test] fn test_obj_equals() { - let mut builtins = HashMap::new(); - init_types(&mut builtins); + init_types(); - let int1 = builtins.create_int(1234); - let int2 = builtins.create_int(1234); + let int1 = IntInst::create(1234); + let int2 = IntInst::create(1234); assert!(int1.borrow().equals(&*int2.borrow())); assert!(int2.borrow().equals(&*int1.borrow())); - let float1 = builtins.create_float(1234.0); + let float1 = FloatInst::create(1234.0); assert!(int1.borrow().equals(&*float1.borrow())); assert!(float1.borrow().equals(&*int2.borrow())); // self-equality - let str1 = builtins.create_str("1234"); + let str1 = StrInst::create("1234"); assert!(str1.borrow().equals(&*str1.borrow())); - let str2 = builtins.create_str("1234"); + let str2 = StrInst::create("1234"); assert!(str1.borrow().equals(&*str2.borrow())); assert!(str2.borrow().equals(&*str1.borrow())); assert!(!str1.borrow().equals(&*float1.borrow())); assert!(!str1.borrow().equals(&*int1.borrow())); - let obj1 = builtins.create_obj(); - let obj2 = builtins.create_obj(); + let obj1 = ObjInst::create(); + let obj2 = ObjInst::create(); assert!(obj1.borrow().equals(&*obj2.borrow())); // these objects aren't equal anymore @@ -840,15 +766,10 @@ fn test_obj_equals() { #[test] fn test_obj_vtable() { - let mut builtins = HashMap::new(); - init_types(&mut builtins); - let str1 = builtins.create_str("asdfasdfasdf"); + init_types(); + let str1 = StrInst::create("asdfasdfasdf"); - let to_string_ptr = str1.borrow_mut().get_attr_lazy( - str1.clone(), - builtins.get("Method").unwrap().clone(), - "to_str", - ); + let to_string_ptr = str1.borrow_mut().get_vtable_attr(str1.clone(), "to_str"); assert!(to_string_ptr.is_some()); let to_string_ptr = to_string_ptr.unwrap(); @@ -858,11 +779,9 @@ fn test_obj_vtable() { }); // now get the method's to_string ptr - let method_to_string_ptr = to_string_ptr.borrow_mut().get_attr_lazy( - to_string_ptr.clone(), - builtins.get("Method").unwrap().clone(), - "to_str", - ); + let method_to_string_ptr = to_string_ptr + .borrow_mut() + .get_vtable_attr(to_string_ptr.clone(), "to_str"); assert!(method_to_string_ptr.is_some()); // this is like doing "asdfasdfasdf".to_string().to_string() diff --git a/src/obj/function.rs b/src/obj/function.rs index c36dcb2..a47325a 100644 --- a/src/obj/function.rs +++ b/src/obj/function.rs @@ -125,7 +125,7 @@ impl Obj for BuiltinFunctionInst { } } - impl_base_obj!(); + impl_base_obj!(BuiltinFunction); } //////////////////////////////////////////////////////////////////////////////// @@ -216,7 +216,7 @@ impl Obj for UserFunctionInst { } } - impl_base_obj!(); + impl_base_obj!(UserFunction); } //////////////////////////////////////////////////////////////////////////////// @@ -249,9 +249,9 @@ impl MethodInst { } } - pub fn create(ty: ObjP, self_binding: ObjP, function: ObjP) -> ObjP { + pub fn create(self_binding: ObjP, function: ObjP) -> ObjP { let ptr = make_ptr(Self::new(self_binding, function)); - ptr.borrow_mut().instantiate(ty.clone()); + ptr.borrow_mut().instantiate(); ptr } @@ -294,5 +294,5 @@ impl Obj for MethodInst { } } - impl_base_obj!(); + impl_base_obj!(Method); } diff --git a/src/obj/macros.rs b/src/obj/macros.rs index 14e16de..1da5b90 100644 --- a/src/obj/macros.rs +++ b/src/obj/macros.rs @@ -1,7 +1,14 @@ macro_rules! impl_base_obj { - ($base_name:ident) => { - fn instantiate(&mut self, ty: $crate::obj::ObjP) { - self.$base_name.instantiate(ty); + ($base_name:ident, $type_name:ident) => { + fn instantiate(&mut self) { + let ty = $crate::obj::BUILTINS.with_borrow(|builtins| { + builtins + .get(stringify!($type_name)) + .expect(concat!("no ", stringify!($type_name))) + .clone() + }); + self.set_attr("__type__", ty); + self.$base_name.instantiate(); } fn is_instantiated(&self) -> bool { @@ -16,6 +23,10 @@ macro_rules! impl_base_obj { self.$base_name.attrs_mut() } + fn type_inst(&self) -> ObjP { + self.$base_name.type_inst() + } + fn as_any(&self) -> &dyn std::any::Any { self } @@ -24,16 +35,16 @@ macro_rules! impl_base_obj { self } }; - () => { - impl_base_obj! { base } + ($type_name:ident) => { + impl_base_obj! { base, $type_name } }; } macro_rules! impl_create { ($($arg:ident : $ty:ty),* $(,)?) => { - pub fn create(ty: $crate::obj::ObjP $(, $arg : $ty )*) -> $crate::obj::ObjP { + pub fn create($($arg : $ty ),*) -> $crate::obj::ObjP { let ptr = make_ptr(Self::new($($arg),*)); - ptr.borrow_mut().instantiate(ty); + ptr.borrow_mut().instantiate(); ptr } } diff --git a/src/vm.rs b/src/vm.rs index 0bcf371..cb650bc 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -1,4 +1,3 @@ -use std::collections::HashMap; use std::rc::Rc; use crate::obj::function::*; @@ -93,19 +92,13 @@ 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: Rc, - constants: Vec, - global_names: Vec, - builtins: HashMap, - ) -> Self { + pub fn new(chunk: Rc, constants: Vec, global_names: Vec) -> Self { // set up globals - let nil = builtins.create_nil(); + let nil = NilInst::create(); let mut globals: Vec<_> = global_names.iter().map(|_| ObjP::clone(&nil)).collect(); let mut register_global = |name: &str, value: ObjP| { @@ -116,9 +109,11 @@ impl Vm { globals[index] = value; }; - for (name, builtin) in builtins.iter() { - register_global(&name, ObjP::clone(&builtin)); - } + BUILTINS.with_borrow(|builtins| { + for (name, builtin) in builtins.iter() { + register_global(&name, builtin.clone()); + } + }); // stack and frames let stack = Vec::new(); @@ -134,7 +129,6 @@ impl Vm { globals, stack, frames, - builtins, } } @@ -261,9 +255,6 @@ impl Vm { } pub fn run(&mut self) { - // Cached pointers that we just always want to have on hand - let method_type = self.builtins().get("Method").unwrap().clone(); - loop { let op = self.dispatch(); @@ -301,10 +292,7 @@ impl Vm { let name = with_obj_downcast(name_obj, |name: &StrInst| Rc::clone(&name.str_value())); let owner = self.pop(); - let value = - owner - .borrow_mut() - .get_attr_lazy(owner.clone(), method_type.clone(), &name); + let value = owner.borrow_mut().get_vtable_attr(owner.clone(), &name); if let Some(value) = value { self.push(value); } else { @@ -410,9 +398,3 @@ impl Vm { } } } - -impl ObjFactory for Vm { - fn builtins(&self) -> &HashMap { - &self.builtins - } -}