Skip to main content

sim_lib_lang_lua/
operator.rs

1use sim_kernel::{Args, Cx, Expr, Result, Symbol, Value};
2
3use crate::{
4    LuaEnv,
5    metatable::lua_metamethod,
6    number::{lua_arith_or_compare, lua_integer_value},
7    table::lua_table_value,
8};
9
10/// Lua core binary and unary operators covered by the current profile.
11#[derive(Clone, Copy, Debug, PartialEq, Eq)]
12pub enum LuaOp {
13    /// Addition.
14    Add,
15    /// Subtraction.
16    Sub,
17    /// Multiplication.
18    Mul,
19    /// Floating-point division.
20    FloatDiv,
21    /// Floor division.
22    FloorDiv,
23    /// Modulo.
24    Mod,
25    /// Exponentiation.
26    Pow,
27    /// Bitwise and.
28    Band,
29    /// Bitwise or.
30    Bor,
31    /// Bitwise xor.
32    Bxor,
33    /// Bitwise left shift.
34    Shl,
35    /// Bitwise right shift.
36    Shr,
37    /// Concatenation.
38    Concat,
39    /// Length.
40    Len,
41    /// Equality.
42    Eq,
43    /// Less-than.
44    Lt,
45    /// Less-than-or-equal.
46    Le,
47}
48
49impl LuaOp {
50    /// Returns the Lua source-level operator spelling.
51    pub fn name(self) -> &'static str {
52        match self {
53            Self::Add => "+",
54            Self::Sub => "-",
55            Self::Mul => "*",
56            Self::FloatDiv => "/",
57            Self::FloorDiv => "//",
58            Self::Mod => "%",
59            Self::Pow => "^",
60            Self::Band => "&",
61            Self::Bor => "|",
62            Self::Bxor => "~",
63            Self::Shl => "<<",
64            Self::Shr => ">>",
65            Self::Concat => "..",
66            Self::Len => "#",
67            Self::Eq => "==",
68            Self::Lt => "<",
69            Self::Le => "<=",
70        }
71    }
72
73    /// Returns the Lua metamethod slot for this operator.
74    pub fn metamethod_slot(self) -> Symbol {
75        Symbol::new(match self {
76            Self::Add => "__add",
77            Self::Sub => "__sub",
78            Self::Mul => "__mul",
79            Self::FloatDiv => "__div",
80            Self::FloorDiv => "__idiv",
81            Self::Mod => "__mod",
82            Self::Pow => "__pow",
83            Self::Band => "__band",
84            Self::Bor => "__bor",
85            Self::Bxor => "__bxor",
86            Self::Shl => "__shl",
87            Self::Shr => "__shr",
88            Self::Concat => "__concat",
89            Self::Len => "__len",
90            Self::Eq => "__eq",
91            Self::Lt => "__lt",
92            Self::Le => "__le",
93        })
94    }
95}
96
97/// Applies a Lua binary operator using metamethods before primitive behavior.
98pub fn lua_binary(
99    cx: &mut Cx,
100    _env: &mut LuaEnv,
101    op: LuaOp,
102    left: Value,
103    right: Value,
104) -> Result<Value> {
105    if let Some(value) = try_binary_metamethod(cx, op, &left, &right)? {
106        return Ok(value);
107    }
108    lua_arith_or_compare(cx, op, left, right)
109}
110
111/// Applies Lua length over strings and tables, with `__len` fallback.
112pub fn lua_len(cx: &mut Cx, _env: &mut LuaEnv, value: Value) -> Result<Value> {
113    if let Some(method) = lua_metamethod(cx, &value, &LuaOp::Len.metamethod_slot())? {
114        return cx.call_value(method, Args::new(vec![value]));
115    }
116    match value.object().as_expr(cx)? {
117        Expr::String(text) => lua_integer_value(cx, text.chars().count() as i64),
118        _ => {
119            let table = lua_table_value(&value)?;
120            let len = table.len_border(cx)?;
121            lua_integer_value(cx, len)
122        }
123    }
124}
125
126fn try_binary_metamethod(
127    cx: &mut Cx,
128    op: LuaOp,
129    left: &Value,
130    right: &Value,
131) -> Result<Option<Value>> {
132    let slot = op.metamethod_slot();
133    let method = lua_metamethod(cx, left, &slot)?.or(lua_metamethod(cx, right, &slot)?);
134    let Some(method) = method else {
135        return Ok(None);
136    };
137    cx.call_value(method, Args::new(vec![left.clone(), right.clone()]))
138        .map(Some)
139}