Files
not-python-rust/src/obj/function.rs

253 lines
6.3 KiB
Rust
Raw Normal View History

use std::fmt::{self, Debug, Display};
use std::ptr;
use std::rc::Rc;
use gc::{Finalize, Trace};
use crate::obj::macros::*;
use crate::obj::{make_ptr, BaseObjInst, Obj, ObjP};
use crate::vm::{Argc, Chunk, Frame, Vm};
////////////////////////////////////////////////////////////////////////////////
// BuiltinFunctionInst
////////////////////////////////////////////////////////////////////////////////
pub type BuiltinFunctionPtr = fn(vm: &mut Vm, args: Vec<ObjP>) -> ObjP;
#[derive(Debug, Trace)]
pub struct BuiltinFunctionInst {
base: BaseObjInst,
name: String,
#[unsafe_ignore_trace]
function: BuiltinFunctionPtr,
arity: Argc,
}
impl BuiltinFunctionInst {
pub fn new(name: impl ToString, function: BuiltinFunctionPtr, arity: Argc) -> Self {
Self {
base: Default::default(),
name: name.to_string(),
function,
arity,
}
}
impl_create!(
name: impl ToString,
function: BuiltinFunctionPtr,
arity: Argc,
);
pub fn name(&self) -> &String {
&self.name
}
}
impl Finalize for BuiltinFunctionInst {
fn finalize(&self) {}
}
impl Display for BuiltinFunctionInst {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(
fmt,
"<BuiltinFunction {}/{} at 0x{:x}>",
self.name(),
self.arity().unwrap(),
self.function as *const BuiltinFunctionPtr as usize
)
}
}
impl Obj for BuiltinFunctionInst {
fn arity(&self) -> Option<Argc> {
Some(self.arity)
}
fn call(&self, vm: &mut Vm, argc: Argc) {
// args
let mut args = Vec::with_capacity(argc as usize);
for _ in 0..argc {
args.push(vm.pop());
}
args.reverse();
// callee (self)
vm.pop();
let result = (self.function)(vm, args);
vm.push(result);
}
fn equals(&self, other: &dyn Obj) -> bool {
// TODO BuiltinFunctionInst::equals : need something more robust than checking addr_eq,
// maybe check the self_binding pointer too?
if let Some(other) = other.as_any().downcast_ref::<BuiltinFunctionInst>() {
ptr::addr_eq(self, other)
} else {
false
}
}
impl_base_obj!();
}
////////////////////////////////////////////////////////////////////////////////
// UserFunctionInst
////////////////////////////////////////////////////////////////////////////////
#[derive(Debug, Clone, Trace)]
pub struct UserFunctionInst {
base: BaseObjInst,
#[unsafe_ignore_trace]
name: Rc<String>,
#[unsafe_ignore_trace]
chunk: Rc<Chunk>,
arity: Argc,
captures: Vec<ObjP>,
}
impl UserFunctionInst {
pub fn new(chunk: Chunk, arity: Argc) -> Self {
Self {
base: Default::default(),
name: Rc::new("(anonymous)".to_string()),
chunk: Rc::new(chunk),
arity,
captures: Default::default(),
}
}
impl_create!(chunk: Chunk, arity: Argc);
pub fn name(&self) -> &String {
&self.name
}
pub fn set_name(&mut self, name: Rc<String>) {
self.name = name;
}
pub fn chunk(&self) -> &Chunk {
&self.chunk
}
pub fn push_capture(&mut self, value: ObjP) {
self.captures.push(value);
}
}
impl Finalize for UserFunctionInst {
fn finalize(&self) {}
}
impl Display for UserFunctionInst {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(
fmt,
"<UserFunction {}/{} at 0x{:x}>",
self.name(),
self.arity().unwrap(),
self as *const _ as usize
)
}
}
impl Obj for UserFunctionInst {
fn arity(&self) -> Option<Argc> {
Some(self.arity)
}
fn call(&self, vm: &mut Vm, argc: Argc) {
assert_eq!(argc, self.arity, "argc must match arity");
let new_frame = Frame {
name: Rc::clone(&self.name),
chunk: Rc::clone(&self.chunk),
ip: 0,
stack_base: vm.stack().len() - (argc as usize),
};
vm.push_frame(new_frame);
for capture in &self.captures {
vm.push(capture.clone());
}
}
fn equals(&self, other: &dyn Obj) -> bool {
if let Some(other) = other.as_any().downcast_ref::<UserFunctionInst>() {
// TODO UserFunctionInst::equals : need something more robust than checking addr_eq.
ptr::addr_eq(self, other)
} else {
false
}
}
impl_base_obj!();
}
////////////////////////////////////////////////////////////////////////////////
// MethodInst
////////////////////////////////////////////////////////////////////////////////
#[derive(Debug, Trace)]
pub struct MethodInst {
base: BaseObjInst,
self_binding: ObjP,
function: ObjP,
}
impl MethodInst {
pub fn new(self_binding: ObjP, function: ObjP) -> Self {
Self {
base: Default::default(),
self_binding,
function,
}
}
pub fn create(ty: ObjP, self_binding: ObjP, function: ObjP) -> ObjP {
let ptr = make_ptr(Self::new(self_binding, function));
ptr.borrow_mut().instantiate(ty.clone());
ptr
}
pub fn self_binding(&self) -> &ObjP {
&self.self_binding
}
}
impl Finalize for MethodInst {
fn finalize(&self) {}
}
impl Display for MethodInst {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(fmt, "{}", self.function.borrow())
}
}
impl Obj for MethodInst {
fn arity(&self) -> Option<Argc> {
// Subtract one from the arity - this is because the VM uses arity() to check against the
// number of arguments passed.
self.function.borrow().arity().map(|arity| arity - 1)
}
fn call(&self, vm: &mut Vm, mut argc: Argc) {
let self_pos = vm.stack().len() - (argc as usize);
vm.stack_mut().insert(self_pos, self.self_binding.clone());
argc += 1;
self.function.borrow().call(vm, argc)
}
fn equals(&self, other: &dyn Obj) -> bool {
if let Some(other) = other.as_any().downcast_ref::<MethodInst>() {
ptr::addr_eq(&*self.self_binding, &*other.self_binding)
&& ptr::addr_eq(&*self.function, &*other.function)
} else {
false
}
}
impl_base_obj!();
}