Compare commits

...

12 Commits

Author SHA1 Message Date
2710a43bb2 Move value to vm/value 2026-03-20 18:16:07 +02:00
139887fd73 Move some other stuff to value as well 2026-03-20 18:14:01 +02:00
b1ddd31fb7 Move value to own file 2026-03-20 18:12:07 +02:00
ced6e79bcc Add block statements 2026-03-20 18:04:35 +02:00
fb82ce6feb Add empty ;-statement 2026-03-20 18:01:40 +02:00
8bb49321ae Add expressions in parenthesis 2026-03-20 18:00:13 +02:00
3eaf91760e Fix various issues with upvalues 2026-03-20 16:05:02 +02:00
57b2793ede Fix upvalues 2026-03-20 15:24:50 +02:00
84eb837534 Add for-in, fix a bunch of bugs 2026-03-20 00:07:26 +02:00
2e853f29b4 Move function call compilation to own function 2026-03-19 22:43:51 +02:00
11f4e4e8a3 Fix while, add to test.lua 2026-03-19 21:37:59 +02:00
50e826061b Add repeat until 2026-03-19 21:36:05 +02:00
8 changed files with 793 additions and 476 deletions

View File

@ -1,3 +1,63 @@
global b = 5
function add(x)
return function (y)
x = x + 1
b = b + 1
return x + y, 1, 2, b
end
end
function min(x, y)
local m = x
if y < x then
m = y
end
return m
end
function f(x, ...)
local b = {10, ..., add(10)(15)}
return x + 5, b
end
global sometable = {}
sometable["hello"] = { 100, 150, add(10)(15) }
print(#sometable["hello"])
sometable["hello"].there = "my dude"
print(sometable.hello.there)
print(max(11.12345, 9))
print(add(10)(15))
print(add(10)(15))
print(b)
print(min(11, 9))
print(10 - 15)
print("hello there!")
print(true or 0)
global value, table = f(10, 11, 12)
print("hello")
for i=1,#table do
print(table[i])
if i > 2 then
goto test
end
end
::test::
local test = table[1]
if test == 10 then
print("first")
elseif test == 11 then
print("second")
else
print("third")
end
print("after if/elseif/else")
local i = 0 local i = 0
print("before") print("before")
@ -5,4 +65,31 @@ while i < 10 do
i = i + 1 i = i + 1
print(i) print(i)
end end
print("after") print("after while")
local i = 0
print("before")
repeat
i = i + 1
print(i)
until i >= 10
print("after repeat")
function ipairs(t)
print("inside!")
local i = 0
return function (state, control)
print(state, control)
i = i + 1
if i > #table then
return nil, nil
end
return i, t[i]
end, "otus", "potus"
end
for k, v in (ipairs(table)) do
print(k, v)
end

View File

@ -1,19 +1,19 @@
use ferrite_lua::{ use ferrite_lua::{
compile, compile,
vm::{self, RuntimeError, RustFunction, VirtualMachine}, vm::{RuntimeError, VirtualMachine, value},
}; };
static TEST: &str = include_str!("../examples/test.lua"); 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 value::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]),
} }
} }
@ -23,8 +23,8 @@ impl RustFunction for Max {
} }
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
pub struct Print; pub struct Print;
impl RustFunction for Print { impl value::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())
} }

View File

