Add parser, vm, objects
Big ol thing. You should check it out sometime Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
This commit is contained in:
36
src/vm/error.rs
Normal file
36
src/vm/error.rs
Normal file
@@ -0,0 +1,36 @@
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug, Clone)]
|
||||
pub enum RuntimeError {
|
||||
#[error("stack overflow")]
|
||||
StackOverflow,
|
||||
#[error("stack underflow")]
|
||||
StackUnderflow,
|
||||
//#[error("unexpected {0}")]
|
||||
//Unexpected(String),
|
||||
//#[error("expected {expected}, but got {got}")]
|
||||
//ExpectedGot { expected: String, got: String },
|
||||
}
|
||||
|
||||
pub type Result<T, E = RuntimeError> = std::result::Result<T, E>;
|
||||
|
||||
// TODO
|
||||
// Error building idea:
|
||||
// pass RuntimeError values upward, but also keep a list of spans/locations
|
||||
// added by functions adding errors upward.
|
||||
|
||||
// e.g.
|
||||
|
||||
/*
|
||||
|
||||
fn do_thing() -> Result<()> {
|
||||
...
|
||||
let result = do_fallible_thing();
|
||||
if let Err(e) = result {
|
||||
e.add_location(context.file, context.span);
|
||||
return Err(e);
|
||||
}
|
||||
...
|
||||
}
|
||||
|
||||
*/
|
||||
49
src/vm/eval.rs
Normal file
49
src/vm/eval.rs
Normal file
@@ -0,0 +1,49 @@
|
||||
use crate::object::*;
|
||||
use crate::syn::ast::*;
|
||||
use crate::vm::{error::*, inst::Inst, machine::Machine};
|
||||
|
||||
/// An evaluation context for the VM.
|
||||
pub struct Eval<'m> {
|
||||
machine: &'m mut Machine,
|
||||
}
|
||||
|
||||
impl<'m> Eval<'m> {
|
||||
pub fn new(machine: &'m mut Machine) -> Self {
|
||||
Self { machine }
|
||||
}
|
||||
|
||||
pub fn eval_expr(&mut self, expr: &SpExpr) -> Result<()> {
|
||||
match expr.inner() {
|
||||
Expr::Atom(atom) => match atom.inner() {
|
||||
Atom::Float(f) => self.machine.stack_push(Value::Float(*f))?,
|
||||
Atom::Int(i) => self.machine.stack_push(Value::Int(*i))?,
|
||||
Atom::Str(s) => self.machine.stack_push(Value::Str(s.clone()))?,
|
||||
Atom::Word(_) => todo!("TODO - word lookup"),
|
||||
},
|
||||
Expr::Quote(quote) => {
|
||||
self.machine.stack_push(Value::Quote(quote.clone()))?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn eval_inst(&mut self, inst: Inst) -> Result<()> {
|
||||
match inst {
|
||||
Inst::Push(v) => self.machine.stack_push(v)?,
|
||||
Inst::Pop => {
|
||||
self.machine
|
||||
.stack_pop()
|
||||
.ok_or(RuntimeError::StackUnderflow)?;
|
||||
}
|
||||
Inst::When => todo!(),
|
||||
Inst::If => todo!(),
|
||||
Inst::Eval => todo!(),
|
||||
Inst::Print => todo!(),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// IDEA: Eval chain
|
||||
// chain of eval-able things, including macros. An eval can add something to the
|
||||
// top of the eval chain and immediately jump to it, such as an include
|
||||
11
src/vm/inst.rs
Normal file
11
src/vm/inst.rs
Normal file
@@ -0,0 +1,11 @@
|
||||
use crate::object::Value;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Inst {
|
||||
Push(Value),
|
||||
Pop,
|
||||
When,
|
||||
If,
|
||||
Eval,
|
||||
Print,
|
||||
}
|
||||
69
src/vm/machine.rs
Normal file
69
src/vm/machine.rs
Normal file
@@ -0,0 +1,69 @@
|
||||
use crate::object::*;
|
||||
use crate::vm::error::*;
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct MachineBuilder {
|
||||
max_stack_size: Option<usize>,
|
||||
max_arena_objects: Option<usize>,
|
||||
}
|
||||
|
||||
impl MachineBuilder {
|
||||
pub fn max_stack_size(mut self, max_stack_size: Option<usize>) -> Self {
|
||||
self.max_stack_size = max_stack_size;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn max_arena_objects(mut self, max_arena_objects: Option<usize>) -> Self {
|
||||
self.max_arena_objects = max_arena_objects;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn finish(self) -> Machine {
|
||||
Machine::new(self.max_stack_size, Arena::new(self.max_arena_objects))
|
||||
}
|
||||
}
|
||||
|
||||
/// The current state of a VM.
|
||||
#[derive(Debug)]
|
||||
pub struct Machine {
|
||||
stack: Vec<Value>,
|
||||
max_stack_size: Option<usize>,
|
||||
arena: Rc<RefCell<Arena>>,
|
||||
}
|
||||
|
||||
impl Machine {
|
||||
pub fn new(max_stack_size: Option<usize>, arena: Arena) -> Self {
|
||||
Machine {
|
||||
stack: Default::default(),
|
||||
max_stack_size,
|
||||
arena: Rc::new(RefCell::new(arena)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn max_stack_size(&self) -> Option<usize> {
|
||||
self.max_stack_size
|
||||
}
|
||||
|
||||
pub fn stack(&self) -> &Vec<Value> {
|
||||
&self.stack
|
||||
}
|
||||
pub fn stack_mut(&mut self) -> &mut Vec<Value> {
|
||||
&mut self.stack
|
||||
}
|
||||
|
||||
pub fn stack_push(&mut self, value: Value) -> Result<()> {
|
||||
if let Some(max) = self.max_stack_size() {
|
||||
if self.stack.len() >= max {
|
||||
return Err(RuntimeError::StackOverflow);
|
||||
}
|
||||
}
|
||||
self.stack_mut().push(value);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn stack_pop(&mut self) -> Option<Value> {
|
||||
self.stack_mut().pop()
|
||||
}
|
||||
}
|
||||
4
src/vm/mod.rs
Normal file
4
src/vm/mod.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
pub mod error;
|
||||
pub mod eval;
|
||||
pub mod inst;
|
||||
pub mod machine;
|
||||
Reference in New Issue
Block a user