135 lines
3.6 KiB
Rust
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,
|
|
}
|