From 26a0cce990c8ec0badb13c441626578bd37121bc Mon Sep 17 00:00:00 2001 From: Erin Date: Sat, 6 Aug 2022 23:20:51 +0200 Subject: [PATCH] added basic VM --- src/interpreter/bytecode.rs | 28 +++++++ src/interpreter/context.rs | 134 ++++++++++++++++++++++++++++++ src/interpreter/mod.rs | 2 + src/interpreter/value/function.rs | 29 ++++++- src/interpreter/value/mod.rs | 6 +- src/interpreter/value/string.rs | 2 +- 6 files changed, 193 insertions(+), 8 deletions(-) create mode 100644 src/interpreter/bytecode.rs create mode 100644 src/interpreter/context.rs diff --git a/src/interpreter/bytecode.rs b/src/interpreter/bytecode.rs new file mode 100644 index 0000000..1dd3063 --- /dev/null +++ b/src/interpreter/bytecode.rs @@ -0,0 +1,28 @@ +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Instruction { + /// Call a function on top of the stack + /// with n arguments + Call(usize), + + /// Unconditional jump by offset + Jump(isize), + + /// Jump by offset if value on + /// top of the stack is falsey (nil) + JumpN(isize), + + /// Load a value from constants table + LdConst(usize), + + /// Load a value from locals table + LdLocal(usize), + + /// Store a value to locals table + StLocal(usize), + + /// Pop a value from stack + Pop, + + /// Duplicate value on stack + Dup, +} diff --git a/src/interpreter/context.rs b/src/interpreter/context.rs new file mode 100644 index 0000000..d073b2e --- /dev/null +++ b/src/interpreter/context.rs @@ -0,0 +1,134 @@ +//! Evaluation context for Wisp + +use super::{ + bytecode::Instruction, + value::{Function, Value}, +}; +use crate::interpreter::value::NativeFun; +use std::rc::Rc; +use thiserror::Error; + +type Result = std::result::Result; + +pub struct Context<'s> { + fun: Rc>, + stack: Vec>, + locals: Vec>, + pc: usize, +} + +impl<'s> Context<'s> { + /// Create a new evaluation context + pub fn new(fun: Rc>) -> Self { + Self { + stack: vec![], + locals: vec![Value::Nil; fun.locals_len], + pc: 0, + fun, + } + } + + /// Execute next instruction + pub fn tick(&mut self) -> Result<()> { + use Instruction::*; + match self + .fun + .bytecode + .get(self.pc) + .copied() + .ok_or(IndexOutOfBoundsError::Bytecode)? + { + Call(n) if n >= self.stack.len() => return Err(IndexOutOfBoundsError::Stack.into()), + Call(n) => { + let fun = self.stack_pop()?; + match fun { + Value::Function(_) => todo!("function calls"), + Value::NativeFun(NativeFun(f)) => { + let v = f(&self.stack.split_off(self.stack.len() - n)); + self.stack.push(v); + } + _ => unimplemented!("invalid function type"), + } + } + Jump(o) => { + self.jump(o); + return Ok(()); + } + JumpN(o) => { + if self.stack_pop()? == Value::Nil { + self.jump(o); + return Ok(()); + } + } + LdConst(i) => { + self.stack.push( + self.fun + .consts + .get(i) + .cloned() + .ok_or(IndexOutOfBoundsError::Const)?, + ); + } + LdLocal(i) => { + self.stack.push( + self.locals + .get(i) + .cloned() + .ok_or(IndexOutOfBoundsError::Local)?, + ); + } + StLocal(i) => { + *self.locals.get_mut(i).ok_or(IndexOutOfBoundsError::Local)? = self.stack_pop()?; + } + Pop => { + self.stack_pop()?; + } + Dup => { + self.stack.push( + self.stack + .last() + .cloned() + .ok_or(IndexOutOfBoundsError::Stack)?, + ); + } + } + + self.pc += 1; + Ok(()) + } + + /// Jump by offset + fn jump(&mut self, offset: isize) { + (if offset.is_negative() { + std::ops::SubAssign::sub_assign + } else { + std::ops::AddAssign::add_assign + })(&mut self.pc, offset.unsigned_abs()); + } + + /// Pop a value from a stack + fn stack_pop(&mut self) -> Result, IndexOutOfBoundsError> { + self.stack.pop().ok_or(IndexOutOfBoundsError::Stack) + } +} + +#[derive(Debug, Clone, Error)] +pub enum Error { + #[error(transparent)] + IndexOutOfBounds(#[from] IndexOutOfBoundsError), +} + +#[derive(Debug, Clone, Error)] +pub enum IndexOutOfBoundsError { + #[error("bytecode index out of bounds")] + Bytecode, + + #[error("const index out of bounds")] + Const, + + #[error("stack index out of bounds")] + Stack, + + #[error("locals index out of bounds")] + Local, +} diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 9294560..2cbd8d5 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -7,4 +7,6 @@ //! A simple silly compiler + VM intended for testing purposes. //! To be replaced by something better soon™️ :ferrisClueless: +pub mod bytecode; +pub mod context; pub mod value; diff --git a/src/interpreter/value/function.rs b/src/interpreter/value/function.rs index d5c2435..5cd739c 100644 --- a/src/interpreter/value/function.rs +++ b/src/interpreter/value/function.rs @@ -1,8 +1,29 @@ -use super::Str; -use crate::syntax::ast::{Expr, Spanned}; +use std::fmt::Debug; + +use super::Value; +use crate::interpreter::bytecode::Instruction; #[derive(Debug, Clone, PartialEq, Eq)] pub struct Function<'s> { - params: Box<[Str<'s>]>, - ast: Box<[Spanned>]>, + pub bytecode: Box<[Instruction]>, + pub consts: Box<[Value<'s>]>, + pub locals_len: usize, } + +#[derive(Clone)] +pub struct NativeFun<'s>(pub fn(&[Value<'s>]) -> Value<'s>); +impl<'s> Debug for NativeFun<'s> { + fn fmt<'a>(&'a self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_tuple("NativeFun") + .field(&(self.0 as fn(&'a _) -> _)) + .finish() + } +} + +impl<'s> PartialEq for NativeFun<'s> { + fn eq<'a>(&'a self, other: &Self) -> bool { + (self.0 as fn(&'a _) -> _) == other.0 + } +} + +impl<'s> Eq for NativeFun<'s> {} diff --git a/src/interpreter/value/mod.rs b/src/interpreter/value/mod.rs index fb5ffb0..9dc3bdd 100644 --- a/src/interpreter/value/mod.rs +++ b/src/interpreter/value/mod.rs @@ -2,7 +2,7 @@ mod function; mod pair; mod string; -pub use function::Function; +pub use function::{Function, NativeFun}; pub use pair::DotPair; pub use string::Str; @@ -24,7 +24,7 @@ pub enum Value<'s> { Number(OrderedF64), String(Str<'s>), Function(Rc>), - NativeFun(fn(&'s [Value<'s>]) -> Value<'s>), + NativeFun(NativeFun<'s>), Macro(Rc>), } @@ -72,7 +72,7 @@ impl<'s> Display for Value<'s> { } } -fn fmt_sequence<'s, T>( +fn fmt_sequence( f: &mut std::fmt::Formatter<'_>, s: impl IntoIterator, start: &str, diff --git a/src/interpreter/value/string.rs b/src/interpreter/value/string.rs index c759823..ae4d812 100644 --- a/src/interpreter/value/string.rs +++ b/src/interpreter/value/string.rs @@ -1,4 +1,4 @@ -use std::{fmt::Display, ops::Deref, rc::Rc, hash::Hash}; +use std::{fmt::Display, hash::Hash, ops::Deref, rc::Rc}; /// A Wisp string type which can hold two variants: /// - Borrowed (usually a reference to source code or built-in literal)