sim-lib-lang-lua 0.1.4

Lua-style surface profile for the SIM expression runtime.
Documentation
use sim_kernel::{Error, Expr, Result, Symbol};

use crate::LuaOp;

#[derive(Clone, Copy)]
pub(crate) enum LuaForm {
    Chunk,
    Block,
    Local,
    LocalValues,
    Assign,
    If,
    Call,
    Closure,
    Varargs,
    Return,
    Break,
    NumericFor,
    GenericFor,
    Stdlib,
    Table,
    Get,
    RawGet,
    RawSet,
    Len,
    Binary(LuaOp),
}

pub(crate) fn lua_form(expr: &Expr) -> Option<(LuaForm, &[Expr])> {
    let Expr::List(items) = expr else {
        return None;
    };
    let (head, args) = items.split_first()?;
    let Expr::Symbol(symbol) = head else {
        return None;
    };
    lua_form_symbol(symbol).map(|form| (form, args))
}

pub(crate) fn required_head<'a>(args: &'a [Expr], context: &str) -> Result<(&'a Expr, &'a [Expr])> {
    args.split_first()
        .ok_or_else(|| Error::Eval(format!("{context} requires a target")))
}

pub(crate) fn binding_symbol(expr: &Expr, context: &str) -> Result<Symbol> {
    match expr {
        Expr::Symbol(symbol) | Expr::Local(symbol) => Ok(symbol.clone()),
        _ => Err(Error::Eval(format!(
            "{context} requires a symbol binding target"
        ))),
    }
}

pub(crate) fn symbol_list(expr: &Expr, context: &str) -> Result<Vec<Symbol>> {
    let items = match expr {
        Expr::List(items) | Expr::Vector(items) => items,
        _ => {
            return Err(Error::Eval(format!("{context} requires a list of symbols")));
        }
    };
    items
        .iter()
        .map(|expr| binding_symbol(expr, context))
        .collect()
}

pub(crate) fn bool_literal(expr: &Expr, context: &str) -> Result<bool> {
    match expr {
        Expr::Bool(value) => Ok(*value),
        _ => Err(Error::Eval(format!("{context} must be a boolean literal"))),
    }
}

fn lua_form_symbol(symbol: &Symbol) -> Option<LuaForm> {
    if !matches!(
        symbol.namespace.as_deref(),
        Some("lua") | Some("lua/core") | None
    ) {
        return None;
    }
    match symbol.name.as_ref() {
        "chunk" => Some(LuaForm::Chunk),
        "block" => Some(LuaForm::Block),
        "local" => Some(LuaForm::Local),
        "local-values" => Some(LuaForm::LocalValues),
        "assign" => Some(LuaForm::Assign),
        "if" => Some(LuaForm::If),
        "call" => Some(LuaForm::Call),
        "closure" | "function" => Some(LuaForm::Closure),
        "varargs" => Some(LuaForm::Varargs),
        "return" => Some(LuaForm::Return),
        "break" => Some(LuaForm::Break),
        "for-num" => Some(LuaForm::NumericFor),
        "for-in" => Some(LuaForm::GenericFor),
        "stdlib" => Some(LuaForm::Stdlib),
        "table" => Some(LuaForm::Table),
        "get" => Some(LuaForm::Get),
        "rawget" => Some(LuaForm::RawGet),
        "rawset" => Some(LuaForm::RawSet),
        "len" => Some(LuaForm::Len),
        "add" => Some(LuaForm::Binary(LuaOp::Add)),
        "sub" => Some(LuaForm::Binary(LuaOp::Sub)),
        "mul" => Some(LuaForm::Binary(LuaOp::Mul)),
        "div" => Some(LuaForm::Binary(LuaOp::FloatDiv)),
        "floordiv" => Some(LuaForm::Binary(LuaOp::FloorDiv)),
        "mod" => Some(LuaForm::Binary(LuaOp::Mod)),
        "pow" => Some(LuaForm::Binary(LuaOp::Pow)),
        "band" => Some(LuaForm::Binary(LuaOp::Band)),
        "bor" => Some(LuaForm::Binary(LuaOp::Bor)),
        "bxor" => Some(LuaForm::Binary(LuaOp::Bxor)),
        "shl" => Some(LuaForm::Binary(LuaOp::Shl)),
        "shr" => Some(LuaForm::Binary(LuaOp::Shr)),
        "concat" => Some(LuaForm::Binary(LuaOp::Concat)),
        "eq" => Some(LuaForm::Binary(LuaOp::Eq)),
        "lt" => Some(LuaForm::Binary(LuaOp::Lt)),
        "le" => Some(LuaForm::Binary(LuaOp::Le)),
        _ => None,
    }
}