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:
506
src/builtins.rs
506
src/builtins.rs
@@ -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())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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::*;
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
684
src/obj.rs
684
src/obj.rs
@@ -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
77
src/obj/bool.rs
Normal 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
162
src/obj/float.rs
Normal 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
186
src/obj/int.rs
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
144
src/obj/str.rs
Normal 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
270
src/obj/ty.rs
Normal 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 { },
|
||||||
|
}
|
||||||
|
}
|
||||||
21
src/vm.rs
21
src/vm.rs
@@ -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));
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user