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 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 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), #[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>>, } impl Environment { fn get_global(&mut self, key: &Constant) -> Option { 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>), Function(Closure), Nil, Table(Rc>>), } impl Value { pub fn as_indexable(self) -> Result { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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::>(); 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 { if self.is_truthy() { Ok(self.clone()) } else { Ok(other.clone()) } } pub fn or(&self, other: &Value) -> Result { 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) -> Result, RuntimeError>; fn as_indexable(&self) -> String; } impl From for Value { fn from(value: T) -> Self { Self::RustFunction(Rc::new(RefCell::new(value))) } } #[derive(Debug, Clone, Default)] pub struct Prototype { pub instructions: Vec, pub parameters: usize, } #[derive(Debug, Clone, Default)] pub struct VirtualMachine { pub(super) environment: Rc>, pub(super) constants: Vec, pub(super) prototypes: HashMap, 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 { 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>, upvalues: HashMap>>, } impl Closure { pub fn run(&self, params: Vec) -> 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>>, inner: Option>, function_register: u16, return_registers: Vec, top: u16, parameters: Vec, } #[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>> { 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>, 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())) } } }