Move value to own file
This commit is contained in:
parent
ced6e79bcc
commit
b1ddd31fb7
@ -1,5 +1,5 @@
|
|||||||
use ferrite_lua::{
|
use ferrite_lua::{
|
||||||
compile,
|
compile, value,
|
||||||
vm::{self, RuntimeError, RustFunction, VirtualMachine},
|
vm::{self, RuntimeError, RustFunction, VirtualMachine},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -8,12 +8,12 @@ static TEST: &str = include_str!("../examples/test.lua");
|
|||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
pub struct Max;
|
pub struct Max;
|
||||||
impl RustFunction for Max {
|
impl RustFunction for Max {
|
||||||
fn execute(&self, parameters: Vec<vm::Value>) -> Result<Vec<vm::Value>, RuntimeError> {
|
fn execute(&self, parameters: Vec<value::Value>) -> Result<Vec<value::Value>, RuntimeError> {
|
||||||
let lhs = parameters.get(0).cloned().unwrap_or(vm::Value::Nil);
|
let lhs = parameters.get(0).cloned().unwrap_or(value::Value::Nil);
|
||||||
let rhs = parameters.get(1).cloned().unwrap_or(vm::Value::Nil);
|
let rhs = parameters.get(1).cloned().unwrap_or(value::Value::Nil);
|
||||||
match lhs.lt(&rhs)? {
|
match lhs.lt(&rhs)? {
|
||||||
vm::Value::Boolean(value) => Ok(vec![if value.0 { rhs } else { lhs }]),
|
value::Value::Boolean(value) => Ok(vec![if value.0 { rhs } else { lhs }]),
|
||||||
_ => Ok(vec![vm::Value::Nil]),
|
_ => Ok(vec![value::Value::Nil]),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -24,7 +24,7 @@ impl RustFunction for Max {
|
|||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
pub struct Print;
|
pub struct Print;
|
||||||
impl RustFunction for Print {
|
impl RustFunction for Print {
|
||||||
fn execute(&self, parameters: Vec<vm::Value>) -> Result<Vec<vm::Value>, RuntimeError> {
|
fn execute(&self, parameters: Vec<value::Value>) -> Result<Vec<value::Value>, RuntimeError> {
|
||||||
println!("{:?}", parameters);
|
println!("{:?}", parameters);
|
||||||
Ok(Vec::new())
|
Ok(Vec::new())
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
//! Usage example:
|
//! Usage example:
|
||||||
//! ```rust
|
//! ```rust
|
||||||
//! use ferrite_lua::{compile, vm};
|
//! use ferrite_lua::{compile, vm, value};
|
||||||
//!
|
//!
|
||||||
//! #[derive(Debug, PartialEq, Eq)]
|
//! #[derive(Debug, PartialEq, Eq)]
|
||||||
//! pub struct Print;
|
//! pub struct Print;
|
||||||
//! impl vm::RustFunction for Print {
|
//! impl vm::RustFunction for Print {
|
||||||
//! fn execute(&self, parameters: Vec<vm::Value>) -> Result<Vec<vm::Value>, vm::RuntimeError> {
|
//! fn execute(&self, parameters: Vec<value::Value>) -> Result<Vec<value::Value>, vm::RuntimeError> {
|
||||||
//! println!("{:?}", parameters);
|
//! println!("{:?}", parameters);
|
||||||
//! Ok(Vec::new())
|
//! Ok(Vec::new())
|
||||||
//! }
|
//! }
|
||||||
@ -42,6 +42,7 @@ use crate::{
|
|||||||
mod ast;
|
mod ast;
|
||||||
mod compile;
|
mod compile;
|
||||||
mod token_stream;
|
mod token_stream;
|
||||||
|
pub mod value;
|
||||||
pub mod vm;
|
pub mod vm;
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
|
|||||||
250
src/value.rs
Normal file
250
src/value.rs
Normal file
@ -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<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))
|
||||||
|
}
|
||||||
|
(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<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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
247
src/vm.rs
247
src/vm.rs
@ -5,6 +5,7 @@ use std::{cell::RefCell, collections::HashMap, fmt::Debug, hash::Hash, rc::Rc};
|
|||||||
use crate::{
|
use crate::{
|
||||||
CompilationUnit,
|
CompilationUnit,
|
||||||
ast::{BinaryOperator, UnaryOperator},
|
ast::{BinaryOperator, UnaryOperator},
|
||||||
|
value::{IndexableValue, Value},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone, Hash, PartialEq, Eq, Copy, PartialOrd, Ord)]
|
#[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<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))
|
|
||||||
}
|
|
||||||
(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<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 {
|
pub trait RustFunction: Debug {
|
||||||
fn execute(&self, parameters: Vec<Value>) -> Result<Vec<Value>, RuntimeError>;
|
fn execute(&self, parameters: Vec<Value>) -> Result<Vec<Value>, RuntimeError>;
|
||||||
fn as_indexable(&self) -> String;
|
fn as_indexable(&self) -> String;
|
||||||
@ -576,7 +333,7 @@ impl VirtualMachine {
|
|||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Closure {
|
pub struct Closure {
|
||||||
vm: VirtualMachine,
|
vm: VirtualMachine,
|
||||||
prototype: u32,
|
pub(crate) prototype: u32,
|
||||||
environment: Rc<RefCell<Environment>>,
|
environment: Rc<RefCell<Environment>>,
|
||||||
upvalues: HashMap<u16, Rc<RefCell<Value>>>,
|
upvalues: HashMap<u16, Rc<RefCell<Value>>>,
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user