Break out obj module into individual files by type

object.rs was getting a little big and the implementations are only
going to get bigger.

Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
This commit is contained in:
2022-01-24 18:35:41 -08:00
parent 95fc8a85df
commit 5cef5b1d43
11 changed files with 404 additions and 357 deletions

View File

@@ -1,4 +1,4 @@
use crate::obj::*;
use crate::obj::prelude::*;
use crate::scope::*;
use crate::syn::ast::*;
use crate::vm::inst::*;

110
src/obj/builtin.rs Normal file
View File

@@ -0,0 +1,110 @@
use crate::obj::{quote::Quote, Obj, VTable};
use crate::syn::span::Span;
use crate::vm::{error::Result, machine::Machine};
use gc::{unsafe_empty_trace, Finalize, Trace};
use std::fmt::{self, Debug};
use std::rc::Rc;
/// The intent of a builtin function once the function proper returns.
///
/// When a builtin function itself returns, it may want to remain on the call
/// stack, or do some kind of cleanup before it's finished. So, a function may
/// 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.
Resume(usize),
/// Pops this builtin off the call stack, returning like normal.
Return,
}
#[derive(Clone, Finalize)]
pub struct BuiltinFn {
name: Rc<String>,
fun: BuiltinFnPtr,
}
unsafe impl Trace for BuiltinFn {
unsafe_empty_trace!();
}
impl PartialEq for BuiltinFn {
fn eq(&self, other: &Self) -> bool {
(&self.fun as *const _ as usize) == (&other.fun as *const _ as usize)
}
}
impl BuiltinFn {
pub fn new(name: String, fun: BuiltinFnPtr) -> Self {
BuiltinFn {
name: Rc::new(name),
fun,
}
}
pub fn call(&self, machine: &mut Machine, reentry: usize) -> Result<BuiltinExit> {
(self.fun)(machine, reentry)
}
}
/// BuiltinFn types must be function pointers, and not closures.
///
/// This has to do with how the garbage collector works, and it apparently does
/// not play nicely with closures.
pub type BuiltinFnPtr = fn(&mut Machine, usize) -> Result<BuiltinExit>;
impl Debug for BuiltinFn {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(
fmt,
"builtin function {} at {:#x}",
self.name,
(&self.fun as *const _ as usize)
)
}
}
#[derive(Debug, Trace, Finalize)]
pub struct BuiltinFnObj {
value: BuiltinFn,
vtable: VTable,
}
impl BuiltinFnObj {
pub fn new(value: BuiltinFn) -> Self {
BuiltinFnObj {
value,
vtable: vtable! {},
}
}
#[allow(dead_code)]
pub fn value(&self) -> &BuiltinFn {
&self.value
}
}
impl Obj for BuiltinFnObj {
fn vtable(&self) -> &VTable {
&self.vtable
}
fn vtable_mut(&mut self) -> &mut VTable {
&mut self.vtable
}
fn call(&self, call_site: Option<Span>, machine: &mut Machine) -> Result<()> {
machine.call_native(call_site, self.value.clone());
Ok(())
}
fn as_any(&self) -> &(dyn std::any::Any + 'static) {
self
}
}

58
src/obj/float.rs Normal file
View File

@@ -0,0 +1,58 @@
use crate::obj::{builtin::BuiltinExit, str::StrObj, Obj, ObjPtr, VTable};
use crate::syn::span::Span;
use crate::vm::{error::*, machine::Machine};
use gc::{Finalize, Trace};
pub type Float = f64;
#[derive(Debug, Trace, Finalize)]
pub struct FloatObj {
value: Float,
vtable: VTable,
}
impl FloatObj {
pub fn new(value: Float) -> Self {
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::<FloatObj>() {
let string = StrObj::new(float.value.to_string()).into_gc();
machine.stack_push(string)?;
Ok(BuiltinExit::Return)
} else {
Err(RuntimeError::WrongValue("float".to_string()))
}
// Create the builtin float string
});
FloatObj {
value,
vtable: vtable! {
"__str__" => float_str,
},
}
}
#[allow(dead_code)]
pub fn value(&self) -> Float {
self.value
}
}
impl Obj for FloatObj {
fn vtable(&self) -> &VTable {
&self.vtable
}
fn vtable_mut(&mut self) -> &mut VTable {
&mut self.vtable
}
fn call(&self, call_site: Option<Span>, _: &mut Machine) -> Result<()> {
Err(RuntimeError::CannotCall("float value".to_string()).with_location(call_site))
}
fn as_any(&self) -> &(dyn std::any::Any + 'static) {
self
}
}

