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.
|
//! A simple silly compiler + VM intended for testing purposes.
|
||||||
//! To be replaced by something better soon™️ :ferrisClueless:
|
//! To be replaced by something better soon™️ :ferrisClueless:
|
||||||
|
|
||||||
|
pub mod bytecode;
|
||||||
|
pub mod context;
|
||||||
pub mod value;
|
pub mod value;
|
||||||
|
|
|
@ -1,8 +1,29 @@
|
||||||
use super::Str;
|
use std::fmt::Debug;
|
||||||
use crate::syntax::ast::{Expr, Spanned};
|
|
||||||
|
use super::Value;
|
||||||
|
use crate::interpreter::bytecode::Instruction;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct Function<'s> {
|
pub struct Function<'s> {
|
||||||
params: Box<[Str<'s>]>,
|
pub bytecode: Box<[Instruction]>,
|
||||||
ast: Box<[Spanned<Expr<'s>>]>,
|
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 pair;
|
||||||
mod string;
|
mod string;
|
||||||
|
|
||||||
pub use function::Function;
|
pub use function::{Function, NativeFun};
|
||||||
pub use pair::DotPair;
|
pub use pair::DotPair;
|
||||||
pub use string::Str;
|
pub use string::Str;
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ pub enum Value<'s> {
|
||||||
Number(OrderedF64),
|
Number(OrderedF64),
|
||||||
String(Str<'s>),
|
String(Str<'s>),
|
||||||
Function(Rc<Function<'s>>),
|
Function(Rc<Function<'s>>),
|
||||||
NativeFun(fn(&'s [Value<'s>]) -> Value<'s>),
|
NativeFun(NativeFun<'s>),
|
||||||
Macro(Rc<Function<'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<'_>,
|
f: &mut std::fmt::Formatter<'_>,
|
||||||
s: impl IntoIterator<Item = T>,
|
s: impl IntoIterator<Item = T>,
|
||||||
start: &str,
|
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:
|
/// A Wisp string type which can hold two variants:
|
||||||
/// - Borrowed (usually a reference to source code or built-in literal)
|
/// - Borrowed (usually a reference to source code or built-in literal)
|
||||||
|
|
Loading…
Reference in a new issue