Split up src/obj.rs

* common macros are in their own private module
* functions are in their own obj::function module

Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
This commit is contained in:
2024-09-24 09:03:34 -07:00
parent d699ad2ff5
commit 078aef70ea
6 changed files with 306 additions and 287 deletions

View File

@@ -8,6 +8,7 @@ use common_macros::hash_map;
use thiserror::Error; use thiserror::Error;
use crate::ast::*; use crate::ast::*;
use crate::obj::function::UserFunctionInst;
use crate::obj::*; use crate::obj::*;
use crate::token::TokenKind; use crate::token::TokenKind;
use crate::vm::*; use crate::vm::*;

View File

@@ -1,4 +1,5 @@
use crate::obj::{ObjP, UserFunctionInst}; use crate::obj::function::UserFunctionInst;
use crate::obj::ObjP;
use crate::vm::{Chunk, JumpOpArg, Op}; use crate::vm::{Chunk, JumpOpArg, Op};
type Row = (String, String, &'static str, String, String); type Row = (String, String, &'static str, String, String);

View File

@@ -1,15 +1,20 @@
// TODO obj.rs - remove the warning suppression // TODO obj.rs - remove the warning suppression
#![allow(dead_code)] #![allow(dead_code)]
mod macros;
// Leave this comment here - macros must come first
pub mod function;
use std::any::Any; use std::any::Any;
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt::{self, Debug, Display}; use std::fmt::{self, Debug, Display};
use std::ptr;
use std::rc::Rc; use std::rc::Rc;
use gc::{Finalize, Gc, GcCell, Trace}; use gc::{Finalize, Gc, GcCell, Trace};
use crate::vm::{Argc, Chunk, Frame, Vm}; use crate::obj::function::*;
use crate::obj::macros::*;
use crate::vm::{Argc, Chunk, Vm};
pub type Ptr<T> = Gc<GcCell<T>>; pub type Ptr<T> = Gc<GcCell<T>>;
pub type ObjP = Ptr<dyn Obj>; pub type ObjP = Ptr<dyn Obj>;
@@ -459,47 +464,6 @@ impl Obj for BaseObjInst {
} }
} }
macro_rules! impl_base_obj {
($base_name:ident) => {
fn instantiate(&mut self, ty: ObjP) {
self.$base_name.instantiate(ty);
}
fn is_instantiated(&self) -> bool {
self.$base_name.is_instantiated()
}
fn attrs(&self) -> &Attrs {
self.$base_name.attrs()
}
fn attrs_mut(&mut self) -> &mut Attrs {
self.$base_name.attrs_mut()
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
};
() => {
impl_base_obj! { base }
};
}
macro_rules! impl_create {
($($arg:ident : $ty:ty),* $(,)?) => {
pub fn create(ty: ObjP $(, $arg : $ty )*) -> ObjP {
let ptr = make_ptr(Self::new($($arg),*));
ptr.borrow_mut().instantiate(ty);
ptr
}
}
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// ObjInst // ObjInst
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@@ -861,249 +825,6 @@ impl Obj for NilInst {
impl_base_obj!(); impl_base_obj!();
} }
////////////////////////////////////////////////////////////////////////////////
// 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(Ptr::clone(&capture));
}
}
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!();
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Tests // Tests
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

252
src/obj/function.rs Normal file
View File

@@ -0,0 +1,252 @@
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!();
}

43
src/obj/macros.rs Normal file
View File

@@ -0,0 +1,43 @@
macro_rules! impl_base_obj {
($base_name:ident) => {
fn instantiate(&mut self, ty: $crate::obj::ObjP) {
self.$base_name.instantiate(ty);
}
fn is_instantiated(&self) -> bool {
self.$base_name.is_instantiated()
}
fn attrs(&self) -> &$crate::obj::Attrs {
self.$base_name.attrs()
}
fn attrs_mut(&mut self) -> &mut $crate::obj::Attrs {
self.$base_name.attrs_mut()
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
};
() => {
impl_base_obj! { base }
};
}
macro_rules! impl_create {
($($arg:ident : $ty:ty),* $(,)?) => {
pub fn create(ty: $crate::obj::ObjP $(, $arg : $ty )*) -> $crate::obj::ObjP {
let ptr = make_ptr(Self::new($($arg),*));
ptr.borrow_mut().instantiate(ty);
ptr
}
}
}
pub(crate) use impl_base_obj;
pub(crate) use impl_create;

View File

@@ -1,6 +1,7 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::rc::Rc; use std::rc::Rc;
use crate::obj::function::*;
use crate::obj::*; use crate::obj::*;
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]