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 <alekratz@gmail.com>
This commit is contained in:
@@ -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!
|
||||
[ "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!
|
||||
@@ -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<Self> {
|
||||
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::<BoolObj>() {
|
||||
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<BoolObj>; 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]))
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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,7 +14,21 @@ pub struct FloatObj {
|
||||
impl FloatObj {
|
||||
pub fn new(value: Float) -> Gc<Self> {
|
||||
// TODO : intern float value
|
||||
let float_str: ObjPtr = builtin_fn!("__str__", |machine, _| {
|
||||
|
||||
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::<FloatObj>() {
|
||||
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::<FloatObj>() {
|
||||
@@ -24,13 +38,12 @@ impl FloatObj {
|
||||
} else {
|
||||
Err(RuntimeError::WrongValue("float".to_string()))
|
||||
}
|
||||
// Create the builtin float string
|
||||
});
|
||||
}),
|
||||
};
|
||||
};
|
||||
Gc::new(FloatObj {
|
||||
value,
|
||||
vtable: vtable! {
|
||||
"__str__" => float_str,
|
||||
},
|
||||
vtable: VTABLE.with(Clone::clone),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -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<Self> {
|
||||
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::<IntObj>() {
|
||||
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::<IntObj>() {
|
||||
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),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ macro_rules! vtable {
|
||||
$crate::obj::VTable::from(
|
||||
[
|
||||
$(
|
||||
($key.to_string(), $value)
|
||||
($key.to_string(), $value as ObjPtr)
|
||||
),*
|
||||
]
|
||||
)
|
||||
|
||||
@@ -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<Self> {
|
||||
let str_str: ObjPtr = builtin_fn!("__str__", |machine, _| {
|
||||
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 obj.as_any().is::<StrObj>() {
|
||||
machine.stack_push(obj_ptr)?;
|
||||
if let Some(string) = obj.as_any().downcast_ref::<StrObj>() {
|
||||
let bool_ = BoolObj::new(string.value.len() != 0);
|
||||
machine.stack_push(bool_)?;
|
||||
Ok(BuiltinExit::Return)
|
||||
} else {
|
||||
Err(RuntimeError::WrongValue("float".to_string()))
|
||||
Err(RuntimeError::WrongValue("int".to_string()))
|
||||
}
|
||||
}),
|
||||
};
|
||||
}
|
||||
});
|
||||
Gc::new(StrObj {
|
||||
value,
|
||||
vtable: vtable! {
|
||||
"__str__" => str_str,
|
||||
},
|
||||
vtable: VTABLE.with(Clone::clone),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -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, _| {
|
||||
self.register_builtin_fun("if", |machine, reentry| {
|
||||
const CALL_BOOL: usize = 1;
|
||||
const RETURN: usize = 2;
|
||||
|
||||
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()))?;
|
||||
|
||||
// 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()?;
|
||||
let condition = machine.stack_pop()?;
|
||||
|
||||
let value = if condition.is_truthy() {
|
||||
if_true
|
||||
// make sure we gotta bool
|
||||
if let Some(condition) = obj_ptr.as_any().downcast_ref::<BoolObj>() {
|
||||
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 {
|
||||
if_false
|
||||
};
|
||||
|
||||
if let Value::Quote(quote) = value {
|
||||
Ok(BuiltinExit::Call(quote))
|
||||
} else {
|
||||
return Err(RuntimeError::CannotCall(
|
||||
"if statement requires quote value".to_string(),
|
||||
panic!(
|
||||
"expected BoolObj from __bool__ function but got {:?} instead",
|
||||
obj_ptr
|
||||
)
|
||||
.into());
|
||||
}
|
||||
} else if reentry == RETURN {
|
||||
// finish the function call
|
||||
Ok(BuiltinExit::Return)
|
||||
} else {
|
||||
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::<StrObj>() {
|
||||
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
|
||||
//
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user