added basic VM
This commit is contained in:
parent
18fd6a35d5
commit
26a0cce990
28
src/interpreter/bytecode.rs
Normal file
28
src/interpreter/bytecode.rs
Normal file
|
@ -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,
|
||||
}
|
134
src/interpreter/context.rs
Normal file
134
src/interpreter/context.rs
Normal file
|
@ -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<T, E = Error> = std::result::Result<T, E>;
|
||||
|
||||
pub struct Context<'s> {
|
||||
fun: Rc<Function<'s>>,
|
||||
stack: Vec<Value<'s>>,
|
||||
locals: Vec<Value<'s>>,
|
||||
pc: usize,
|
||||
}
|
||||
|
||||
impl<'s> Context<'s> {
|
||||
/// Create a new evaluation context
|
||||
pub fn new(fun: Rc<Function<'s>>) -> 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<Value<'s>, 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,
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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<Expr<'s>>]>,
|
||||
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> {}
|
||||
|
|
|
@ -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<Function<'s>>),
|
||||
NativeFun(fn(&'s [Value<'s>]) -> Value<'s>),
|
||||
NativeFun(NativeFun<'s>),
|
||||
Macro(Rc<Function<'s>>),
|
||||
}
|
||||
|
||||
|
@ -72,7 +72,7 @@ impl<'s> Display for Value<'s> {
|
|||
}
|
||||
}
|
||||
|
||||
fn fmt_sequence<'s, T>(
|
||||
fn fmt_sequence<T>(
|
||||
f: &mut std::fmt::Formatter<'_>,
|
||||
s: impl IntoIterator<Item = T>,
|
||||
start: &str,
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue