Add do_call macro, implement Bool builtins, add tests

* I noticed that `fn call(...)` in all objects was identical, so I made
  a macro for it. This should make things a little easier to read, since
  do_call is about 30 lines a pop.
* Bool has a constructor now, and a to_int and to_float implementations
* Add tests for constructors and add new bool tests

Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
This commit is contained in:
2024-09-30 12:41:13 -07:00
parent b9429d7c19
commit 3d0da0ec85
10 changed files with 240 additions and 78 deletions

View File

@@ -33,6 +33,39 @@ pub fn init_global_builtins() {
//
// This would probably be doable in a procedural macro.
macro_rules! impl_do_call {
($name:ident) => {
pub(crate) fn do_call(vm: &mut Vm, state: FunctionState) -> FunctionResult {
match state {
FunctionState::Begin => {
// get the top item off the stack and call to_float on it
let arg = vm.peek();
let method = if let Some(method) =
arg.borrow().get_vtable_attr(arg.clone(), stringify!($name))
{
method
} else {
// TODO builtins::do_call - throw exception when target doesn't have a
// to_$name method
// BLOCKED-ON: exceptions
todo!(
concat!("{} does not have a ", stringify!($name), " method"),
arg.borrow().ty_name()
);
};
vm.push(method.clone());
method.borrow().call(vm, 0);
// resume execution
FunctionResult::Yield(0)
}
FunctionState::Resume(0) => FunctionResult::Return,
_ => unreachable!(),
}
}
};
}
////////////////////////////////////////////////////////////////////////////////
// Global functions
////////////////////////////////////////////////////////////////////////////////
@@ -211,31 +244,7 @@ impl BaseObj {
// Str implementations
////////////////////////////////////////////////////////////////////////////////
impl Str {
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_str on it
let arg = vm.peek();
let method =
if let Some(method) = arg.borrow().get_vtable_attr(arg.clone(), "to_str") {
method
} else {
// TODO Str::do_call - throw exception when target doesn't have a to_str()
// method
// BLOCKED-ON: exceptions
todo!("{} does not have a to_str() method", arg.borrow().ty_name());
// NOTE - every object should have to_str, right?
};
vm.push(method.clone());
method.borrow().call(vm, 0);
// resume execution
FunctionResult::Yield(0)
}
FunctionState::Resume(0) => FunctionResult::Return,
_ => unreachable!(),
}
}
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,
@@ -380,31 +389,7 @@ impl Int {
Float::create(int_value as f64).into()
}
pub(crate) fn do_call(vm: &mut Vm, state: FunctionState) -> FunctionResult {
match state {
FunctionState::Begin => {
// Pop the argument and call `to_int` on it
let arg = vm.peek();
let method =
if let Some(method) = arg.borrow().get_vtable_attr(arg.clone(), "to_int") {
method
} else {
// TODO Int::do_call - throw exception when arg doesn't have to_int()
// method
// BLOCKED-ON: exceptions
todo!("{} does not have a to_int() method", arg.borrow().ty_name());
};
vm.push(method.clone());
method.borrow().call(vm, 0);
FunctionResult::Yield(0)
}
FunctionState::Resume(0) => FunctionResult::Return,
_ => unreachable!(),
}
}
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,
@@ -522,33 +507,7 @@ macro_rules! float_bin_op_logical {
}
impl Float {
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(), "to_float") {
method
} else {
// TODO Float::do_call - throw exception when target doesn't have a to_float()
// method
// BLOCKED-ON: exceptions
todo!(
"{} does not have a to_float() 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!(),
}
}
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,
@@ -590,3 +549,23 @@ impl Float {
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()
}
}

View File

@@ -210,7 +210,17 @@ pub fn init_types() {
__pos__ => BuiltinFunction::create("__pos__", Float::pos, 1),
__neg__ => BuiltinFunction::create("__neg__", Float::neg, 1),
},
Bool { },
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 { },
BuiltinFunction { },
UserFunction { },