@ -5,7 +5,7 @@ use crate::{
Parse, TokenRange, TokenStream, TokenStreamError, Parse, TokenRange, TokenStream, TokenStreamError,
lexer::{Keyword, Position, Token}, lexer::{Keyword, Position, Token},
}, },
vm::{LuaFloat, LuaInteger}, vm::value::{LuaFloat, LuaInteger},
}; };
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -238,6 +238,7 @@ impl Parse for Block {
Token::Keyword(Keyword::End) Token::Keyword(Keyword::End)
| Token::Keyword(Keyword::ElseIf) | Token::Keyword(Keyword::ElseIf)
| Token::Keyword(Keyword::Else) | Token::Keyword(Keyword::Else)
| Token::Keyword(Keyword::Until)
| Token::Eof | Token::Eof
) )
) { ) {
@ -271,11 +272,14 @@ pub enum Statement {
Node<Expression>, Node<Expression>,
Block, Block,
), ),
GenericFor(Vec<Node<String>>, Node<ExpressionList>, Block),
While(Node<Expression>, Block), While(Node<Expression>, Block),
Repeat(Block, Node<Expression>), Repeat(Block, Node<Expression>),
Break, Break,
Label(Node<String>), Label(Node<String>),
GoTo(Node<String>), GoTo(Node<String>),
Empty,
Block(Block),
} }
impl Parse for Statement { impl Parse for Statement {
@ -376,6 +380,22 @@ impl Parse for Statement {
} else if stream.peek() == Some(Token::Keyword(Keyword::For)) { } else if stream.peek() == Some(Token::Keyword(Keyword::For)) {
stream.next(); stream.next();
let counter_name = stream.parse()?; let counter_name = stream.parse()?;
if let Some(Token::Symbol(',') | Token::Keyword(Keyword::In)) = stream.peek() {
let mut counters = vec![counter_name];
while let Some(Token::Symbol(',')) = stream.peek() {
stream.next();
counters.push(stream.parse()?);
}
stream.expect(Token::Keyword(Keyword::In))?;
let expr_list = stream.parse()?;
stream.expect(Token::Keyword(Keyword::Do))?;
let block = stream.parse()?;
stream.expect(Token::Keyword(Keyword::End))?;
Ok(Self::GenericFor(counters, expr_list, block))
} else {
stream.expect_symbol('=')?; stream.expect_symbol('=')?;
let init = stream.parse()?; let init = stream.parse()?;
stream.expect_symbol(',')?; stream.expect_symbol(',')?;
@ -402,6 +422,7 @@ impl Parse for Statement {
step, step,
block, block,
)) ))
}
} else if let Some(Token::Keyword(Keyword::While)) = stream.peek() { } else if let Some(Token::Keyword(Keyword::While)) = stream.peek() {
stream.next(); stream.next();
let expr = stream.parse()?; let expr = stream.parse()?;
@ -432,6 +453,14 @@ impl Parse for Statement {
} else if let Some(Token::Keyword(Keyword::GoTo)) = stream.peek() { } else if let Some(Token::Keyword(Keyword::GoTo)) = stream.peek() {
stream.next(); stream.next();
Ok(Self::GoTo(stream.parse()?)) Ok(Self::GoTo(stream.parse()?))
} else if let Some(Token::Symbol(';')) = stream.peek() {
stream.next();
Ok(Self::Empty)
} else if let Some(Token::Keyword(Keyword::Do)) = stream.peek() {
stream.next();
let block = stream.parse()?;
stream.expect(Token::Keyword(Keyword::End))?;
Ok(Self::Block(block))
} else { } else {
Err(stream.expecting_err("statement")) Err(stream.expecting_err("statement"))
} }
@ -495,6 +524,8 @@ pub enum Expression {
TableConstructor(Vec<(Option<Node<Expression>>, Node<Expression>)>), TableConstructor(Vec<(Option<Node<Expression>>, Node<Expression>)>),
IndexedAccess(Box<Node<Expression>>, Box<Node<Expression>>), IndexedAccess(Box<Node<Expression>>, Box<Node<Expression>>),
Ellipsis, Ellipsis,
/// Raw access to a register
Register(u16),
} }
impl Parse for Expression { impl Parse for Expression {
@ -668,6 +699,11 @@ impl Parse for PrimaryExpression {
Expression::TableConstructor(entries) Expression::TableConstructor(entries)
} else if let Ok(_) = stream.parse::<Ellipsis>() { } else if let Ok(_) = stream.parse::<Ellipsis>() {
Expression::Ellipsis Expression::Ellipsis
} else if let Some(Token::Symbol('(')) = stream.peek() {
stream.next();
let expression = stream.parse()?;
stream.expect_symbol(')')?;
expression
} else { } else {
Expression::ValueRef(stream.parse()?) Expression::ValueRef(stream.parse()?)
}; };

View File

@ -1,11 +1,17 @@
use std::collections::{HashMap, HashSet}; use std::{
collections::{HashMap, HashSet},
num::IntErrorKind,
};
use crate::{ use crate::{
ast::{ ast::{
AccessModifier, BinaryOperator, Block, Expression, IdentOrEllipsis, Literal, Statement, AccessModifier, BinaryOperator, Block, Expression, ExpressionList, IdentOrEllipsis,
UnaryOperator, Literal, Node, Statement, UnaryOperator,
},
vm::{
Constant, Instruction, Prototype,
value::{LuaBool, LuaInteger},
}, },
vm::{Constant, Instruction, LuaBool, LuaInteger, Prototype},
}; };
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -25,15 +31,27 @@ impl State {
} }
} }
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug)]
pub struct Scope { pub struct Scope {
pub locals: HashMap<String, u16>, pub locals: HashMap<String, u16>,
pub register_counter: LocalCounter, pub register_counter: LocalCounter,
pub highest_upvalue: u16, pub highest_upvalue: i32,
pub upvalues: HashMap<String, u16>, pub upvalues: HashMap<String, u16>,
pub is_vararg: bool, pub is_vararg: bool,
} }
impl Default for Scope {
fn default() -> Self {
Self {
locals: Default::default(),
register_counter: Default::default(),
highest_upvalue: -1,
upvalues: Default::default(),
is_vararg: Default::default(),
}
}
}
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default)]
pub struct LocalCounter(u16, Vec<u16>); pub struct LocalCounter(u16, Vec<u16>);
@ -207,6 +225,14 @@ impl Statement {
constants.extend(block.find_constants(scope, Vec::new())); constants.extend(block.find_constants(scope, Vec::new()));
constants constants
} }
Statement::GenericFor(_, expr_list, block) => {
let mut constants = HashSet::new();
for expr in &expr_list.kind.0 {
constants.extend(expr.kind.find_constants(scope));
}
constants.extend(block.find_constants(scope, Vec::new()));
constants
}
Statement::While(node, block) => { Statement::While(node, block) => {
let mut constants = HashSet::new(); let mut constants = HashSet::new();
constants.extend(node.kind.find_constants(scope)); constants.extend(node.kind.find_constants(scope));
@ -222,6 +248,12 @@ impl Statement {
Statement::Break => HashSet::new(), Statement::Break => HashSet::new(),
Statement::Label(_) => HashSet::new(), Statement::Label(_) => HashSet::new(),
Statement::GoTo(_) => HashSet::new(), Statement::GoTo(_) => HashSet::new(),
Statement::Empty => HashSet::new(),
Statement::Block(block) => {
let mut constants = HashSet::new();
constants.extend(block.find_constants(scope, Vec::new()));
constants
}
} }
} }
@ -385,11 +417,8 @@ impl Statement {
ret_registers.extend(registers); ret_registers.extend(registers);
} }
let first_ret_register = ret_registers let new_ret_registers = scope.register_counter.consecutive(ret_registers.len() + 1);
.iter() let first_ret_register = new_ret_registers.first().unwrap();
.cloned()
.next()
.unwrap_or(scope.register_counter.0);
for (i, ret_register) in ret_registers.iter_mut().enumerate() { for (i, ret_register) in ret_registers.iter_mut().enumerate() {
let new_reg = first_ret_register + i as u16; let new_reg = first_ret_register + i as u16;
if *ret_register != new_reg { if *ret_register != new_reg {
@ -406,7 +435,7 @@ impl Statement {
} }
instructions.push(PreInstr::Instr(Instruction::Return( instructions.push(PreInstr::Instr(Instruction::Return(
first_ret_register, *first_ret_register,
if vararg { if vararg {
0 0
} else { } else {
@ -481,6 +510,111 @@ impl Statement {
))); )));
instructions.push(PreInstr::Instr(Instruction::Jmp(-(instr_len + 4)))); instructions.push(PreInstr::Instr(Instruction::Jmp(-(instr_len + 4))));
} }
Statement::GenericFor(names, expr_list, block) => {
let mut expr_regs = Vec::new();
for (i, expr) in expr_list.kind.0.iter().enumerate() {
let (instr, regs) = expr.kind.compile(
state,
scope,
if i == expr_list.kind.0.len() - 1 {
Some(4 - expr_list.kind.0.len() + 1)
} else {
Some(1)
},
);
instructions.extend(instr);
expr_regs.extend(regs);
}
dbg!(&expr_regs);
let mut inner_scope = scope.clone();
let iterator_reg = *expr_regs.get(0).unwrap();
let state_reg = *expr_regs.get(1).unwrap();
let initial_value_reg = *expr_regs.get(2).unwrap();
let closing_value_reg = *expr_regs.get(3).unwrap();
inner_scope
.locals
.insert("_ITERATOR".to_owned(), iterator_reg);
inner_scope.locals.insert("_STATE".to_owned(), state_reg);
inner_scope
.locals
.insert("_INIT_VALUE".to_owned(), initial_value_reg);
inner_scope
.locals
.insert("_CLOSING_VAL".to_owned(), closing_value_reg);
let (instr, res_regs) = compile_function_call(
Node::empty(Expression::Register(iterator_reg)),
Node::empty(ExpressionList(vec![
Node::empty(Expression::Register(state_reg)),
Node::empty(Expression::Register(initial_value_reg)),
])),
state,
&mut inner_scope,
Some(names.len()),
);
instructions.extend(instr);
let mut counter_regs = Vec::new();
for (i, name) in names.iter().enumerate() {
let reg = inner_scope.register_counter.next();
counter_regs.push(reg);
inner_scope.locals.insert(name.kind.clone(), reg);
instructions.push(PreInstr::Instr(Instruction::Move(
reg,
*res_regs.get(i).unwrap(),
)));
}
let eql_res = inner_scope.register_counter.next();
let nil_reg = inner_scope.register_counter.next();
instructions.push(PreInstr::Instr(Instruction::LoadNil(nil_reg, nil_reg)));
instructions.push(PreInstr::Instr(Instruction::Equal(
eql_res,
*counter_regs.first().unwrap(),
nil_reg,
)));
instructions.push(PreInstr::Instr(Instruction::Test(
inner_scope.register_counter.next(),
eql_res,
1,
)));
let block_instr = block.compile(state, &mut inner_scope);
let block_instr_len = block_instr.len() as i32;
let (func_instr, res_regs) = compile_function_call(
Node::empty(Expression::Register(iterator_reg)),
Node::empty(ExpressionList(vec![
Node::empty(Expression::Register(state_reg)),
Node::empty(Expression::Register(initial_value_reg)),
])),
state,
scope,
Some(names.len()),
);
let func_instr_len = func_instr.len() as i32;
instructions.push(PreInstr::Instr(Instruction::Jmp(
block_instr_len + func_instr_len + counter_regs.len() as i32 + 2,
)));
instructions.extend(block_instr);
instructions.push(PreInstr::Instr(Instruction::Move(
initial_value_reg,
*counter_regs.first().unwrap(),
)));
instructions.extend(func_instr);
for (counter_reg, res_reg) in counter_regs.iter().zip(res_regs) {
instructions.push(PreInstr::Instr(Instruction::Move(*counter_reg, res_reg)));
}
instructions.push(PreInstr::Instr(Instruction::Jmp(
-(block_instr_len + func_instr_len + counter_regs.len() as i32 + 6),
)))
}
Statement::While(expr, block) => { Statement::While(expr, block) => {
let (instr, expr_regs) = expr.kind.compile(state, scope, Some(1)); let (instr, expr_regs) = expr.kind.compile(state, scope, Some(1));
let expr_instr_len = instr.len() as i32; let expr_instr_len = instr.len() as i32;
@ -491,20 +625,47 @@ impl Statement {
0, 0,
))); )));
let block_instructions = block.compile(state, scope); let mut inner_scope = scope.clone();
let block_instructions = block.compile(state, &mut inner_scope);
let block_instr_len = block_instructions.len() as i32; let block_instr_len = block_instructions.len() as i32;
instructions.push(PreInstr::Instr(Instruction::Jmp(block_instr_len + 1))); instructions.push(PreInstr::Instr(Instruction::Jmp(block_instr_len + 1)));
instructions.extend(block_instructions); instructions.extend(block_instructions);
instructions.push(PreInstr::Instr(Instruction::Jmp(
-(block_instr_len + expr_instr_len + 3),
)));
}
Statement::Repeat(block, expr) => {
let mut inner_scope = scope.clone();
let block_instructions = block.compile(state, &mut inner_scope);
let block_instr_len = block_instructions.len() as i32;
instructions.extend(block_instructions);
let (instr, expr_regs) = expr.kind.compile(state, scope, Some(1));
let expr_instr_len = instr.len() as i32;
instructions.extend(instr);
instructions.push(PreInstr::Instr(Instruction::Test(
scope.register_counter.next(),
*expr_regs.first().unwrap(),
0,
)));
instructions.push(PreInstr::Instr(Instruction::Jmp( instructions.push(PreInstr::Instr(Instruction::Jmp(
-(block_instr_len + expr_instr_len + 2), -(block_instr_len + expr_instr_len + 2),
))); )));
} }
Statement::Repeat(block, expr) => todo!(),
Statement::Break => instructions.push(PreInstr::Break), Statement::Break => instructions.push(PreInstr::Break),
Statement::Label(node) => instructions.push(PreInstr::Label(node.kind.clone())), Statement::Label(node) => instructions.push(PreInstr::Label(node.kind.clone())),
Statement::GoTo(node) => instructions.push(PreInstr::GoTo(node.kind.clone())), Statement::GoTo(node) => instructions.push(PreInstr::GoTo(node.kind.clone())),
Statement::Empty => {}
Statement::Block(block) => {
let mut inner_scope = scope.clone();
instructions.extend(block.compile(state, &mut inner_scope));
}
} }
for reg in 0..scope.register_counter.0 { for reg in 0..scope.register_counter.0 {
@ -597,6 +758,7 @@ impl Expression {
constants constants
} }
Expression::Ellipsis => HashSet::new(), Expression::Ellipsis => HashSet::new(),
Expression::Register(_) => HashSet::new(),
} }
} }
@ -733,14 +895,19 @@ impl Expression {
} }
} }
inner_scope.highest_upvalue = scope.highest_upvalue + scope.register_counter.0; inner_scope.highest_upvalue =
scope.highest_upvalue + scope.register_counter.0 as i32;
inner_scope.upvalues = scope.upvalues.clone(); inner_scope.upvalues = scope.upvalues.clone();
dbg!(&scope.highest_upvalue, &scope.register_counter.0);
for (name, reg) in &scope.locals { for (name, reg) in &scope.locals {
let new_reg = *reg + scope.highest_upvalue + 1; let upvalue_reg = *reg + (scope.highest_upvalue + 1) as u16;
inner_scope.upvalues.insert(name.clone(), new_reg); inner_scope.upvalues.insert(name.clone(), upvalue_reg);
} }
dbg!(&inner_scope.upvalues);
let instructions = block.compile(state, &mut inner_scope); let instructions = block.compile(state, &mut inner_scope);
state.prototypes.push(Prototype { state.prototypes.push(Prototype {
instructions: process_pre_instrs(instructions), instructions: process_pre_instrs(instructions),
@ -764,88 +931,7 @@ impl Expression {
(instructions, vec![local]) (instructions, vec![local])
} }
Expression::FunctionCall(expr, params) => { Expression::FunctionCall(expr, params) => {
let mut instructions = Vec::new(); compile_function_call(*expr.clone(), params.clone(), state, scope, expected_values)
let (instr, registers) = expr.kind.compile(state, scope, Some(1));
instructions.extend(instr);
let old_function_reg = registers.first().unwrap();
let mut registers = scope
.register_counter
.consecutive(params.kind.0.len() + 1)
.into_iter();
let mut param_scope = scope.clone();
let mut original_param_regs = Vec::new();
let mut vararg = false;
for (i, param) in params.kind.0.iter().enumerate() {
let (instr, registers) = param.kind.compile(
state,
&mut param_scope,
if i == params.kind.0.len() - 1 {
None
} else {
Some(1)
},
);
instructions.extend(instr);
if registers.len() > 0 {
original_param_regs.push(*registers.first().unwrap());
} else {
vararg = true;
}
}
let function_reg = registers.next().unwrap();
let mut param_regs = Vec::new();
for _ in &original_param_regs {
param_regs.push(registers.next().unwrap());
}
for (i, param_reg) in original_param_regs.iter().enumerate().rev() {
let new_reg = param_regs.get(i).unwrap();
if param_reg != new_reg {
instructions.push(PreInstr::Instr(Instruction::Move(*new_reg, *param_reg)));
}
}
if function_reg != *old_function_reg {
instructions.push(PreInstr::Instr(Instruction::Move(
function_reg,
*old_function_reg,
)));
}
if vararg {
let last_reg = param_regs.last().unwrap_or(&function_reg) + 1;
instructions.push(PreInstr::Instr(Instruction::MoveRetValues(last_reg)));
}
let last_param_reg = param_regs.last().unwrap_or(&function_reg);
let mut return_regs = Vec::new();
if let Some(expected_values) = expected_values {
for i in 0..expected_values {
let return_reg = i as u16 + function_reg;
if return_reg > *last_param_reg {
return_regs.push(scope.register_counter.next());
} else {
return_regs.push(return_reg);
}
}
}
instructions.push(PreInstr::Instr(Instruction::Call(
*&function_reg,
if vararg { 0 } else { param_regs.len() as u16 },
if return_regs.len() == 0 {
0
} else {
return_regs.len() as u16 + 1
},
)));
(instructions, return_regs)
} }
Expression::Literal(literal) => { Expression::Literal(literal) => {
let mut instructions = Vec::new(); let mut instructions = Vec::new();
@ -942,6 +1028,94 @@ impl Expression {
(instructions, vec![new_reg]) (instructions, vec![new_reg])
} }
} }
Expression::Register(reg) => (Vec::new(), vec![*reg]),
} }
} }
} }
fn compile_function_call(
expr: Node<Expression>,
params: Node<ExpressionList>,
state: &mut State,
scope: &mut Scope,
expected_values: Option<usize>,
) -> (Vec<PreInstr>, Vec<u16>) {
let mut instructions = Vec::new();
let (instr, registers) = expr.kind.compile(state, scope, Some(1));
instructions.extend(instr);
let old_function_reg = registers.first().unwrap();
let mut registers = scope
.register_counter
.consecutive(params.kind.0.len() + 1)
.into_iter();
let mut param_scope = scope.clone();
let mut original_param_regs = Vec::new();
let mut vararg = false;
for (i, param) in params.kind.0.iter().enumerate() {
let (instr, registers) = param.kind.compile(
state,
&mut param_scope,
if i == params.kind.0.len() - 1 {
None
} else {
Some(1)
},
);
instructions.extend(instr);
if registers.len() > 0 {
original_param_regs.push(*registers.first().unwrap());
} else {
vararg = true;
}
}
let function_reg = registers.next().unwrap();
let mut param_regs = Vec::new();
for _ in &original_param_regs {
param_regs.push(registers.next().unwrap());
}
for (i, param_reg) in original_param_regs.iter().enumerate().rev() {
let new_reg = param_regs.get(i).unwrap();
if param_reg != new_reg {
instructions.push(PreInstr::Instr(Instruction::Move(*new_reg, *param_reg)));
}
}
if function_reg != *old_function_reg {
instructions.push(PreInstr::Instr(Instruction::Move(
function_reg,
*old_function_reg,
)));
}
if vararg {
let last_reg = param_regs.last().unwrap_or(&function_reg) + 1;
instructions.push(PreInstr::Instr(Instruction::MoveRetValues(last_reg)));
}
let last_param_reg = param_regs.last().unwrap_or(&function_reg);
let mut return_regs = Vec::new();
if let Some(expected_values) = expected_values {
for i in 0..expected_values {
let return_reg = i as u16 + function_reg;
return_regs.push(return_reg);
}
}
instructions.push(PreInstr::Instr(Instruction::Call(
*&function_reg,
if vararg { 0 } else { param_regs.len() as u16 },
if return_regs.len() == 0 {
0
} else {
return_regs.len() as u16 + 1
},
)));
(instructions, return_regs)
}

