1180 lines
44 KiB
Rust
1180 lines
44 KiB
Rust
use thiserror::Error;
|
|
|
|
use std::{cell::RefCell, collections::HashMap, fmt::Debug, hash::Hash, rc::Rc};
|
|
|
|
use crate::{
|
|
CompilationUnit,
|
|
ast::{BinaryOperator, UnaryOperator},
|
|
};
|
|
|
|
#[derive(Clone, Hash, PartialEq, Eq, Copy, PartialOrd, Ord)]
|
|
pub struct VMFloat(u64);
|
|
|
|
impl VMFloat {
|
|
pub fn lua_number(&self) -> LuaFloat {
|
|
LuaFloat(f64::from_bits(self.0))
|
|
}
|
|
}
|
|
|
|
impl Debug for VMFloat {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
self.lua_number().fmt(f)
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Copy)]
|
|
pub struct LuaFloat(pub f64);
|
|
|
|
impl LuaFloat {
|
|
pub fn vm_number(&self) -> VMFloat {
|
|
VMFloat(f64::to_bits(self.0))
|
|
}
|
|
}
|
|
|
|
impl Debug for LuaFloat {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
self.0.fmt(f)
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
|
pub struct LuaInteger(pub i64);
|
|
|
|
impl Debug for LuaInteger {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
self.0.fmt(f)
|
|
}
|
|
}
|
|
|
|
impl From<LuaInteger> for LuaFloat {
|
|
fn from(value: LuaInteger) -> Self {
|
|
LuaFloat(value.0 as f64)
|
|
}
|
|
}
|
|
|
|
impl From<&LuaInteger> for LuaFloat {
|
|
fn from(value: &LuaInteger) -> Self {
|
|
LuaFloat(value.0 as f64)
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
|
pub struct LuaBool(pub bool);
|
|
|
|
impl Debug for LuaBool {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
self.0.fmt(f)
|
|
}
|
|
}
|
|
|
|
impl From<LuaBool> for LuaInteger {
|
|
fn from(value: LuaBool) -> Self {
|
|
LuaInteger(value.0 as i64)
|
|
}
|
|
}
|
|
|
|
impl From<&LuaBool> for LuaInteger {
|
|
fn from(value: &LuaBool) -> Self {
|
|
LuaInteger(value.0 as i64)
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Hash, PartialEq, Eq)]
|
|
pub enum Constant {
|
|
String(String),
|
|
Float(VMFloat),
|
|
Integer(LuaInteger),
|
|
Bool(LuaBool),
|
|
Nil,
|
|
}
|
|
|
|
impl From<&str> for Constant {
|
|
fn from(value: &str) -> Self {
|
|
Constant::String(value.to_owned())
|
|
}
|
|
}
|
|
|
|
impl Debug for Constant {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
match self {
|
|
Self::String(arg0) => f.debug_tuple("String").field(arg0).finish(),
|
|
Self::Float(arg0) => f.debug_tuple("Float").field(&arg0.lua_number()).finish(),
|
|
Self::Integer(arg0) => f.debug_tuple("Integer").field(arg0).finish(),
|
|
Self::Bool(arg0) => f.debug_tuple("Boolean").field(arg0).finish(),
|
|
Self::Nil => f.debug_tuple("Nil").finish(),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Copy)]
|
|
pub enum Instruction {
|
|
/// R(A) := R(B)
|
|
Move(u16, u16),
|
|
/// R(A) ... R(A + func_register - top) := previous function return values
|
|
MoveRetValues(u16),
|
|
/// R(A) := K(Bx)
|
|
LoadK(u16, u32),
|
|
/// R(A), ..., R(B) := nil
|
|
LoadNil(u16, u16),
|
|
/// G[K(Bx)] := R(A)
|
|
SetGlobal(u16, u32),
|
|
/// R(A) := G[K(Bx)]
|
|
GetGlobal(u16, u32),
|
|
/// R(A) := U[B]
|
|
GetUpVal(u16, u16),
|
|
/// U[B] := R(A)
|
|
SetUpVal(u16, u16),
|
|
/// R(A)[R(B)] := R(C)
|
|
SetTable(u16, u16, u16),
|
|
/// R(A)[B...] := all values returned from previous function
|
|
SetList(u16, u32),
|
|
/// R(A) := R(B)[R(C)]
|
|
GetTable(u16, u16, u16),
|
|
/// R(A) := {}
|
|
NewTable(u16),
|
|
/// R(A) := R(B) + R(C)
|
|
Add(u16, u16, u16),
|
|
/// R(A) := -R(B)
|
|
Unm(u16, u16),
|
|
/// R(A) := #R(B) (length operator)
|
|
Len(u16, u16),
|
|
/// R(A) := R(B) == R(C)
|
|
Equal(u16, u16, u16),
|
|
/// R(A) := R(B) < R(C)
|
|
LessThan(u16, u16, u16),
|
|
/// R(A) := R(B) <= R(C)
|
|
LessThanOrEqual(u16, u16, u16),
|
|
/// R(A) := R(B) or R(C)
|
|
Or(u16, u16, u16),
|
|
/// R(A) := R(B) and R(C)
|
|
And(u16, u16, u16),
|
|
/// PC += sAx
|
|
Jmp(i32),
|
|
/// PC = Ax
|
|
GoTo(u32),
|
|
/// if (R(B) <=> C) then R(A) := R(B) else PC++
|
|
Test(u16, u16, u16),
|
|
/// if R(C) >= 0
|
|
/// then if R(A) < R(B)
|
|
/// PC ++
|
|
/// otherwise if R(A) > R(B)
|
|
/// PC ++
|
|
ForTest(u16, u16, u16),
|
|
/// [func] [params.len()] [ret_regs.len()]
|
|
/// R(A), ... R(A+C-2) := R(A)(R(A+1), ... R(A+B-1))
|
|
Call(u16, u16, u16),
|
|
/// return R(A), ... , R(B)
|
|
Return(u16, u16),
|
|
/// close stack variables up to R(A)
|
|
Close(u16),
|
|
/// R(A) := closure(KPROTO[Bx], R(A), ..., R(A+n))
|
|
Closure(u16, u32),
|
|
/// R(A), ... , R(A+B-2) := varargs
|
|
Vararg(u16, u16),
|
|
}
|
|
|
|
impl Debug for Instruction {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
match self {
|
|
Instruction::Move(arg0, arg1) => write!(f, "MOVE {} {}", arg0, arg1),
|
|
Instruction::MoveRetValues(arg0) => write!(f, "MOVERETVALS {}", arg0),
|
|
Instruction::LoadK(arg0, arg1) => write!(f, "LOADK {} {}", arg0, arg1),
|
|
Instruction::SetGlobal(arg0, arg1) => write!(f, "SETGLOBAL {} {}", arg0, arg1),
|
|
Instruction::GetGlobal(arg0, arg1) => write!(f, "GETGLOBAL {} {}", arg0, arg1),
|
|
Instruction::GetUpVal(arg0, arg1) => write!(f, "GETUPVAL {} {}", arg0, arg1),
|
|
Instruction::SetUpVal(arg0, arg1) => write!(f, "SETUPVAL {} {}", arg0, arg1),
|
|
Instruction::SetTable(arg0, arg1, arg2) => {
|
|
write!(f, "SETTABLE {} {} {}", arg0, arg1, arg2)
|
|
}
|
|
Instruction::GetTable(arg0, arg1, arg2) => {
|
|
write!(f, "GETTABLE {} {} {}", arg0, arg1, arg2)
|
|
}
|
|
Instruction::SetList(arg0, arg1) => write!(f, "SETLIST {} {}", arg0, arg1),
|
|
Instruction::NewTable(arg0) => write!(f, "NEWTABLE {}", arg0),
|
|
Instruction::Jmp(arg0) => write!(f, "JMP {}", arg0),
|
|
Instruction::GoTo(arg0) => write!(f, "GOTO {}", arg0),
|
|
Instruction::Test(arg0, arg1, arg2) => write!(f, "TEST {} {} {}", arg0, arg1, arg2),
|
|
Instruction::ForTest(arg0, arg1, arg2) => {
|
|
write!(f, "FORTEST {} {} {}", arg0, arg1, arg2)
|
|
}
|
|
Instruction::Call(arg0, arg1, arg2) => write!(f, "CALL {} {} {}", arg0, arg1, arg2),
|
|
Instruction::Close(arg0) => write!(f, "CLOSE {}", arg0),
|
|
Instruction::Closure(arg0, arg1) => write!(f, "CLOSURE {} {}", arg0, arg1),
|
|
Instruction::Return(arg0, arg1) => write!(f, "RETURN {} {}", arg0, arg1),
|
|
Instruction::Equal(arg0, arg1, arg2) => write!(f, "EQ {} {} {}", arg0, arg1, arg2),
|
|
Instruction::LessThan(arg0, arg1, arg2) => write!(f, "LT {} {} {}", arg0, arg1, arg2),
|
|
Instruction::LessThanOrEqual(arg0, arg1, arg2) => {
|
|
write!(f, "LE {} {} {}", arg0, arg1, arg2)
|
|
}
|
|
Instruction::Add(arg0, arg1, arg2) => write!(f, "ADD {} {} {}", arg0, arg1, arg2),
|
|
Instruction::LoadNil(arg0, arg1) => write!(f, "LOADNIL {} {}", arg0, arg1),
|
|
Instruction::Unm(arg0, arg1) => write!(f, "UNM {} {}", arg0, arg1),
|
|
Instruction::Len(arg0, arg1) => write!(f, "LEN {} {}", arg0, arg1),
|
|
Instruction::Or(arg0, arg1, arg2) => write!(f, "OR {} {} {}", arg0, arg1, arg2),
|
|
Instruction::And(arg0, arg1, arg2) => write!(f, "AND {} {} {}", arg0, arg1, arg2),
|
|
Instruction::Vararg(arg0, arg1) => write!(f, "VARARG {} {}", arg0, arg1),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Error, Debug)]
|
|
pub enum RuntimeError {
|
|
#[error("Unable to perform {0:?} operator between {1:?} and {2:?}")]
|
|
InvalidOperands(BinaryOperator, Value, Value),
|
|
#[error("Unable to perform {0:?} operator to {1:?}")]
|
|
InvalidOperand(UnaryOperator, Value),
|
|
#[error("Tried calling a non-function: {0:?}")]
|
|
TriedCallingNonFunction(Value),
|
|
#[error("Global not found: {0:?}")]
|
|
GlobalNotFound(Option<Constant>),
|
|
#[error("Unable to index tables with {0:?}")]
|
|
InvalidTableIndex(Value),
|
|
#[error("Value is not a table: {0:?}")]
|
|
NotTable(Value),
|
|
#[error("Value is not coercable to a float: {0:?}")]
|
|
NotFloatable(Value),
|
|
#[error("Value does not have a length: {0:?}")]
|
|
NotLengthable(Value),
|
|
#[error("{0}")]
|
|
Custom(String),
|
|
}
|
|
|
|
#[derive(Debug, Clone, Default)]
|
|
pub(crate) struct Environment {
|
|
pub globals: HashMap<Constant, Rc<RefCell<Value>>>,
|
|
}
|
|
|
|
impl Environment {
|
|
fn get_global(&mut self, key: &Constant) -> Option<StackValue> {
|
|
let value = self.globals.get_mut(key)?;
|
|
Some(match &*value.borrow() {
|
|
_ => StackValue::Value(value.borrow().clone()),
|
|
})
|
|
}
|
|
|
|
fn set_global(&mut self, key: Constant, value: StackValue) {
|
|
if let Some(existing) = self.globals.get_mut(&key) {
|
|
match value {
|
|
StackValue::Value(value) => {
|
|
*existing.borrow_mut() = value;
|
|
}
|
|
}
|
|
} else {
|
|
match value {
|
|
StackValue::Value(value) => {
|
|
self.globals.insert(key, Rc::new(RefCell::new(value)));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub enum Value {
|
|
String(String),
|
|
Float(VMFloat),
|
|
Integer(LuaInteger),
|
|
Boolean(LuaBool),
|
|
RustFunction(Rc<RefCell<dyn RustFunction>>),
|
|
Function(Closure),
|
|
Nil,
|
|
Table(Rc<RefCell<HashMap<IndexableValue, Value>>>),
|
|
}
|
|
|
|
impl Value {
|
|
pub fn as_indexable(self) -> Result<IndexableValue, RuntimeError> {
|
|
match self {
|
|
Value::String(value) => Ok(IndexableValue::String(value)),
|
|
Value::Float(value) => Ok(IndexableValue::Float(value)),
|
|
Value::Integer(value) => Ok(IndexableValue::Integer(value)),
|
|
Value::Boolean(value) => Ok(IndexableValue::Bool(value)),
|
|
Value::RustFunction(value) => {
|
|
Ok(IndexableValue::RustFunction(value.borrow().as_indexable()))
|
|
}
|
|
Value::Function(closure) => Ok(IndexableValue::Function(closure.prototype)),
|
|
Value::Nil => Err(RuntimeError::InvalidTableIndex(self)),
|
|
Value::Table(_) => Err(RuntimeError::InvalidTableIndex(self)),
|
|
}
|
|
}
|
|
|
|
pub fn as_float(&self) -> Result<LuaFloat, RuntimeError> {
|
|
match self {
|
|
Value::Float(vmfloat) => Ok(vmfloat.lua_number()),
|
|
Value::Integer(lua_integer) => Ok(lua_integer.into()),
|
|
Value::Boolean(lua_boolean) => Ok(LuaFloat(lua_boolean.0 as u64 as f64)),
|
|
_ => Err(RuntimeError::NotFloatable(self.clone())),
|
|
}
|
|
}
|
|
|
|
pub fn as_integer(&self) -> Result<LuaInteger, RuntimeError> {
|
|
match self {
|
|
Value::Integer(lua_integer) => Ok(*lua_integer),
|
|
Value::Boolean(lua_boolean) => Ok(LuaInteger(lua_boolean.0 as i64)),
|
|
_ => Err(RuntimeError::NotFloatable(self.clone())),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, PartialEq, Eq, Hash, Clone, Ord, PartialOrd)]
|
|
pub enum IndexableValue {
|
|
String(String),
|
|
Float(VMFloat),
|
|
Integer(LuaInteger),
|
|
Bool(LuaBool),
|
|
RustFunction(String),
|
|
Function(u32),
|
|
}
|
|
|
|
impl Value {
|
|
pub fn add(&self, other: &Value) -> Result<Value, RuntimeError> {
|
|
match (self, other) {
|
|
(Value::Integer(_) | Value::Boolean(_), Value::Integer(_) | Value::Boolean(_)) => {
|
|
let res = LuaInteger(self.as_integer()?.0 + other.as_integer()?.0);
|
|
Ok(Value::Integer(res))
|
|
}
|
|
(
|
|
Value::Float(_) | Value::Integer(_) | Value::Boolean(_),
|
|
Value::Float(_) | Value::Integer(_) | Value::Boolean(_),
|
|
) => {
|
|
let res = LuaFloat(self.as_float()?.0 + other.as_float()?.0);
|
|
Ok(Value::Float(res.vm_number()))
|
|
}
|
|
_ => Err(RuntimeError::InvalidOperands(
|
|
BinaryOperator::Add,
|
|
self.clone(),
|
|
other.clone(),
|
|
)),
|
|
}
|
|
}
|
|
|
|
pub fn eq(&self, other: &Value) -> Result<Value, RuntimeError> {
|
|
match (self, other) {
|
|
(Value::Integer(lhs), Value::Integer(rhs)) => {
|
|
Ok(Value::Boolean(LuaBool(lhs.0 == rhs.0)))
|
|
}
|
|
(Value::Float(_) | Value::Integer(_), Value::Float(_) | Value::Integer(_)) => {
|
|
let res = LuaBool(self.as_float()?.0 == other.as_float()?.0);
|
|
Ok(Value::Boolean(res))
|
|
}
|
|
_ => Err(RuntimeError::InvalidOperands(
|
|
BinaryOperator::Equal,
|
|
self.clone(),
|
|
other.clone(),
|
|
)),
|
|
}
|
|
}
|
|
|
|
pub fn lt(&self, other: &Value) -> Result<Value, RuntimeError> {
|
|
match (self, other) {
|
|
(Value::Integer(lhs), Value::Integer(rhs)) => {
|
|
Ok(Value::Boolean(LuaBool(lhs.0 < rhs.0)))
|
|
}
|
|
(Value::Float(_) | Value::Integer(_), Value::Float(_) | Value::Integer(_)) => {
|
|
let res = LuaBool(self.as_float()?.0 < other.as_float()?.0);
|
|
Ok(Value::Boolean(res))
|
|
}
|
|
_ => Err(RuntimeError::InvalidOperands(
|
|
BinaryOperator::LessThan,
|
|
self.clone(),
|
|
other.clone(),
|
|
)),
|
|
}
|
|
}
|
|
|
|
pub fn lte(&self, other: &Value) -> Result<Value, RuntimeError> {
|
|
match (self, other) {
|
|
(Value::Integer(lhs), Value::Integer(rhs)) => {
|
|
Ok(Value::Boolean(LuaBool(lhs.0 <= rhs.0)))
|
|
}
|
|
(Value::Float(_) | Value::Integer(_), Value::Float(_) | Value::Integer(_)) => {
|
|
let res = LuaBool(self.as_float()?.0 <= other.as_float()?.0);
|
|
Ok(Value::Boolean(res))
|
|
}
|
|
_ => Err(RuntimeError::InvalidOperands(
|
|
BinaryOperator::LessThanOrEqual,
|
|
self.clone(),
|
|
other.clone(),
|
|
)),
|
|
}
|
|
}
|
|
|
|
pub fn unm(&self) -> Result<Value, RuntimeError> {
|
|
match self {
|
|
Value::Integer(lhs) => {
|
|
let res = LuaInteger(-lhs.0);
|
|
Ok(Value::Integer(res))
|
|
}
|
|
Value::Float(lhs) => {
|
|
let res = LuaFloat(-lhs.lua_number().0);
|
|
Ok(Value::Float(res.vm_number()))
|
|
}
|
|
Value::Boolean(val) => {
|
|
let res = LuaBool(!val.0);
|
|
Ok(Value::Boolean(res))
|
|
}
|
|
_ => Err(RuntimeError::InvalidOperand(
|
|
UnaryOperator::Negation,
|
|
self.clone(),
|
|
)),
|
|
}
|
|
}
|
|
|
|
pub fn len(&self) -> Result<Value, RuntimeError> {
|
|
match self {
|
|
Value::String(value) => Ok(Self::Integer(LuaInteger(value.len() as i64))),
|
|
Value::Table(table) => {
|
|
let table = table.borrow();
|
|
let mut int_indexes = table
|
|
.keys()
|
|
.filter(|v| match v {
|
|
IndexableValue::Integer(_) => true,
|
|
_ => false,
|
|
})
|
|
.map(|v| match v {
|
|
IndexableValue::Integer(int) => int.0,
|
|
_ => panic!(),
|
|
})
|
|
.collect::<Vec<_>>();
|
|
int_indexes.push(0);
|
|
int_indexes.sort();
|
|
|
|
for idx in int_indexes {
|
|
let idx_value = table.get(&IndexableValue::Integer(LuaInteger(idx)));
|
|
let idx_value_after = idx
|
|
.checked_add_unsigned(1)
|
|
.and_then(|i| table.get(&IndexableValue::Integer(LuaInteger(i))));
|
|
|
|
// Directly from https://www.lua.org/manual/5.5/manual.html#3.4.7
|
|
if (idx == 0 || idx_value.is_some()) && idx_value_after.is_none() {
|
|
return Ok(Self::Integer(LuaInteger(idx)));
|
|
}
|
|
}
|
|
panic!()
|
|
}
|
|
Value::Float(_) => Err(RuntimeError::NotLengthable(self.clone())),
|
|
Value::Integer(_) => Err(RuntimeError::NotLengthable(self.clone())),
|
|
Value::Boolean(_) => Err(RuntimeError::NotLengthable(self.clone())),
|
|
Value::RustFunction(_) => Err(RuntimeError::NotLengthable(self.clone())),
|
|
Value::Function(_) => Err(RuntimeError::NotLengthable(self.clone())),
|
|
Value::Nil => Err(RuntimeError::NotLengthable(self.clone())),
|
|
}
|
|
}
|
|
|
|
pub fn and(&self, other: &Value) -> Result<Value, RuntimeError> {
|
|
if self.is_truthy() {
|
|
Ok(self.clone())
|
|
} else {
|
|
Ok(other.clone())
|
|
}
|
|
}
|
|
|
|
pub fn or(&self, other: &Value) -> Result<Value, RuntimeError> {
|
|
if self.is_truthy() {
|
|
Ok(self.clone())
|
|
} else {
|
|
Ok(other.clone())
|
|
}
|
|
}
|
|
|
|
pub fn is_truthy(&self) -> bool {
|
|
match self {
|
|
Value::String(_) => true,
|
|
Value::Float(_) => true,
|
|
Value::Integer(_) => true,
|
|
Value::Boolean(lua_bool) => lua_bool.0,
|
|
Value::RustFunction(_) => true,
|
|
Value::Function(_) => true,
|
|
Value::Nil => false,
|
|
Value::Table(_) => true,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Debug for Value {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
match self {
|
|
Value::Float(arg0) => f.debug_tuple("Float").field(&arg0.lua_number()).finish(),
|
|
Value::Integer(arg0) => f.debug_tuple("Integer").field(arg0).finish(),
|
|
Value::Boolean(arg0) => f.debug_tuple("Boolean").field(arg0).finish(),
|
|
Value::String(value) => f.debug_tuple("String").field(value).finish(),
|
|
Value::RustFunction(arg0) => f.debug_tuple("RustFunction").field(arg0).finish(),
|
|
Value::Function(closure) => f
|
|
.debug_tuple(&format!("Function({})", closure.prototype))
|
|
.finish(),
|
|
Value::Nil => write!(f, "Nil"),
|
|
Value::Table(hash_map) => {
|
|
let mut table = f.debug_struct("Table");
|
|
for (key, value) in hash_map.borrow().iter() {
|
|
table.field(&format!("{:?}", key), value);
|
|
}
|
|
table.finish()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub trait RustFunction: Debug {
|
|
fn execute(&self, parameters: Vec<Value>) -> Result<Vec<Value>, RuntimeError>;
|
|
fn as_indexable(&self) -> String;
|
|
}
|
|
|
|
impl<T: RustFunction + 'static> From<T> for Value {
|
|
fn from(value: T) -> Self {
|
|
Self::RustFunction(Rc::new(RefCell::new(value)))
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Default)]
|
|
pub struct Prototype {
|
|
pub instructions: Vec<Instruction>,
|
|
pub parameters: usize,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Default)]
|
|
pub struct VirtualMachine {
|
|
pub(super) environment: Rc<RefCell<Environment>>,
|
|
pub(super) constants: Vec<Constant>,
|
|
pub(super) prototypes: HashMap<u32, Prototype>,
|
|
pub(super) proto_counter: u32,
|
|
}
|
|
|
|
impl VirtualMachine {
|
|
pub fn new_prototype(&mut self, instructions: Prototype) -> u32 {
|
|
let proto_id = self.proto_counter;
|
|
self.proto_counter += 1;
|
|
self.prototypes.insert(proto_id, instructions);
|
|
proto_id
|
|
}
|
|
}
|
|
|
|
impl VirtualMachine {
|
|
pub(crate) fn create_closure(&self, prototype: u32) -> Closure {
|
|
Closure {
|
|
vm: self.clone(),
|
|
prototype,
|
|
environment: self.environment.clone(),
|
|
upvalues: HashMap::new(),
|
|
}
|
|
}
|
|
|
|
pub fn set_global(&mut self, key: Constant, value: Value) {
|
|
self.environment
|
|
.borrow_mut()
|
|
.set_global(key, StackValue::Value(value));
|
|
}
|
|
|
|
pub fn get_globals(&self) -> HashMap<Constant, Value> {
|
|
let mut values = HashMap::new();
|
|
for (key, value) in &self.environment.borrow().globals {
|
|
values.insert(key.clone(), value.borrow().clone());
|
|
}
|
|
|
|
values
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct Closure {
|
|
vm: VirtualMachine,
|
|
prototype: u32,
|
|
environment: Rc<RefCell<Environment>>,
|
|
upvalues: HashMap<u16, Rc<RefCell<Value>>>,
|
|
}
|
|
|
|
impl Closure {
|
|
pub fn run(&self, params: Vec<Value>) -> ClosureRunner {
|
|
let mut stack = HashMap::new();
|
|
for (i, param) in params.iter().enumerate() {
|
|
stack.insert(i as u16, Rc::new(RefCell::new(param.clone())));
|
|
}
|
|
ClosureRunner {
|
|
closure: self.clone(),
|
|
program_counter: 0,
|
|
stack,
|
|
inner: None,
|
|
function_register: 0,
|
|
return_registers: Vec::new(),
|
|
top: 0,
|
|
parameters: params,
|
|
}
|
|
}
|
|
|
|
fn get_upvalue(&mut self, idx: u16) -> StackValue {
|
|
let value = self.upvalues.get(&idx);
|
|
if let Some(value) = value {
|
|
match &*value.borrow() {
|
|
_ => StackValue::Value(value.borrow().clone()),
|
|
}
|
|
} else {
|
|
StackValue::Value(Value::Nil)
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct ClosureRunner {
|
|
closure: Closure,
|
|
program_counter: usize,
|
|
stack: HashMap<u16, Rc<RefCell<Value>>>,
|
|
inner: Option<Box<ClosureRunner>>,
|
|
function_register: u16,
|
|
return_registers: Vec<u16>,
|
|
top: u16,
|
|
parameters: Vec<Value>,
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
enum StackValue {
|
|
Value(Value),
|
|
}
|
|
|
|
impl ClosureRunner {
|
|
fn set_stack(&mut self, idx: u16, value: StackValue) {
|
|
if let Some(stack_slot) = self.stack.get_mut(&idx) {
|
|
match value {
|
|
StackValue::Value(value) => {
|
|
*stack_slot.borrow_mut() = value;
|
|
}
|
|
}
|
|
} else {
|
|
match value {
|
|
StackValue::Value(value) => {
|
|
self.stack.insert(idx, Rc::new(RefCell::new(value)));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn get_stack(&mut self, idx: u16) -> StackValue {
|
|
let value = self.stack.get(&idx);
|
|
if let Some(value) = value {
|
|
match &*value.borrow() {
|
|
_ => StackValue::Value(value.borrow().clone()),
|
|
}
|
|
} else {
|
|
StackValue::Value(Value::Nil)
|
|
}
|
|
}
|
|
|
|
fn close_upvalues(&self) -> HashMap<u16, Rc<RefCell<Value>>> {
|
|
let highest_upvalue = self
|
|
.closure
|
|
.upvalues
|
|
.iter()
|
|
.map(|(v, _)| *v)
|
|
.max()
|
|
.unwrap_or(0);
|
|
|
|
let mut upvalues = self.closure.upvalues.clone();
|
|
for (reg, value) in &self.stack {
|
|
upvalues.insert(reg + highest_upvalue + 1, value.clone());
|
|
}
|
|
upvalues
|
|
}
|
|
|
|
pub fn execute(&mut self, unit: &CompilationUnit) -> ClosureRunner {
|
|
let mut vm = self.closure.vm.clone();
|
|
vm.constants = unit.constants.clone();
|
|
let proto_id = vm.new_prototype(Prototype {
|
|
instructions: unit.instructions.clone(),
|
|
parameters: 0,
|
|
});
|
|
for prototype in &unit.state.prototypes {
|
|
vm.new_prototype(prototype.clone());
|
|
}
|
|
|
|
let closure = Closure {
|
|
vm,
|
|
prototype: proto_id,
|
|
environment: self.closure.environment.clone(),
|
|
upvalues: self.close_upvalues(),
|
|
};
|
|
closure.run(Vec::new())
|
|
}
|
|
|
|
pub fn next(&mut self) -> Result<Option<Vec<Value>>, RuntimeError> {
|
|
if let Some(inner) = &mut self.inner {
|
|
match inner.next() {
|
|
Ok(ret_values) => {
|
|
if let Some(ret_values) = ret_values {
|
|
self.inner = None;
|
|
if self.return_registers.len() == 0 {
|
|
for (i, value) in ret_values.iter().enumerate() {
|
|
self.stack.insert(
|
|
self.function_register + i as u16 + 1,
|
|
Rc::new(RefCell::new(value.clone())),
|
|
);
|
|
}
|
|
self.top = self.function_register + ret_values.len() as u16;
|
|
}
|
|
for (i, reg) in self.return_registers.iter().enumerate() {
|
|
self.stack.insert(
|
|
*reg,
|
|
Rc::new(RefCell::new(
|
|
ret_values.get(i).cloned().unwrap_or(Value::Nil),
|
|
)),
|
|
);
|
|
}
|
|
} else {
|
|
return Ok(None);
|
|
}
|
|
}
|
|
Err(e) => return Err(e),
|
|
}
|
|
}
|
|
|
|
let prototype = self
|
|
.closure
|
|
.vm
|
|
.prototypes
|
|
.get(&self.closure.prototype)
|
|
.unwrap()
|
|
.clone();
|
|
let constants = self.closure.vm.constants.clone();
|
|
|
|
if let Some(instr) = prototype.instructions.get(self.program_counter) {
|
|
match instr {
|
|
Instruction::Move(a, b) => {
|
|
let b = self.get_stack(*b);
|
|
self.set_stack(*a, b);
|
|
}
|
|
Instruction::MoveRetValues(res) => {
|
|
let length = self.top - self.function_register;
|
|
|
|
let mut values = Vec::new();
|
|
for i in 0..length {
|
|
let b = self.get_stack(self.function_register + i + 1);
|
|
values.push(b);
|
|
}
|
|
for (i, val) in values.into_iter().enumerate() {
|
|
self.set_stack(*res + i as u16, val);
|
|
}
|
|
|
|
self.top = *res + length;
|
|
}
|
|
Instruction::LoadK(reg, constant) => {
|
|
self.set_stack(
|
|
*reg,
|
|
StackValue::Value(match constants.get(*constant as usize).unwrap() {
|
|
Constant::String(value) => Value::String(value.clone()),
|
|
Constant::Float(value) => Value::Float(*value),
|
|
Constant::Integer(value) => Value::Integer(*value),
|
|
Constant::Bool(lua_bool) => Value::Boolean(*lua_bool),
|
|
Constant::Nil => Value::Nil,
|
|
}),
|
|
);
|
|
}
|
|
Instruction::LoadNil(from_reg, to_reg) => {
|
|
for i in *from_reg..=*to_reg {
|
|
self.stack.insert(i, Rc::new(RefCell::new(Value::Nil)));
|
|
}
|
|
}
|
|
Instruction::SetGlobal(reg, global) => {
|
|
let value = self.get_stack(*reg);
|
|
self.closure
|
|
.environment
|
|
.borrow_mut()
|
|
.set_global(constants.get(*global as usize).unwrap().clone(), value);
|
|
}
|
|
Instruction::GetGlobal(reg, global) => {
|
|
let glob = self
|
|
.closure
|
|
.environment
|
|
.borrow_mut()
|
|
.get_global(constants.get(*global as usize).unwrap());
|
|
if let Some(global) = glob {
|
|
self.set_stack(*reg, global);
|
|
} else {
|
|
return Err(RuntimeError::GlobalNotFound(
|
|
constants.get(*global as usize).cloned(),
|
|
));
|
|
}
|
|
}
|
|
Instruction::GetUpVal(reg, upvalreg) => {
|
|
let upvalue = self.closure.get_upvalue(*upvalreg);
|
|
self.set_stack(*reg, upvalue);
|
|
}
|
|
Instruction::SetUpVal(upvalreg, reg) => {
|
|
*self.closure.upvalues.get(upvalreg).unwrap().borrow_mut() = self
|
|
.stack
|
|
.get(reg)
|
|
.map(|v| v.borrow().clone())
|
|
.unwrap_or(Value::Nil);
|
|
}
|
|
Instruction::SetTable(tablereg, indexreg, valuereg) => {
|
|
let table = self.stack.get(tablereg);
|
|
match table {
|
|
Some(value) => {
|
|
let mut table = value.borrow_mut();
|
|
if let Value::Table(table) = &mut *table {
|
|
let index_value = self
|
|
.stack
|
|
.get(indexreg)
|
|
.map(|v| v.borrow().clone())
|
|
.unwrap_or(Value::Nil)
|
|
.as_indexable()?;
|
|
let value = self
|
|
.stack
|
|
.get(valuereg)
|
|
.map(|v| v.borrow().clone())
|
|
.unwrap_or(Value::Nil);
|
|
match value {
|
|
Value::Nil => {
|
|
table.borrow_mut().remove(&index_value);
|
|
}
|
|
_ => {
|
|
table.borrow_mut().insert(index_value, value);
|
|
}
|
|
}
|
|
} else {
|
|
return Err(RuntimeError::NotTable(table.clone()));
|
|
}
|
|
}
|
|
None => todo!(),
|
|
}
|
|
}
|
|
Instruction::GetTable(res, tablereg, indexreg) => {
|
|
let value = match self.stack.get(tablereg).cloned() {
|
|
Some(value) => {
|
|
let table = value.borrow();
|
|
if let Value::Table(table) = &*table {
|
|
let table = table.borrow();
|
|
let index_value = self
|
|
.stack
|
|
.get(indexreg)
|
|
.map(|v| v.borrow().clone())
|
|
.unwrap_or(Value::Nil)
|
|
.as_indexable()?;
|
|
let value = table.get(&index_value);
|
|
match value {
|
|
Some(value) => StackValue::Value(value.clone()),
|
|
None => StackValue::Value(Value::Nil),
|
|
}
|
|
} else {
|
|
return Err(RuntimeError::NotTable(table.clone()));
|
|
}
|
|
}
|
|
None => {
|
|
return Err(RuntimeError::NotTable(Value::Nil));
|
|
}
|
|
};
|
|
|
|
self.set_stack(*res, value);
|
|
}
|
|
Instruction::SetList(reg, start_idx) => {
|
|
let table = self.stack.get(reg).cloned();
|
|
match table {
|
|
Some(value) => {
|
|
let mut table = value.borrow_mut();
|
|
if let Value::Table(table) = &mut *table {
|
|
let mut counter = *start_idx as i64;
|
|
for i in self.function_register..self.top {
|
|
let value = self.get_stack(i + 1);
|
|
match value {
|
|
StackValue::Value(value) => table.borrow_mut().insert(
|
|
IndexableValue::Integer(LuaInteger(counter)),
|
|
value.clone(),
|
|
),
|
|
};
|
|
counter += 1;
|
|
}
|
|
} else {
|
|
return Err(RuntimeError::NotTable(table.clone()));
|
|
}
|
|
}
|
|
None => todo!(),
|
|
}
|
|
}
|
|
Instruction::NewTable(reg) => {
|
|
self.set_stack(
|
|
*reg,
|
|
StackValue::Value(Value::Table(Rc::new(RefCell::new(HashMap::new())))),
|
|
);
|
|
}
|
|
Instruction::Jmp(b) => {
|
|
self.program_counter = (self.program_counter as i32 + *b) as usize
|
|
}
|
|
Instruction::GoTo(a) => {
|
|
self.program_counter = *a as usize;
|
|
return Ok(None);
|
|
}
|
|
Instruction::Test(a, b, c) => {
|
|
let is_true = match self
|
|
.stack
|
|
.get(b)
|
|
.map(|v| v.borrow().clone())
|
|
.unwrap_or(Value::Nil)
|
|
{
|
|
Value::Boolean(val) => (val.0 as u16) == *c,
|
|
_ => false,
|
|
};
|
|
if is_true {
|
|
let b = self.get_stack(*b);
|
|
self.set_stack(*a, b);
|
|
} else {
|
|
self.program_counter += 1;
|
|
}
|
|
}
|
|
Instruction::ForTest(counter_reg, end_reg, step_reg) => {
|
|
let step_gte_0 = match self.get_stack(*step_reg) {
|
|
StackValue::Value(value) => Value::Integer(LuaInteger(0)).lt(&value),
|
|
};
|
|
let counter = match self.get_stack(*counter_reg) {
|
|
StackValue::Value(value) => value,
|
|
};
|
|
let end = match self.get_stack(*end_reg) {
|
|
StackValue::Value(value) => value,
|
|
};
|
|
|
|
if step_gte_0?.is_truthy() {
|
|
if !end.lt(&counter)?.is_truthy() {
|
|
self.program_counter += 1;
|
|
}
|
|
} else {
|
|
if !counter.lt(&end)?.is_truthy() {
|
|
self.program_counter += 1;
|
|
}
|
|
}
|
|
}
|
|
Instruction::Call(func_reg, param_len, ret_len) => {
|
|
let param_start_func_reg = *func_reg;
|
|
|
|
let param_len = if *param_len == 0 {
|
|
self.top - self.top.min(param_start_func_reg + 1)
|
|
} else {
|
|
*param_len
|
|
};
|
|
self.function_register = *func_reg;
|
|
|
|
let mut params = Vec::new();
|
|
for i in 0..param_len {
|
|
params.push(
|
|
self.stack
|
|
.get(&(param_start_func_reg + i + 1))
|
|
.map(|v| v.borrow().clone())
|
|
.unwrap_or(Value::Nil)
|
|
.clone(),
|
|
);
|
|
}
|
|
|
|
let value = self
|
|
.stack
|
|
.get(func_reg)
|
|
.map(|v| v.borrow().clone())
|
|
.unwrap_or(Value::Nil);
|
|
match value {
|
|
Value::RustFunction(func) => {
|
|
let ret_values = func.borrow_mut().execute(params)?;
|
|
if *ret_len != 0 {
|
|
for i in 0..=(*ret_len - 2) {
|
|
self.set_stack(
|
|
*func_reg + i,
|
|
StackValue::Value(
|
|
ret_values
|
|
.get(i as usize)
|
|
.cloned()
|
|
.unwrap_or(Value::Nil),
|
|
),
|
|
);
|
|
}
|
|
} else {
|
|
for (i, value) in ret_values.iter().enumerate() {
|
|
self.set_stack(
|
|
*func_reg + i as u16 + 1,
|
|
StackValue::Value(value.clone()),
|
|
);
|
|
}
|
|
self.top = *func_reg + ret_values.len() as u16;
|
|
}
|
|
}
|
|
Value::Function(closure) => {
|
|
self.return_registers = Vec::new();
|
|
if *ret_len != 0 {
|
|
for i in 0..=(*ret_len - 2) {
|
|
self.return_registers.push(*func_reg + i);
|
|
}
|
|
}
|
|
self.inner = Some(Box::new(closure.run(params)));
|
|
}
|
|
_ => return Err(RuntimeError::TriedCallingNonFunction(value.clone())),
|
|
}
|
|
}
|
|
Instruction::Close(_) => {}
|
|
Instruction::Closure(reg, protok) => {
|
|
self.set_stack(
|
|
*reg,
|
|
StackValue::Value(Value::Function(Closure {
|
|
vm: self.closure.vm.clone(),
|
|
prototype: *protok,
|
|
environment: self.closure.environment.clone(),
|
|
upvalues: self.close_upvalues(),
|
|
})),
|
|
);
|
|
}
|
|
Instruction::Return(reg_start, reg_end) => {
|
|
self.program_counter += 1;
|
|
let mut ret_values = Vec::new();
|
|
let (reg_start, reg_end) = if *reg_end == 0 {
|
|
if self.top > 0 {
|
|
(*reg_start, self.top - 1)
|
|
} else {
|
|
(*reg_start, *reg_end)
|
|
}
|
|
} else {
|
|
(*reg_start, *reg_end)
|
|
};
|
|
for i in reg_start..=reg_end {
|
|
ret_values.push(
|
|
self.stack
|
|
.get(&i)
|
|
.map(|v| v.borrow().clone())
|
|
.unwrap_or(Value::Nil),
|
|
);
|
|
}
|
|
return Ok(Some(ret_values));
|
|
}
|
|
Instruction::Add(res, lhs, rhs) => {
|
|
let lhs = self
|
|
.stack
|
|
.get(lhs)
|
|
.map(|v| v.borrow().clone())
|
|
.unwrap_or(Value::Nil);
|
|
let rhs = self
|
|
.stack
|
|
.get(rhs)
|
|
.map(|v| v.borrow().clone())
|
|
.unwrap_or(Value::Nil);
|
|
self.set_stack(*res, StackValue::Value(lhs.add(&rhs)?));
|
|
}
|
|
Instruction::Equal(res, lhs, rhs) => {
|
|
let lhs = self
|
|
.stack
|
|
.get(lhs)
|
|
.map(|v| v.borrow().clone())
|
|
.unwrap_or(Value::Nil);
|
|
let rhs = self
|
|
.stack
|
|
.get(rhs)
|
|
.map(|v| v.borrow().clone())
|
|
.unwrap_or(Value::Nil);
|
|
self.set_stack(*res, StackValue::Value(lhs.eq(&rhs)?));
|
|
}
|
|
Instruction::LessThan(res, lhs, rhs) => {
|
|
let lhs = self
|
|
.stack
|
|
.get(lhs)
|
|
.map(|v| v.borrow().clone())
|
|
.unwrap_or(Value::Nil);
|
|
let rhs = self
|
|
.stack
|
|
.get(rhs)
|
|
.map(|v| v.borrow().clone())
|
|
.unwrap_or(Value::Nil);
|
|
self.set_stack(*res, StackValue::Value(lhs.lt(&rhs)?));
|
|
}
|
|
Instruction::LessThanOrEqual(res, lhs, rhs) => {
|
|
let lhs = self
|
|
.stack
|
|
.get(lhs)
|
|
.map(|v| v.borrow().clone())
|
|
.unwrap_or(Value::Nil);
|
|
let rhs = self
|
|
.stack
|
|
.get(rhs)
|
|
.map(|v| v.borrow().clone())
|
|
.unwrap_or(Value::Nil);
|
|
self.set_stack(*res, StackValue::Value(lhs.lte(&rhs)?));
|
|
}
|
|
Instruction::Unm(res, reg) => {
|
|
self.set_stack(
|
|
*res,
|
|
StackValue::Value(
|
|
self.stack
|
|
.get(reg)
|
|
.map(|v| v.borrow().clone())
|
|
.unwrap_or(Value::Nil)
|
|
.unm()?,
|
|
),
|
|
);
|
|
}
|
|
Instruction::Len(res, reg) => {
|
|
self.set_stack(
|
|
*res,
|
|
StackValue::Value(
|
|
self.stack
|
|
.get(reg)
|
|
.map(|v| v.borrow().clone())
|
|
.unwrap_or(Value::Nil)
|
|
.len()?,
|
|
),
|
|
);
|
|
}
|
|
Instruction::Or(res, lhs, rhs) => {
|
|
let lhs = self
|
|
.stack
|
|
.get(lhs)
|
|
.map(|v| v.borrow().clone())
|
|
.unwrap_or(Value::Nil);
|
|
let rhs = self
|
|
.stack
|
|
.get(rhs)
|
|
.map(|v| v.borrow().clone())
|
|
.unwrap_or(Value::Nil);
|
|
self.set_stack(*res, StackValue::Value(lhs.or(&rhs)?));
|
|
}
|
|
Instruction::And(res, lhs, rhs) => {
|
|
let lhs = self
|
|
.stack
|
|
.get(lhs)
|
|
.map(|v| v.borrow().clone())
|
|
.unwrap_or(Value::Nil);
|
|
let rhs = self
|
|
.stack
|
|
.get(rhs)
|
|
.map(|v| v.borrow().clone())
|
|
.unwrap_or(Value::Nil);
|
|
self.set_stack(*res, StackValue::Value(lhs.and(&rhs)?));
|
|
}
|
|
Instruction::Vararg(reg, len) => {
|
|
self.function_register = reg - 1;
|
|
if *len == 0 {
|
|
self.top = self.function_register + self.parameters.len() as u16
|
|
- prototype.parameters as u16;
|
|
|
|
for (i, param) in self
|
|
.parameters
|
|
.clone()
|
|
.iter()
|
|
.skip(prototype.parameters)
|
|
.enumerate()
|
|
{
|
|
self.set_stack(*reg + i as u16, StackValue::Value(param.clone()));
|
|
}
|
|
} else {
|
|
let len = len - 2;
|
|
self.top = self.function_register + len + 1;
|
|
for i in 0..len {
|
|
self.set_stack(
|
|
*reg + i as u16,
|
|
StackValue::Value(
|
|
self.parameters
|
|
.iter()
|
|
.skip(prototype.parameters)
|
|
.next()
|
|
.unwrap_or(&Value::Nil)
|
|
.clone(),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
self.program_counter += 1;
|
|
|
|
Ok(None)
|
|
} else {
|
|
Ok(Some(Vec::new()))
|
|
}
|
|
}
|
|
}
|