44
src/obj/int.rs Normal file
View File

@@ -0,0 +1,44 @@
use crate::obj::{Obj, VTable};
use crate::syn::span::Span;
use crate::vm::{error::*, machine::Machine};
use gc::{Finalize, Trace};
pub type Int = i64;
#[derive(Debug, Trace, Finalize)]
pub struct IntObj {
value: Int,
vtable: VTable,
}
impl IntObj {
pub fn new(value: Int) -> Self {
IntObj {
value,
vtable: vtable! {},
}
}
#[allow(dead_code)]
pub fn value(&self) -> Int {
self.value
}
}
impl Obj for IntObj {
fn vtable(&self) -> &VTable {
&self.vtable
}
fn vtable_mut(&mut self) -> &mut VTable {
&mut self.vtable
}
fn call(&self, call_site: Option<Span>, _: &mut Machine) -> Result<()> {
Err(RuntimeError::CannotCall("int value".to_string()).with_location(call_site))
}
fn as_any(&self) -> &(dyn std::any::Any + 'static) {
self
}
}

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

@@ -0,0 +1,23 @@
#[macro_export]
macro_rules! vtable {
($($key:expr => $value:expr),* $(,)?) => {{
$crate::obj::VTable::from(
[
$(
($key.to_string(), $value)
),*
]
)
}};
}
#[macro_export]
macro_rules! builtin_fn {
($name:expr, $fun:expr) => {{
$crate::obj::builtin::BuiltinFnObj::new($crate::obj::builtin::BuiltinFn::new(
$name.to_string(),
$fun,
))
.into_gc()
}};
}

View File

