sexprs-vm 0.0.5

sexprs is a minimal lisp dialect to evaluate written in Rust.
Documentation
use std::collections::VecDeque;
use std::fmt::Debug;

use sexprs_data_structures::{Symbol, Value};
use sexprs_util::try_result;
use unique_pointer::UniquePointer;

use crate::{
    Context, Result, Sym, SymTable,
    SymbolTable,
};

#[derive(Clone)]
pub struct VirtualMachine<'c> {
    pub symbols: SymbolTable<'c>,
    stack: VecDeque<UniquePointer<Context<'c>>>,
}

impl<'c> Debug for VirtualMachine<'c> {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(
            f,
            "VirtualMachine {{
    symbols: {:#?},
    stack_size: {:#?}
}}",
            &self.symbols,
            self.stack.len()
        )
    }
}

impl<'c> VirtualMachine<'c> {
    pub fn new() -> VirtualMachine<'c> {
        let vm = VirtualMachine {
            symbols: SymbolTable::new(),
            stack: VecDeque::new(),
        };
        vm
    }

    pub fn setq(&mut self, symbol: Symbol<'c>, value: Value<'c>) -> Result<Value<'c>> {
        let context = self.push_context();
        let previous = try_result!(self.symbols.set_global(context, &symbol, &Sym::Value(value)));
        self.update_symbols();
        Ok(previous)
    }
    pub fn symbols(&self) -> SymTable<'c> {
        self.symbols.locals.clone()
    }

    pub(crate) fn push_context(&mut self) -> UniquePointer<Context<'c>> {
        let context = UniquePointer::<Context<'c>>::from(Context::new(
            UniquePointer::read_only(self),
            self.symbols.clone(),
        ));
        self.stack.push_front(context.clone());
        context
    }

    pub(crate) fn last_context(&self) -> Option<&UniquePointer<Context<'c>>> {
        self.stack.front()
    }

    pub(crate) fn update_symbols(&mut self) {
        if let Some(context) = self.last_context() {
            self.symbols.extend(context.symbols.clone());
        }
    }

    pub fn eval_string(&mut self, string: &'c str) -> Result<Value<'c>> {
        let value = try_result!(self.push_context().eval_string(string));
        self.update_symbols();
        Ok(value)
    }

    pub fn eval(&mut self, item: Value<'c>) -> Result<Value<'c>> {
        let value = try_result!(self.push_context().eval(item));
        self.update_symbols();
        Ok(value)
    }

    pub fn eval_symbol_function(
        &mut self,
        sym: &Symbol<'c>,
        list: Value<'c>,
    ) -> Result<Value<'c>> {
        let value = try_result!(self
            .push_context()
            .eval_symbol_function(sym, list));
        self.update_symbols();
        Ok(value)
    }
}