Add calling metamethods

This commit is contained in:
Sofia 2026-03-20 20:45:51 +02:00
parent 232a729fe2
commit 5c4dbacaa7

View File

@ -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<Value, RuntimeError>,
metamethod: &str,
lhs: Value,
rhs: Value,
) -> Result<Value, RuntimeError> {
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<Value>,
) -> Result<Value, RuntimeError> {
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