Files
not-python-rust/src/obj/ty.rs
Alek Ratzloff c6e242c39d Change up how function calls are handled
This is a big one.

For a while, builtin functions were a bit cumbersome and not easily
re-entrant. If you needed to call a function from within a builtin
function, the only method of doing so was to take a `FunctionState`
parameter, which would either be "Begin", meaning the function was being
called for the first time, or "Resume", meaning the function was being
re-entered. This meant that if we wanted to call another function within
this function, we'd have to set up a whole `match` statement to figure
out whether we were re-entering the function or starting out. It was a
mess and not very ergonomic, and most importantly, made it very
difficult to implement hashmaps.

Now, builtin functions are handled a little more elegantly. A native
function is pushed to the stack, where it is detected in the
`Vm::dispatch()` function. It is then called, like normal. If the
builtin function then needs to call *another* function, it will push
that function to the stack and call it, and then call `Vm::resume()` to
resume VM execution. `Vm::dispatch()` is then called again, this time
with the current function on top of the stack. If it's another builtin
function, the above is repeated. If it's a user-defined function, then
bytecode is executed in the main `loop` inside of resume. Ultimately, we
are able to compose builtin functions like we would any other internal
function to the program. Overall this should speed things up a little,
make them a whole lot easier to read, and make them a million times
easier to compose with other builtin parts of Rust.

Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
2024-10-11 16:22:19 -07:00

329 lines
12 KiB
Rust

