From 239fc9a0f829df738a6df7335a0014c7b6177706 Mon Sep 17 00:00:00 2001 From: Sofia Date: Fri, 20 Mar 2026 21:22:52 +0200 Subject: [PATCH] Add all metamethods --- src/vm/mod.rs | 253 ++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 213 insertions(+), 40 deletions(-) diff --git a/src/vm/mod.rs b/src/vm/mod.rs index 9fbba7b..b807236 100644 --- a/src/vm/mod.rs +++ b/src/vm/mod.rs @@ -5,7 +5,7 @@ use std::{cell::RefCell, collections::HashMap, fmt::Debug, i64, rc::Rc}; use crate::{ CompilationUnit, ast::{BinaryOperator, UnaryOperator}, - vm::value::{Constant, IndexableValue, LuaInteger, RustFunction, Table, Value}, + vm::value::{Constant, IndexableValue, LuaBool, LuaInteger, RustFunction, Table, Value}, }; pub mod value; @@ -77,6 +77,8 @@ pub enum Instruction { /// R(A) := R(B) + R(C) Add(u16, u16, u16), + /// R(A) := R(B) - R(C) + Sub(u16, u16, u16), /// R(A) := R(B) * R(C) Mult(u16, u16, u16), /// R(A) := R(B) / R(C) @@ -185,6 +187,7 @@ impl Debug for Instruction { } Instruction::Add(arg0, arg1, arg2) => write!(f, "ADD {} {} {}", arg0, arg1, arg2), + Instruction::Sub(arg0, arg1, arg2) => write!(f, "SUB {} {} {}", arg0, arg1, arg2), Instruction::Mult(arg0, arg1, arg2) => write!(f, "MUL {} {} {}", arg0, arg1, arg2), Instruction::Div(arg0, arg1, arg2) => write!(f, "DIV {} {} {}", arg0, arg1, arg2), Instruction::IDiv(arg0, arg1, arg2) => write!(f, "IDIV {} {} {}", arg0, arg1, arg2), @@ -216,7 +219,9 @@ pub enum RuntimeError { #[error("Unable to perform {0:?} operator between {1:?} and {2:?}")] InvalidOperands(BinaryOperator, Value, Value), #[error("Unable to call metamethod {0} for values {1:?} and {2:?}")] - InvalidOperation(String, Value, Value), + InvalidBinop(String, Value, Value), + #[error("Unable to call metamethod {0} for value {1:?}")] + InvalidUnop(String, Value), #[error("Metatable is not a table: {0:?}")] MetatableNotTable(Value), #[error("Metafunction not found: {0}")] @@ -801,14 +806,22 @@ impl ClosureRunner { Instruction::Concat(res, lhs, rhs) => { let (lhs, rhs) = self.lhs_and_rhs(lhs, rhs); - self.set_stack(*res, StackValue::Value(lhs.concat(&rhs)?)); + self.set_stack( + *res, + StackValue::Value(self.result_or_metamethod_binop( + lhs.concat(&rhs), + "__concat", + lhs, + rhs, + )?), + ); } Instruction::Add(res, lhs, rhs) => { let (lhs, rhs) = self.lhs_and_rhs(lhs, rhs); self.set_stack( *res, - StackValue::Value(self.result_or_metamethod( + StackValue::Value(self.result_or_metamethod_binop( lhs.add(&rhs), "__add", lhs, @@ -816,59 +829,190 @@ impl ClosureRunner { )?), ); } + Instruction::Sub(res, lhs, rhs) => { + let (lhs, rhs) = self.lhs_and_rhs(lhs, rhs); + self.set_stack( + *res, + StackValue::Value(self.result_or_metamethod_binop( + rhs.unm().and_then(|rhs| lhs.add(&rhs)), + "__sub", + lhs, + rhs, + )?), + ); + } Instruction::Mult(res, lhs, rhs) => { let (lhs, rhs) = self.lhs_and_rhs(lhs, rhs); - self.set_stack(*res, StackValue::Value(lhs.mult(&rhs)?)); + self.set_stack( + *res, + StackValue::Value(self.result_or_metamethod_binop( + lhs.mult(&rhs), + "__mult", + lhs, + rhs, + )?), + ); } Instruction::Div(res, lhs, rhs) => { let (lhs, rhs) = self.lhs_and_rhs(lhs, rhs); - self.set_stack(*res, StackValue::Value(lhs.div(&rhs)?)); + self.set_stack( + *res, + StackValue::Value(self.result_or_metamethod_binop( + lhs.div(&rhs), + "__div", + lhs, + rhs, + )?), + ); } Instruction::IDiv(res, lhs, rhs) => { let (lhs, rhs) = self.lhs_and_rhs(lhs, rhs); - self.set_stack(*res, StackValue::Value(lhs.idiv(&rhs)?)); + self.set_stack( + *res, + StackValue::Value(self.result_or_metamethod_binop( + lhs.idiv(&rhs), + "__idiv", + lhs, + rhs, + )?), + ); } Instruction::Mod(res, lhs, rhs) => { let (lhs, rhs) = self.lhs_and_rhs(lhs, rhs); - self.set_stack(*res, StackValue::Value(lhs.r#mod(&rhs)?)); + self.set_stack( + *res, + StackValue::Value(self.result_or_metamethod_binop( + lhs.r#mod(&rhs), + "__mod", + lhs, + rhs, + )?), + ); } Instruction::Exp(res, lhs, rhs) => { let (lhs, rhs) = self.lhs_and_rhs(lhs, rhs); - self.set_stack(*res, StackValue::Value(lhs.exp(&rhs)?)); + self.set_stack( + *res, + StackValue::Value(self.result_or_metamethod_binop( + lhs.exp(&rhs), + "__exp", + lhs, + rhs, + )?), + ); } Instruction::BitAnd(res, lhs, rhs) => { let (lhs, rhs) = self.lhs_and_rhs(lhs, rhs); - self.set_stack(*res, StackValue::Value(lhs.band(&rhs)?)); + self.set_stack( + *res, + StackValue::Value(self.result_or_metamethod_binop( + lhs.band(&rhs), + "__band", + lhs, + rhs, + )?), + ); } Instruction::BitOr(res, lhs, rhs) => { let (lhs, rhs) = self.lhs_and_rhs(lhs, rhs); - self.set_stack(*res, StackValue::Value(lhs.bor(&rhs)?)); + self.set_stack( + *res, + StackValue::Value(self.result_or_metamethod_binop( + lhs.bor(&rhs), + "__bor", + lhs, + rhs, + )?), + ); } Instruction::BitXOr(res, lhs, rhs) => { let (lhs, rhs) = self.lhs_and_rhs(lhs, rhs); - self.set_stack(*res, StackValue::Value(lhs.bxor(&rhs)?)); + self.set_stack( + *res, + StackValue::Value(self.result_or_metamethod_binop( + lhs.bxor(&rhs), + "__bxor", + lhs, + rhs, + )?), + ); } Instruction::BitSRight(res, lhs, rhs) => { let (lhs, rhs) = self.lhs_and_rhs(lhs, rhs); - self.set_stack(*res, StackValue::Value(lhs.bsright(&rhs)?)); + self.set_stack( + *res, + StackValue::Value(self.result_or_metamethod_binop( + lhs.bsright(&rhs), + "__shr", + lhs, + rhs, + )?), + ); } Instruction::BitSLeft(res, lhs, rhs) => { let (lhs, rhs) = self.lhs_and_rhs(lhs, rhs); - self.set_stack(*res, StackValue::Value(lhs.bsleft(&rhs)?)); + self.set_stack( + *res, + StackValue::Value(self.result_or_metamethod_binop( + lhs.bsleft(&rhs), + "__shl", + lhs, + rhs, + )?), + ); } Instruction::Equal(res, lhs, rhs) => { let (lhs, rhs) = self.lhs_and_rhs(lhs, rhs); - self.set_stack(*res, StackValue::Value(lhs.eq(&rhs)?)); + match (&lhs, &rhs) { + (Value::Table { metatable, .. }, Value::Table { .. }) => { + self.set_stack( + *res, + StackValue::Value(Value::Boolean(LuaBool( + self.call_metamethod( + metatable, + "__eq", + vec![lhs.clone(), rhs.clone()], + )? + .is_truthy(), + ))), + ); + } + _ => { + self.set_stack(*res, StackValue::Value(lhs.eq(&rhs)?)); + } + } } Instruction::LessThan(res, lhs, rhs) => { let (lhs, rhs) = self.lhs_and_rhs(lhs, rhs); - self.set_stack(*res, StackValue::Value(lhs.lt(&rhs)?)); + self.set_stack( + *res, + StackValue::Value(Value::Boolean(LuaBool( + self.result_or_metamethod_binop( + lhs.lt(&rhs), + "__lt", + lhs.clone(), + rhs.clone(), + )? + .is_truthy(), + ))), + ); } Instruction::LessThanOrEqual(res, lhs, rhs) => { let (lhs, rhs) = self.lhs_and_rhs(lhs, rhs); - self.set_stack(*res, StackValue::Value(lhs.lte(&rhs)?)); + self.set_stack( + *res, + StackValue::Value(Value::Boolean(LuaBool( + self.result_or_metamethod_binop( + lhs.lte(&rhs), + "__le", + lhs.clone(), + rhs.clone(), + )? + .is_truthy(), + ))), + ); } Instruction::Or(res, lhs, rhs) => { @@ -881,28 +1025,41 @@ impl ClosureRunner { } Instruction::Unm(res, reg) => { + let value = self + .stack + .get(reg) + .map(|v| v.borrow().clone()) + .unwrap_or(Value::Nil); self.set_stack( *res, - StackValue::Value( - self.stack - .get(reg) - .map(|v| v.borrow().clone()) - .unwrap_or(Value::Nil) - .unm()?, - ), + StackValue::Value(self.result_or_metamethod_unop( + value.unm(), + "__unm", + value.clone(), + )?), ); } Instruction::Len(res, reg) => { - self.set_stack( - *res, - StackValue::Value( - self.stack - .get(reg) - .map(|v| v.borrow().clone()) - .unwrap_or(Value::Nil) - .len()?, - ), - ); + let value = self + .stack + .get(reg) + .map(|v| v.borrow().clone()) + .unwrap_or(Value::Nil); + match &value { + Value::Table { metatable, .. } => { + match self.call_metamethod(&metatable, "__len", vec![value.clone()]) { + Ok(result) => { + self.set_stack(*res, StackValue::Value(result)); + } + Err(_) => { + self.set_stack(*res, StackValue::Value(value.len()?)); + } + } + } + _ => { + self.set_stack(*res, StackValue::Value(value.len()?)); + } + } } Instruction::Not(res, reg) => { self.set_stack( @@ -972,7 +1129,27 @@ impl ClosureRunner { } } - fn result_or_metamethod( + fn result_or_metamethod_unop( + &self, + value: Result, + metamethod: &str, + param: Value, + ) -> Result { + match value { + Ok(value) => Ok(value), + Err(_) => match ¶m { + Value::Table { metatable, .. } => { + self.call_metamethod(metatable, metamethod, vec![param.clone()]) + } + _ => Err(RuntimeError::InvalidUnop( + metamethod.to_owned(), + param.clone(), + )), + }, + } + } + + fn result_or_metamethod_binop( &self, value: Result, metamethod: &str, @@ -988,11 +1165,7 @@ impl ClosureRunner { (_, Value::Table { metatable, .. }) => { self.call_metamethod(metatable, metamethod, vec![lhs.clone(), rhs.clone()]) } - _ => Err(RuntimeError::InvalidOperation( - metamethod.to_owned(), - lhs, - rhs, - )), + _ => Err(RuntimeError::InvalidBinop(metamethod.to_owned(), lhs, rhs)), }, } }