Add calling metamethods
This commit is contained in:
parent
232a729fe2
commit
5c4dbacaa7
@ -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
|
||||
|
||||
Loading…
Reference in New Issue
Block a user