@@ -1,125 +1,30 @@
use crate::syn::ast::SpStmt;
#[macro_use]
mod macros;
pub mod builtin;
pub mod float;
pub mod int;
pub mod quote;
pub mod str;
pub mod prelude {
pub use crate::obj::builtin::*;
pub use crate::obj::float::*;
pub use crate::obj::int::*;
pub use crate::obj::quote::*;
pub use crate::obj::str::*;
pub use crate::obj::{Obj, ObjPtr, VTable};
}
use crate::syn::span::Span;
use crate::vm::{error::*, machine::Machine};
use crate::{scope::Scope, syn::span::Span, vm::inst::SpInst};
use gc::{unsafe_empty_trace, Finalize, Gc, Trace};
use gc::{Finalize, Gc, Trace};
use std::any::Any;
use std::collections::HashMap;
use std::fmt::{self, Debug};
use std::rc::Rc;
use std::fmt::Debug;
pub type ObjPtr = Gc<dyn Obj + 'static>;
pub type Str = String;
pub type Int = i64;
pub type Float = f64;
pub type VTable = HashMap<String, ObjPtr>;
// /////////////////////////////////////////////////////////////////////////////
// Quote
// /////////////////////////////////////////////////////////////////////////////
/// A handle to a quote pointing to an element in a `QuoteTable`.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Finalize)]
pub struct Quote(usize);
impl Quote {
pub fn index(&self) -> usize {
self.0
}
}
unsafe impl Trace for Quote {
unsafe_empty_trace!();
}
/// A table of compiled quotes, their expression trees, and their spans.
#[derive(Debug, Clone, Default)]
pub struct QuoteTable {
table: Vec<(Span, Scope, Vec<SpStmt>, Rc<Vec<SpInst>>)>,
}
impl QuoteTable {
pub fn insert(
&mut self,
span: Span,
scope: Scope,
quote: Vec<SpStmt>,
compiled: Rc<Vec<SpInst>>,
) -> Quote {
let next = Quote(self.table.len());
self.table.push((span, scope, quote, compiled));
next
}
pub fn get(&self, quote: Quote) -> &(Span, Scope, Vec<SpStmt>, Rc<Vec<SpInst>>) {
&self.table[quote.index()]
}
}
/// The intent of a builtin function once the function proper returns.
///
/// When a builtin function itself returns, it may want to remain on the call
/// stack, or do some kind of cleanup before it's finished. So, a function may
/// 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.
Resume(usize),
/// Pops this builtin off the call stack, returning like normal.
Return,
}
#[derive(Clone, Finalize)]
pub struct BuiltinFn {
name: Rc<String>,
fun: BuiltinFnPtr,
}
unsafe impl Trace for BuiltinFn {
unsafe_empty_trace!();
}
impl PartialEq for BuiltinFn {
fn eq(&self, other: &Self) -> bool {
(&self.fun as *const _ as usize) == (&other.fun as *const _ as usize)
}
}
impl BuiltinFn {
pub fn new(name: String, fun: BuiltinFnPtr) -> Self {
BuiltinFn {
name: Rc::new(name),
fun,
}
}
pub fn call(&self, machine: &mut Machine, reentry: usize) -> Result<BuiltinExit> {
(self.fun)(machine, reentry)
}
}
/// BuiltinFn types must be function pointers, and not closures.
///
/// This has to do with how the garbage collector works, and it apparently does
/// not play nicely with closures.
pub type BuiltinFnPtr = fn(&mut Machine, usize) -> Result<BuiltinExit>;
impl Debug for BuiltinFn {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(
fmt,
"builtin function {} at {:#x}",
self.name,
(&self.fun as *const _ as usize)
)
}
}
/// An object that has a vtable and common functions.
pub trait Obj: Trace + Finalize + Debug {
/// Gets the vtable for this object.
@@ -161,239 +66,3 @@ pub trait Obj: Trace + Finalize + Debug {
fn as_any(&self) -> &(dyn Any + 'static);
}
macro_rules! vtable {
($($key:expr => $value:expr),* $(,)?) => {{
VTable::from(
[
$(
($key.to_string(), $value)
),*
]
)
}};
}
macro_rules! builtin_fn {
($name:expr, $fun:expr) => {{
BuiltinFnObj::new(BuiltinFn::new($name.to_string(), $fun)).into_gc()
}};
}
#[derive(Debug, Trace, Finalize)]
pub struct FloatObj {
value: Float,
vtable: VTable,
}
impl FloatObj {
pub fn new(value: Float) -> Self {
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::<FloatObj>() {
let string = StrObj::new(float.value.to_string()).into_gc();
machine.stack_push(string)?;
Ok(BuiltinExit::Return)
} else {
Err(RuntimeError::WrongValue("float".to_string()))
}
// Create the builtin float string
});
FloatObj {
value,
vtable: vtable! {
"__str__" => float_str,
},
}
}
#[allow(dead_code)]
pub fn value(&self) -> Float {
self.value
}
}
impl Obj for FloatObj {
fn vtable(&self) -> &VTable {
&self.vtable
}
fn vtable_mut(&mut self) -> &mut VTable {
&mut self.vtable
}
fn call(&self, call_site: Option<Span>, _: &mut Machine) -> Result<()> {
Err(RuntimeError::CannotCall("float value".to_string()).with_location(call_site))
}
fn as_any(&self) -> &(dyn Any + 'static) {
self
}
}
#[derive(Debug, Trace, Finalize)]
pub struct IntObj {
value: Int,
vtable: VTable,
}
impl IntObj {
pub fn new(value: Int) -> Self {
IntObj {
value,
vtable: vtable! {},
}
}
#[allow(dead_code)]
pub fn value(&self) -> Int {
self.value
}
}
impl Obj for IntObj {
fn vtable(&self) -> &VTable {
&self.vtable
}
fn vtable_mut(&mut self) -> &mut VTable {
&mut self.vtable
}
fn call(&self, call_site: Option<Span>, _: &mut Machine) -> Result<()> {
Err(RuntimeError::CannotCall("int value".to_string()).with_location(call_site))
}
fn as_any(&self) -> &(dyn Any + 'static) {
self
}
}
#[derive(Debug, Trace, Finalize)]
pub struct StrObj {
value: String,
vtable: VTable,
}
impl StrObj {
pub fn new(value: String) -> Self {
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::<StrObj>() {
machine.stack_push(obj_ptr)?;
Ok(BuiltinExit::Return)
} else {
Err(RuntimeError::WrongValue("float".to_string()))
}
});
StrObj {
value,
vtable: vtable! {
"__str__" => str_str,
},
}
}
#[allow(dead_code)]
pub fn value(&self) -> &String {
&self.value
}
}
impl Obj for StrObj {
fn vtable(&self) -> &VTable {
&self.vtable
}
fn vtable_mut(&mut self) -> &mut VTable {
&mut self.vtable
}
fn call(&self, call_site: Option<Span>, _: &mut Machine) -> Result<()> {
Err(RuntimeError::CannotCall("string value".to_string()).with_location(call_site))
}
fn as_any(&self) -> &(dyn Any + 'static) {
self
}
}
#[derive(Debug, Trace, Finalize)]
pub struct QuoteObj {
value: Quote,
vtable: VTable,
}
impl QuoteObj {
pub fn new(value: Quote) -> Self {
QuoteObj {
value,
vtable: vtable! {},
}
}
#[allow(dead_code)]
pub fn value(&self) -> Quote {
self.value
}
}
impl Obj for QuoteObj {
fn vtable(&self) -> &VTable {
&self.vtable
}
fn vtable_mut(&mut self) -> &mut VTable {
&mut self.vtable
}
fn call(&self, _call_site: Option<Span>, machine: &mut Machine) -> Result<()> {
machine.call_quote(self.value);
Ok(())
}
fn as_any(&self) -> &(dyn Any + 'static) {
self
}
}
#[derive(Debug, Trace, Finalize)]
pub struct BuiltinFnObj {
value: BuiltinFn,
vtable: VTable,
}
impl BuiltinFnObj {
pub fn new(value: BuiltinFn) -> Self {
BuiltinFnObj {
value,
vtable: vtable! {},
}
}
#[allow(dead_code)]
pub fn value(&self) -> &BuiltinFn {
&self.value
}
}
impl Obj for BuiltinFnObj {
fn vtable(&self) -> &VTable {
&self.vtable
}
fn vtable_mut(&mut self) -> &mut VTable {
&mut self.vtable
}
fn call(&self, call_site: Option<Span>, machine: &mut Machine) -> Result<()> {
machine.call_native(call_site, self.value.clone());
Ok(())
}
fn as_any(&self) -> &(dyn Any + 'static) {
self
}
}