use std::collections::HashMap;
use std::fmt::{self, Debug, Display};
use std::rc::Rc;
use gc::{Finalize, Trace};
use crate::obj::{macros::*, prelude::*, Obj, BUILTINS};
use crate::vm::{Argc, Vm};
#[derive(Trace, Finalize)]
pub struct Ty {
base: Obj,
#[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 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.
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()
);
// Additionally, we want to duplicate the type argument on the stack.
//
// When you call a new constructor, e.g. `MyType(arg1, arg2, arg3)`, the stack will look like this:
//
// -- stack top
// arg3
// arg2
// arg1
// <Ty MyType> <---- NOTE - this is what we are actually calling, and is being treated as a function
// ...
// -- stack bottom
//
// With a custom type, we want to be getting the __init__ function off of the type. Since
// we don't have access to the function in the call stack (in this case, it's our type),
// we duplicate the type to be the first argument.
vm.stack_mut().insert(index, this);
let function = self
.vtable
.get("__call__")
.expect("Why does a type not have a __call__ member?");
function.borrow().call(vm, argc + 1);
}
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 {
// Constructor
// TODO Ty::do_call, Ty::init - implement these methods
__call__ => BuiltinFunction::create("__call__", Obj::not_implemented_un, 1),
__init__ => BuiltinFunction::create("__init__", Obj::not_implemented_un, 1),
// Conversion methods
to_str => BuiltinFunction::create("to_str", Obj::to_str, 1),
to_repr => BuiltinFunction::create("to_repr", Obj::to_repr, 1),
to_bool => BuiltinFunction::create("to_bool", Obj::to_bool, 1),
to_int => BuiltinFunction::create("to_int", Obj::not_implemented_un, 1),
to_float => BuiltinFunction::create("to_float", Obj::not_implemented_un, 1),
to_list => BuiltinFunction::create("to_list", Obj::not_implemented_un, 1),
// Operators
__add__ => BuiltinFunction::create("__add__", Obj::not_implemented_bin, 2),
__sub__ => BuiltinFunction::create("__sub__", Obj::not_implemented_bin, 2),
__mul__ => BuiltinFunction::create("__mul__", Obj::not_implemented_bin, 2),
__div__ => BuiltinFunction::create("__div__", Obj::not_implemented_bin, 2),
__and__ => BuiltinFunction::create("__and__", Obj::and, 2),
__or__ => BuiltinFunction::create("__or__", Obj::or, 2),
__ne__ => BuiltinFunction::create("__ne__", Obj::ne, 2),
__eq__ => BuiltinFunction::create("__eq__", Obj::eq, 2),
__gt__ => BuiltinFunction::create("__gt__", Obj::not_implemented_bin, 2),
__ge__ => BuiltinFunction::create("__ge__", Obj::not_implemented_bin, 2),
__lt__ => BuiltinFunction::create("__lt__", Obj::not_implemented_bin, 2),
__le__ => BuiltinFunction::create("__le__", Obj::not_implemented_bin, 2),
__pos__ => BuiltinFunction::create("__pos__", Obj::not_implemented_un, 1),
__neg__ => BuiltinFunction::create("__neg__", Obj::not_implemented_un, 1),
__not__ => BuiltinFunction::create("__not__", Obj::not, 1),
// Methods
len => BuiltinFunction::create("len", Obj::not_implemented_un, 1),
hash => BuiltinFunction::create("hash", Obj::not_implemented_bin, 2),
},
Obj {
// Constructor
__call__ => BuiltinFunction::create("__call__", Obj::do_call, 1),
__init__ => BuiltinFunction::create("__init__", Obj::init, 1),
// Methods
},
List {
// Constructor
__call__ => BuiltinFunction::create("__call__", List::do_call, 2),
__init__ => BuiltinFunction::create("__init__", List::init, 2),
// Conversion methods
to_repr => BuiltinFunction::create("to_repr", List::to_repr, 1),
to_list => BuiltinFunction::create("to_list", List::to_list, 1),
// Operators
__index__ => BuiltinFunction::create("__index__", List::index, 2),
// Methods
len => BuiltinFunction::create("len", List::len, 1),
push => BuiltinFunction::create("push", List::push, 2),
pop => BuiltinFunction::create("pop", List::pop, 1),
extend => BuiltinFunction::create("extend", List::extend, 2),
},
Str {
// Constructor
__call__ => BuiltinFunction::create("__call__", Str::do_call, 2),
__init__ => BuiltinFunction::create("__init__", Str::init, 2),
// 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),
to_list => BuiltinFunction::create("to_list", Str::to_list, 1),
// Operators
__add__ => BuiltinFunction::create("__add__", Str::add, 2),
__mul__ => BuiltinFunction::create("__mul__", Str::mul, 2),
__index__ => BuiltinFunction::create("__index__", Str::index, 2),
// Methods
len => BuiltinFunction::create("len", Str::len, 1),
// TODO Str methods - .lower, .upper, .slice, etc
},
Int {
// Constructor
__call__ => BuiltinFunction::create("__call__", Int::do_call, 2),
__init__ => BuiltinFunction::create("__init__", Int::init, 2),
// Conversion methods
to_int => BuiltinFunction::create("to_int", Int::to_int, 1),
to_float => BuiltinFunction::create("to_float", Int::to_float, 1),
// 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),
// Methods
},
Float {
// Constructor
__call__ => BuiltinFunction::create("__call__", Float::do_call, 2),
__init__ => BuiltinFunction::create("__init__", Float::init, 2),
// Conversion methods
to_int => BuiltinFunction::create("to_int", Float::to_int, 1),
to_float => BuiltinFunction::create("to_float", Float::to_float, 1),
// 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),
// Methods
},
Bool {
// Constructor
__call__ => BuiltinFunction::create("__call__", Bool::do_call, 2),
__init__ => BuiltinFunction::create("__init__", Bool::init, 2),
// Conversion methods
to_int => BuiltinFunction::create("to_int", Bool::to_int, 1),
to_float => BuiltinFunction::create("to_float", Bool::to_float, 1),
// Operators
// Methods
},
Nil {
// Constructor
__call__ => BuiltinFunction::create("__call__", Nil::do_call, 1),
__init__ => BuiltinFunction::create("__init__", Nil::init, 1),
// Conversion methods
// Operators
// Methods
},
BuiltinFunction {
// Constructor
// Conversion methods
// Operators
// Methods
},
UserFunction {
// Constructor
// Conversion methods
// Operators
// Methods
},
Method {
// Constructor
// Conversion methods
// Operators
// Methods
},
Module {
// Constructor
// Conversion methods
// Operators
// Methods
},
}
}