Add all metamethods
This commit is contained in:
parent
c919f3a6ea
commit
239fc9a0f8
253
src/vm/mod.rs
253
src/vm/mod.rs
@ -5,7 +5,7 @@ use std::{cell::RefCell, collections::HashMap, fmt::Debug, i64, rc::Rc};
|
|||||||
use crate::{
|
use crate::{
|
||||||
CompilationUnit,
|
CompilationUnit,
|
||||||
ast::{BinaryOperator, UnaryOperator},
|
ast::{BinaryOperator, UnaryOperator},
|
||||||
vm::value::{Constant, IndexableValue, LuaInteger, RustFunction, Table, Value},
|
vm::value::{Constant, IndexableValue, LuaBool, LuaInteger, RustFunction, Table, Value},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub mod value;
|
pub mod value;
|
||||||
@ -77,6 +77,8 @@ pub enum Instruction {
|
|||||||
|
|
||||||
/// R(A) := R(B) + R(C)
|
/// R(A) := R(B) + R(C)
|
||||||
Add(u16, u16, u16),
|
Add(u16, u16, u16),
|
||||||
|
/// R(A) := R(B) - R(C)
|
||||||
|
Sub(u16, u16, u16),
|
||||||
/// R(A) := R(B) * R(C)
|
/// R(A) := R(B) * R(C)
|
||||||
Mult(u16, u16, u16),
|
Mult(u16, u16, u16),
|
||||||
/// R(A) := R(B) / R(C)
|
/// 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::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::Mult(arg0, arg1, arg2) => write!(f, "MUL {} {} {}", arg0, arg1, arg2),
|
||||||
Instruction::Div(arg0, arg1, arg2) => write!(f, "DIV {} {} {}", 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),
|
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:?}")]
|
#[error("Unable to perform {0:?} operator between {1:?} and {2:?}")]
|
||||||
InvalidOperands(BinaryOperator, Value, Value),
|
InvalidOperands(BinaryOperator, Value, Value),
|
||||||
#[error("Unable to call metamethod {0} for values {1:?} and {2:?}")]
|
#[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:?}")]
|
#[error("Metatable is not a table: {0:?}")]
|
||||||
MetatableNotTable(Value),
|
MetatableNotTable(Value),
|
||||||
#[error("Metafunction not found: {0}")]
|
#[error("Metafunction not found: {0}")]
|
||||||
@ -801,14 +806,22 @@ impl ClosureRunner {
|
|||||||
|
|
||||||
Instruction::Concat(res, lhs, rhs) => {
|
Instruction::Concat(res, lhs, rhs) => {
|
||||||
let (lhs, rhs) = self.lhs_and_rhs(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) => {
|
Instruction::Add(res, lhs, rhs) => {
|
||||||
let (lhs, rhs) = self.lhs_and_rhs(lhs, rhs);
|
let (lhs, rhs) = self.lhs_and_rhs(lhs, rhs);
|
||||||
self.set_stack(
|
self.set_stack(
|
||||||
*res,
|
*res,
|
||||||
StackValue::Value(self.result_or_metamethod(
|
StackValue::Value(self.result_or_metamethod_binop(
|
||||||
lhs.add(&rhs),
|
lhs.add(&rhs),
|
||||||
"__add",
|
"__add",
|
||||||
lhs,
|
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) => {
|
Instruction::Mult(res, lhs, rhs) => {
|
||||||
let (lhs, rhs) = self.lhs_and_rhs(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) => {
|
Instruction::Div(res, lhs, rhs) => {
|
||||||
let (lhs, rhs) = self.lhs_and_rhs(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) => {
|
Instruction::IDiv(res, lhs, rhs) => {
|
||||||
let (lhs, rhs) = self.lhs_and_rhs(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) => {
|
Instruction::Mod(res, lhs, rhs) => {
|
||||||
let (lhs, rhs) = self.lhs_and_rhs(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) => {
|
Instruction::Exp(res, lhs, rhs) => {
|
||||||
let (lhs, rhs) = self.lhs_and_rhs(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) => {
|
Instruction::BitAnd(res, lhs, rhs) => {
|
||||||
let (lhs, rhs) = self.lhs_and_rhs(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) => {
|
Instruction::BitOr(res, lhs, rhs) => {
|
||||||
let (lhs, rhs) = self.lhs_and_rhs(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) => {
|
Instruction::BitXOr(res, lhs, rhs) => {
|
||||||
let (lhs, rhs) = self.lhs_and_rhs(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) => {
|
Instruction::BitSRight(res, lhs, rhs) => {
|
||||||
let (lhs, rhs) = self.lhs_and_rhs(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) => {
|
Instruction::BitSLeft(res, lhs, rhs) => {
|
||||||
let (lhs, rhs) = self.lhs_and_rhs(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) => {
|
Instruction::Equal(res, lhs, rhs) => {
|
||||||
let (lhs, rhs) = self.lhs_and_rhs(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) => {
|
Instruction::LessThan(res, lhs, rhs) => {
|
||||||
let (lhs, rhs) = self.lhs_and_rhs(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) => {
|
Instruction::LessThanOrEqual(res, lhs, rhs) => {
|
||||||
let (lhs, rhs) = self.lhs_and_rhs(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) => {
|
Instruction::Or(res, lhs, rhs) => {
|
||||||
@ -881,28 +1025,41 @@ impl ClosureRunner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Instruction::Unm(res, reg) => {
|
Instruction::Unm(res, reg) => {
|
||||||
|
let value = self
|
||||||
|
.stack
|
||||||
|
.get(reg)
|
||||||
|
.map(|v| v.borrow().clone())
|
||||||
|
.unwrap_or(Value::Nil);
|
||||||
self.set_stack(
|
self.set_stack(
|
||||||
*res,
|
*res,
|
||||||
StackValue::Value(
|
StackValue::Value(self.result_or_metamethod_unop(
|
||||||
self.stack
|
value.unm(),
|
||||||
.get(reg)
|
"__unm",
|
||||||
.map(|v| v.borrow().clone())
|
value.clone(),
|
||||||
.unwrap_or(Value::Nil)
|
)?),
|
||||||
.unm()?,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Instruction::Len(res, reg) => {
|
Instruction::Len(res, reg) => {
|
||||||
self.set_stack(
|
let value = self
|
||||||
*res,
|
.stack
|
||||||
StackValue::Value(
|
.get(reg)
|
||||||
self.stack
|
.map(|v| v.borrow().clone())
|
||||||
.get(reg)
|
.unwrap_or(Value::Nil);
|
||||||
.map(|v| v.borrow().clone())
|
match &value {
|
||||||
.unwrap_or(Value::Nil)
|
Value::Table { metatable, .. } => {
|
||||||
.len()?,
|
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) => {
|
Instruction::Not(res, reg) => {
|
||||||
self.set_stack(
|
self.set_stack(
|
||||||
@ -972,7 +1129,27 @@ impl ClosureRunner {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn result_or_metamethod(
|
fn result_or_metamethod_unop(
|
||||||
|
&self,
|
||||||
|
value: Result<Value, RuntimeError>,
|
||||||
|
metamethod: &str,
|
||||||
|
param: Value,
|
||||||
|
) -> Result<Value, RuntimeError> {
|
||||||
|
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,
|
&self,
|
||||||
value: Result<Value, RuntimeError>,
|
value: Result<Value, RuntimeError>,
|
||||||
metamethod: &str,
|
metamethod: &str,
|
||||||
@ -988,11 +1165,7 @@ impl ClosureRunner {
|
|||||||
(_, Value::Table { metatable, .. }) => {
|
(_, Value::Table { metatable, .. }) => {
|
||||||
self.call_metamethod(metatable, metamethod, vec![lhs.clone(), rhs.clone()])
|
self.call_metamethod(metatable, metamethod, vec![lhs.clone(), rhs.clone()])
|
||||||
}
|
}
|
||||||
_ => Err(RuntimeError::InvalidOperation(
|
_ => Err(RuntimeError::InvalidBinop(metamethod.to_owned(), lhs, rhs)),
|
||||||
metamethod.to_owned(),
|
|
||||||
lhs,
|
|
||||||
rhs,
|
|
||||||
)),
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user