diff --git a/src/builtins.rs b/src/builtins.rs index 1058ac9..66e3121 100644 --- a/src/builtins.rs +++ b/src/builtins.rs @@ -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() + } +} diff --git a/src/obj.rs b/src/obj.rs index 0af5dd3..add52f5 100644 --- a/src/obj.rs +++ b/src/obj.rs @@ -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 { }, diff --git a/tests/bool.npp b/tests/bool.npp new file mode 100644 index 0000000..b4b9bf6 --- /dev/null +++ b/tests/bool.npp @@ -0,0 +1,72 @@ +# Bool type operator and function tests + +a = true +b = false + +# __not__ +println("__not__") +println(!a) +println(!!a) +println(!!!a) +println(!!!!a) +println(!b) +println(!!b) +println(!!!b) +println(!!!!b) + +# __and__ +println("__and__") +println(a && b) +println(b && a) +println(a && a) +println(b && b) + +# __or__ +println("__or__") +println(a || b) +println(b || a) +println(a || a) +println(b || b) + +# __eq__ +println("__eq__") +println(a == a) +println(a == b) +println(b == b) +println(b == a) +println(a == true) +println(true == a) +println(a == false) +println(false == a) +println(b == true) +println(true == b) +println(b == false) +println(false == b) + +# __ne__ +println("__ne__") +println(a != a) +println(a != b) +println(b != b) +println(b != a) +println(a != true) +println(true != a) +println(a != false) +println(false != a) +println(b != true) +println(true != b) +println(b != false) +println(false != b) + +# constructor +println("constructor") +println(Bool("")) +println(Bool(0)) +println(Bool(0.0)) +println(Bool(false)) +# This is a fun one. Bool constructor does *not* parse bools, it just checks if they are empty. +println(Bool("false")) +println(Bool("true")) +println(Bool(1)) +println(Bool(1.0)) +println(Bool(true)) diff --git a/tests/bool.npp.expect b/tests/bool.npp.expect new file mode 100644 index 0000000..e210737 --- /dev/null +++ b/tests/bool.npp.expect @@ -0,0 +1,55 @@ +__not__ +false +true +false +true +true +false +true +false +__and__ +false +false +true +false +__or__ +true +true +true +false +__eq__ +true +false +true +false +true +true +false +false +false +false +true +true +__ne__ +false +true +false +true +false +false +true +true +true +true +false +false +constructor +false +false +false +false +true +true +true +true +true diff --git a/tests/float.npp b/tests/float.npp index 633e3d7..f989dd7 100644 --- a/tests/float.npp +++ b/tests/float.npp @@ -128,3 +128,12 @@ println(-0.0) println(--0.0) println(---0.0) +# constructor +println("constructor") +println(Float("10")) +println(Float("10e3")) +println(Float("12e-3")) +println(Float(1234)) +println(Float(1.5)) +println(Float(true)) +println(Float(false)) diff --git a/tests/float.npp.expect b/tests/float.npp.expect index 1c3beac..ced34d1 100644 --- a/tests/float.npp.expect +++ b/tests/float.npp.expect @@ -96,3 +96,11 @@ __neg__ -0.0 0.0 -0.0 +constructor +10.0 +10000.0 +0.012 +1234.0 +1.5 +1.0 +0.0 diff --git a/tests/int.npp b/tests/int.npp index bdb4737..2390236 100644 --- a/tests/int.npp +++ b/tests/int.npp @@ -123,3 +123,11 @@ println(10 - -20) println(-10 - 20) println(-10 - -20) println(-0xff) + +# constructor +println("constructor") +println(Int("10")) +println(Int(1234)) +println(Int(1.5)) +println(Int(true)) +println(Int(false)) diff --git a/tests/int.npp.expect b/tests/int.npp.expect index 4c044af..62ebb6f 100644 --- a/tests/int.npp.expect +++ b/tests/int.npp.expect @@ -93,3 +93,9 @@ __neg__ -30 10 -255 +constructor +10 +1234 +1 +1 +0 diff --git a/tests/str.npp b/tests/str.npp index b189c85..058c182 100644 --- a/tests/str.npp +++ b/tests/str.npp @@ -33,8 +33,17 @@ println(a + a) println(a + a + a) println(a + b) println(b + a) + # __mul__ println("__mul__") println(a * 4) println(b * 5) println((a * 6).to_repr()) + +# constructor +println("constructor") +println(Str("asdf")) +println(Str(1234)) +println(Str(1.0)) +println(Str(true)) +println(Str(false)) diff --git a/tests/str.npp.expect b/tests/str.npp.expect index ef1453b..a01220a 100644 --- a/tests/str.npp.expect +++ b/tests/str.npp.expect @@ -24,3 +24,9 @@ __mul__ asdfasdfasdfasdf This is a longer sentenceThis is a longer sentenceThis is a longer sentenceThis is a longer sentenceThis is a longer sentence 'asdfasdfasdfasdfasdfasdf' +constructor +asdf +1234 +1.0 +true +false