From b1ddd31fb7a1fa38eca349bc25e52750ad6a56e3 Mon Sep 17 00:00:00 2001 From: Sofia Date: Fri, 20 Mar 2026 18:12:07 +0200 Subject: [PATCH] Move value to own file --- examples/test.rs | 14 +-- src/lib.rs | 5 +- src/value.rs | 250 +++++++++++++++++++++++++++++++++++++++++++++++ src/vm.rs | 247 +--------------------------------------------- 4 files changed, 262 insertions(+), 254 deletions(-) create mode 100644 src/value.rs diff --git a/examples/test.rs b/examples/test.rs index 40e0a9a..69e175a 100644 --- a/examples/test.rs +++ b/examples/test.rs @@ -1,5 +1,5 @@ use ferrite_lua::{ - compile, + compile, value, vm::{self, RuntimeError, RustFunction, VirtualMachine}, }; @@ -8,12 +8,12 @@ static TEST: &str = include_str!("../examples/test.lua"); #[derive(Debug, PartialEq, Eq)] pub struct Max; impl RustFunction for Max { - fn execute(&self, parameters: Vec) -> Result, RuntimeError> { - let lhs = parameters.get(0).cloned().unwrap_or(vm::Value::Nil); - let rhs = parameters.get(1).cloned().unwrap_or(vm::Value::Nil); + fn execute(&self, parameters: Vec) -> Result, RuntimeError> { + let lhs = parameters.get(0).cloned().unwrap_or(value::Value::Nil); + let rhs = parameters.get(1).cloned().unwrap_or(value::Value::Nil); match lhs.lt(&rhs)? { - vm::Value::Boolean(value) => Ok(vec![if value.0 { rhs } else { lhs }]), - _ => Ok(vec![vm::Value::Nil]), + value::Value::Boolean(value) => Ok(vec![if value.0 { rhs } else { lhs }]), + _ => Ok(vec![value::Value::Nil]), } } @@ -24,7 +24,7 @@ impl RustFunction for Max { #[derive(Debug, PartialEq, Eq)] pub struct Print; impl RustFunction for Print { - fn execute(&self, parameters: Vec) -> Result, RuntimeError> { + fn execute(&self, parameters: Vec) -> Result, RuntimeError> { println!("{:?}", parameters); Ok(Vec::new()) } diff --git a/src/lib.rs b/src/lib.rs index 5edb62e..0f7a590 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,11 +1,11 @@ //! Usage example: //! ```rust -//! use ferrite_lua::{compile, vm}; +//! use ferrite_lua::{compile, vm, value}; //! //! #[derive(Debug, PartialEq, Eq)] //! pub struct Print; //! impl vm::RustFunction for Print { -//! fn execute(&self, parameters: Vec) -> Result, vm::RuntimeError> { +//! fn execute(&self, parameters: Vec) -> Result, vm::RuntimeError> { //! println!("{:?}", parameters); //! Ok(Vec::new()) //! } @@ -42,6 +42,7 @@ use crate::{ mod ast; mod compile; mod token_stream; +pub mod value; pub mod vm; #[derive(Error, Debug)] diff --git a/src/value.rs b/src/value.rs new file mode 100644 index 0000000..07ca88a --- /dev/null +++ b/src/value.rs @@ -0,0 +1,250 @@ +use std::{cell::RefCell, collections::HashMap, fmt::Debug, rc::Rc}; + +use crate::{ + ast::{BinaryOperator, UnaryOperator}, + vm::{Closure, LuaBool, LuaFloat, LuaInteger, RuntimeError, RustFunction, VMFloat}, +}; + +#[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)) + } + (Value::Nil, Value::Nil) => Ok(Value::Boolean(LuaBool(true))), + (Value::Nil, _) | (_, Value::Nil) => Ok(Value::Boolean(LuaBool(false))), + _ => Ok(Value::Boolean(LuaBool( + self.clone().as_indexable()? == other.clone().as_indexable()?, + ))), + } + } + + 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() + } + } + } +} diff --git a/src/vm.rs b/src/vm.rs index a9c4e0a..327aa30 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -5,6 +5,7 @@ use std::{cell::RefCell, collections::HashMap, fmt::Debug, hash::Hash, rc::Rc}; use crate::{ CompilationUnit, ast::{BinaryOperator, UnaryOperator}, + value::{IndexableValue, Value}, }; #[derive(Clone, Hash, PartialEq, Eq, Copy, PartialOrd, Ord)] @@ -269,250 +270,6 @@ impl Environment { } } -#[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)) - } - (Value::Nil, Value::Nil) => Ok(Value::Boolean(LuaBool(true))), - (Value::Nil, _) | (_, Value::Nil) => Ok(Value::Boolean(LuaBool(false))), - _ => Ok(Value::Boolean(LuaBool( - self.clone().as_indexable()? == other.clone().as_indexable()?, - ))), - } - } - - 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; @@ -576,7 +333,7 @@ impl VirtualMachine { #[derive(Debug, Clone)] pub struct Closure { vm: VirtualMachine, - prototype: u32, + pub(crate) prototype: u32, environment: Rc>, upvalues: HashMap>>, }