wisp/src/interpreter/context.rs
2022-08-06 23:20:51 +02:00

135 lines
3.6 KiB
Rust

//! 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,
}