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#[derive(Clone, Copy, Debug, PartialEq, Eq)]
12pub enum LuaOp {
13 Add,
15 Sub,
17 Mul,
19 FloatDiv,
21 FloorDiv,
23 Mod,
25 Pow,
27 Band,
29 Bor,
31 Bxor,
33 Shl,
35 Shr,
37 Concat,
39 Len,
41 Eq,
43 Lt,
45 Le,
47}
48
49impl LuaOp {
50 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 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
97pub 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
111pub 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}