Most object types get their own file now

This is hopefully going to make navigating the source tree easier.
Hopefully.

The only types that don't get their own files are:

* function types (UserFunction, BuiltinFunction, Method), which all live
  in obj/function.rs
* Nil, which lives in obj.rs
* Obj, which lives in obj.rs

Type definitions and init_types now live in obj/ty.rs.

New obj::prelude module for common imports.

Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
This commit is contained in:
2024-09-30 15:15:41 -07:00
parent 724a6b6f99
commit 43183d6553
11 changed files with 1082 additions and 1017 deletions

View File

@@ -1,6 +1,5 @@
//! Builtin functions. //! Builtin functions.
use crate::obj::function::{BuiltinFunction, FunctionResult, FunctionState}; use crate::obj::prelude::*;
use crate::obj::*;
use crate::vm::Vm; use crate::vm::Vm;
pub fn init_global_builtins() { pub fn init_global_builtins() {
@@ -33,39 +32,6 @@ pub fn init_global_builtins() {
// //
// This would probably be doable in a procedural macro. // 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 // Global functions
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@@ -109,473 +75,3 @@ pub(crate) fn print(vm: &mut Vm, state: FunctionState) -> FunctionResult {
_ => unreachable!(), _ => 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<i64, _> =
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<f64, _> =
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::<Str>(&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>() {
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>() {
Int::create(lhs_value $op int_inst.int_value())
} else if let Some(float_inst) = rhs.borrow().as_any().downcast_ref::<Float>() {
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::<Int>() {
Bool::create(lhs_value $op int_inst.int_value())
} else if let Some(float_inst) = rhs.borrow().as_any().downcast_ref::<Float>() {
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>() {
Int::create(lhs_value * int_inst.int_value())
} else if let Some(float_inst) = rhs.borrow().as_any().downcast_ref::<Float>() {
Float::create(lhs_value as f64 * float_inst.float_value())
} else if let Some(str_inst) = rhs.borrow().as_any().downcast_ref::<Str>() {
// 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::<Int>() {
Float::create(lhs_value $op int_inst.int_value() as f64)
} else if let Some(float_inst) = rhs.borrow().as_any().downcast_ref::<Float>() {
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::<Int>() {
Bool::create(lhs_value $op int_inst.int_value() as f64)
} else if let Some(float_inst) = rhs.borrow().as_any().downcast_ref::<Float>() {
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())
}
}

View File

@@ -8,8 +8,8 @@ use common_macros::hash_map;
use thiserror::Error; use thiserror::Error;
use crate::ast::*; use crate::ast::*;
use crate::obj::function::UserFunction; use crate::obj::prelude::*;
use crate::obj::*; use crate::obj::BUILTINS;
use crate::token::TokenKind; use crate::token::TokenKind;
use crate::vm::*; use crate::vm::*;

View File

@@ -53,7 +53,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
} }
// initialize type system // initialize type system
obj::init_types(); obj::ty::init_types();
crate::builtins::init_global_builtins(); crate::builtins::init_global_builtins();
// compile // compile

View File

@@ -1,9 +1,14 @@
// TODO obj.rs - remove the warning suppression // TODO obj.rs - remove the warning suppression
#![allow(dead_code)] //#![allow(dead_code)]
mod macros; mod macros;
// Leave this comment here - macros must come first // Leave this comment here - macros must come first
pub mod bool;
pub mod float;
pub mod function; pub mod function;
pub mod int;
pub mod str;
pub mod ty;
use std::any::Any; use std::any::Any;
use std::cell::RefCell; use std::cell::RefCell;
@@ -13,14 +18,36 @@ use std::rc::Rc;
use gc::{Finalize, Gc, GcCell, Trace}; use gc::{Finalize, Gc, GcCell, Trace};
use crate::obj::function::*;
use crate::obj::macros::*; use crate::obj::macros::*;
use crate::obj::prelude::*;
use crate::vm::{Argc, Vm}; use crate::vm::{Argc, Vm};
pub type Ptr<T> = Gc<GcCell<T>>; pub type Ptr<T> = Gc<GcCell<T>>;
pub type ObjP = Ptr<dyn Object>; pub type ObjP = Ptr<dyn Object>;
pub type Attrs = HashMap<String, ObjP>; pub type Attrs = HashMap<String, ObjP>;
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 // 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. /// 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<T>(ptr: &ObjP) -> bool pub fn obj_is_inst<T>(ptr: &ObjP) -> bool
where where
T: Object + 'static, T: Object + 'static,
@@ -65,6 +93,7 @@ where
borrowed.as_any().downcast_ref::<T>().is_some() borrowed.as_any().downcast_ref::<T>().is_some()
} }
/// Upcast a pointer to a concrete object up to a trait object pointer.
pub fn upcast_obj<T: Object>(ptr: Ptr<T>) -> ObjP { pub fn upcast_obj<T: Object>(ptr: Ptr<T>) -> ObjP {
unsafe { unsafe {
let ptr = Ptr::into_raw(ptr) as *const GcCell<dyn Object>; let ptr = Ptr::into_raw(ptr) as *const GcCell<dyn Object>;
@@ -72,170 +101,6 @@ pub fn upcast_obj<T: Object>(ptr: Ptr<T>) -> ObjP {
} }
} }
thread_local! {
pub static BUILTINS: RefCell<HashMap<String, ObjP>> = 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<RwLock<T>>` pointer type has to /// Convenience function for creating pointers, in case the `Arc<RwLock<T>>` pointer type has to
/// change. /// change.
/// ///
@@ -245,6 +110,10 @@ pub fn make_ptr<T: Object>(obj: T) -> ObjP {
upcast_obj(Ptr::new(GcCell::new(obj))) upcast_obj(Ptr::new(GcCell::new(obj)))
} }
thread_local! {
pub static BUILTINS: RefCell<HashMap<String, ObjP>> = RefCell::new(HashMap::default());
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Object // Object
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@@ -274,8 +143,8 @@ pub trait Object: Debug + Display + Any + Trace {
let mut ty = self.ty(); let mut ty = self.ty();
loop { loop {
let vtable_entry = let vtable_entry =
with_obj_downcast_mut(ty.clone(), |ty: &mut Ty| ty.vtable.get(name).cloned()).map( with_obj_downcast_mut(ty.clone(), |ty: &mut Ty| ty.vtable().get(name).cloned())
|vtable_entry| { .map(|vtable_entry| {
let ptr = if obj_is_inst::<BuiltinFunction>(&vtable_entry) let ptr = if obj_is_inst::<BuiltinFunction>(&vtable_entry)
|| obj_is_inst::<UserFunction>(&vtable_entry) || obj_is_inst::<UserFunction>(&vtable_entry)
{ {
@@ -288,8 +157,7 @@ pub trait Object: Debug + Display + Any + Trace {
// floating around. // floating around.
//self.set_attr(name, ptr.clone()); //self.set_attr(name, ptr.clone());
ptr ptr
}, });
);
if vtable_entry.is_some() { if vtable_entry.is_some() {
return vtable_entry; return vtable_entry;
} }
@@ -306,7 +174,7 @@ pub trait Object: Debug + Display + Any + Trace {
} }
fn ty_name(&self) -> Rc<String> { fn ty_name(&self) -> Rc<String> {
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<Argc> { fn arity(&self) -> Option<Argc> {
@@ -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 // Obj
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@@ -448,329 +446,7 @@ impl Object for Obj {
} }
} }
impl_base_obj!(Object); impl_base_obj!(Obj);
}
////////////////////////////////////////////////////////////////////////////////
// Ty
////////////////////////////////////////////////////////////////////////////////
#[derive(Trace, Finalize)]
pub struct Ty {
base: BaseObj,
#[unsafe_ignore_trace]
name: Rc<String>,
vtable: HashMap<String, ObjP>,
}
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<String> {
&self.name
}
impl_create!(name: impl ToString);
}
impl Debug for Ty {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(
fmt,
"<Ty {} at {:#x}>",
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::<Ty>() {
// 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<Argc> {
// 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<String>,
}
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<String> {
&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::<Str>() {
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::<Int>() {
self.int_value == other.int_value
} else if let Some(other) = other.as_any().downcast_ref::<Float>() {
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::<Float>() {
self.float_value == other.float_value
} else if let Some(other) = other.as_any().downcast_ref::<Int>() {
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::<Bool>() {
self.bool_value == other.bool_value
} else {
false
}
}
impl_base_obj!(Bool);
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@@ -814,13 +490,27 @@ impl Object for Nil {
impl_base_obj!(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 // Tests
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#[test] #[test]
fn test_new_objects() { fn test_new_objects() {
init_types(); ty::init_types();
let type_value = Ty::create("Ty"); let type_value = Ty::create("Ty");
assert_eq!(&*type_value.borrow().ty_name(), "Ty"); assert_eq!(&*type_value.borrow().ty_name(), "Ty");
@@ -840,7 +530,7 @@ fn test_new_objects() {
#[test] #[test]
fn test_obj_equals() { fn test_obj_equals() {
init_types(); ty::init_types();
let int1 = Int::create(1234); let int1 = Int::create(1234);
let int2 = Int::create(1234); let int2 = Int::create(1234);
@@ -878,7 +568,7 @@ fn test_obj_equals() {
#[test] #[test]
fn test_obj_vtable() { fn test_obj_vtable() {
init_types(); ty::init_types();
let str1 = Str::create("asdfasdfasdf"); let str1 = Str::create("asdfasdfasdf");
let to_string_ptr = str1.borrow_mut().get_vtable_attr(str1.clone(), "to_str"); let to_string_ptr = str1.borrow_mut().get_vtable_attr(str1.clone(), "to_str");

77
src/obj/bool.rs Normal file
View File

@@ -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::<Bool>() {
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()
}
}

162
src/obj/float.rs Normal file
View File

@@ -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::<Float>() {
self.float_value == other.float_value
} else if let Some(other) = other.as_any().downcast_ref::<Int>() {
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::<Int>() {
Float::create(lhs_value $op int_inst.int_value() as f64)
} else if let Some(float_inst) = rhs.borrow().as_any().downcast_ref::<Float>() {
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::<Int>() {
Bool::create(lhs_value $op int_inst.int_value() as f64)
} else if let Some(float_inst) = rhs.borrow().as_any().downcast_ref::<Float>() {
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()
}
}

186
src/obj/int.rs Normal file
View File

@@ -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::<Int>() {
self.int_value == other.int_value
} else if let Some(other) = other.as_any().downcast_ref::<Float>() {
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>() {
Int::create(lhs_value $op int_inst.int_value())
} else if let Some(float_inst) = rhs.borrow().as_any().downcast_ref::<Float>() {
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::<Int>() {
Bool::create(lhs_value $op int_inst.int_value())
} else if let Some(float_inst) = rhs.borrow().as_any().downcast_ref::<Float>() {
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>() {
Int::create(lhs_value * int_inst.int_value())
} else if let Some(float_inst) = rhs.borrow().as_any().downcast_ref::<Float>() {
Float::create(lhs_value as f64 * float_inst.float_value())
} else if let Some(str_inst) = rhs.borrow().as_any().downcast_ref::<Str>() {
// 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()
}
}

View File

@@ -38,13 +38,54 @@ macro_rules! impl_base_obj {
macro_rules! impl_create { macro_rules! impl_create {
($($arg:ident : $ty:ty),* $(,)?) => { ($($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 { 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.borrow_mut().instantiate();
ptr 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_base_obj;
pub(crate) use impl_create; pub(crate) use impl_create;
pub(crate) use impl_do_call;

144
src/obj/str.rs Normal file
View File

@@ -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<String>,
}
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<String> {
&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::<Str>() {
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<i64, _> =
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<f64, _> =
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::<Str>(&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>() {
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()
}
}

270
src/obj/ty.rs Normal file
View File

@@ -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<String>,
vtable: HashMap<String, ObjP>,
}
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<String> {
&self.name
}
pub fn vtable(&self) -> &HashMap<String, ObjP> {
&self.vtable
}
impl_create!(name: impl ToString);
}
impl Debug for Ty {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(
fmt,
"<Ty {} at {:#x}>",
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::<Ty>() {
// 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<Argc> {
// 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 { },
}
}

View File

@@ -1,7 +1,7 @@
use std::rc::Rc; use std::rc::Rc;
use crate::obj::function::*; use crate::obj::prelude::*;
use crate::obj::*; use crate::obj::BUILTINS;
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]
pub enum Op { pub enum Op {
@@ -246,7 +246,7 @@ impl Vm {
/// Peek the top value of the stack. /// Peek the top value of the stack.
pub fn peek(&self) -> ObjP { 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. /// Push a value to the stack.
@@ -263,13 +263,12 @@ impl Vm {
self.pop(); self.pop();
} }
Op::PushConstant(constant_id) => { 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); self.push(constant);
} }
Op::GetLocal(local_index) => { Op::GetLocal(local_index) => {
let local = &self.chunk().expect("no chunk").locals[local_index as usize]; let local = &self.chunk().expect("no chunk").locals[local_index as usize];
let value = let value = self.stack[self.frame().stack_base + local.slot as usize].clone();
Ptr::clone(&self.stack[self.frame().stack_base + local.slot as usize]);
self.push(value); self.push(value);
} }
Op::SetLocal(local_index) => { Op::SetLocal(local_index) => {
@@ -279,7 +278,7 @@ impl Vm {
self.stack[index] = value; self.stack[index] = value;
} }
Op::GetGlobal(global_index) => { 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); self.push(value);
} }
Op::SetGlobal(global_index) => { Op::SetGlobal(global_index) => {
@@ -288,7 +287,7 @@ impl Vm {
} }
Op::GetAttr(constant_id) => { Op::GetAttr(constant_id) => {
// need both declarations to borrow cell value // 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 = let name =
with_obj_downcast(name_obj, |name: &Str| Rc::clone(&name.str_value())); with_obj_downcast(name_obj, |name: &Str| Rc::clone(&name.str_value()));
let owner = self.pop(); let owner = self.pop();
@@ -307,7 +306,7 @@ impl Vm {
} }
} }
Op::SetAttr(constant_id) => { 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 = let name =
with_obj_downcast(name_obj, |name: &Str| Rc::clone(&name.str_value())); with_obj_downcast(name_obj, |name: &Str| Rc::clone(&name.str_value()));
let value = self.pop(); let value = self.pop();
@@ -337,7 +336,7 @@ impl Vm {
Op::Call(argc) => { Op::Call(argc) => {
let argc = argc; let argc = argc;
let index = self.stack.len() - (argc as usize) - 1; 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() { let arity = if let Some(arity) = fun_ptr.borrow().arity() {
arity arity
@@ -385,7 +384,7 @@ impl Vm {
let mut fun: UserFunction = with_obj_downcast(fun_ptr, UserFunction::clone); let mut fun: UserFunction = with_obj_downcast(fun_ptr, UserFunction::clone);
let frame_index = self.frames.len() - (depth as usize) - 1; let frame_index = self.frames.len() - (depth as usize) - 1;
let stack_base = self.frames[frame_index].stack_base; 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); fun.push_capture(value);
self.push(make_ptr(fun)); self.push(make_ptr(fun));
} }