View File

@ -1,11 +1,11 @@
//! Usage example: //! Usage example:
//! ```rust //! ```rust
//! use ferrite_lua::{compile, vm}; //! use ferrite_lua::{compile, vm::{self, value}};
//! //!
//! #[derive(Debug, PartialEq, Eq)] //! #[derive(Debug, PartialEq, Eq)]
//! pub struct Print; //! pub struct Print;
//! impl vm::RustFunction for Print { //! impl value::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())
//! } //! }

View File

@ -23,6 +23,7 @@ pub enum Keyword {
Nil, Nil,
Not, Not,
For, For,
In,
While, While,
Repeat, Repeat,
Until, Until,
@ -48,6 +49,7 @@ impl Keyword {
"nil" => Keyword::Nil, "nil" => Keyword::Nil,
"not" => Keyword::Not, "not" => Keyword::Not,
"for" => Keyword::For, "for" => Keyword::For,
"in" => Keyword::In,
"do" => Keyword::Do, "do" => Keyword::Do,
"break" => Keyword::Break, "break" => Keyword::Break,
"goto" => Keyword::GoTo, "goto" => Keyword::GoTo,
@ -76,6 +78,7 @@ impl ToString for Keyword {
Keyword::Nil => "nil", Keyword::Nil => "nil",
Keyword::Not => "not", Keyword::Not => "not",
Keyword::For => "for", Keyword::For => "for",
Keyword::In => "in",
Keyword::While => "while", Keyword::While => "while",
Keyword::Repeat => "repeat", Keyword::Repeat => "repeat",
Keyword::Until => "until", Keyword::Until => "until",

View File

@ -5,79 +5,10 @@ use std::{cell::RefCell, collections::HashMap, fmt::Debug, hash::Hash, rc::Rc};
use crate::{ use crate::{
CompilationUnit, CompilationUnit,
ast::{BinaryOperator, UnaryOperator}, ast::{BinaryOperator, UnaryOperator},
vm::value::{IndexableValue, LuaBool, LuaInteger, VMFloat, Value},
}; };
#[derive(Clone, Hash, PartialEq, Eq, Copy, PartialOrd, Ord)] pub mod value;
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)] #[derive(Clone, Hash, PartialEq, Eq)]
pub enum Constant { pub enum Constant {
@ -269,261 +200,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))
}
_ => 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)] #[derive(Debug, Clone, Default)]
pub struct Prototype { pub struct Prototype {
pub instructions: Vec<Instruction>, pub instructions: Vec<Instruction>,
@ -576,7 +252,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>>>,
} }
@ -587,6 +263,7 @@ impl Closure {
for (i, param) in params.iter().enumerate() { for (i, param) in params.iter().enumerate() {
stack.insert(i as u16, Rc::new(RefCell::new(param.clone()))); stack.insert(i as u16, Rc::new(RefCell::new(param.clone())));
} }
ClosureRunner { ClosureRunner {
closure: self.clone(), closure: self.clone(),
program_counter: 0, program_counter: 0,
@ -596,6 +273,7 @@ impl Closure {
return_registers: Vec::new(), return_registers: Vec::new(),
top: 0, top: 0,
parameters: params, parameters: params,
to_close_upvalues: 0,
} }
} }
@ -620,6 +298,7 @@ pub struct ClosureRunner {
return_registers: Vec<u16>, return_registers: Vec<u16>,
top: u16, top: u16,
parameters: Vec<Value>, parameters: Vec<Value>,
to_close_upvalues: u16,
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -660,13 +339,17 @@ impl ClosureRunner {
.closure .closure
.upvalues .upvalues
.iter() .iter()
.map(|(v, _)| *v) .map(|(v, _)| *v as i32)
.max() .max()
.unwrap_or(0); .unwrap_or(-1);
let mut upvalues = self.closure.upvalues.clone(); let mut upvalues = self.closure.upvalues.clone();
for (reg, value) in &self.stack { for (reg, value) in self
upvalues.insert(reg + highest_upvalue + 1, value.clone()); .stack
.iter()
.filter(|(r, _)| **r < self.to_close_upvalues)
{
upvalues.insert(reg + (highest_upvalue + 1) as u16, value.clone());
} }
upvalues upvalues
} }
@ -699,19 +382,17 @@ impl ClosureRunner {
self.inner = None; self.inner = None;
if self.return_registers.len() == 0 { if self.return_registers.len() == 0 {
for (i, value) in ret_values.iter().enumerate() { for (i, value) in ret_values.iter().enumerate() {
self.stack.insert( self.set_stack(
self.function_register + i as u16 + 1, self.function_register + i as u16 + 1,
Rc::new(RefCell::new(value.clone())), StackValue::Value(value.clone()),
); );
} }
self.top = self.function_register + ret_values.len() as u16; self.top = self.function_register + ret_values.len() as u16;
} }
for (i, reg) in self.return_registers.iter().enumerate() { for (i, reg) in self.return_registers.clone().iter().enumerate() {
self.stack.insert( self.set_stack(
*reg, *reg,
Rc::new(RefCell::new( StackValue::Value(ret_values.get(i).cloned().unwrap_or(Value::Nil)),
ret_values.get(i).cloned().unwrap_or(Value::Nil),
)),
); );
} }
} else { } else {
@ -765,7 +446,7 @@ impl ClosureRunner {
} }
Instruction::LoadNil(from_reg, to_reg) => { Instruction::LoadNil(from_reg, to_reg) => {
for i in *from_reg..=*to_reg { for i in *from_reg..=*to_reg {
self.stack.insert(i, Rc::new(RefCell::new(Value::Nil))); self.set_stack(i, StackValue::Value(Value::Nil));
} }
} }
Instruction::SetGlobal(reg, global) => { Instruction::SetGlobal(reg, global) => {
@ -998,15 +679,18 @@ impl ClosureRunner {
_ => return Err(RuntimeError::TriedCallingNonFunction(value.clone())), _ => return Err(RuntimeError::TriedCallingNonFunction(value.clone())),
} }
} }
Instruction::Close(_) => {} Instruction::Close(up_until) => {
self.to_close_upvalues = *up_until;
}
Instruction::Closure(reg, protok) => { Instruction::Closure(reg, protok) => {
let upvalues = self.close_upvalues();
self.set_stack( self.set_stack(
*reg, *reg,
StackValue::Value(Value::Function(Closure { StackValue::Value(Value::Function(Closure {
vm: self.closure.vm.clone(), vm: self.closure.vm.clone(),
prototype: *protok, prototype: *protok,
environment: self.closure.environment.clone(), environment: self.closure.environment.clone(),
upvalues: self.close_upvalues(), upvalues,
})), })),
); );
} }

333
src/vm/value.rs Normal file
View File

@ -0,0 +1,333 @@
use std::{cell::RefCell, collections::HashMap, fmt::Debug, rc::Rc};
use crate::{
ast::{BinaryOperator, UnaryOperator},
vm::{Closure, RuntimeError},
};
#[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)
}
}
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(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()
}
}
}
}