Skip to main content

sim_lib_lang_lua/
eval.rs

1use std::sync::Arc;
2
3use sim_kernel::{Cx, Error, Expr, Result, Symbol, Value};
4use sim_lib_standard_core::{
5    Arity, CoercionPolicy, GuestRuntimeKit, SharedOrganRuntime, TruthPolicy,
6};
7
8use crate::{
9    LuaEnv, LuaOp, LuaResult,
10    call::call_lua_value,
11    closure::{lua_closure_value, lua_varargs_values},
12    forms::{LuaForm, binding_symbol, bool_literal, lua_form, required_head, symbol_list},
13    load::install_lua_load_stdlib,
14    loops::{eval_generic_for, eval_numeric_for},
15    lua_binary, lua_core_profile, lua_get, lua_len, lua_rawget, lua_rawset, lua_table_from_values,
16    stdlib_base::install_lua_base_stdlib,
17    stdlib_coroutine::install_lua_coroutine_stdlib,
18    stdlib_debug::install_lua_debug_stdlib,
19    stdlib_io::install_lua_io_stdlib,
20    stdlib_math::install_lua_math_stdlib,
21    stdlib_os::install_lua_os_stdlib,
22    stdlib_package::install_lua_package_stdlib,
23    stdlib_string::install_lua_string_stdlib,
24    stdlib_table::install_lua_table_stdlib,
25    stdlib_utf8::install_lua_utf8_stdlib,
26};
27
28/// Eval policy for the Lua core profile.
29#[derive(Clone, Debug)]
30pub struct LuaEvalPolicy {
31    kit: GuestRuntimeKit,
32}
33
34impl LuaEvalPolicy {
35    /// Build the Lua core eval policy and register its runtime kit.
36    pub fn new(cx: &mut Cx) -> Result<Self> {
37        let mut runtime = SharedOrganRuntime::new();
38        let profile = lua_core_profile();
39        let profile_symbol = profile.symbol.clone();
40        runtime.register_profile(profile)?;
41        runtime.register_kit(&profile_symbol, lua_runtime_kit(cx)?)?;
42        let kit = runtime
43            .kit(&profile_symbol)
44            .cloned()
45            .ok_or(Error::UnknownSymbol {
46                symbol: profile_symbol,
47            })?;
48        Ok(Self { kit })
49    }
50
51    /// Borrow the language-neutral runtime policy kit.
52    pub fn kit(&self) -> &GuestRuntimeKit {
53        &self.kit
54    }
55
56    /// Install the Lua standard library fragments implemented by this profile.
57    pub fn install_stdlib(&self, cx: &mut Cx, env: &mut LuaEnv) -> Result<()> {
58        install_lua_base_stdlib(cx, self, env)?;
59        install_lua_coroutine_stdlib(cx, self, env)?;
60        install_lua_load_stdlib(cx, self, env)?;
61        install_lua_math_stdlib(cx, self, env)?;
62        install_lua_package_stdlib(cx, self, env)?;
63        install_lua_io_stdlib(cx, self, env)?;
64        install_lua_os_stdlib(cx, self, env)?;
65        install_lua_debug_stdlib(cx, self, env)?;
66        install_lua_table_stdlib(cx, self, env)?;
67        install_lua_string_stdlib(cx, self, env)?;
68        install_lua_utf8_stdlib(cx, self, env)
69    }
70
71    /// Evaluate a Lua core expression.
72    pub fn eval(&self, cx: &mut Cx, env: &mut LuaEnv, expr: &Expr) -> Result<LuaResult> {
73        if let Some((form, args)) = lua_form(expr) {
74            return match form {
75                LuaForm::Chunk | LuaForm::Block => self.eval_block(cx, env, args),
76                LuaForm::Local => self.eval_local(cx, env, args),
77                LuaForm::LocalValues => self.eval_local_values(cx, env, args),
78                LuaForm::Assign => self.eval_assign(cx, env, args),
79                LuaForm::If => self.eval_if(cx, env, args),
80                LuaForm::Call => self.eval_call_form(cx, env, args),
81                LuaForm::Closure => self.eval_closure(cx, env, args),
82                LuaForm::Varargs => self.eval_varargs(env, args),
83                LuaForm::Return => self.eval_return(cx, env, args),
84                LuaForm::Break => self.eval_break(args),
85                LuaForm::NumericFor => {
86                    eval_numeric_for(cx, self, env, args, |policy, cx, env, expr| {
87                        policy.eval_one(cx, env, expr)
88                    })
89                }
90                LuaForm::GenericFor => {
91                    eval_generic_for(cx, self, env, args, |policy, cx, env, expr| {
92                        policy.eval_one(cx, env, expr)
93                    })
94                }
95                LuaForm::Stdlib => self.eval_stdlib(cx, env, args),
96                LuaForm::Table => self.eval_table(cx, env, args),
97                LuaForm::Get => self.eval_get(cx, env, args),
98                LuaForm::RawGet => self.eval_rawget(cx, env, args),
99                LuaForm::RawSet => self.eval_rawset(cx, env, args),
100                LuaForm::Len => self.eval_len(cx, env, args),
101                LuaForm::Binary(op) => self.eval_binary(cx, env, op, args),
102            };
103        }
104
105        match expr {
106            Expr::Block(body) => self.eval_block(cx, env, body),
107            Expr::Call { operator, args } => self.eval_call(cx, env, operator, args),
108            _ => self.eval_atom(cx, env, expr).map(LuaResult::one),
109        }
110    }
111
112    fn eval_block(&self, cx: &mut Cx, env: &mut LuaEnv, body: &[Expr]) -> Result<LuaResult> {
113        let mut last = vec![self.kit.nil.clone()];
114        for (index, expr) in body.iter().enumerate() {
115            let result = self.eval(cx, env, expr)?;
116            if result.is_return() || result.is_break() {
117                return Ok(result);
118            }
119            let values = result.into_values();
120            last = if index + 1 == body.len() {
121                values
122            } else {
123                self.kit.adjust_values(values, Arity::AtLeastOne)
124            };
125        }
126        Ok(LuaResult::values(last))
127    }
128
129    fn eval_local(&self, cx: &mut Cx, env: &mut LuaEnv, args: &[Expr]) -> Result<LuaResult> {
130        let (name_expr, value_exprs) = required_head(args, "lua local")?;
131        if value_exprs.len() > 1 {
132            return Err(Error::Eval("lua local accepts one initializer".to_owned()));
133        }
134        let name = binding_symbol(name_expr, "lua local")?;
135        let value = match value_exprs.first() {
136            Some(value_expr) => self.eval_one(cx, env, value_expr)?,
137            None => self.kit.nil.clone(),
138        };
139        env.define(name, value.clone())?;
140        Ok(LuaResult::one(value))
141    }
142
143    fn eval_local_values(&self, cx: &mut Cx, env: &mut LuaEnv, args: &[Expr]) -> Result<LuaResult> {
144        let (names_expr, value_exprs) = required_head(args, "lua local-values")?;
145        let names = symbol_list(names_expr, "lua local-values")?;
146        let values = self.assignment_values(cx, env, value_exprs, names.len())?;
147        for (name, value) in names.into_iter().zip(values.iter().cloned()) {
148            env.define(name, value)?;
149        }
150        Ok(LuaResult::values(values))
151    }
152
153    fn eval_assign(&self, cx: &mut Cx, env: &mut LuaEnv, args: &[Expr]) -> Result<LuaResult> {
154        let (name_expr, value_exprs) = required_head(args, "lua assign")?;
155        if value_exprs.len() != 1 {
156            return Err(Error::Eval("lua assign requires one value".to_owned()));
157        }
158        let name = binding_symbol(name_expr, "lua assign")?;
159        let value = self.eval_one(cx, env, &value_exprs[0])?;
160        let assigned = env.assign(&name, value)?;
161        Ok(LuaResult::one(assigned))
162    }
163
164    fn eval_if(&self, cx: &mut Cx, env: &mut LuaEnv, args: &[Expr]) -> Result<LuaResult> {
165        if !(2..=3).contains(&args.len()) {
166            return Err(Error::Eval(
167                "lua if requires condition, then, and optional else".to_owned(),
168            ));
169        }
170        let condition = self.eval_one(cx, env, &args[0])?;
171        if self.kit.is_truthy(cx, &condition)? {
172            self.eval(cx, env, &args[1])
173        } else if let Some(else_expr) = args.get(2) {
174            self.eval(cx, env, else_expr)
175        } else {
176            Ok(LuaResult::one(self.kit.nil.clone()))
177        }
178    }
179
180    fn eval_call_form(&self, cx: &mut Cx, env: &mut LuaEnv, args: &[Expr]) -> Result<LuaResult> {
181        let (operator, value_exprs) = required_head(args, "lua call")?;
182        self.eval_call(cx, env, operator, value_exprs)
183    }
184
185    fn eval_call(
186        &self,
187        cx: &mut Cx,
188        env: &mut LuaEnv,
189        operator: &Expr,
190        args: &[Expr],
191    ) -> Result<LuaResult> {
192        let callee = self.eval_one(cx, env, operator)?;
193        let values = self.eval_argument_values(cx, env, args)?;
194        call_lua_value(cx, self, callee, values).map(LuaResult::values)
195    }
196
197    fn eval_closure(&self, cx: &mut Cx, env: &mut LuaEnv, args: &[Expr]) -> Result<LuaResult> {
198        if !(4..=5).contains(&args.len()) {
199            return Err(Error::Eval(
200                "lua closure requires name, params, vararg flag, body, and optional captures"
201                    .to_owned(),
202            ));
203        }
204        let name = binding_symbol(&args[0], "lua closure")?;
205        let params = symbol_list(&args[1], "lua closure params")?;
206        let vararg = bool_literal(&args[2], "lua closure vararg flag")?;
207        let captures = match args.get(4) {
208            Some(expr) => symbol_list(expr, "lua closure captures")?,
209            None => Vec::new(),
210        };
211        lua_closure_value(cx, env, name, params, vararg, args[3].clone(), captures)
212            .map(LuaResult::one)
213    }
214
215    fn eval_varargs(&self, env: &mut LuaEnv, args: &[Expr]) -> Result<LuaResult> {
216        if !args.is_empty() {
217            return Err(Error::Eval("lua varargs accepts no operands".to_owned()));
218        }
219        let value = env.get(&Symbol::new("..."))?;
220        let values = lua_varargs_values(&value)
221            .ok_or_else(|| Error::Eval("lua varargs local is not a vararg bundle".to_owned()))?;
222        Ok(LuaResult::values(values))
223    }
224
225    fn eval_return(&self, cx: &mut Cx, env: &mut LuaEnv, args: &[Expr]) -> Result<LuaResult> {
226        Ok(LuaResult::return_values(
227            self.eval_multi_exprs(cx, env, args)?,
228        ))
229    }
230
231    fn eval_break(&self, args: &[Expr]) -> Result<LuaResult> {
232        if !args.is_empty() {
233            return Err(Error::Eval("lua break accepts no operands".to_owned()));
234        }
235        Ok(LuaResult::break_signal())
236    }
237
238    fn eval_stdlib(&self, cx: &mut Cx, env: &mut LuaEnv, args: &[Expr]) -> Result<LuaResult> {
239        if !args.is_empty() {
240            return Err(Error::Eval("lua stdlib accepts no operands".to_owned()));
241        }
242        self.install_stdlib(cx, env)?;
243        Ok(LuaResult::one(self.kit.nil.clone()))
244    }
245
246    fn eval_table(&self, cx: &mut Cx, env: &mut LuaEnv, args: &[Expr]) -> Result<LuaResult> {
247        if !args.len().is_multiple_of(2) {
248            return Err(Error::Eval(
249                "lua table requires key/value expression pairs".to_owned(),
250            ));
251        }
252        let mut entries = Vec::with_capacity(args.len() / 2);
253        for pair in args.chunks_exact(2) {
254            entries.push((
255                self.eval_one(cx, env, &pair[0])?,
256                self.eval_one(cx, env, &pair[1])?,
257            ));
258        }
259        lua_table_from_values(cx, entries).map(LuaResult::one)
260    }
261
262    fn eval_get(&self, cx: &mut Cx, env: &mut LuaEnv, args: &[Expr]) -> Result<LuaResult> {
263        let [table, key] = args else {
264            return Err(Error::Eval("lua get requires table and key".to_owned()));
265        };
266        let table = self.eval_one(cx, env, table)?;
267        let key = self.eval_one(cx, env, key)?;
268        Ok(LuaResult::one(
269            lua_get(cx, &table, &key)?.unwrap_or_else(|| self.kit.nil.clone()),
270        ))
271    }
272
273    fn eval_rawget(&self, cx: &mut Cx, env: &mut LuaEnv, args: &[Expr]) -> Result<LuaResult> {
274        let [table, key] = args else {
275            return Err(Error::Eval("lua rawget requires table and key".to_owned()));
276        };
277        let table = self.eval_one(cx, env, table)?;
278        let key = self.eval_one(cx, env, key)?;
279        Ok(LuaResult::one(
280            lua_rawget(cx, &table, &key)?.unwrap_or_else(|| self.kit.nil.clone()),
281        ))
282    }
283
284    fn eval_rawset(&self, cx: &mut Cx, env: &mut LuaEnv, args: &[Expr]) -> Result<LuaResult> {
285        let [table, key, value] = args else {
286            return Err(Error::Eval(
287                "lua rawset requires table, key, and value".to_owned(),
288            ));
289        };
290        let table = self.eval_one(cx, env, table)?;
291        let key = self.eval_one(cx, env, key)?;
292        let value = self.eval_one(cx, env, value)?;
293        lua_rawset(cx, &table, key, value.clone())?;
294        Ok(LuaResult::one(value))
295    }
296
297    fn eval_len(&self, cx: &mut Cx, env: &mut LuaEnv, args: &[Expr]) -> Result<LuaResult> {
298        let [value] = args else {
299            return Err(Error::Eval("lua len requires one value".to_owned()));
300        };
301        let value = self.eval_one(cx, env, value)?;
302        lua_len(cx, env, value).map(LuaResult::one)
303    }
304
305    fn eval_binary(
306        &self,
307        cx: &mut Cx,
308        env: &mut LuaEnv,
309        op: LuaOp,
310        args: &[Expr],
311    ) -> Result<LuaResult> {
312        let [left, right] = args else {
313            return Err(Error::Eval(format!(
314                "lua operator {} requires two operands",
315                op.name()
316            )));
317        };
318        let left = self.eval_one(cx, env, left)?;
319        let right = self.eval_one(cx, env, right)?;
320        lua_binary(cx, env, op, left, right).map(LuaResult::one)
321    }
322
323    fn eval_one(&self, cx: &mut Cx, env: &mut LuaEnv, expr: &Expr) -> Result<Value> {
324        match self.eval(cx, env, expr)? {
325            LuaResult::Values(values) => Ok(self
326                .kit
327                .adjust_values(values, Arity::AtLeastOne)
328                .into_iter()
329                .next()
330                .unwrap_or_else(|| self.kit.nil.clone())),
331            LuaResult::Return(_) => Err(Error::Eval(
332                "lua return cannot be used as a value expression".to_owned(),
333            )),
334            LuaResult::Break => Err(Error::Eval(
335                "lua break cannot be used as a value expression".to_owned(),
336            )),
337        }
338    }
339
340    fn eval_values(&self, cx: &mut Cx, env: &mut LuaEnv, expr: &Expr) -> Result<Vec<Value>> {
341        match self.eval(cx, env, expr)? {
342            LuaResult::Values(values) => {
343                if let [value] = values.as_slice()
344                    && let Some(values) = lua_varargs_values(value)
345                {
346                    return Ok(values);
347                }
348                Ok(values)
349            }
350            LuaResult::Return(_) => Err(Error::Eval(
351                "lua return cannot be used as a value expression".to_owned(),
352            )),
353            LuaResult::Break => Err(Error::Eval(
354                "lua break cannot be used as a value expression".to_owned(),
355            )),
356        }
357    }
358
359    fn eval_multi_exprs(
360        &self,
361        cx: &mut Cx,
362        env: &mut LuaEnv,
363        exprs: &[Expr],
364    ) -> Result<Vec<Value>> {
365        let Some((last, prefix)) = exprs.split_last() else {
366            return Ok(Vec::new());
367        };
368        let mut values = Vec::with_capacity(exprs.len());
369        for expr in prefix {
370            values.push(self.eval_one(cx, env, expr)?);
371        }
372        values.extend(self.eval_values(cx, env, last)?);
373        Ok(values)
374    }
375
376    fn eval_argument_values(
377        &self,
378        cx: &mut Cx,
379        env: &mut LuaEnv,
380        exprs: &[Expr],
381    ) -> Result<Vec<Value>> {
382        self.eval_multi_exprs(cx, env, exprs)
383    }
384
385    fn assignment_values(
386        &self,
387        cx: &mut Cx,
388        env: &mut LuaEnv,
389        exprs: &[Expr],
390        count: usize,
391    ) -> Result<Vec<Value>> {
392        Ok(self
393            .kit
394            .adjust_values(self.eval_multi_exprs(cx, env, exprs)?, Arity::Exact(count)))
395    }
396
397    fn eval_atom(&self, cx: &mut Cx, env: &mut LuaEnv, expr: &Expr) -> Result<Value> {
398        match expr {
399            Expr::Nil => Ok(self.kit.nil.clone()),
400            Expr::Bool(value) => cx.factory().bool(*value),
401            Expr::Number(number) => cx
402                .factory()
403                .number_literal(number.domain.clone(), number.canonical.clone()),
404            Expr::String(value) => cx.factory().string(value.clone()),
405            Expr::Bytes(value) => cx.factory().bytes(value.clone()),
406            Expr::Symbol(symbol) => {
407                if env.contains(symbol) {
408                    env.get(symbol)
409                } else {
410                    cx.factory().symbol(symbol.clone())
411                }
412            }
413            Expr::Local(symbol) => env.get(symbol),
414            Expr::List(items) => {
415                let mut values = Vec::with_capacity(items.len());
416                for item in items {
417                    values.push(self.eval_one(cx, env, item)?);
418                }
419                cx.factory().list(values)
420            }
421            Expr::Vector(items) => {
422                let mut values = Vec::with_capacity(items.len());
423                for item in items {
424                    values.push(self.eval_one(cx, env, item)?);
425                }
426                cx.factory().list(values)
427            }
428            Expr::Map(entries) => {
429                let mut values = Vec::with_capacity(entries.len() * 2);
430                for (key, value) in entries {
431                    values.push(self.eval_one(cx, env, key)?);
432                    values.push(self.eval_one(cx, env, value)?);
433                }
434                cx.factory().list(values)
435            }
436            Expr::Set(items) => {
437                let mut values = Vec::with_capacity(items.len());
438                for item in items {
439                    values.push(self.eval_one(cx, env, item)?);
440                }
441                cx.factory().list(values)
442            }
443            Expr::Block(body) => match self.eval_block(cx, env, body)? {
444                LuaResult::Values(values) => Ok(self
445                    .kit
446                    .adjust_values(values, Arity::AtLeastOne)
447                    .into_iter()
448                    .next()
449                    .unwrap_or_else(|| self.kit.nil.clone())),
450                LuaResult::Return(_) => Err(Error::Eval(
451                    "lua return cannot be used as a value expression".to_owned(),
452                )),
453                LuaResult::Break => Err(Error::Eval(
454                    "lua break cannot be used as a value expression".to_owned(),
455                )),
456            },
457            Expr::Quote { expr, .. } => cx.factory().expr((**expr).clone()),
458            Expr::Annotated { expr, .. } => self.eval_one(cx, env, expr),
459            Expr::Extension { .. }
460            | Expr::Infix { .. }
461            | Expr::Prefix { .. }
462            | Expr::Postfix { .. } => cx.factory().expr(expr.clone()),
463            Expr::Call { .. } => unreachable!("calls are handled before atom evaluation"),
464        }
465    }
466}
467
468fn lua_runtime_kit(cx: &mut Cx) -> Result<GuestRuntimeKit> {
469    Ok(GuestRuntimeKit::new(
470        Arc::new(LuaTruthPolicy),
471        Arc::new(LuaCoercionPolicy),
472        cx.factory().nil()?,
473    ))
474}
475
476struct LuaTruthPolicy;
477
478impl TruthPolicy for LuaTruthPolicy {
479    fn is_truthy(&self, cx: &mut Cx, value: &Value) -> Result<bool> {
480        Ok(!matches!(
481            value.object().as_expr(cx)?,
482            Expr::Nil | Expr::Bool(false)
483        ))
484    }
485}
486
487struct LuaCoercionPolicy;
488
489impl CoercionPolicy for LuaCoercionPolicy {
490    fn to_number(&self, _cx: &mut Cx, _value: &Value) -> Result<Option<Value>> {
491        Ok(None)
492    }
493
494    fn to_string(&self, _cx: &mut Cx, _value: &Value) -> Result<Option<Value>> {
495        Ok(None)
496    }
497}