From cb9e09064afa938aec9edf5ba9676332101a5864 Mon Sep 17 00:00:00 2001 From: Alek Ratzloff Date: Fri, 4 Feb 2022 17:29:41 -0800 Subject: [PATCH] Implement __bool__, add `if` builtin, vtable imporvements * `if` builtin is the big one here - branching! Yay! * The `if` builtin requires __bool__ to be implemented for things. This is added for all builtin objects. * Vtables have slightly better ergonomics. Signed-off-by: Alek Ratzloff --- examples/branch.sy | 14 ++++----- src/obj/bool.rs | 24 +++++++++++++-- src/obj/builtin.rs | 5 +-- src/obj/float.rs | 45 +++++++++++++++++---------- src/obj/int.rs | 30 ++++++++++++++++-- src/obj/macros.rs | 2 +- src/obj/str.rs | 35 ++++++++++++--------- src/vm/builtins.rs | 77 ++++++++++++++++++++++++++++++++++++---------- src/vm/machine.rs | 4 --- 9 files changed, 168 insertions(+), 68 deletions(-) diff --git a/examples/branch.sy b/examples/branch.sy index d00dfec..baf6a03 100644 --- a/examples/branch.sy +++ b/examples/branch.sy @@ -1,7 +1,7 @@ -0 [ "fail - 0 is not truthy" println! ] [ "OK" println! ] if! -1 [ "OK" println! ] [ "fail - 1 is truthy" println! ] if! -1.1 [ "OK" println! ] [ "fail - 1.1 is truthy" println! ] if! -0.1 [ "OK" println! ] [ "fail - 0.1 is truthy" println! ] if! --0.1 [ "OK" println! ] [ "fail - -0.1 is truthy" println! ] if! --0.0 [ "fail - -0.0 is not truthy" println! ] [ "OK" println! ] if! -0.0 [ "fail - 0.0 is not truthy" println! ] [ "OK" println! ] if! \ No newline at end of file +[ "fail - 0 is not truthy" println! ] [ "OK" println! ] 0 if! +[ "OK" println! ] [ "fail - 1 is truthy" println! ] 1 if! +[ "OK" println! ] [ "fail - 1.1 is truthy" println! ] 1.1 if! +[ "OK" println! ] [ "fail - 0.1 is truthy" println! ] 0.1 if! +[ "OK" println! ] [ "fail - -0.1 is truthy" println! ] -0.1 if! +[ "fail - -0.0 is not truthy" println! ] [ "OK" println! ] -0.0 if! +[ "fail - 0.0 is not truthy" println! ] [ "OK" println! ] 0.0 if! \ No newline at end of file diff --git a/src/obj/bool.rs b/src/obj/bool.rs index b1464cd..75bbc21 100644 --- a/src/obj/bool.rs +++ b/src/obj/bool.rs @@ -1,4 +1,4 @@ -use crate::obj::{Obj, VTable}; +use crate::obj::prelude::*; use crate::syn::span::Span; use crate::vm::{error::*, machine::Machine}; use gc::{Finalize, Gc, Trace}; @@ -14,9 +14,27 @@ pub struct BoolObj { impl BoolObj { pub fn new(value: Bool) -> Gc { thread_local! { + static VTABLE: VTable = vtable! { + "__bool__" => builtin_fn!("__bool__", |_, _| { + // return self, don't do anything + Ok(BuiltinExit::Return) + }), + "__str__" => builtin_fn!("__str__", |machine, _| { + let obj_ptr: ObjPtr = machine.stack_pop()?; + let obj: &(dyn Obj + 'static) = &*obj_ptr; + if let Some(bool_) = obj.as_any().downcast_ref::() { + let string = StrObj::new(bool_.value.to_string()); + machine.stack_push(string)?; + Ok(BuiltinExit::Return) + } else { + Err(RuntimeError::WrongValue("bool".to_string())) + } + }), + }; + static BOOLS: [Gc; 2] = [ - Gc::new(BoolObj { value: false, vtable: vtable! {} }), - Gc::new(BoolObj { value: true, vtable: vtable! {} }), + Gc::new(BoolObj { value: false, vtable: VTABLE.with(Clone::clone), }), + Gc::new(BoolObj { value: true, vtable: VTABLE.with(Clone::clone), }), ]; } BOOLS.with(|bools| Gc::clone(&bools[value as usize])) diff --git a/src/obj/builtin.rs b/src/obj/builtin.rs index 45a55b3..2d69303 100644 --- a/src/obj/builtin.rs +++ b/src/obj/builtin.rs @@ -1,4 +1,4 @@ -use crate::obj::{quote::Quote, Obj, VTable}; +use crate::obj::{Obj, VTable}; use crate::syn::span::Span; use crate::vm::{error::Result, machine::Machine}; use gc::{unsafe_empty_trace, Finalize, Gc, Trace}; @@ -12,9 +12,6 @@ use std::rc::Rc; /// pre-empt itself with the understanding that it will eventually return. #[derive(Debug, Clone, Copy)] pub enum BuiltinExit { - /// Pop this builtin off the call stack, and then call the given quote. - Call(Quote), - /// Continue with the assumption that some other function has been pushed to /// the call stack, updating the reentry for this function call to the /// specified constant. diff --git a/src/obj/float.rs b/src/obj/float.rs index 614a786..081753d 100644 --- a/src/obj/float.rs +++ b/src/obj/float.rs @@ -1,4 +1,4 @@ -use crate::obj::{builtin::BuiltinExit, str::StrObj, Obj, ObjPtr, VTable}; +use crate::obj::prelude::*; use crate::syn::span::Span; use crate::vm::{error::*, machine::Machine}; use gc::{Finalize, Gc, Trace}; @@ -14,23 +14,36 @@ pub struct FloatObj { impl FloatObj { pub fn new(value: Float) -> Gc { // TODO : intern float value - let float_str: ObjPtr = builtin_fn!("__str__", |machine, _| { - let obj_ptr: ObjPtr = machine.stack_pop()?; - let obj: &(dyn Obj + 'static) = &*obj_ptr; - if let Some(float) = obj.as_any().downcast_ref::() { - let string = StrObj::new(float.value.to_string()); - machine.stack_push(string)?; - Ok(BuiltinExit::Return) - } else { - Err(RuntimeError::WrongValue("float".to_string())) - } - // Create the builtin float string - }); + + thread_local! { + static VTABLE: VTable = vtable! { + "__bool__" => builtin_fn!("__bool__", |machine, _| { + let obj_ptr: ObjPtr = machine.stack_pop()?; + let obj: &(dyn Obj + 'static) = &*obj_ptr; + if let Some(float) = obj.as_any().downcast_ref::() { + let bool_ = BoolObj::new(float.value != 0.0); + machine.stack_push(bool_)?; + Ok(BuiltinExit::Return) + } else { + Err(RuntimeError::WrongValue("float".to_string())) + } + }), + "__str__" => builtin_fn!("__str__", |machine, _| { + let obj_ptr: ObjPtr = machine.stack_pop()?; + let obj: &(dyn Obj + 'static) = &*obj_ptr; + if let Some(float) = obj.as_any().downcast_ref::() { + let string = StrObj::new(float.value.to_string()); + machine.stack_push(string)?; + Ok(BuiltinExit::Return) + } else { + Err(RuntimeError::WrongValue("float".to_string())) + } + }), + }; + }; Gc::new(FloatObj { value, - vtable: vtable! { - "__str__" => float_str, - }, + vtable: VTABLE.with(Clone::clone), }) } diff --git a/src/obj/int.rs b/src/obj/int.rs index 1a9bfe8..849c7ca 100644 --- a/src/obj/int.rs +++ b/src/obj/int.rs @@ -1,4 +1,4 @@ -use crate::obj::{Obj, VTable}; +use crate::obj::prelude::*; use crate::syn::span::Span; use crate::vm::{error::*, machine::Machine}; use gc::{Finalize, Gc, Trace}; @@ -13,10 +13,36 @@ pub struct IntObj { impl IntObj { pub fn new(value: Int) -> Gc { + thread_local! { + static VTABLE: VTable = vtable! { + "__bool__" => builtin_fn!("__bool__", |machine, _| { + let obj_ptr: ObjPtr = machine.stack_pop()?; + let obj: &(dyn Obj + 'static) = &*obj_ptr; + if let Some(int) = obj.as_any().downcast_ref::() { + let bool_ = BoolObj::new(int.value != 0); + machine.stack_push(bool_)?; + Ok(BuiltinExit::Return) + } else { + Err(RuntimeError::WrongValue("int".to_string())) + } + }), + "__str__" => builtin_fn!("__str__", |machine, _| { + let obj_ptr: ObjPtr = machine.stack_pop()?; + let obj: &(dyn Obj + 'static) = &*obj_ptr; + if let Some(int) = obj.as_any().downcast_ref::() { + let string = StrObj::new(int.value.to_string()); + machine.stack_push(string)?; + Ok(BuiltinExit::Return) + } else { + Err(RuntimeError::WrongValue("int".to_string())) + } + }), + }; + }; // TODO : intern int value Gc::new(IntObj { value, - vtable: vtable! {}, + vtable: VTABLE.with(Clone::clone), }) } diff --git a/src/obj/macros.rs b/src/obj/macros.rs index b0ef6af..573e067 100644 --- a/src/obj/macros.rs +++ b/src/obj/macros.rs @@ -4,7 +4,7 @@ macro_rules! vtable { $crate::obj::VTable::from( [ $( - ($key.to_string(), $value) + ($key.to_string(), $value as ObjPtr) ),* ] ) diff --git a/src/obj/str.rs b/src/obj/str.rs index a0c03f6..4ca87bd 100644 --- a/src/obj/str.rs +++ b/src/obj/str.rs @@ -1,4 +1,4 @@ -use crate::obj::{builtin::BuiltinExit, Obj, ObjPtr, VTable}; +use crate::obj::prelude::*; use crate::syn::span::Span; use crate::vm::{error::*, machine::Machine}; use gc::{Finalize, Gc, Trace}; @@ -13,21 +13,28 @@ pub struct StrObj { impl StrObj { pub fn new(value: String) -> Gc { - let str_str: ObjPtr = builtin_fn!("__str__", |machine, _| { - let obj_ptr: ObjPtr = machine.stack_pop()?; - let obj: &(dyn Obj + 'static) = &*obj_ptr; - if obj.as_any().is::() { - machine.stack_push(obj_ptr)?; - Ok(BuiltinExit::Return) - } else { - Err(RuntimeError::WrongValue("float".to_string())) - } - }); + thread_local! { + static VTABLE: VTable = vtable! { + "__str__" => builtin_fn!("__str__", |_, _| { + // return self, don't do anything + Ok(BuiltinExit::Return) + }), + "__bool__" => builtin_fn!("__bool__", |machine, _| { + let obj_ptr: ObjPtr = machine.stack_pop()?; + let obj: &(dyn Obj + 'static) = &*obj_ptr; + if let Some(string) = obj.as_any().downcast_ref::() { + let bool_ = BoolObj::new(string.value.len() != 0); + machine.stack_push(bool_)?; + Ok(BuiltinExit::Return) + } else { + Err(RuntimeError::WrongValue("int".to_string())) + } + }), + }; + } Gc::new(StrObj { value, - vtable: vtable! { - "__str__" => str_str, - }, + vtable: VTABLE.with(Clone::clone), }) } diff --git a/src/vm/builtins.rs b/src/vm/builtins.rs index 24f47d9..b2839d4 100644 --- a/src/vm/builtins.rs +++ b/src/vm/builtins.rs @@ -3,6 +3,7 @@ use crate::vm::{ error::RuntimeError, machine::{Machine, MachineBuilder}, }; +use gc::Gc; impl MachineBuilder { /// Registers all builtins for calling on the machine. @@ -32,31 +33,50 @@ impl MachineBuilder { Ok(BuiltinExit::Return) }); - /* // // if // - self.register_builtin_fun("if", |machine, _| { - let if_false = machine.stack_pop()?; - let if_true = machine.stack_pop()?; - let condition = machine.stack_pop()?; + self.register_builtin_fun("if", |machine, reentry| { + const CALL_BOOL: usize = 1; + const RETURN: usize = 2; - let value = if condition.is_truthy() { - if_true - } else { - if_false - }; + if reentry == 0 { + // get __bool__ attr from condition stack member + let bool_fun = machine + .stack_peek() + .ok_or_else(|| RuntimeError::StackUnderflow)? + .get("__bool__") + .ok_or_else(|| RuntimeError::UnsetWord("__bool__".to_string()))?; - if let Value::Quote(quote) = value { - Ok(BuiltinExit::Call(quote)) + // call it + let call_site = machine.call_stack().last().unwrap().call_site().cloned(); + bool_fun.call(call_site, machine)?; + Ok(BuiltinExit::Resume(CALL_BOOL)) + } else if reentry == CALL_BOOL { + let obj_ptr = machine.stack_pop()?; + let if_false = machine.stack_pop()?; + let if_true = machine.stack_pop()?; + // make sure we gotta bool + if let Some(condition) = obj_ptr.as_any().downcast_ref::() { + let call_obj = if condition.value() { if_true } else { if_false }; + // TODO - maybe it's possible to determine where we're at in + // the source code, but it probably requires saving a state + // that I'm not willing to commit to yet + call_obj.call(None, machine)?; + Ok(BuiltinExit::Resume(RETURN)) + } else { + panic!( + "expected BoolObj from __bool__ function but got {:?} instead", + obj_ptr + ) + } + } else if reentry == RETURN { + // finish the function call + Ok(BuiltinExit::Return) } else { - return Err(RuntimeError::CannotCall( - "if statement requires quote value".to_string(), - ) - .into()); + unreachable!() } }); - */ // // print @@ -145,5 +165,28 @@ impl MachineBuilder { Ok(BuiltinExit::Return) }); */ + + // + // getattr + // + self.register_builtin_fun("getattr", |machine, _| { + let obj = machine.stack_pop()?; + let attr = machine.stack_pop()?; + if let Some(string) = attr.as_any().downcast_ref::() { + let value = Gc::clone( + obj.vtable() + .get(string.value()) + .ok_or_else(|| RuntimeError::UnsetWord(string.value().clone()))?, + ); + machine.stack_push(value)?; + } else { + panic!("expected StrObj for attr in getattr call"); + } + Ok(BuiltinExit::Return) + }); + + // + // setattr + // } } diff --git a/src/vm/machine.rs b/src/vm/machine.rs index 507096b..cd6f7fc 100644 --- a/src/vm/machine.rs +++ b/src/vm/machine.rs @@ -237,10 +237,6 @@ impl Machine { let exit = fun.call(self, reentry)?; match exit { - BuiltinExit::Call(quote) => { - self.call_stack.pop(); - self.call_quote(quote); - } BuiltinExit::Resume(reentry) => { if let Frame::Native(frame) = &mut self.call_stack[current_frame] { frame.reentry = reentry;