84
src/obj/quote.rs Normal file
View File

@@ -0,0 +1,84 @@
use crate::obj::{Obj, VTable};
use crate::syn::ast::SpStmt;
use crate::vm::{error::*, machine::Machine};
use crate::{scope::Scope, syn::span::Span, vm::inst::SpInst};
use gc::{unsafe_empty_trace, Finalize, Trace};
use std::fmt::Debug;
use std::rc::Rc;
/// A handle to a quote pointing to an element in a `QuoteTable`.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Finalize)]
pub struct Quote(usize);
impl Quote {
pub fn index(&self) -> usize {
self.0
}
}
unsafe impl Trace for Quote {
unsafe_empty_trace!();
}
#[derive(Debug, Trace, Finalize)]
pub struct QuoteObj {
value: Quote,
vtable: VTable,
}
impl QuoteObj {
pub fn new(value: Quote) -> Self {
QuoteObj {
value,
vtable: vtable! {},
}
}
#[allow(dead_code)]
pub fn value(&self) -> Quote {
self.value
}
}
impl Obj for QuoteObj {
fn vtable(&self) -> &VTable {
&self.vtable
}
fn vtable_mut(&mut self) -> &mut VTable {
&mut self.vtable
}
fn call(&self, _call_site: Option<Span>, machine: &mut Machine) -> Result<()> {
machine.call_quote(self.value);
Ok(())
}
fn as_any(&self) -> &(dyn std::any::Any + 'static) {
self
}
}
/// A table of compiled quotes, their expression trees, and their spans.
#[derive(Debug, Clone, Default)]
pub struct QuoteTable {
table: Vec<(Span, Scope, Vec<SpStmt>, Rc<Vec<SpInst>>)>,
}
impl QuoteTable {
pub fn insert(
&mut self,
span: Span,
scope: Scope,
quote: Vec<SpStmt>,
compiled: Rc<Vec<SpInst>>,
) -> Quote {
let next = Quote(self.table.len());
self.table.push((span, scope, quote, compiled));
next
}
pub fn get(&self, quote: Quote) -> &(Span, Scope, Vec<SpStmt>, Rc<Vec<SpInst>>) {
&self.table[quote.index()]
}
}

