diff --git a/src/compile.rs b/src/compile.rs index 5d4fb39..322fd46 100644 --- a/src/compile.rs +++ b/src/compile.rs @@ -1,4 +1,4 @@ -use crate::obj::*; +use crate::obj::prelude::*; use crate::scope::*; use crate::syn::ast::*; use crate::vm::inst::*; diff --git a/src/obj/builtin.rs b/src/obj/builtin.rs new file mode 100644 index 0000000..df4b73f --- /dev/null +++ b/src/obj/builtin.rs @@ -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, + 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 { + (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; + +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, machine: &mut Machine) -> Result<()> { + machine.call_native(call_site, self.value.clone()); + Ok(()) + } + + fn as_any(&self) -> &(dyn std::any::Any + 'static) { + self + } +} diff --git a/src/obj/float.rs b/src/obj/float.rs new file mode 100644 index 0000000..1d616ac --- /dev/null +++ b/src/obj/float.rs @@ -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::() { + 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, _: &mut Machine) -> Result<()> { + Err(RuntimeError::CannotCall("float value".to_string()).with_location(call_site)) + } + + fn as_any(&self) -> &(dyn std::any::Any + 'static) { + self + } +} diff --git a/src/obj/int.rs b/src/obj/int.rs new file mode 100644 index 0000000..ca9137b --- /dev/null +++ b/src/obj/int.rs @@ -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, _: &mut Machine) -> Result<()> { + Err(RuntimeError::CannotCall("int value".to_string()).with_location(call_site)) + } + + fn as_any(&self) -> &(dyn std::any::Any + 'static) { + self + } +} diff --git a/src/obj/macros.rs b/src/obj/macros.rs new file mode 100644 index 0000000..2cc8d9c --- /dev/null +++ b/src/obj/macros.rs @@ -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() + }}; +} diff --git a/src/obj/mod.rs b/src/obj/mod.rs index 1b9c104..9b1f162 100644 --- a/src/obj/mod.rs +++ b/src/obj/mod.rs @@ -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; -pub type Str = String; -pub type Int = i64; -pub type Float = f64; pub type VTable = HashMap; -// ///////////////////////////////////////////////////////////////////////////// -// 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, Rc>)>, -} - -impl QuoteTable { - pub fn insert( - &mut self, - span: Span, - scope: Scope, - quote: Vec, - compiled: Rc>, - ) -> Quote { - let next = Quote(self.table.len()); - self.table.push((span, scope, quote, compiled)); - next - } - - pub fn get(&self, quote: Quote) -> &(Span, Scope, Vec, Rc>) { - &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, - 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 { - (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; - -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::() { - 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, _: &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, _: &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::() { - 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, _: &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, 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, machine: &mut Machine) -> Result<()> { - machine.call_native(call_site, self.value.clone()); - Ok(()) - } - - fn as_any(&self) -> &(dyn Any + 'static) { - self - } -} diff --git a/src/obj/quote.rs b/src/obj/quote.rs new file mode 100644 index 0000000..855f66e --- /dev/null +++ b/src/obj/quote.rs @@ -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, 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, Rc>)>, +} + +impl QuoteTable { + pub fn insert( + &mut self, + span: Span, + scope: Scope, + quote: Vec, + compiled: Rc>, + ) -> Quote { + let next = Quote(self.table.len()); + self.table.push((span, scope, quote, compiled)); + next + } + + pub fn get(&self, quote: Quote) -> &(Span, Scope, Vec, Rc>) { + &self.table[quote.index()] + } +} diff --git a/src/obj/str.rs b/src/obj/str.rs new file mode 100644 index 0000000..d0f7539 --- /dev/null +++ b/src/obj/str.rs @@ -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::() { + 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, _: &mut Machine) -> Result<()> { + Err(RuntimeError::CannotCall("string value".to_string()).with_location(call_site)) + } + + fn as_any(&self) -> &(dyn std::any::Any + 'static) { + self + } +} diff --git a/src/syn/ast.rs b/src/syn/ast.rs index 60cbb07..f3403a7 100644 --- a/src/syn/ast.rs +++ b/src/syn/ast.rs @@ -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. diff --git a/src/vm/builtins.rs b/src/vm/builtins.rs index 92ea7ec..ad3d389 100644 --- a/src/vm/builtins.rs +++ b/src/vm/builtins.rs @@ -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() { diff --git a/src/vm/machine.rs b/src/vm/machine.rs index 8e08d57..e375afa 100644 --- a/src/vm/machine.rs +++ b/src/vm/machine.rs @@ -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::*};