sexprs-vm 0.0.5

sexprs is a minimal lisp dialect to evaluate written in Rust.
Documentation
use std::collections::BTreeMap;
use std::fmt::{Debug, Display};

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

use crate::{builtin, BuiltinFunction, Context, Function, Result, Sym};

pub type SymTable<'c> = BTreeMap<Symbol<'c>, Sym<'c>>;

#[derive(Clone)]
pub struct SymbolTable<'c> {
    pub(crate) globals: SymTable<'c>,
    pub(crate) locals: SymTable<'c>,
    pub(crate) function_locals: SymTable<'c>,
}
impl<'c> Debug for SymbolTable<'c> {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(
            f,
            "SymbolTable {{
        globals: {:#?},
        locals: {:#?}
    }}",
            &self.globals, &self.locals,
        )
    }
}
impl<'c> Display for SymbolTable<'c> {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(
            f,
            "SymbolTable {{
        globals: {:#?},
        function_locals: {:#?},
        locals: {:#?}
    }}",
            &self.globals, &self.function_locals, &self.locals,
        )
    }
}

impl<'c> SymbolTable<'c> {
    pub fn new() -> SymbolTable<'c> {
        SymbolTable::with_locals(SymTable::new())
    }

    pub fn with_locals(locals: SymTable<'c>) -> SymbolTable<'c> {
        let mut globals = SymTable::<'c>::new();
        register_builtin_function(&mut globals, "t", builtin::identity::t);

        register_builtin_function(&mut globals, "setq", builtin::state::setq);
        register_builtin_function(&mut globals, "defun", builtin::state::defun);

        register_builtin_function(&mut globals, "car", builtin::list::car);
        register_builtin_function(&mut globals, "cdr", builtin::list::cdr);
        register_builtin_function(&mut globals, "cons", builtin::list::cons);
        register_builtin_function(&mut globals, "list", builtin::list::list);
        register_builtin_function(&mut globals, "append", builtin::list::append);
        register_builtin_function(&mut globals, "quote", builtin::list::quote);
        register_builtin_function(&mut globals, "print", builtin::string::print);
        register_builtin_function(&mut globals, "backquote", builtin::list::backquote);

        register_builtin_function(&mut globals, "if", builtin::r#if::r#if);

        register_builtin_function(&mut globals, "listp", builtin::r#type::listp);
        register_builtin_function(&mut globals, "null", builtin::r#type::null);

        register_builtin_function(&mut globals, "*", builtin::math::arithmetic::mul);
        register_builtin_function(&mut globals, "+", builtin::math::arithmetic::add);
        register_builtin_function(&mut globals, "-", builtin::math::arithmetic::sub);
        register_builtin_function(&mut globals, "/", builtin::math::arithmetic::div);

        let table = SymbolTable {
            globals: globals.clone(),
            function_locals: SymTable::new(),
            locals,
        };
        table
    }

    pub fn extend(&mut self, other: Self) {
        self.globals.extend(other.globals.clone());
        self.locals.extend(other.locals.clone());
    }

    pub fn set_global(
        &mut self,
        context: UniquePointer<Context<'c>>,
        sym: &Symbol<'c>,
        item: &Sym<'c>,
    ) -> Result<Value<'c>> {
        let previous =
            try_result!(set_within_map(&mut self.globals, context, sym, item));
        Ok(previous)
    }

    pub fn set_local(
        &mut self,
        context: UniquePointer<Context<'c>>,
        sym: &Symbol<'c>,
        item: &Sym<'c>,
    ) -> Result<Value<'c>> {
        let previous =
            try_result!(set_within_map(&mut self.locals, context, sym, item));
        Ok(previous)
    }

    pub fn set_function_local(
        &mut self,
        context: UniquePointer<Context<'c>>,
        sym: &Symbol<'c>,
        item: &Sym<'c>,
    ) -> Result<Value<'c>> {
        let previous =
            try_result!(set_within_map(&mut self.function_locals, context, sym, item));
        Ok(previous)
    }

    pub fn get(
        &mut self,
        _vm: UniquePointer<Context<'c>>,
        sym: &Symbol<'c>,
    ) -> Result<Sym<'c>> {
        if let Some(value) = self
            .function_locals
            .get(sym)
            .map(Clone::clone)
            .or_else(|| self.locals.get(sym).map(Clone::clone))
            .or_else(|| self.globals.get(sym).map(Clone::clone))
        {
            return Ok(value);
        } else {
            self.locals
                .insert(sym.clone(), Sym::Value(sym.as_value()));
            return Ok(Sym::Value(sym.as_value()));
        }
    }
}

fn register_builtin_function<'c>(
    table: &mut SymTable<'c>,
    sym: &str,
    function: BuiltinFunction,
) {
    let function = Sym::<'c>::Function(Function::Builtin {
        name: Symbol::new(sym),
        function: function,
    });
    table.insert(Symbol::new(sym), function.clone());
}

fn set_within_map<'c>(
    map: &mut SymTable<'c>,
    _context_: UniquePointer<Context<'c>>,
    sym: &Symbol<'c>,
    item: &Sym<'c>,
) -> Result<Value<'c>> {
    let previous = map.insert(sym.clone(), item.clone());

    Ok(match previous.unwrap_or_else(|| item.clone()) {
        Sym::Value(_) => item.clone(),
        Sym::Function(Function::Defun { .. }) => item.clone(),
        Sym::Function(Function::Builtin { .. }) => item.clone(),
    }
    .as_value())
}