diff --git a/src/builtins.rs b/src/builtins.rs index e7fac2a..da58c5e 100644 --- a/src/builtins.rs +++ b/src/builtins.rs @@ -1,6 +1,5 @@ //! Builtin functions. -use crate::obj::function::{BuiltinFunction, FunctionResult, FunctionState}; -use crate::obj::*; +use crate::obj::prelude::*; use crate::vm::Vm; pub fn init_global_builtins() { @@ -33,39 +32,6 @@ pub fn init_global_builtins() { // // This would probably be doable in a procedural macro. -macro_rules! impl_do_call { - ($name:ident) => { - pub(crate) fn do_call(vm: &mut Vm, state: FunctionState) -> FunctionResult { - match state { - FunctionState::Begin => { - // get the top item off the stack and call to_float on it - let arg = vm.peek(); - let method = if let Some(method) = - arg.borrow().get_vtable_attr(arg.clone(), stringify!($name)) - { - method - } else { - // TODO builtins::do_call - throw exception when target doesn't have a - // to_$name method - // BLOCKED-ON: exceptions - todo!( - concat!("{} does not have a ", stringify!($name), " method"), - arg.borrow().ty_name() - ); - }; - vm.push(method.clone()); - method.borrow().call(vm, 0); - - // resume execution - FunctionResult::Yield(0) - } - FunctionState::Resume(0) => FunctionResult::Return, - _ => unreachable!(), - } - } - }; -} - //////////////////////////////////////////////////////////////////////////////// // Global functions //////////////////////////////////////////////////////////////////////////////// @@ -109,473 +75,3 @@ pub(crate) fn print(vm: &mut Vm, state: FunctionState) -> FunctionResult { _ => unreachable!(), } } - -//////////////////////////////////////////////////////////////////////////////// -// BaseObj implementations -//////////////////////////////////////////////////////////////////////////////// - -impl BaseObj { - // - // Common functions - // - - pub(crate) fn to_str(vm: &mut Vm, state: FunctionState) -> FunctionResult { - match state { - FunctionState::Begin => { - let this = vm.frame_stack()[0].clone(); - let method = this - .borrow() - .get_vtable_attr(this.clone(), "to_repr") - .clone() - .expect("no to_repr"); - vm.push(method.clone()); - method.borrow().call(vm, 0); - FunctionResult::Yield(0) - } - FunctionState::Resume(0) => FunctionResult::Return, - _ => unreachable!(), - } - } - - pub(crate) fn to_repr(vm: &mut Vm, _state: FunctionState) -> FunctionResult { - Str::create(format!("{:?}", vm.frame_stack()[0].borrow())).into() - } - - pub(crate) fn to_bool(vm: &mut Vm, _state: FunctionState) -> FunctionResult { - Bool::create(vm.frame_stack()[0].borrow().is_truthy()).into() - } - - // - // Operators - // - - pub(crate) fn and(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(); - Bool::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(); - Bool::create(result).into() - } - - pub(crate) fn ne(vm: &mut Vm, state: FunctionState) -> FunctionResult { - match state { - 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 = lhs - .borrow() - .get_vtable_attr(lhs.clone(), "__eq__") - .expect("no __eq__"); - - let rhs = vm.frame_stack()[1].clone(); - vm.push(rhs); - method.borrow().call(vm, 1); - FunctionResult::Yield(0) - } - FunctionState::Resume(0) => { - let result = !vm.peek().borrow().is_truthy(); - Bool::create(result).into() - } - _ => { - unreachable!() - } - } - } - - pub(crate) fn eq(vm: &mut Vm, _state: FunctionState) -> FunctionResult { - let lhs = vm.frame_stack()[0].borrow(); - let rhs = vm.frame_stack()[1].borrow(); - let equals = lhs.equals(&*rhs); - Bool::create(equals).into() - } - - pub(crate) fn not(vm: &mut Vm, state: FunctionState) -> FunctionResult { - match state { - FunctionState::Begin => { - let obj = vm.peek(); - let method = obj - .borrow() - .get_vtable_attr(obj.clone(), "to_bool") - .expect("no to_bool"); - vm.push(method.clone()); - method.borrow().call(vm, 0); - FunctionResult::Yield(0) - } - FunctionState::Resume(0) => { - let value = vm.peek().borrow().is_truthy(); - Bool::create(!value).into() - } - _ => unreachable!(), - } - } - - // - // Not implemented - // - - pub(crate) fn not_implemented_un(vm: &mut Vm, _state: FunctionState) -> FunctionResult { - let fname = &vm.frame().name; - // TODO BaseObj::not_implemented_un - throw an exception of some kind for not - // implemented/not available errors on unary operators - // BLOCKED-ON: exceptions - todo!( - "Raise some kind of not implemented/not callable error for {} function (self: {})", - fname, - vm.frame_stack()[0].borrow() - ) - } - - pub(crate) fn not_implemented_bin(vm: &mut Vm, _state: FunctionState) -> FunctionResult { - // TODO BaseObj::not_implemented_un - throw an exception of some kind for not - // implemented/not available errors on unary operators - // BLOCKED-ON: exceptions - let fname = &vm.frame().name; - todo!("Raise some kind of not implemented/not callable error for {} function (self: {}, rhs: {})", fname, vm.frame_stack()[0].borrow(), vm.frame_stack()[1].borrow()) - } -} - -//////////////////////////////////////////////////////////////////////////////// -// Str implementations -//////////////////////////////////////////////////////////////////////////////// -impl Str { - impl_do_call!(to_str); - - pub(crate) fn init(_vm: &mut Vm, _state: FunctionState) -> FunctionResult { - // This is a no-op. We don't want the user-exposed `__init__` function to do anything, - // instantiation is done in the `__call__` function. - FunctionResult::ReturnPush(Nil::create()) - } - - pub(crate) fn to_str(_vm: &mut Vm, _state: FunctionState) -> FunctionResult { - // top item of the stack should just be ourselves, so return immediately - FunctionResult::Return - } - - pub(crate) fn to_int(vm: &mut Vm, _state: FunctionState) -> FunctionResult { - let parsed: Result = - with_obj_downcast(vm.frame_stack()[0].clone(), |str_inst: &Str| { - str_inst.str_value().parse() - }); - match parsed { - Ok(int) => Int::create(int).into(), - // TODO Str::to_int - throw an exception when we fail to parse an integer - // BLOCKED-ON - exceptions - Err(e) => todo!("error parsing string to an integer: {}", e), - } - } - - pub(crate) fn to_float(vm: &mut Vm, _state: FunctionState) -> FunctionResult { - let parsed: Result = - with_obj_downcast(vm.frame_stack()[0].clone(), |str_inst: &Str| { - str_inst.str_value().parse() - }); - match parsed { - Ok(float) => Float::create(float).into(), - // TODO Str::to_int - throw an exception when we fail to parse an integer - // BLOCKED-ON - exceptions - Err(e) => todo!("error parsing string to a float: {}", e), - } - } - - pub(crate) fn len(vm: &mut Vm, _state: FunctionState) -> FunctionResult { - let len = with_obj_downcast(vm.frame_stack()[0].clone(), |str_inst: &Str| { - str_inst.str_value().len() as i64 - }); - Int::create(len).into() - } - - pub(crate) fn add(vm: &mut Vm, _state: FunctionState) -> FunctionResult { - let lhs = vm.frame_stack()[0].clone(); - let rhs = vm.frame_stack()[1].clone(); - if !obj_is_inst::(&rhs) { - // TODO Str::add - throw an exception when the RHS is not a string - // BLOCKED-ON: exceptions - todo!( - "can only concatenate Str, got {} instead", - rhs.borrow().ty_name() - ) - } - - let new = format!("{}{}", lhs.borrow(), rhs.borrow()); - Str::create(new).into() - } - - pub(crate) fn mul(vm: &mut Vm, _state: FunctionState) -> FunctionResult { - let lhs = vm.frame_stack()[0].clone(); - let rhs = vm.frame_stack()[1].clone(); - let repeat_count = if let Some(int_inst) = rhs.borrow().as_any().downcast_ref::() { - int_inst.int_value() - } else { - // TODO Str::mul - throw an exception when the RHS is not an int - // BLOCKED-ON: exceptions - todo!( - "can only repeat Str with Int, got {} instead", - rhs.borrow().ty_name() - ) - }; - let repeat_count = repeat_count.max(0) as usize; - let new = format!("{}", lhs.borrow()).repeat(repeat_count); - Str::create(new).into() - } -} - -//////////////////////////////////////////////////////////////////////////////// -// Int implementations -//////////////////////////////////////////////////////////////////////////////// - -macro_rules! int_bin_op_math { - ($function:ident, $op:tt) => { - pub(crate) fn $function(vm: &mut Vm, _state: FunctionState) -> FunctionResult { - let lhs = vm.frame_stack()[0].clone(); - let rhs = vm.frame_stack()[1].clone(); - - let lhs_value = with_obj_downcast(lhs, Int::int_value); - - let result = if let Some(int_inst) = rhs.borrow().as_any().downcast_ref::() { - Int::create(lhs_value $op int_inst.int_value()) - } else if let Some(float_inst) = rhs.borrow().as_any().downcast_ref::() { - Float::create(lhs_value as f64 $op float_inst.float_value()) - } else { - // TODO Int arithmetic operator - throw an exception when RHS is not Int, Float - // BLOCKED-ON: exceptions - todo!( - concat!("cannot use '", stringify!($op), "' operator with Int and {}"), - rhs.borrow().ty_name() - ) - }; - result.into() - } - } -} - -macro_rules! int_bin_op_logical { - ($function:ident, $op:tt) => { - pub(crate) fn $function(vm: &mut Vm, _state: FunctionState) -> FunctionResult { - let lhs = vm.frame_stack()[0].clone(); - let rhs = vm.frame_stack()[1].clone(); - - let lhs_value = with_obj_downcast(lhs, Int::int_value); - - let result = if let Some(int_inst) = rhs.borrow().as_any().downcast_ref::() { - Bool::create(lhs_value $op int_inst.int_value()) - } else if let Some(float_inst) = rhs.borrow().as_any().downcast_ref::() { - Bool::create((lhs_value as f64) $op float_inst.float_value()) - } else { - // TODO Int logical operator - throw an exception when RHS is not Int, Float - // BLOCKED-ON: exceptions - todo!( - concat!("cannot use '", stringify!($op), "' operator with Int and {}"), - rhs.borrow().ty_name() - ) - }; - result.into() - } - } -} - -impl Int { - pub(crate) fn to_int(_vm: &mut Vm, _state: FunctionState) -> FunctionResult { - FunctionResult::Return - } - - pub(crate) fn to_float(vm: &mut Vm, _state: FunctionState) -> FunctionResult { - let int_value = with_obj_downcast(vm.frame_stack()[0].clone(), Int::int_value); - Float::create(int_value as f64).into() - } - - impl_do_call!(to_int); - - pub(crate) fn init(_vm: &mut Vm, _state: FunctionState) -> FunctionResult { - // This is a no-op. We don't want the user-exposed `__init__` function to do anything, - // instantiation is done in the `__call__` function. - FunctionResult::ReturnPush(Nil::create()) - } - - int_bin_op_math!(add, +); - int_bin_op_math!(sub, -); - - pub(crate) fn mul(vm: &mut Vm, _state: FunctionState) -> FunctionResult { - // can't bin_op_math this one because it needs the string case - let lhs = vm.frame_stack()[0].clone(); - let rhs = vm.frame_stack()[1].clone(); - - let lhs_value = with_obj_downcast(lhs, Int::int_value); - - let result = if let Some(int_inst) = rhs.borrow().as_any().downcast_ref::() { - Int::create(lhs_value * int_inst.int_value()) - } else if let Some(float_inst) = rhs.borrow().as_any().downcast_ref::() { - Float::create(lhs_value as f64 * float_inst.float_value()) - } else if let Some(str_inst) = rhs.borrow().as_any().downcast_ref::() { - // TODO Int::mul - maybe convert this to just call Str.mul with arguments reversed? - // Just so we have the same logic here - Str::create(str_inst.str_value().repeat(lhs_value as usize)) - } else { - // TODO Int::mul - throw an exception when RHS is not Int, Float, Str - // BLOCKED-ON: exceptions - todo!( - "cannot use '*' operator with Int and {}", - rhs.borrow().ty_name() - ) - }; - result.into() - } - - // TODO Int::div - handle divide by zero - // BLOCKED-ON: exceptions - // NOTE - we will probably need to get rid of the macro here to handle that :( - int_bin_op_math!(div, /); - - // __eq__ will use the default .equals implementation - //int_bin_op_logical!(eq, ==); - // __ne__ will call __eq__ and negate it - int_bin_op_logical!(gt, >); - int_bin_op_logical!(ge, >=); - int_bin_op_logical!(lt, <); - int_bin_op_logical!(le, <=); - - pub(crate) fn pos(vm: &mut Vm, _state: FunctionState) -> FunctionResult { - let lhs = vm.frame_stack()[0].clone(); - let value = with_obj_downcast(lhs, Int::int_value); - Int::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, Int::int_value); - Int::create(-value).into() - } -} - -//////////////////////////////////////////////////////////////////////////////// -// Float implementations -//////////////////////////////////////////////////////////////////////////////// - -macro_rules! float_bin_op_math { - ($function:ident, $op:tt) => { - pub(crate) fn $function(vm: &mut Vm, _state: FunctionState) -> FunctionResult { - let lhs = vm.frame_stack()[0].clone(); - let rhs = vm.frame_stack()[1].clone(); - - let lhs_value = with_obj_downcast(lhs, Float::float_value); - - let result = if let Some(int_inst) = rhs.borrow().as_any().downcast_ref::() { - Float::create(lhs_value $op int_inst.int_value() as f64) - } else if let Some(float_inst) = rhs.borrow().as_any().downcast_ref::() { - Float::create(lhs_value $op float_inst.float_value()) - } else { - // TODO Int arithmetic operator - throw an exception when RHS is not Int, Float - // BLOCKED-ON: exceptions - todo!( - concat!("cannot use '", stringify!($op), "' operator with Float and {}"), - rhs.borrow().ty_name() - ) - }; - result.into() - } - } -} - -macro_rules! float_bin_op_logical { - ($function:ident, $op:tt) => { - pub(crate) fn $function(vm: &mut Vm, _state: FunctionState) -> FunctionResult { - let lhs = vm.frame_stack()[0].clone(); - let rhs = vm.frame_stack()[1].clone(); - - let lhs_value = with_obj_downcast(lhs, Float::float_value); - - let result = if let Some(int_inst) = rhs.borrow().as_any().downcast_ref::() { - Bool::create(lhs_value $op int_inst.int_value() as f64) - } else if let Some(float_inst) = rhs.borrow().as_any().downcast_ref::() { - Bool::create(lhs_value $op float_inst.float_value()) - } else { - // TODO Int logical operator - throw an exception when RHS is not Int, Float - // BLOCKED-ON: exceptions - todo!( - concat!("cannot use '", stringify!($op), "' operator with Float and {}"), - rhs.borrow().ty_name() - ) - }; - result.into() - } - } -} - -impl Float { - impl_do_call!(to_float); - - pub(crate) fn init(_vm: &mut Vm, _state: FunctionState) -> FunctionResult { - // This is a no-op. We don't want the user-exposed `__init__` function to do anything, - // instantiation is done in the `__call__` function. - FunctionResult::ReturnPush(Nil::create()) - } - - pub(crate) fn to_int(vm: &mut Vm, _state: FunctionState) -> FunctionResult { - let float_value = with_obj_downcast(vm.frame_stack()[0].clone(), Float::float_value); - Int::create(float_value as i64).into() - } - - pub(crate) fn to_float(_vm: &mut Vm, _state: FunctionState) -> FunctionResult { - FunctionResult::Return - } - - float_bin_op_math!(add, +); - float_bin_op_math!(sub, -); - float_bin_op_math!(mul, *); - float_bin_op_math!(div, /); - - // __eq__ will use the default .equals implementation - //int_bin_op_logical!(eq, ==); - // __ne__ will call __eq__ and negate it - float_bin_op_logical!(gt, >); - float_bin_op_logical!(ge, >=); - float_bin_op_logical!(lt, <); - float_bin_op_logical!(le, <=); - - pub(crate) fn pos(vm: &mut Vm, _state: FunctionState) -> FunctionResult { - let lhs = vm.frame_stack()[0].clone(); - let value = with_obj_downcast(lhs, Float::float_value); - Float::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, Float::float_value); - Float::create(-value).into() - } -} - -impl Bool { - impl_do_call!(to_bool); - - pub(crate) fn init(_vm: &mut Vm, _state: FunctionState) -> FunctionResult { - // This is a no-op. We don't want the user-exposed `__init__` function to do anything, - // instantiation is done in the `__call__` function. - FunctionResult::ReturnPush(Nil::create()) - } - - pub(crate) fn to_int(vm: &mut Vm, _state: FunctionState) -> FunctionResult { - let bool_value = with_obj_downcast(vm.frame_stack()[0].clone(), Bool::bool_value); - Int::create(bool_value as i64).into() - } - - pub(crate) fn to_float(vm: &mut Vm, _state: FunctionState) -> FunctionResult { - let bool_value = with_obj_downcast(vm.frame_stack()[0].clone(), Bool::bool_value); - Float::create(bool_value as i64 as f64).into() - } -} - -impl Nil { - pub(crate) fn do_call(_vm: &mut Vm, _state: FunctionState) -> FunctionResult { - FunctionResult::ReturnPush(Nil::create()) - } - - pub(crate) fn init(_vm: &mut Vm, _state: FunctionState) -> FunctionResult { - FunctionResult::ReturnPush(Nil::create()) - } -} diff --git a/src/compiler.rs b/src/compiler.rs index 55a2123..585ed66 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -8,8 +8,8 @@ use common_macros::hash_map; use thiserror::Error; use crate::ast::*; -use crate::obj::function::UserFunction; -use crate::obj::*; +use crate::obj::prelude::*; +use crate::obj::BUILTINS; use crate::token::TokenKind; use crate::vm::*; diff --git a/src/main.rs b/src/main.rs index 9118d39..847a8ae 100644 --- a/src/main.rs +++ b/src/main.rs @@ -53,7 +53,7 @@ fn main() -> Result<(), Box> { } // initialize type system - obj::init_types(); + obj::ty::init_types(); crate::builtins::init_global_builtins(); // compile diff --git a/src/obj.rs b/src/obj.rs index 7294e42..d6b2306 100644 --- a/src/obj.rs +++ b/src/obj.rs @@ -1,9 +1,14 @@ // TODO obj.rs - remove the warning suppression -#![allow(dead_code)] +//#![allow(dead_code)] mod macros; // Leave this comment here - macros must come first +pub mod bool; +pub mod float; pub mod function; +pub mod int; +pub mod str; +pub mod ty; use std::any::Any; use std::cell::RefCell; @@ -13,14 +18,36 @@ use std::rc::Rc; use gc::{Finalize, Gc, GcCell, Trace}; -use crate::obj::function::*; use crate::obj::macros::*; +use crate::obj::prelude::*; use crate::vm::{Argc, Vm}; pub type Ptr = Gc>; pub type ObjP = Ptr; pub type Attrs = HashMap; +pub mod prelude { + #![allow(unused_imports)] + + // Common object functions + pub use crate::obj::{ + make_ptr, obj_is_inst, upcast_obj, with_obj_downcast, with_obj_downcast_mut, + }; + + // Object trait + pub use crate::obj::Object; + + // Object types + pub use crate::obj::ObjP; + + pub use crate::obj::function::{BuiltinFunction, Method, UserFunction}; + pub use crate::obj::{bool::Bool, float::Float, int::Int, str::Str, ty::Ty}; + pub use crate::obj::{Nil, Obj}; + + // Other auxiliary types and functions + pub use crate::obj::function::{BuiltinFunctionPtr, FunctionResult, FunctionState}; +} + // TODO obj::with_obj_downcast - optimize downcasts of "known" types with an unchecked downcast /// Downcast an object pointer to a concrete type, and do something with that object. @@ -57,6 +84,7 @@ where } } +/// Check whether a specificied object pointer is of a concrete type. pub fn obj_is_inst(ptr: &ObjP) -> bool where T: Object + 'static, @@ -65,6 +93,7 @@ where borrowed.as_any().downcast_ref::().is_some() } +/// Upcast a pointer to a concrete object up to a trait object pointer. pub fn upcast_obj(ptr: Ptr) -> ObjP { unsafe { let ptr = Ptr::into_raw(ptr) as *const GcCell; @@ -72,170 +101,6 @@ pub fn upcast_obj(ptr: Ptr) -> ObjP { } } -thread_local! { - pub static BUILTINS: RefCell> = RefCell::new(HashMap::default()); -} - -pub fn init_types() { - #![allow(non_snake_case)] - macro_rules! types { - ( - base_type: $base_type:ident, - $( - $name:ident { - $( $vtable_name:ident => $vtable_value:expr ),* $(,)? - } - ),* $(,)? - ) => {{ - $( - let $name = make_ptr(Ty::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` - // function does some stuff that may accidentally cause infinite recursion while we are - // setting up these fundamental types. - $({ - let base_type = $base_type.clone(); - $name.borrow_mut().set_attr("__ty__", base_type); - with_obj_downcast_mut($name.clone(), |ty: &mut Ty| { ty.base.is_instantiated = true; }); - })* - - $({ - $( - let vtable_name = stringify!($vtable_name); - let vtable_value = $vtable_value; - with_obj_downcast_mut($name.clone(), |ty: &mut Ty| { - ty.vtable.insert(vtable_name.to_string(), vtable_value); - }); - )* - })* - }}; - } - - types! { - // base type - base_type: Ty, - // type definitions - Ty { - // Conversion methods - to_str => BuiltinFunction::create("to_str", BaseObj::to_str, 1), - to_repr => BuiltinFunction::create("to_repr", BaseObj::to_repr, 1), - to_bool => BuiltinFunction::create("to_bool", BaseObj::to_bool, 1), - to_int => BuiltinFunction::create("to_int", BaseObj::not_implemented_un, 1), - to_float => BuiltinFunction::create("to_float", BaseObj::not_implemented_un, 1), - len => BuiltinFunction::create("len", BaseObj::not_implemented_un, 1), - - // Constructor - // TODO Ty::do_call, Ty::init - implement these methods - __call__ => BuiltinFunction::create("__call__", BaseObj::not_implemented_un, 1), - __init__ => BuiltinFunction::create("__init__", BaseObj::not_implemented_un, 1), - - // Operators - __add__ => BuiltinFunction::create("__add__", BaseObj::not_implemented_bin, 2), - __sub__ => BuiltinFunction::create("__sub__", BaseObj::not_implemented_bin, 2), - __mul__ => BuiltinFunction::create("__mul__", BaseObj::not_implemented_bin, 2), - __div__ => BuiltinFunction::create("__div__", BaseObj::not_implemented_bin, 2), - __and__ => BuiltinFunction::create("__and__", BaseObj::and, 2), - __or__ => BuiltinFunction::create("__or__", BaseObj::or, 2), - __ne__ => BuiltinFunction::create("__ne__", BaseObj::ne, 2), - __eq__ => BuiltinFunction::create("__eq__", BaseObj::eq, 2), - __gt__ => BuiltinFunction::create("__gt__", BaseObj::not_implemented_bin, 2), - __ge__ => BuiltinFunction::create("__ge__", BaseObj::not_implemented_bin, 2), - __lt__ => BuiltinFunction::create("__lt__", BaseObj::not_implemented_bin, 2), - __le__ => BuiltinFunction::create("__le__", BaseObj::not_implemented_bin, 2), - __pos__ => BuiltinFunction::create("__pos__", BaseObj::not_implemented_un, 1), - __neg__ => BuiltinFunction::create("__neg__", BaseObj::not_implemented_un, 1), - __not__ => BuiltinFunction::create("__not__", BaseObj::not, 1), - }, - Obj { - //__call__ => BuiltinFunction::create("__call__", - }, - Str { - // Conversion methods - to_str => BuiltinFunction::create("to_str", Str::to_str, 1), - to_int => BuiltinFunction::create("to_int", Str::to_int, 1), - to_float => BuiltinFunction::create("to_float", Str::to_float, 1), - len => BuiltinFunction::create("len", Str::len, 1), - - // Constructor - __call__ => BuiltinFunction::create("__call__", Str::do_call, 2), - __init__ => BuiltinFunction::create("__init__", Str::init, 2), - - // Operators - __add__ => BuiltinFunction::create("__add__", Str::add, 2), - __mul__ => BuiltinFunction::create("__mul__", Str::mul, 2), - // .lower, .upper, .slice, etc - }, - Int { - // Conversion methods - to_int => BuiltinFunction::create("to_int", Int::to_int, 1), - to_float => BuiltinFunction::create("to_float", Int::to_float, 1), - - // Constructor - __call__ => BuiltinFunction::create("__call__", Int::do_call, 2), - __init__ => BuiltinFunction::create("__init__", Int::init, 2), - - // Operators - __add__ => BuiltinFunction::create("__add__", Int::add, 2), - __sub__ => BuiltinFunction::create("__sub__", Int::sub, 2), - __mul__ => BuiltinFunction::create("__mul__", Int::mul, 2), - __div__ => BuiltinFunction::create("__div__", Int::div, 2), - //__eq__ => BuiltinFunction::create("__eq__", Int::eq, 2), - __gt__ => BuiltinFunction::create("__gt__", Int::gt, 2), - __ge__ => BuiltinFunction::create("__ge__", Int::ge, 2), - __lt__ => BuiltinFunction::create("__lt__", Int::lt, 2), - __le__ => BuiltinFunction::create("__le__", Int::le, 2), - __pos__ => BuiltinFunction::create("__pos__", Int::pos, 1), - __neg__ => BuiltinFunction::create("__neg__", Int::neg, 1), - }, - Float { - // Conversion methods - to_int => BuiltinFunction::create("to_int", Float::to_int, 1), - to_float => BuiltinFunction::create("to_float", Float::to_float, 1), - - // Constructor - __call__ => BuiltinFunction::create("__call__", Float::do_call, 2), - __init__ => BuiltinFunction::create("__init__", Float::init, 2), - - // Operators - __add__ => BuiltinFunction::create("__add__", Float::add, 2), - __sub__ => BuiltinFunction::create("__sub__", Float::sub, 2), - __mul__ => BuiltinFunction::create("__mul__", Float::mul, 2), - __div__ => BuiltinFunction::create("__div__", Float::div, 2), - __gt__ => BuiltinFunction::create("__gt__", Float::gt, 2), - __ge__ => BuiltinFunction::create("__ge__", Float::ge, 2), - __lt__ => BuiltinFunction::create("__lt__", Float::lt, 2), - __le__ => BuiltinFunction::create("__le__", Float::le, 2), - __pos__ => BuiltinFunction::create("__pos__", Float::pos, 1), - __neg__ => BuiltinFunction::create("__neg__", Float::neg, 1), - }, - Bool { - // Conversion methods - to_int => BuiltinFunction::create("to_int", Bool::to_int, 1), - to_float => BuiltinFunction::create("to_float", Bool::to_float, 1), - - // Constructor - __call__ => BuiltinFunction::create("__call__", Bool::do_call, 2), - __init__ => BuiltinFunction::create("__init__", Bool::init, 2), - - // Operators - }, - Nil { - // Conversion methods - - // Constructor - __call__ => BuiltinFunction::create("__call__", Nil::do_call, 1), - __init__ => BuiltinFunction::create("__init__", Nil::init, 1), - - // Operators - }, - BuiltinFunction { }, - UserFunction { }, - Method { }, - } -} - /// Convenience function for creating pointers, in case the `Arc>` pointer type has to /// change. /// @@ -245,6 +110,10 @@ pub fn make_ptr(obj: T) -> ObjP { upcast_obj(Ptr::new(GcCell::new(obj))) } +thread_local! { + pub static BUILTINS: RefCell> = RefCell::new(HashMap::default()); +} + //////////////////////////////////////////////////////////////////////////////// // Object //////////////////////////////////////////////////////////////////////////////// @@ -274,8 +143,8 @@ pub trait Object: Debug + Display + Any + Trace { let mut ty = self.ty(); loop { let vtable_entry = - with_obj_downcast_mut(ty.clone(), |ty: &mut Ty| ty.vtable.get(name).cloned()).map( - |vtable_entry| { + with_obj_downcast_mut(ty.clone(), |ty: &mut Ty| ty.vtable().get(name).cloned()) + .map(|vtable_entry| { let ptr = if obj_is_inst::(&vtable_entry) || obj_is_inst::(&vtable_entry) { @@ -288,8 +157,7 @@ pub trait Object: Debug + Display + Any + Trace { // floating around. //self.set_attr(name, ptr.clone()); ptr - }, - ); + }); if vtable_entry.is_some() { return vtable_entry; } @@ -306,7 +174,7 @@ pub trait Object: Debug + Display + Any + Trace { } fn ty_name(&self) -> Rc { - with_obj_downcast(self.ty(), |ty: &Ty| Rc::clone(&ty.name)) + with_obj_downcast(self.ty(), |ty: &Ty| Rc::clone(&ty.name())) } fn arity(&self) -> Option { @@ -403,6 +271,136 @@ impl Object for BaseObj { } } +// +// BaseObj implementations +// + +impl BaseObj { + // + // Common functions + // + + pub(crate) fn to_str(vm: &mut Vm, state: FunctionState) -> FunctionResult { + match state { + FunctionState::Begin => { + let this = vm.frame_stack()[0].clone(); + let method = this + .borrow() + .get_vtable_attr(this.clone(), "to_repr") + .clone() + .expect("no to_repr"); + vm.push(method.clone()); + method.borrow().call(vm, 0); + FunctionResult::Yield(0) + } + FunctionState::Resume(0) => FunctionResult::Return, + _ => unreachable!(), + } + } + + pub(crate) fn to_repr(vm: &mut Vm, _state: FunctionState) -> FunctionResult { + Str::create(format!("{:?}", vm.frame_stack()[0].borrow())).into() + } + + pub(crate) fn to_bool(vm: &mut Vm, _state: FunctionState) -> FunctionResult { + Bool::create(vm.frame_stack()[0].borrow().is_truthy()).into() + } + + // + // Operators + // + + pub(crate) fn and(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(); + Bool::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(); + Bool::create(result).into() + } + + pub(crate) fn ne(vm: &mut Vm, state: FunctionState) -> FunctionResult { + match state { + 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 = lhs + .borrow() + .get_vtable_attr(lhs.clone(), "__eq__") + .expect("no __eq__"); + + let rhs = vm.frame_stack()[1].clone(); + vm.push(rhs); + method.borrow().call(vm, 1); + FunctionResult::Yield(0) + } + FunctionState::Resume(0) => { + let result = !vm.peek().borrow().is_truthy(); + Bool::create(result).into() + } + _ => { + unreachable!() + } + } + } + + pub(crate) fn eq(vm: &mut Vm, _state: FunctionState) -> FunctionResult { + let lhs = vm.frame_stack()[0].borrow(); + let rhs = vm.frame_stack()[1].borrow(); + let equals = lhs.equals(&*rhs); + Bool::create(equals).into() + } + + pub(crate) fn not(vm: &mut Vm, state: FunctionState) -> FunctionResult { + match state { + FunctionState::Begin => { + let obj = vm.peek(); + let method = obj + .borrow() + .get_vtable_attr(obj.clone(), "to_bool") + .expect("no to_bool"); + vm.push(method.clone()); + method.borrow().call(vm, 0); + FunctionResult::Yield(0) + } + FunctionState::Resume(0) => { + let value = vm.peek().borrow().is_truthy(); + Bool::create(!value).into() + } + _ => unreachable!(), + } + } + + // + // Not implemented + // + + pub(crate) fn not_implemented_un(vm: &mut Vm, _state: FunctionState) -> FunctionResult { + let fname = &vm.frame().name; + // TODO BaseObj::not_implemented_un - throw an exception of some kind for not + // implemented/not available errors on unary operators + // BLOCKED-ON: exceptions + todo!( + "Raise some kind of not implemented/not callable error for {} function (self: {})", + fname, + vm.frame_stack()[0].borrow() + ) + } + + pub(crate) fn not_implemented_bin(vm: &mut Vm, _state: FunctionState) -> FunctionResult { + // TODO BaseObj::not_implemented_un - throw an exception of some kind for not + // implemented/not available errors on unary operators + // BLOCKED-ON: exceptions + let fname = &vm.frame().name; + todo!("Raise some kind of not implemented/not callable error for {} function (self: {}, rhs: {})", fname, vm.frame_stack()[0].borrow(), vm.frame_stack()[1].borrow()) + } +} + //////////////////////////////////////////////////////////////////////////////// // Obj //////////////////////////////////////////////////////////////////////////////// @@ -448,329 +446,7 @@ impl Object for Obj { } } - impl_base_obj!(Object); -} - -//////////////////////////////////////////////////////////////////////////////// -// Ty -//////////////////////////////////////////////////////////////////////////////// - -#[derive(Trace, Finalize)] -pub struct Ty { - base: BaseObj, - #[unsafe_ignore_trace] - name: Rc, - vtable: HashMap, -} - -impl Ty { - pub fn new(name: impl ToString) -> Self { - Self { - name: Rc::new(name.to_string()), - base: Default::default(), - vtable: Default::default(), - } - } - - pub fn name(&self) -> &Rc { - &self.name - } - - impl_create!(name: impl ToString); -} - -impl Debug for Ty { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - write!( - fmt, - "", - self.name, - (self as *const _ as usize) - ) - } -} - -impl Display for Ty { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - Debug::fmt(self, fmt) - } -} - -impl Object for Ty { - fn equals(&self, other: &dyn Object) -> bool { - if let Some(other) = other.as_any().downcast_ref::() { - // TODO Ty::equals : something more robust than this - // Tys should hold equality if they have the same name - // the problem is that Ty.get_attr("__ty__") is going to return itself, so we have - // to go through attributes to specially exclude to the __ty__ attribute if it points - // to ourself. - // How do we detect that it's pointing to ourself? I suppose pointers are the way - self.name == other.name - } else { - false - } - } - - fn call(&self, vm: &mut Vm, argc: Argc) { - // TODO Object::call - need to handle "this object cannot be called" errors - // BLOCKED-ON: exceptions - - // I don't think there's any way we could call this *without* it being a method. - // If you do e.g. `Int.__call__`, Int is an object, so it should be doing `__call__` as a - // vtable value. - - if cfg!(debug_assertions) { - let index = vm.stack().len() - 1 - argc as usize; - let this = vm.stack()[index].clone(); - assert!( - std::ptr::addr_eq(&*this.borrow(), self), - "calling {}.__call__ on type that is not ourselves", - self.ty_name() - ); - } - - let function = self - .vtable - .get("__call__") - .expect("Why does a type not have a __call__ member?"); - function.borrow().call(vm, argc); - } - - fn arity(&self) -> Option { - // HACK XXX NOTE Ty __call__ arity : - // We need to tread carefully here. Normally, `__call__` would be wrapped as a method. - // However, we have to get the `__call__` member directly from the vtable. - // We are subtracting 1 from the arity, because whenever it *does* become a method, the - // arity will match when we call `Ty` directly. - self.vtable - .get("__call__") - .and_then(|function| function.borrow().arity()) - .map(|n| n - 1) - } - - impl_base_obj!(Ty); -} - -//////////////////////////////////////////////////////////////////////////////// -// Str -//////////////////////////////////////////////////////////////////////////////// - -#[derive(Trace, Finalize)] -pub struct Str { - base: BaseObj, - #[unsafe_ignore_trace] - str_value: Rc, -} - -impl Str { - pub fn new(str_value: impl ToString) -> Self { - Self { - base: Default::default(), - str_value: Rc::new(str_value.to_string()), - } - } - - impl_create!(str_value: impl ToString); - - pub fn str_value(&self) -> &Rc { - &self.str_value - } -} - -impl Display for Str { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - write!(fmt, "{}", self.str_value) - } -} - -impl Debug for Str { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - write!(fmt, "'{}'", self.str_value.as_str().escape_default()) - } -} - -impl Object for Str { - fn is_truthy(&self) -> bool { - !self.str_value.is_empty() - } - - fn equals(&self, other: &dyn Object) -> bool { - if let Some(other) = other.as_any().downcast_ref::() { - self.str_value == other.str_value - } else { - false - } - } - - impl_base_obj!(Str); -} - -//////////////////////////////////////////////////////////////////////////////// -// Int -//////////////////////////////////////////////////////////////////////////////// - -#[derive(Trace, Finalize)] -pub struct Int { - base: BaseObj, - pub(crate) int_value: i64, -} - -impl Int { - pub fn new(int_value: i64) -> Self { - Self { - int_value, - base: Default::default(), - } - } - - impl_create!(int_value: i64); - - pub fn int_value(&self) -> i64 { - self.int_value - } -} - -impl Display for Int { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - write!(fmt, "{}", self.int_value) - } -} - -impl Debug for Int { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - write!(fmt, "{}", self.int_value) - } -} - -impl Object for Int { - fn is_truthy(&self) -> bool { - self.int_value != 0 - } - - fn equals(&self, other: &dyn Object) -> bool { - if let Some(other) = other.as_any().downcast_ref::() { - self.int_value == other.int_value - } else if let Some(other) = other.as_any().downcast_ref::() { - self.int_value as f64 == other.float_value - } else { - false - } - } - - impl_base_obj!(Int); -} - -//////////////////////////////////////////////////////////////////////////////// -// Float -//////////////////////////////////////////////////////////////////////////////// - -#[derive(Trace, Finalize)] -pub struct Float { - base: BaseObj, - float_value: f64, -} - -impl Float { - pub fn new(float_value: f64) -> Self { - Self { - float_value, - base: Default::default(), - } - } - - impl_create!(float_value: f64); - - pub fn float_value(&self) -> f64 { - self.float_value - } -} - -impl Debug for Float { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - // we want to force the .0 if it's a whole number - if self.float_value == self.float_value.floor() { - write!(fmt, "{}.0", self.float_value) - } else { - write!(fmt, "{}", self.float_value) - } - } -} - -impl Display for Float { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - Debug::fmt(self, fmt) - } -} - -impl Object for Float { - fn is_truthy(&self) -> bool { - self.float_value != 0.0 - } - - fn equals(&self, other: &dyn Object) -> bool { - if let Some(other) = other.as_any().downcast_ref::() { - self.float_value == other.float_value - } else if let Some(other) = other.as_any().downcast_ref::() { - self.float_value == other.int_value as f64 - } else { - false - } - } - - impl_base_obj!(Float); -} - -//////////////////////////////////////////////////////////////////////////////// -// Bool -//////////////////////////////////////////////////////////////////////////////// - -#[derive(Trace, Finalize)] -pub struct Bool { - base: BaseObj, - bool_value: bool, -} - -impl Bool { - pub fn new(bool_value: bool) -> Self { - Self { - bool_value, - base: Default::default(), - } - } - - impl_create!(bool_value: bool); - - pub fn bool_value(&self) -> bool { - self.bool_value - } -} - -impl Display for Bool { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - write!(fmt, "{}", self.bool_value) - } -} - -impl Debug for Bool { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - write!(fmt, "{}", self.bool_value) - } -} - -impl Object for Bool { - fn is_truthy(&self) -> bool { - self.bool_value - } - - fn equals(&self, other: &dyn Object) -> bool { - if let Some(other) = other.as_any().downcast_ref::() { - self.bool_value == other.bool_value - } else { - false - } - } - - impl_base_obj!(Bool); + impl_base_obj!(Obj); } //////////////////////////////////////////////////////////////////////////////// @@ -814,13 +490,27 @@ impl Object for Nil { impl_base_obj!(Nil); } +// +// Nil function implementations +// + +impl Nil { + pub(crate) fn do_call(_vm: &mut Vm, _state: FunctionState) -> FunctionResult { + FunctionResult::ReturnPush(Nil::create()) + } + + pub(crate) fn init(_vm: &mut Vm, _state: FunctionState) -> FunctionResult { + FunctionResult::ReturnPush(Nil::create()) + } +} + //////////////////////////////////////////////////////////////////////////////// // Tests //////////////////////////////////////////////////////////////////////////////// #[test] fn test_new_objects() { - init_types(); + ty::init_types(); let type_value = Ty::create("Ty"); assert_eq!(&*type_value.borrow().ty_name(), "Ty"); @@ -840,7 +530,7 @@ fn test_new_objects() { #[test] fn test_obj_equals() { - init_types(); + ty::init_types(); let int1 = Int::create(1234); let int2 = Int::create(1234); @@ -878,7 +568,7 @@ fn test_obj_equals() { #[test] fn test_obj_vtable() { - init_types(); + ty::init_types(); let str1 = Str::create("asdfasdfasdf"); let to_string_ptr = str1.borrow_mut().get_vtable_attr(str1.clone(), "to_str"); diff --git a/src/obj/bool.rs b/src/obj/bool.rs new file mode 100644 index 0000000..e10cec2 --- /dev/null +++ b/src/obj/bool.rs @@ -0,0 +1,77 @@ +use std::fmt::{self, Debug, Display}; + +use gc::{Finalize, Trace}; + +use crate::obj::macros::*; +use crate::obj::prelude::*; +use crate::obj::BaseObj; +use crate::vm::Vm; + +#[derive(Trace, Finalize)] +pub struct Bool { + base: BaseObj, + bool_value: bool, +} + +impl Bool { + pub fn new(bool_value: bool) -> Self { + Self { + bool_value, + base: Default::default(), + } + } + + impl_create!(bool_value: bool); + + pub fn bool_value(&self) -> bool { + self.bool_value + } +} + +impl Display for Bool { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + write!(fmt, "{}", self.bool_value) + } +} + +impl Debug for Bool { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + write!(fmt, "{}", self.bool_value) + } +} + +impl Object for Bool { + fn is_truthy(&self) -> bool { + self.bool_value + } + + fn equals(&self, other: &dyn Object) -> bool { + if let Some(other) = other.as_any().downcast_ref::() { + self.bool_value == other.bool_value + } else { + false + } + } + + impl_base_obj!(Bool); +} + +impl Bool { + impl_do_call!(to_bool); + + pub(crate) fn init(_vm: &mut Vm, _state: FunctionState) -> FunctionResult { + // This is a no-op. We don't want the user-exposed `__init__` function to do anything, + // instantiation is done in the `__call__` function. + FunctionResult::ReturnPush(Nil::create()) + } + + pub(crate) fn to_int(vm: &mut Vm, _state: FunctionState) -> FunctionResult { + let bool_value = with_obj_downcast(vm.frame_stack()[0].clone(), Bool::bool_value); + Int::create(bool_value as i64).into() + } + + pub(crate) fn to_float(vm: &mut Vm, _state: FunctionState) -> FunctionResult { + let bool_value = with_obj_downcast(vm.frame_stack()[0].clone(), Bool::bool_value); + Float::create(bool_value as i64 as f64).into() + } +} diff --git a/src/obj/float.rs b/src/obj/float.rs new file mode 100644 index 0000000..72bfd4d --- /dev/null +++ b/src/obj/float.rs @@ -0,0 +1,162 @@ +use std::fmt::{self, Debug, Display}; + +use gc::{Finalize, Trace}; + +use crate::obj::macros::*; +use crate::obj::prelude::*; +use crate::obj::BaseObj; +use crate::vm::Vm; + +#[derive(Trace, Finalize)] +pub struct Float { + base: BaseObj, + float_value: f64, +} + +impl Float { + pub fn new(float_value: f64) -> Self { + Self { + float_value, + base: Default::default(), + } + } + + impl_create!(float_value: f64); + + pub fn float_value(&self) -> f64 { + self.float_value + } +} + +impl Debug for Float { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + // we want to force the .0 if it's a whole number + if self.float_value == self.float_value.floor() { + write!(fmt, "{}.0", self.float_value) + } else { + write!(fmt, "{}", self.float_value) + } + } +} + +impl Display for Float { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + Debug::fmt(self, fmt) + } +} + +impl Object for Float { + fn is_truthy(&self) -> bool { + self.float_value != 0.0 + } + + fn equals(&self, other: &dyn Object) -> bool { + if let Some(other) = other.as_any().downcast_ref::() { + self.float_value == other.float_value + } else if let Some(other) = other.as_any().downcast_ref::() { + self.float_value == other.int_value as f64 + } else { + false + } + } + + impl_base_obj!(Float); +} + +//////////////////////////////////////////////////////////////////////////////// +// Float implementations +//////////////////////////////////////////////////////////////////////////////// + +macro_rules! float_bin_op_math { + ($function:ident, $op:tt) => { + pub(crate) fn $function(vm: &mut Vm, _state: FunctionState) -> FunctionResult { + let lhs = vm.frame_stack()[0].clone(); + let rhs = vm.frame_stack()[1].clone(); + + let lhs_value = with_obj_downcast(lhs, Float::float_value); + + let result = if let Some(int_inst) = rhs.borrow().as_any().downcast_ref::() { + Float::create(lhs_value $op int_inst.int_value() as f64) + } else if let Some(float_inst) = rhs.borrow().as_any().downcast_ref::() { + Float::create(lhs_value $op float_inst.float_value()) + } else { + // TODO Int arithmetic operator - throw an exception when RHS is not Int, Float + // BLOCKED-ON: exceptions + todo!( + concat!("cannot use '", stringify!($op), "' operator with Float and {}"), + rhs.borrow().ty_name() + ) + }; + result.into() + } + } +} + +macro_rules! float_bin_op_logical { + ($function:ident, $op:tt) => { + pub(crate) fn $function(vm: &mut Vm, _state: FunctionState) -> FunctionResult { + let lhs = vm.frame_stack()[0].clone(); + let rhs = vm.frame_stack()[1].clone(); + + let lhs_value = with_obj_downcast(lhs, Float::float_value); + + let result = if let Some(int_inst) = rhs.borrow().as_any().downcast_ref::() { + Bool::create(lhs_value $op int_inst.int_value() as f64) + } else if let Some(float_inst) = rhs.borrow().as_any().downcast_ref::() { + Bool::create(lhs_value $op float_inst.float_value()) + } else { + // TODO Int logical operator - throw an exception when RHS is not Int, Float + // BLOCKED-ON: exceptions + todo!( + concat!("cannot use '", stringify!($op), "' operator with Float and {}"), + rhs.borrow().ty_name() + ) + }; + result.into() + } + } +} + +impl Float { + impl_do_call!(to_float); + + pub(crate) fn init(_vm: &mut Vm, _state: FunctionState) -> FunctionResult { + // This is a no-op. We don't want the user-exposed `__init__` function to do anything, + // instantiation is done in the `__call__` function. + FunctionResult::ReturnPush(Nil::create()) + } + + pub(crate) fn to_int(vm: &mut Vm, _state: FunctionState) -> FunctionResult { + let float_value = with_obj_downcast(vm.frame_stack()[0].clone(), Float::float_value); + Int::create(float_value as i64).into() + } + + pub(crate) fn to_float(_vm: &mut Vm, _state: FunctionState) -> FunctionResult { + FunctionResult::Return + } + + float_bin_op_math!(add, +); + float_bin_op_math!(sub, -); + float_bin_op_math!(mul, *); + float_bin_op_math!(div, /); + + // __eq__ will use the default .equals implementation + //int_bin_op_logical!(eq, ==); + // __ne__ will call __eq__ and negate it + float_bin_op_logical!(gt, >); + float_bin_op_logical!(ge, >=); + float_bin_op_logical!(lt, <); + float_bin_op_logical!(le, <=); + + pub(crate) fn pos(vm: &mut Vm, _state: FunctionState) -> FunctionResult { + let lhs = vm.frame_stack()[0].clone(); + let value = with_obj_downcast(lhs, Float::float_value); + Float::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, Float::float_value); + Float::create(-value).into() + } +} diff --git a/src/obj/int.rs b/src/obj/int.rs new file mode 100644 index 0000000..9295a00 --- /dev/null +++ b/src/obj/int.rs @@ -0,0 +1,186 @@ +use std::fmt::{self, Debug, Display}; + +use gc::{Finalize, Trace}; + +use crate::obj::macros::*; +use crate::obj::prelude::*; +use crate::obj::BaseObj; +use crate::vm::Vm; + +#[derive(Trace, Finalize)] +pub struct Int { + base: BaseObj, + pub(crate) int_value: i64, +} + +impl Int { + pub fn new(int_value: i64) -> Self { + Self { + int_value, + base: Default::default(), + } + } + + impl_create!(int_value: i64); + + pub fn int_value(&self) -> i64 { + self.int_value + } +} + +impl Display for Int { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + write!(fmt, "{}", self.int_value) + } +} + +impl Debug for Int { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + write!(fmt, "{}", self.int_value) + } +} + +impl Object for Int { + fn is_truthy(&self) -> bool { + self.int_value != 0 + } + + fn equals(&self, other: &dyn Object) -> bool { + if let Some(other) = other.as_any().downcast_ref::() { + self.int_value == other.int_value + } else if let Some(other) = other.as_any().downcast_ref::() { + self.int_value as f64 == other.float_value() + } else { + false + } + } + + impl_base_obj!(Int); +} + +//////////////////////////////////////////////////////////////////////////////// +// Int implementations +//////////////////////////////////////////////////////////////////////////////// + +macro_rules! int_bin_op_math { + ($function:ident, $op:tt) => { + pub(crate) fn $function(vm: &mut Vm, _state: FunctionState) -> FunctionResult { + let lhs = vm.frame_stack()[0].clone(); + let rhs = vm.frame_stack()[1].clone(); + + let lhs_value = with_obj_downcast(lhs, Int::int_value); + + let result = if let Some(int_inst) = rhs.borrow().as_any().downcast_ref::() { + Int::create(lhs_value $op int_inst.int_value()) + } else if let Some(float_inst) = rhs.borrow().as_any().downcast_ref::() { + Float::create(lhs_value as f64 $op float_inst.float_value()) + } else { + // TODO Int arithmetic operator - throw an exception when RHS is not Int, Float + // BLOCKED-ON: exceptions + todo!( + concat!("cannot use '", stringify!($op), "' operator with Int and {}"), + rhs.borrow().ty_name() + ) + }; + result.into() + } + } +} + +macro_rules! int_bin_op_logical { + ($function:ident, $op:tt) => { + pub(crate) fn $function(vm: &mut Vm, _state: FunctionState) -> FunctionResult { + let lhs = vm.frame_stack()[0].clone(); + let rhs = vm.frame_stack()[1].clone(); + + let lhs_value = with_obj_downcast(lhs, Int::int_value); + + let result = if let Some(int_inst) = rhs.borrow().as_any().downcast_ref::() { + Bool::create(lhs_value $op int_inst.int_value()) + } else if let Some(float_inst) = rhs.borrow().as_any().downcast_ref::() { + Bool::create((lhs_value as f64) $op float_inst.float_value()) + } else { + // TODO Int logical operator - throw an exception when RHS is not Int, Float + // BLOCKED-ON: exceptions + todo!( + concat!("cannot use '", stringify!($op), "' operator with Int and {}"), + rhs.borrow().ty_name() + ) + }; + result.into() + } + } +} + +impl Int { + pub(crate) fn to_int(_vm: &mut Vm, _state: FunctionState) -> FunctionResult { + FunctionResult::Return + } + + pub(crate) fn to_float(vm: &mut Vm, _state: FunctionState) -> FunctionResult { + let int_value = with_obj_downcast(vm.frame_stack()[0].clone(), Int::int_value); + Float::create(int_value as f64).into() + } + + impl_do_call!(to_int); + + pub(crate) fn init(_vm: &mut Vm, _state: FunctionState) -> FunctionResult { + // This is a no-op. We don't want the user-exposed `__init__` function to do anything, + // instantiation is done in the `__call__` function. + FunctionResult::ReturnPush(Nil::create()) + } + + int_bin_op_math!(add, +); + int_bin_op_math!(sub, -); + + pub(crate) fn mul(vm: &mut Vm, _state: FunctionState) -> FunctionResult { + // can't bin_op_math this one because it needs the string case + let lhs = vm.frame_stack()[0].clone(); + let rhs = vm.frame_stack()[1].clone(); + + let lhs_value = with_obj_downcast(lhs, Int::int_value); + + let result = if let Some(int_inst) = rhs.borrow().as_any().downcast_ref::() { + Int::create(lhs_value * int_inst.int_value()) + } else if let Some(float_inst) = rhs.borrow().as_any().downcast_ref::() { + Float::create(lhs_value as f64 * float_inst.float_value()) + } else if let Some(str_inst) = rhs.borrow().as_any().downcast_ref::() { + // TODO Int::mul - maybe convert this to just call Str.mul with arguments reversed? + // Just so we have the same logic here + Str::create(str_inst.str_value().repeat(lhs_value as usize)) + } else { + // TODO Int::mul - throw an exception when RHS is not Int, Float, Str + // BLOCKED-ON: exceptions + todo!( + "cannot use '*' operator with Int and {}", + rhs.borrow().ty_name() + ) + }; + result.into() + } + + // TODO Int::div - handle divide by zero + // BLOCKED-ON: exceptions + // NOTE - we will probably need to get rid of the macro here to handle that :( + int_bin_op_math!(div, /); + + // __eq__ will use the default .equals implementation + //int_bin_op_logical!(eq, ==); + // __ne__ will call __eq__ and negate it + int_bin_op_logical!(gt, >); + int_bin_op_logical!(ge, >=); + int_bin_op_logical!(lt, <); + int_bin_op_logical!(le, <=); + + pub(crate) fn pos(vm: &mut Vm, _state: FunctionState) -> FunctionResult { + let lhs = vm.frame_stack()[0].clone(); + let value = with_obj_downcast(lhs, Int::int_value); + Int::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, Int::int_value); + Int::create(-value).into() + } +} diff --git a/src/obj/macros.rs b/src/obj/macros.rs index 50b5fb8..16117a7 100644 --- a/src/obj/macros.rs +++ b/src/obj/macros.rs @@ -38,13 +38,54 @@ macro_rules! impl_base_obj { macro_rules! impl_create { ($($arg:ident : $ty:ty),* $(,)?) => { + // TODO - obj::macros::impl_create - remove this #[allow(dead_code)] designator + #[allow(dead_code)] pub fn create($($arg : $ty ),*) -> $crate::obj::ObjP { - let ptr = make_ptr(Self::new($($arg),*)); + let ptr = $crate::obj::make_ptr(Self::new($($arg),*)); ptr.borrow_mut().instantiate(); ptr } } } +macro_rules! impl_do_call { + ($name:ident) => { + pub(crate) fn do_call( + vm: &mut $crate::vm::Vm, + state: $crate::obj::function::FunctionState, + ) -> $crate::obj::function::FunctionResult { + match state { + $crate::obj::function::FunctionState::Begin => { + // get the top item off the stack and call to_float on it + let arg = vm.peek(); + let method = if let Some(method) = + arg.borrow().get_vtable_attr(arg.clone(), stringify!($name)) + { + method + } else { + // TODO builtins::do_call - throw exception when target doesn't have a + // to_$name method + // BLOCKED-ON: exceptions + todo!( + concat!("{} does not have a ", stringify!($name), " method"), + arg.borrow().ty_name() + ); + }; + vm.push(method.clone()); + method.borrow().call(vm, 0); + + // resume execution + $crate::obj::function::FunctionResult::Yield(0) + } + $crate::obj::function::FunctionState::Resume(0) => { + $crate::obj::function::FunctionResult::Return + } + _ => unreachable!(), + } + } + }; +} + pub(crate) use impl_base_obj; pub(crate) use impl_create; +pub(crate) use impl_do_call; diff --git a/src/obj/str.rs b/src/obj/str.rs new file mode 100644 index 0000000..f4d71a0 --- /dev/null +++ b/src/obj/str.rs @@ -0,0 +1,144 @@ +use std::fmt::{self, Debug, Display}; +use std::rc::Rc; + +use gc::{Finalize, Trace}; + +use crate::obj::macros::*; +use crate::obj::prelude::*; +use crate::obj::BaseObj; +use crate::vm::Vm; + +#[derive(Trace, Finalize)] +pub struct Str { + base: BaseObj, + #[unsafe_ignore_trace] + str_value: Rc, +} + +impl Str { + pub fn new(str_value: impl ToString) -> Self { + Self { + base: Default::default(), + str_value: Rc::new(str_value.to_string()), + } + } + + impl_create!(str_value: impl ToString); + + pub fn str_value(&self) -> &Rc { + &self.str_value + } +} + +impl Display for Str { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + write!(fmt, "{}", self.str_value) + } +} + +impl Debug for Str { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + write!(fmt, "'{}'", self.str_value.as_str().escape_default()) + } +} + +impl Object for Str { + fn is_truthy(&self) -> bool { + !self.str_value.is_empty() + } + + fn equals(&self, other: &dyn Object) -> bool { + if let Some(other) = other.as_any().downcast_ref::() { + self.str_value == other.str_value + } else { + false + } + } + + impl_base_obj!(Str); +} + +//////////////////////////////////////////////////////////////////////////////// +// Builtin implementations +//////////////////////////////////////////////////////////////////////////////// +impl Str { + impl_do_call!(to_str); + + pub(crate) fn init(_vm: &mut Vm, _state: FunctionState) -> FunctionResult { + // This is a no-op. We don't want the user-exposed `__init__` function to do anything, + // instantiation is done in the `__call__` function. + FunctionResult::ReturnPush(Nil::create()) + } + + pub(crate) fn to_str(_vm: &mut Vm, _state: FunctionState) -> FunctionResult { + // top item of the stack should just be ourselves, so return immediately + FunctionResult::Return + } + + pub(crate) fn to_int(vm: &mut Vm, _state: FunctionState) -> FunctionResult { + let parsed: Result = + with_obj_downcast(vm.frame_stack()[0].clone(), |str_inst: &Str| { + str_inst.str_value().parse() + }); + match parsed { + Ok(int) => Int::create(int).into(), + // TODO Str::to_int - throw an exception when we fail to parse an integer + // BLOCKED-ON - exceptions + Err(e) => todo!("error parsing string to an integer: {}", e), + } + } + + pub(crate) fn to_float(vm: &mut Vm, _state: FunctionState) -> FunctionResult { + let parsed: Result = + with_obj_downcast(vm.frame_stack()[0].clone(), |str_inst: &Str| { + str_inst.str_value().parse() + }); + match parsed { + Ok(float) => Float::create(float).into(), + // TODO Str::to_int - throw an exception when we fail to parse an integer + // BLOCKED-ON - exceptions + Err(e) => todo!("error parsing string to a float: {}", e), + } + } + + pub(crate) fn len(vm: &mut Vm, _state: FunctionState) -> FunctionResult { + let len = with_obj_downcast(vm.frame_stack()[0].clone(), |str_inst: &Str| { + str_inst.str_value().len() as i64 + }); + Int::create(len).into() + } + + pub(crate) fn add(vm: &mut Vm, _state: FunctionState) -> FunctionResult { + let lhs = vm.frame_stack()[0].clone(); + let rhs = vm.frame_stack()[1].clone(); + if !obj_is_inst::(&rhs) { + // TODO Str::add - throw an exception when the RHS is not a string + // BLOCKED-ON: exceptions + todo!( + "can only concatenate Str, got {} instead", + rhs.borrow().ty_name() + ) + } + + let new = format!("{}{}", lhs.borrow(), rhs.borrow()); + Str::create(new).into() + } + + pub(crate) fn mul(vm: &mut Vm, _state: FunctionState) -> FunctionResult { + let lhs = vm.frame_stack()[0].clone(); + let rhs = vm.frame_stack()[1].clone(); + let repeat_count = if let Some(int_inst) = rhs.borrow().as_any().downcast_ref::() { + int_inst.int_value() + } else { + // TODO Str::mul - throw an exception when the RHS is not an int + // BLOCKED-ON: exceptions + todo!( + "can only repeat Str with Int, got {} instead", + rhs.borrow().ty_name() + ) + }; + let repeat_count = repeat_count.max(0) as usize; + let new = format!("{}", lhs.borrow()).repeat(repeat_count); + Str::create(new).into() + } +} diff --git a/src/obj/ty.rs b/src/obj/ty.rs new file mode 100644 index 0000000..75a30af --- /dev/null +++ b/src/obj/ty.rs @@ -0,0 +1,270 @@ +use std::collections::HashMap; +use std::fmt::{self, Debug, Display}; +use std::rc::Rc; + +use gc::{Finalize, Trace}; + +use crate::obj::macros::*; +use crate::obj::prelude::*; +use crate::obj::{BaseObj, BUILTINS}; +use crate::vm::{Argc, Vm}; + +#[derive(Trace, Finalize)] +pub struct Ty { + base: BaseObj, + #[unsafe_ignore_trace] + name: Rc, + vtable: HashMap, +} + +impl Ty { + pub fn new(name: impl ToString) -> Self { + Self { + name: Rc::new(name.to_string()), + base: Default::default(), + vtable: Default::default(), + } + } + + pub fn name(&self) -> &Rc { + &self.name + } + + pub fn vtable(&self) -> &HashMap { + &self.vtable + } + + impl_create!(name: impl ToString); +} + +impl Debug for Ty { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + write!( + fmt, + "", + self.name, + (self as *const _ as usize) + ) + } +} + +impl Display for Ty { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + Debug::fmt(self, fmt) + } +} + +impl Object for Ty { + fn equals(&self, other: &dyn Object) -> bool { + if let Some(other) = other.as_any().downcast_ref::() { + // TODO Ty::equals : something more robust than this + // Tys should hold equality if they have the same name + // the problem is that Ty.get_attr("__ty__") is going to return itself, so we have + // to go through attributes to specially exclude to the __ty__ attribute if it points + // to ourself. + // How do we detect that it's pointing to ourself? I suppose pointers are the way + self.name == other.name + } else { + false + } + } + + fn call(&self, vm: &mut Vm, argc: Argc) { + // TODO Object::call - need to handle "this object cannot be called" errors + // BLOCKED-ON: exceptions + + // I don't think there's any way we could call this *without* it being a method. + // If you do e.g. `Int.__call__`, Int is an object, so it should be doing `__call__` as a + // vtable value. + + if cfg!(debug_assertions) { + let index = vm.stack().len() - 1 - argc as usize; + let this = vm.stack()[index].clone(); + assert!( + std::ptr::addr_eq(&*this.borrow(), self), + "calling {}.__call__ on type that is not ourselves", + self.ty_name() + ); + } + + let function = self + .vtable + .get("__call__") + .expect("Why does a type not have a __call__ member?"); + function.borrow().call(vm, argc); + } + + fn arity(&self) -> Option { + // HACK XXX NOTE Ty __call__ arity : + // We need to tread carefully here. Normally, `__call__` would be wrapped as a method. + // However, we have to get the `__call__` member directly from the vtable. + // We are subtracting 1 from the arity, because whenever it *does* become a method, the + // arity will match when we call `Ty` directly. + self.vtable + .get("__call__") + .and_then(|function| function.borrow().arity()) + .map(|n| n - 1) + } + + impl_base_obj!(Ty); +} + +pub fn init_types() { + #![allow(non_snake_case)] + macro_rules! types { + ( + base_type: $base_type:ident, + $( + $name:ident { + $( $vtable_name:ident => $vtable_value:expr ),* $(,)? + } + ),* $(,)? + ) => {{ + $( + let $name = make_ptr(Ty::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` + // function does some stuff that may accidentally cause infinite recursion while we are + // setting up these fundamental types. + $({ + let base_type = $base_type.clone(); + $name.borrow_mut().set_attr("__ty__", base_type); + with_obj_downcast_mut($name.clone(), |ty: &mut Ty| { ty.base.is_instantiated = true; }); + })* + + $({ + $( + let vtable_name = stringify!($vtable_name); + let vtable_value = $vtable_value; + with_obj_downcast_mut($name.clone(), |ty: &mut Ty| { + ty.vtable.insert(vtable_name.to_string(), vtable_value); + }); + )* + })* + }}; + } + + types! { + // base type + base_type: Ty, + // type definitions + Ty { + // Conversion methods + to_str => BuiltinFunction::create("to_str", BaseObj::to_str, 1), + to_repr => BuiltinFunction::create("to_repr", BaseObj::to_repr, 1), + to_bool => BuiltinFunction::create("to_bool", BaseObj::to_bool, 1), + to_int => BuiltinFunction::create("to_int", BaseObj::not_implemented_un, 1), + to_float => BuiltinFunction::create("to_float", BaseObj::not_implemented_un, 1), + len => BuiltinFunction::create("len", BaseObj::not_implemented_un, 1), + + // Constructor + // TODO Ty::do_call, Ty::init - implement these methods + __call__ => BuiltinFunction::create("__call__", BaseObj::not_implemented_un, 1), + __init__ => BuiltinFunction::create("__init__", BaseObj::not_implemented_un, 1), + + // Operators + __add__ => BuiltinFunction::create("__add__", BaseObj::not_implemented_bin, 2), + __sub__ => BuiltinFunction::create("__sub__", BaseObj::not_implemented_bin, 2), + __mul__ => BuiltinFunction::create("__mul__", BaseObj::not_implemented_bin, 2), + __div__ => BuiltinFunction::create("__div__", BaseObj::not_implemented_bin, 2), + __and__ => BuiltinFunction::create("__and__", BaseObj::and, 2), + __or__ => BuiltinFunction::create("__or__", BaseObj::or, 2), + __ne__ => BuiltinFunction::create("__ne__", BaseObj::ne, 2), + __eq__ => BuiltinFunction::create("__eq__", BaseObj::eq, 2), + __gt__ => BuiltinFunction::create("__gt__", BaseObj::not_implemented_bin, 2), + __ge__ => BuiltinFunction::create("__ge__", BaseObj::not_implemented_bin, 2), + __lt__ => BuiltinFunction::create("__lt__", BaseObj::not_implemented_bin, 2), + __le__ => BuiltinFunction::create("__le__", BaseObj::not_implemented_bin, 2), + __pos__ => BuiltinFunction::create("__pos__", BaseObj::not_implemented_un, 1), + __neg__ => BuiltinFunction::create("__neg__", BaseObj::not_implemented_un, 1), + __not__ => BuiltinFunction::create("__not__", BaseObj::not, 1), + }, + Obj { + //__call__ => BuiltinFunction::create("__call__", + }, + Str { + // Conversion methods + to_str => BuiltinFunction::create("to_str", Str::to_str, 1), + to_int => BuiltinFunction::create("to_int", Str::to_int, 1), + to_float => BuiltinFunction::create("to_float", Str::to_float, 1), + len => BuiltinFunction::create("len", Str::len, 1), + + // Constructor + __call__ => BuiltinFunction::create("__call__", Str::do_call, 2), + __init__ => BuiltinFunction::create("__init__", Str::init, 2), + + // Operators + __add__ => BuiltinFunction::create("__add__", Str::add, 2), + __mul__ => BuiltinFunction::create("__mul__", Str::mul, 2), + // .lower, .upper, .slice, etc + }, + Int { + // Conversion methods + to_int => BuiltinFunction::create("to_int", Int::to_int, 1), + to_float => BuiltinFunction::create("to_float", Int::to_float, 1), + + // Constructor + __call__ => BuiltinFunction::create("__call__", Int::do_call, 2), + __init__ => BuiltinFunction::create("__init__", Int::init, 2), + + // Operators + __add__ => BuiltinFunction::create("__add__", Int::add, 2), + __sub__ => BuiltinFunction::create("__sub__", Int::sub, 2), + __mul__ => BuiltinFunction::create("__mul__", Int::mul, 2), + __div__ => BuiltinFunction::create("__div__", Int::div, 2), + //__eq__ => BuiltinFunction::create("__eq__", Int::eq, 2), + __gt__ => BuiltinFunction::create("__gt__", Int::gt, 2), + __ge__ => BuiltinFunction::create("__ge__", Int::ge, 2), + __lt__ => BuiltinFunction::create("__lt__", Int::lt, 2), + __le__ => BuiltinFunction::create("__le__", Int::le, 2), + __pos__ => BuiltinFunction::create("__pos__", Int::pos, 1), + __neg__ => BuiltinFunction::create("__neg__", Int::neg, 1), + }, + Float { + // Conversion methods + to_int => BuiltinFunction::create("to_int", Float::to_int, 1), + to_float => BuiltinFunction::create("to_float", Float::to_float, 1), + + // Constructor + __call__ => BuiltinFunction::create("__call__", Float::do_call, 2), + __init__ => BuiltinFunction::create("__init__", Float::init, 2), + + // Operators + __add__ => BuiltinFunction::create("__add__", Float::add, 2), + __sub__ => BuiltinFunction::create("__sub__", Float::sub, 2), + __mul__ => BuiltinFunction::create("__mul__", Float::mul, 2), + __div__ => BuiltinFunction::create("__div__", Float::div, 2), + __gt__ => BuiltinFunction::create("__gt__", Float::gt, 2), + __ge__ => BuiltinFunction::create("__ge__", Float::ge, 2), + __lt__ => BuiltinFunction::create("__lt__", Float::lt, 2), + __le__ => BuiltinFunction::create("__le__", Float::le, 2), + __pos__ => BuiltinFunction::create("__pos__", Float::pos, 1), + __neg__ => BuiltinFunction::create("__neg__", Float::neg, 1), + }, + Bool { + // Conversion methods + to_int => BuiltinFunction::create("to_int", Bool::to_int, 1), + to_float => BuiltinFunction::create("to_float", Bool::to_float, 1), + + // Constructor + __call__ => BuiltinFunction::create("__call__", Bool::do_call, 2), + __init__ => BuiltinFunction::create("__init__", Bool::init, 2), + + // Operators + }, + Nil { + // Conversion methods + + // Constructor + __call__ => BuiltinFunction::create("__call__", Nil::do_call, 1), + __init__ => BuiltinFunction::create("__init__", Nil::init, 1), + + // Operators + }, + BuiltinFunction { }, + UserFunction { }, + Method { }, + } +} diff --git a/src/vm.rs b/src/vm.rs index 26bc83b..1e7e738 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -1,7 +1,7 @@ use std::rc::Rc; -use crate::obj::function::*; -use crate::obj::*; +use crate::obj::prelude::*; +use crate::obj::BUILTINS; #[derive(Debug, Clone, Copy, PartialEq)] pub enum Op { @@ -246,7 +246,7 @@ impl Vm { /// Peek the top value of the stack. pub fn peek(&self) -> ObjP { - self.stack.last().map(Ptr::clone).expect("stack empty") + self.stack.last().map(ObjP::clone).expect("stack empty") } /// Push a value to the stack. @@ -263,13 +263,12 @@ impl Vm { self.pop(); } Op::PushConstant(constant_id) => { - let constant = Ptr::clone(&self.constants[constant_id as usize]); + let constant = self.constants[constant_id as usize].clone(); self.push(constant); } Op::GetLocal(local_index) => { let local = &self.chunk().expect("no chunk").locals[local_index as usize]; - let value = - Ptr::clone(&self.stack[self.frame().stack_base + local.slot as usize]); + let value = self.stack[self.frame().stack_base + local.slot as usize].clone(); self.push(value); } Op::SetLocal(local_index) => { @@ -279,7 +278,7 @@ impl Vm { self.stack[index] = value; } Op::GetGlobal(global_index) => { - let value = Ptr::clone(&self.globals[global_index as usize]); + let value = self.globals[global_index as usize].clone(); self.push(value); } Op::SetGlobal(global_index) => { @@ -288,7 +287,7 @@ impl Vm { } Op::GetAttr(constant_id) => { // need both declarations to borrow cell value - let name_obj = Ptr::clone(&self.constants[constant_id as usize]); + let name_obj = self.constants[constant_id as usize].clone(); let name = with_obj_downcast(name_obj, |name: &Str| Rc::clone(&name.str_value())); let owner = self.pop(); @@ -307,7 +306,7 @@ impl Vm { } } Op::SetAttr(constant_id) => { - let name_obj = Ptr::clone(&self.constants[constant_id as usize]); + let name_obj = self.constants[constant_id as usize].clone(); let name = with_obj_downcast(name_obj, |name: &Str| Rc::clone(&name.str_value())); let value = self.pop(); @@ -337,7 +336,7 @@ impl Vm { Op::Call(argc) => { let argc = argc; let index = self.stack.len() - (argc as usize) - 1; - let fun_ptr = Ptr::clone(&self.stack[index]); + let fun_ptr = self.stack[index].clone(); let arity = if let Some(arity) = fun_ptr.borrow().arity() { arity @@ -385,7 +384,7 @@ impl Vm { let mut fun: UserFunction = with_obj_downcast(fun_ptr, UserFunction::clone); let frame_index = self.frames.len() - (depth as usize) - 1; let stack_base = self.frames[frame_index].stack_base; - let value = Ptr::clone(&self.stack[stack_base + (slot as usize)]); + let value = self.stack[stack_base + (slot as usize)].clone(); fun.push_capture(value); self.push(make_ptr(fun)); }