Objs replace Values
* Obj is now a trait instead of a struct * Value enum is gone, replaced with individual structs that implement Obj * All instances where a Value was used, a Gc<(dyn Obj + 'static)> takes its place. ObjPtr is shorthand for this. * A __str__ method for objects is implemented for a couple builtin types as a proof-of-concept * println and print builtin functions are implemented using this interface There's still a lot to do, mostly with interned values. For example, whenever a new string object is created, a new __str__ function object is created each time, rather than reusing the first one that was created. Stuff like that. Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
use crate::object::{QuoteTable, Value};
|
||||
use crate::object::*;
|
||||
use crate::scope::*;
|
||||
use crate::syn::ast::*;
|
||||
use crate::vm::inst::*;
|
||||
@@ -66,7 +66,7 @@ impl<'s> Compile<'s> {
|
||||
let quote =
|
||||
self.quote_table
|
||||
.insert(expr.span().clone(), locals, stmts.clone(), compiled);
|
||||
let inst = Inst::PushValue(Value::Quote(quote));
|
||||
let inst = Inst::PushValue(QuoteObj::new(quote).into_gc());
|
||||
vec![SpInst::new(expr.span().clone(), inst)]
|
||||
}
|
||||
}
|
||||
@@ -74,9 +74,9 @@ impl<'s> Compile<'s> {
|
||||
|
||||
fn compile_atom(&mut self, atom: &SpAtom) -> Vec<SpInst> {
|
||||
let inst = match atom.inner() {
|
||||
Atom::Float(f) => Inst::PushValue(Value::Float(*f)),
|
||||
Atom::Int(i) => Inst::PushValue(Value::Int(*i)),
|
||||
Atom::Str(s) => Inst::PushValue(Value::Str(s.clone())),
|
||||
Atom::Float(f) => Inst::PushValue(FloatObj::new(*f).into_gc()),
|
||||
Atom::Int(i) => Inst::PushValue(IntObj::new(*i).into_gc()),
|
||||
Atom::Str(s) => Inst::PushValue(StrObj::new(s.clone()).into_gc()),
|
||||
Atom::Assign(text) => {
|
||||
let word = self.scope_stack.insert_local(text);
|
||||
Inst::Store(word)
|
||||
|
||||
201
src/object.rs
201
src/object.rs
@@ -7,11 +7,12 @@ 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, Gc, Trace};
|
||||
use std::any::Any;
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::{self, Debug, Display};
|
||||
use std::fmt::{self, Debug};
|
||||
use std::rc::Rc;
|
||||
|
||||
pub type ObjPtr = Gc<dyn Obj>;
|
||||
pub type ObjPtr = Gc<dyn Obj + 'static>;
|
||||
pub type Str = String;
|
||||
pub type Int = i64;
|
||||
pub type Float = f64;
|
||||
@@ -63,64 +64,6 @@ impl QuoteTable {
|
||||
}
|
||||
}
|
||||
|
||||
// /////////////////////////////////////////////////////////////////////////////
|
||||
// Value
|
||||
// /////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Value {
|
||||
Array(Vec<Value>),
|
||||
Float(Float),
|
||||
Int(Int),
|
||||
Str(Str),
|
||||
Quote(Quote),
|
||||
//ObjPtr(ObjPtr),
|
||||
BuiltinFn(BuiltinFn),
|
||||
}
|
||||
|
||||
impl Value {
|
||||
pub fn name(&self) -> &str {
|
||||
use Value::*;
|
||||
match self {
|
||||
Array(_) => "array",
|
||||
Float(_) => "float",
|
||||
Int(_) => "int",
|
||||
Str(_) => "str",
|
||||
Quote(_) => "quote",
|
||||
//ObjPtr(_) => "object",
|
||||
BuiltinFn(_) => "builtin function",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_truthy(&self) -> bool {
|
||||
use Value::*;
|
||||
match self {
|
||||
Array(a) => a.len() > 0,
|
||||
Float(f) => *f != 0.0,
|
||||
Int(i) => *i != 0,
|
||||
Str(s) => s.len() > 0,
|
||||
Quote(_) => true,
|
||||
//ObjPtr(_) => true,
|
||||
BuiltinFn(_) => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Value {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
use Value::*;
|
||||
match self {
|
||||
Array(_) => write!(fmt, "[array]"),
|
||||
Float(f) => write!(fmt, "{}", f),
|
||||
Int(i) => write!(fmt, "{}", i),
|
||||
Str(s) => write!(fmt, "{}", s),
|
||||
Quote(q) => write!(fmt, "[quoted value #{}]", q.index()),
|
||||
//ObjPtr(o) => write!(fmt, "[object #{}]", o.slot()),
|
||||
BuiltinFn(b) => write!(fmt, "[{:?}]", b),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 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
|
||||
@@ -169,7 +112,10 @@ impl BuiltinFn {
|
||||
}
|
||||
}
|
||||
|
||||
//pub type BuiltinFnPtr = dyn Fn(&mut Machine, usize) -> Result<BuiltinExit>;
|
||||
/// 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 {
|
||||
@@ -183,7 +129,7 @@ impl Debug for BuiltinFn {
|
||||
}
|
||||
|
||||
/// An object that has a vtable and common functions.
|
||||
pub trait Obj: Trace + Finalize {
|
||||
pub trait Obj: Trace + Finalize + Debug {
|
||||
/// Gets the vtable for this object.
|
||||
fn vtable(&self) -> &VTable;
|
||||
|
||||
@@ -191,7 +137,7 @@ pub trait Obj: Trace + Finalize {
|
||||
fn vtable_mut(&mut self) -> &mut VTable;
|
||||
|
||||
/// Calls this object.
|
||||
fn call(&self, machine: &mut Machine) -> Result<()>;
|
||||
fn call(&self, call_site: Option<Span>, machine: &mut Machine) -> Result<()>;
|
||||
|
||||
/// Gets a value in the vtable.
|
||||
///
|
||||
@@ -208,6 +154,20 @@ pub trait Obj: Trace + Finalize {
|
||||
fn set(&mut self, key: String, value: ObjPtr) -> Option<ObjPtr> {
|
||||
self.vtable_mut().insert(key, value)
|
||||
}
|
||||
|
||||
/// Allocate a new GC pointer and convert it to a `dyn Obj` pointer.
|
||||
fn into_gc(self) -> Gc<dyn Obj>
|
||||
where
|
||||
Self: Sized + 'static,
|
||||
{
|
||||
let gc = Gc::new(self);
|
||||
unsafe {
|
||||
let ptr = Gc::into_raw(gc) as *const dyn Obj;
|
||||
Gc::from_raw(ptr)
|
||||
}
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &(dyn Any + 'static);
|
||||
}
|
||||
|
||||
macro_rules! vtable {
|
||||
@@ -222,12 +182,45 @@ macro_rules! vtable {
|
||||
}};
|
||||
}
|
||||
|
||||
#[derive(Trace, Finalize)]
|
||||
macro_rules! builtin_fn {
|
||||
($name:expr, $fun:expr) => {{
|
||||
BuiltinFnObj::new(BuiltinFn::new($name.to_string(), Rc::new($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,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn value(&self) -> Float {
|
||||
self.value
|
||||
}
|
||||
}
|
||||
|
||||
impl Obj for FloatObj {
|
||||
fn vtable(&self) -> &VTable {
|
||||
&self.vtable
|
||||
@@ -237,12 +230,16 @@ impl Obj for FloatObj {
|
||||
&mut self.vtable
|
||||
}
|
||||
|
||||
fn call(&self, _: &mut Machine) -> Result<()> {
|
||||
Err(RuntimeError::CannotCall("float value".to_string()))
|
||||
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(Trace, Finalize)]
|
||||
#[derive(Debug, Trace, Finalize)]
|
||||
pub struct IntObj {
|
||||
value: Int,
|
||||
vtable: VTable,
|
||||
@@ -255,6 +252,10 @@ impl IntObj {
|
||||
vtable: vtable! {},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn value(&self) -> Int {
|
||||
self.value
|
||||
}
|
||||
}
|
||||
|
||||
impl Obj for IntObj {
|
||||
@@ -266,12 +267,16 @@ impl Obj for IntObj {
|
||||
&mut self.vtable
|
||||
}
|
||||
|
||||
fn call(&self, _: &mut Machine) -> Result<()> {
|
||||
Err(RuntimeError::CannotCall("int value".to_string()))
|
||||
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(Trace, Finalize)]
|
||||
#[derive(Debug, Trace, Finalize)]
|
||||
pub struct StrObj {
|
||||
value: String,
|
||||
vtable: VTable,
|
||||
@@ -279,11 +284,27 @@ pub struct StrObj {
|
||||
|
||||
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! {},
|
||||
vtable: vtable! {
|
||||
"__str__" => str_str,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn value(&self) -> &String {
|
||||
&self.value
|
||||
}
|
||||
}
|
||||
|
||||
impl Obj for StrObj {
|
||||
@@ -295,12 +316,16 @@ impl Obj for StrObj {
|
||||
&mut self.vtable
|
||||
}
|
||||
|
||||
fn call(&self, _: &mut Machine) -> Result<()> {
|
||||
Err(RuntimeError::CannotCall("string value".to_string()))
|
||||
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(Trace, Finalize)]
|
||||
#[derive(Debug, Trace, Finalize)]
|
||||
pub struct QuoteObj {
|
||||
value: Quote,
|
||||
vtable: VTable,
|
||||
@@ -313,6 +338,10 @@ impl QuoteObj {
|
||||
vtable: vtable! {},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn value(&self) -> Quote {
|
||||
self.value
|
||||
}
|
||||
}
|
||||
|
||||
impl Obj for QuoteObj {
|
||||
@@ -324,12 +353,17 @@ impl Obj for QuoteObj {
|
||||
&mut self.vtable
|
||||
}
|
||||
|
||||
fn call(&self, _: &mut Machine) -> Result<()> {
|
||||
Err(RuntimeError::CannotCall("quote value".to_string()))
|
||||
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(Trace, Finalize)]
|
||||
#[derive(Debug, Trace, Finalize)]
|
||||
pub struct BuiltinFnObj {
|
||||
value: BuiltinFn,
|
||||
vtable: VTable,
|
||||
@@ -342,6 +376,10 @@ impl BuiltinFnObj {
|
||||
vtable: vtable! {},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn value(&self) -> &BuiltinFn {
|
||||
&self.value
|
||||
}
|
||||
}
|
||||
|
||||
impl Obj for BuiltinFnObj {
|
||||
@@ -353,7 +391,12 @@ impl Obj for BuiltinFnObj {
|
||||
&mut self.vtable
|
||||
}
|
||||
|
||||
fn call(&self, _: &mut Machine) -> Result<()> {
|
||||
Err(RuntimeError::CannotCall("quote value".to_string()))
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::object::{BuiltinExit, Int, Value};
|
||||
use crate::object::{BuiltinExit, Obj, StrObj};
|
||||
use crate::vm::{error::RuntimeError, machine::MachineBuilder};
|
||||
|
||||
impl MachineBuilder {
|
||||
@@ -19,6 +19,7 @@ impl MachineBuilder {
|
||||
panic!();
|
||||
});
|
||||
|
||||
/*
|
||||
//
|
||||
// if
|
||||
//
|
||||
@@ -26,6 +27,7 @@ impl MachineBuilder {
|
||||
let if_false = machine.stack_pop()?;
|
||||
let if_true = machine.stack_pop()?;
|
||||
let condition = machine.stack_pop()?;
|
||||
|
||||
let value = if condition.is_truthy() {
|
||||
if_true
|
||||
} else {
|
||||
@@ -41,25 +43,75 @@ impl MachineBuilder {
|
||||
.into());
|
||||
}
|
||||
});
|
||||
*/
|
||||
|
||||
//
|
||||
// print
|
||||
//
|
||||
self.register_builtin_fun("print", |machine, _| {
|
||||
let value = machine.stack_pop()?;
|
||||
print!("{}", value);
|
||||
self.register_builtin_fun("print", |machine, reentry| {
|
||||
const CALL_STR: usize = 1;
|
||||
if reentry == 0 {
|
||||
let obj = machine
|
||||
.stack_peek()
|
||||
.ok_or_else(|| RuntimeError::StackUnderflow)?;
|
||||
// Push the value's __str__ value, call it, and then print it.
|
||||
let str_fun = obj
|
||||
.get("__str__")
|
||||
.ok_or_else(|| RuntimeError::UnsetWord("__str__".to_string()))?;
|
||||
let call_site = machine.call_stack().last().unwrap().call_site().cloned();
|
||||
str_fun.call(call_site, machine)?;
|
||||
Ok(BuiltinExit::Resume(CALL_STR))
|
||||
} else if reentry == CALL_STR {
|
||||
let obj_ptr = machine.stack_pop()?;
|
||||
let str_obj: &(dyn Obj + 'static) = &*obj_ptr;
|
||||
if let Some(string) = str_obj.as_any().downcast_ref::<StrObj>() {
|
||||
print!("{}", string.value());
|
||||
Ok(BuiltinExit::Return)
|
||||
} else {
|
||||
panic!(
|
||||
"expected StrObj from __str__ function but got {:?} instead",
|
||||
str_obj
|
||||
);
|
||||
}
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
});
|
||||
|
||||
//
|
||||
// println
|
||||
//
|
||||
self.register_builtin_fun("println", |machine, _| {
|
||||
let value = machine.stack_pop()?;
|
||||
println!("{}", value);
|
||||
self.register_builtin_fun("println", |machine, reentry| {
|
||||
const CALL_STR: usize = 1;
|
||||
if reentry == 0 {
|
||||
let obj = machine
|
||||
.stack_peek()
|
||||
.ok_or_else(|| RuntimeError::StackUnderflow)?;
|
||||
// Push the value's __str__ value, call it, and then print it.
|
||||
let str_fun = obj
|
||||
.get("__str__")
|
||||
.ok_or_else(|| RuntimeError::UnsetWord("__str__".to_string()))?;
|
||||
let call_site = machine.call_stack().last().unwrap().call_site().cloned();
|
||||
str_fun.call(call_site, machine)?;
|
||||
Ok(BuiltinExit::Resume(CALL_STR))
|
||||
} else if reentry == CALL_STR {
|
||||
let obj_ptr = machine.stack_pop()?;
|
||||
let str_obj: &(dyn Obj + 'static) = &*obj_ptr;
|
||||
if let Some(string) = str_obj.as_any().downcast_ref::<StrObj>() {
|
||||
println!("{}", string.value());
|
||||
Ok(BuiltinExit::Return)
|
||||
} else {
|
||||
panic!(
|
||||
"expected StrObj from __str__ function but got {:?} instead",
|
||||
str_obj
|
||||
);
|
||||
}
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
});
|
||||
|
||||
/*
|
||||
//
|
||||
// ==
|
||||
//
|
||||
@@ -79,5 +131,6 @@ impl MachineBuilder {
|
||||
machine.stack_push(Value::Int((lhs != rhs) as Int))?;
|
||||
Ok(BuiltinExit::Return)
|
||||
});
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,9 +12,12 @@ pub enum RuntimeError {
|
||||
#[error("unset word '{0}'")]
|
||||
UnsetWord(String),
|
||||
|
||||
#[error("cannot call non-quote value '{0}'")]
|
||||
#[error("cannot call value '{0}'")]
|
||||
CannotCall(String),
|
||||
|
||||
#[error("expected {0} value")]
|
||||
WrongValue(String),
|
||||
|
||||
#[error("at {0}")]
|
||||
Span(Span, Box<RuntimeError>),
|
||||
}
|
||||
@@ -24,13 +27,13 @@ pub type Result<T, E = RuntimeError> = std::result::Result<T, E>;
|
||||
/// Add a location to the implemented value.
|
||||
pub trait WithLocation {
|
||||
type Out;
|
||||
fn with_location(self, span: Span) -> Self::Out;
|
||||
fn with_location(self, span: Option<Span>) -> Self::Out;
|
||||
}
|
||||
|
||||
impl<T> WithLocation for Result<T, RuntimeError> {
|
||||
type Out = Result<T, RuntimeError>;
|
||||
|
||||
fn with_location(self, span: Span) -> Self::Out {
|
||||
fn with_location(self, span: Option<Span>) -> Self::Out {
|
||||
if let Err(e) = self {
|
||||
Err(e.with_location(span))
|
||||
} else {
|
||||
@@ -42,7 +45,11 @@ impl<T> WithLocation for Result<T, RuntimeError> {
|
||||
impl WithLocation for RuntimeError {
|
||||
type Out = RuntimeError;
|
||||
|
||||
fn with_location(self, span: Span) -> Self::Out {
|
||||
fn with_location(self, span: Option<Span>) -> Self::Out {
|
||||
if let Some(span) = span {
|
||||
RuntimeError::Span(span, Box::new(self))
|
||||
} else {
|
||||
self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
use crate::object::Value;
|
||||
use crate::object::ObjPtr;
|
||||
use crate::scope::Word;
|
||||
use crate::syn::span::Spanned;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Inst {
|
||||
/// Push a constant value to the stack.
|
||||
PushValue(Value),
|
||||
PushValue(ObjPtr),
|
||||
|
||||
/// Load a word's value onto the stack.
|
||||
Load(Word),
|
||||
|
||||
@@ -16,7 +16,7 @@ impl Frame {
|
||||
pub fn call_site(&self) -> Option<&Span> {
|
||||
use Frame::*;
|
||||
match self {
|
||||
Native(frame) => Some(&frame.call_site),
|
||||
Native(frame) => frame.call_site.as_ref(),
|
||||
Quote(frame) => {
|
||||
if frame.pc >= frame.code.len() {
|
||||
frame.code.last().map(|inst| inst.span())
|
||||
@@ -42,14 +42,14 @@ impl From<QuoteFrame> for Frame {
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct NativeFrame {
|
||||
call_site: Span,
|
||||
call_site: Option<Span>,
|
||||
fun: BuiltinFn,
|
||||
reentry: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct QuoteFrame {
|
||||
locals: BTreeMap<Word, Option<Value>>,
|
||||
locals: BTreeMap<Word, Option<ObjPtr>>,
|
||||
code: Rc<Vec<SpInst>>,
|
||||
pc: usize,
|
||||
}
|
||||
@@ -63,8 +63,8 @@ impl QuoteFrame {
|
||||
/// The current state of a VM.
|
||||
#[derive(Debug)]
|
||||
pub struct Machine {
|
||||
globals: BTreeMap<Word, Value>,
|
||||
stack: Vec<Value>,
|
||||
globals: BTreeMap<Word, ObjPtr>,
|
||||
stack: Vec<ObjPtr>,
|
||||
max_stack_size: Option<usize>,
|
||||
quote_table: QuoteTable,
|
||||
scope_stack: ScopeStack,
|
||||
@@ -73,7 +73,7 @@ pub struct Machine {
|
||||
|
||||
impl Machine {
|
||||
pub fn new(
|
||||
globals: BTreeMap<Word, Value>,
|
||||
globals: BTreeMap<Word, ObjPtr>,
|
||||
max_stack_size: Option<usize>,
|
||||
scope_stack: ScopeStack,
|
||||
) -> Self {
|
||||
@@ -95,15 +95,19 @@ impl Machine {
|
||||
self.max_stack_size
|
||||
}
|
||||
|
||||
pub fn stack(&self) -> &Vec<Value> {
|
||||
pub fn call_stack(&self) -> &Vec<Frame> {
|
||||
&self.call_stack
|
||||
}
|
||||
|
||||
pub fn stack(&self) -> &Vec<ObjPtr> {
|
||||
&self.stack
|
||||
}
|
||||
|
||||
pub fn stack_mut(&mut self) -> &mut Vec<Value> {
|
||||
pub fn stack_mut(&mut self) -> &mut Vec<ObjPtr> {
|
||||
&mut self.stack
|
||||
}
|
||||
|
||||
pub fn stack_push(&mut self, value: Value) -> Result<()> {
|
||||
pub fn stack_push(&mut self, value: ObjPtr) -> Result<()> {
|
||||
if let Some(max) = self.max_stack_size() {
|
||||
if self.stack().len() >= max {
|
||||
return Err(RuntimeError::StackOverflow.into());
|
||||
@@ -113,13 +117,17 @@ impl Machine {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn stack_pop(&mut self) -> Result<Value> {
|
||||
pub fn stack_pop(&mut self) -> Result<ObjPtr> {
|
||||
self.stack_mut()
|
||||
.pop()
|
||||
.ok_or_else(|| RuntimeError::StackUnderflow.into())
|
||||
}
|
||||
|
||||
pub fn lookup_word(&self, word: &Word) -> Option<&Value> {
|
||||
pub fn stack_peek(&self) -> Option<&ObjPtr> {
|
||||
self.stack().last()
|
||||
}
|
||||
|
||||
pub fn lookup_word(&self, word: &Word) -> Option<&ObjPtr> {
|
||||
self.call_stack
|
||||
.iter()
|
||||
.rev()
|
||||
@@ -135,7 +143,7 @@ impl Machine {
|
||||
.or_else(|| self.globals.get(word))
|
||||
}
|
||||
|
||||
pub fn store_local(&mut self, word: Word, value: Value) {
|
||||
pub fn store_local(&mut self, word: Word, value: ObjPtr) {
|
||||
let last = self
|
||||
.call_stack
|
||||
.iter_mut()
|
||||
@@ -156,7 +164,7 @@ impl Machine {
|
||||
self.call_stack.last()
|
||||
}
|
||||
|
||||
fn call_quote(&mut self, quote: Quote) {
|
||||
pub(crate) fn call_quote(&mut self, quote: Quote) {
|
||||
let (_span, locals, _expr, code) = self.quote_table.get(quote);
|
||||
|
||||
// create a new stack frame
|
||||
@@ -170,7 +178,7 @@ impl Machine {
|
||||
);
|
||||
}
|
||||
|
||||
fn call_native(&mut self, call_site: Span, native: BuiltinFn) {
|
||||
pub(crate) fn call_native(&mut self, call_site: Option<Span>, native: BuiltinFn) {
|
||||
self.call_stack.push(Frame::Native(NativeFrame {
|
||||
call_site,
|
||||
fun: native,
|
||||
@@ -193,9 +201,7 @@ impl Machine {
|
||||
if let Err(mut error) = self.eval_stmt_list(stmts) {
|
||||
// go through the call stack and make the error
|
||||
for frame in self.call_stack.iter() {
|
||||
if let Some(call_site) = frame.call_site() {
|
||||
error = error.with_location(call_site.clone());
|
||||
}
|
||||
error = error.with_location(frame.call_site().cloned());
|
||||
}
|
||||
Err(error)
|
||||
} else {
|
||||
@@ -203,22 +209,7 @@ impl Machine {
|
||||
}
|
||||
}
|
||||
|
||||
fn eval_stmt_list(&mut self, stmts: Vec<SpStmt>) -> Result<()> {
|
||||
// TODO - figure out the best way to figure out the call stack
|
||||
// locations, and build an error out of that
|
||||
self.scope_stack.push_scope();
|
||||
let mut compile = Compile::new(&mut self.scope_stack, &mut self.quote_table);
|
||||
let code = Rc::new(compile.compile(stmts));
|
||||
let locals = self.scope_stack.pop_scope().unwrap();
|
||||
self.call_stack.push(
|
||||
QuoteFrame {
|
||||
locals: locals.keys().map(|w| (*w, None)).collect(),
|
||||
code,
|
||||
pc: 0,
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
|
||||
pub fn resume(&mut self) -> Result<()> {
|
||||
// Run instructions
|
||||
loop {
|
||||
// nothing left to execute
|
||||
@@ -243,7 +234,7 @@ impl Machine {
|
||||
let current_frame = self.call_stack.len() - 1;
|
||||
let fun = frame.fun.clone();
|
||||
let reentry = frame.reentry;
|
||||
let exit = self.do_call_native(fun, reentry)?;
|
||||
let exit = fun.call(self, reentry)?;
|
||||
|
||||
match exit {
|
||||
BuiltinExit::Call(quote) => {
|
||||
@@ -268,8 +259,22 @@ impl Machine {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn do_call_native(&mut self, native: BuiltinFn, reentry: usize) -> Result<BuiltinExit> {
|
||||
native.call(self, reentry)
|
||||
fn eval_stmt_list(&mut self, stmts: Vec<SpStmt>) -> Result<()> {
|
||||
// TODO - figure out the best way to figure out the call stack
|
||||
// locations, and build an error out of that
|
||||
self.scope_stack.push_scope();
|
||||
let mut compile = Compile::new(&mut self.scope_stack, &mut self.quote_table);
|
||||
let code = Rc::new(compile.compile(stmts));
|
||||
let locals = self.scope_stack.pop_scope().unwrap();
|
||||
self.call_stack.push(
|
||||
QuoteFrame {
|
||||
locals: locals.keys().map(|w| (*w, None)).collect(),
|
||||
code,
|
||||
pc: 0,
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
self.resume()
|
||||
}
|
||||
|
||||
fn eval_inst(&mut self, inst: SpInst) -> Result<()> {
|
||||
@@ -290,15 +295,10 @@ impl Machine {
|
||||
let value = self.stack_pop()?;
|
||||
self.store_local(word.clone(), value);
|
||||
}
|
||||
Inst::Call => match self.stack_pop()? {
|
||||
Value::Quote(quote) => {
|
||||
self.call_quote(quote);
|
||||
Inst::Call => {
|
||||
let value = self.stack_pop()?;
|
||||
value.call(Some(inst.span().clone()), self)?;
|
||||
}
|
||||
Value::BuiltinFn(builtin) => {
|
||||
self.call_native(inst.span().clone(), builtin);
|
||||
}
|
||||
value => return Err(RuntimeError::CannotCall(value.name().to_string()).into()),
|
||||
},
|
||||
};
|
||||
|
||||
// Update this frame's PC
|
||||
@@ -314,7 +314,7 @@ impl Machine {
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct MachineBuilder {
|
||||
pub(super) globals: BTreeMap<Word, Value>,
|
||||
pub(super) globals: BTreeMap<Word, ObjPtr>,
|
||||
pub(super) scope_stack: ScopeStack,
|
||||
pub(super) max_stack_size: Option<usize>,
|
||||
}
|
||||
@@ -332,11 +332,11 @@ impl MachineBuilder {
|
||||
) {
|
||||
self.register_global(
|
||||
name,
|
||||
Value::BuiltinFn(BuiltinFn::new(name.to_string(), Rc::new(fun))),
|
||||
BuiltinFnObj::new(BuiltinFn::new(name.to_string(), Rc::new(fun))).into_gc(),
|
||||
);
|
||||
}
|
||||
|
||||
pub(super) fn register_global(&mut self, name: &str, value: Value) {
|
||||
pub(super) fn register_global(&mut self, name: &str, value: ObjPtr) {
|
||||
let word = self.scope_stack.insert_local(name);
|
||||
self.globals.insert(word, value);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user