diff --git a/src/vm/mod.rs b/src/vm/mod.rs index 7a3a1cc..3a55f7b 100644 --- a/src/vm/mod.rs +++ b/src/vm/mod.rs @@ -182,6 +182,14 @@ impl Debug for Instruction { 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), + #[error("Metatable is not a table: {0:?}")] + MetatableNotTable(Value), + #[error("Metafunction not found: {0}")] + MetafunctionNotFound(String), + #[error("Metafunction is not callable: {0:?}")] + MetafunctionNotCallable(Value), #[error("Unable to perform {0:?} operator to {1:?}")] InvalidOperand(UnaryOperator, Value), #[error("Tried calling a non-function: {0:?}")] @@ -751,7 +759,15 @@ impl ClosureRunner { Instruction::Add(res, lhs, rhs) => { let (lhs, rhs) = self.lhs_and_rhs(lhs, rhs); - self.set_stack(*res, StackValue::Value(lhs.add(&rhs)?)); + self.set_stack( + *res, + StackValue::Value(self.result_or_metamethod( + lhs.add(&rhs), + "__add", + lhs, + rhs, + )?), + ); } Instruction::Mult(res, lhs, rhs) => { let (lhs, rhs) = self.lhs_and_rhs(lhs, rhs); @@ -909,6 +925,68 @@ impl ClosureRunner { } } + fn result_or_metamethod( + &self, + value: Result, + metamethod: &str, + lhs: Value, + rhs: Value, + ) -> Result { + match value { + Ok(value) => Ok(value), + Err(_) => match (&lhs, &rhs) { + (Value::Table { metatable, .. }, _) => { + self.call_metamethod(*metatable.clone(), metamethod, vec![lhs, rhs]) + } + (_, Value::Table { metatable, .. }) => { + self.call_metamethod(*metatable.clone(), metamethod, vec![rhs, lhs]) + } + _ => Err(RuntimeError::InvalidOperation( + metamethod.to_owned(), + lhs, + rhs, + )), + }, + } + } + + fn call_metamethod( + &self, + metatable: Value, + metamethod: &str, + params: Vec, + ) -> Result { + match metatable { + Value::Table { contents, .. } => { + if let Some(value) = contents + .borrow() + .get(&IndexableValue::String(metamethod.to_owned())) + { + match value { + Value::RustFunction(function) => { + let result = function.borrow_mut().execute(params)?; + Ok(result.into_iter().next().unwrap()) + } + Value::Function(closure) => { + let mut runnable = closure.run(params); + #[allow(unused_assignments)] + let mut return_value = None; + while { + return_value = runnable.next()?; + return_value.is_none() + } {} + Ok(return_value.unwrap().into_iter().next().unwrap()) + } + _ => Err(RuntimeError::MetafunctionNotCallable(value.clone())), + } + } else { + Err(RuntimeError::MetafunctionNotFound(metamethod.to_owned())) + } + } + _ => Err(RuntimeError::MetatableNotTable(metatable)), + } + } + fn lhs_and_rhs(&self, lhs: &u16, rhs: &u16) -> (Value, Value) { ( self.stack