Skip to main content

bubbles/runtime/eval/
mod.rs

1//! Expression evaluator - walks an [`Expr`] AST and produces a [`Value`].
2
3mod binary;
4mod unary;
5
6use crate::compiler::expr::Expr;
7use crate::error::{DialogueError, Result};
8use crate::value::{Value, VariableStorage};
9
10/// Evaluate an [`Expr`] AST node using `storage` for variable reads and `fns` for function calls.
11///
12/// `fns` receives the function name and evaluated arguments and must return a [`Value`].
13///
14/// # Errors
15/// Returns [`crate::error::DialogueError`] for undefined variables, type mismatches, or failed
16/// function calls.
17pub fn eval<S, F>(expr: &Expr, storage: &S, fns: &F) -> Result<Value>
18where
19    S: VariableStorage,
20    F: Fn(&str, Vec<Value>) -> Result<Value>,
21{
22    match expr {
23        Expr::Number(n) => Ok(Value::Number(*n)),
24        Expr::Text(s) => Ok(Value::Text(s.clone())),
25        Expr::Bool(b) => Ok(Value::Bool(*b)),
26        Expr::Var(name) => storage
27            .get_ref(name)
28            .map(std::borrow::Cow::into_owned)
29            .ok_or_else(|| DialogueError::UndefinedVariable(name.clone())),
30        Expr::Call { name, args } => {
31            let evaluated: Result<Vec<Value>> =
32                args.iter().map(|a| eval(a, storage, fns)).collect();
33            fns(name, evaluated?)
34        }
35        Expr::Unary { op, expr } => unary::eval_unary(*op, expr, storage, fns),
36        Expr::Binary { left, op, right } => binary::eval_binary(left, *op, right, storage, fns),
37    }
38}
39
40#[cfg(test)]
41#[path = "eval_tests.rs"]
42mod tests;