56
src/obj/str.rs Normal file
View File

@@ -0,0 +1,56 @@
use crate::obj::{builtin::BuiltinExit, Obj, ObjPtr, VTable};
use crate::syn::span::Span;
use crate::vm::{error::*, machine::Machine};
use gc::{Finalize, Trace};
pub type Str = String;
#[derive(Debug, Trace, Finalize)]
pub struct StrObj {
value: String,
vtable: VTable,
}
impl StrObj {
pub fn new(value: String) -> Self {
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::<StrObj>() {
machine.stack_push(obj_ptr)?;
Ok(BuiltinExit::Return)
} else {
Err(RuntimeError::WrongValue("float".to_string()))
}
});
StrObj {
value,
vtable: vtable! {
"__str__" => str_str,
},
}
}
#[allow(dead_code)]
pub fn value(&self) -> &String {
&self.value
}
}
impl Obj for StrObj {
fn vtable(&self) -> &VTable {
&self.vtable
}
fn vtable_mut(&mut self) -> &mut VTable {
&mut self.vtable
}
fn call(&self, call_site: Option<Span>, _: &mut Machine) -> Result<()> {
Err(RuntimeError::CannotCall("string value".to_string()).with_location(call_site))
}
fn as_any(&self) -> &(dyn std::any::Any + 'static) {
self
}
}

View File

@@ -1,4 +1,4 @@
use crate::obj::{Float, Int, Str};
use crate::obj::{float::Float, int::Int, str::Str};
use crate::syn::span::*;
/// A single statement, which may be a meta-expression or a normal expression.

View File

@@ -1,5 +1,8 @@
use crate::obj::{BuiltinExit, Obj, StrObj};
use crate::vm::{error::RuntimeError, machine::MachineBuilder};
use crate::obj::{builtin::BuiltinExit, str::StrObj, Obj};
use crate::vm::{
error::RuntimeError,
machine::{Machine, MachineBuilder},
};
impl MachineBuilder {
/// Registers all builtins for calling on the machine.
@@ -9,7 +12,7 @@ impl MachineBuilder {
//
// panic
//
self.register_builtin_fun("panic", |machine, _| {
self.register_builtin_fun("panic", |machine: &mut Machine, _| {
println!("!!! panic");
println!("!!! top of stack");
for (i, value) in machine.stack().iter().enumerate().rev() {

View File

@@ -1,5 +1,5 @@
use crate::compile::Compile;
use crate::obj::*;
use crate::obj::prelude::*;
use crate::scope::*;
use crate::syn::{ast::SpStmt, span::Span};
use crate::vm::{error::*, inst::*};