Compare commits

..

10 Commits

Author SHA1 Message Date
2afe480be1 Fix warnings 2026-03-17 22:26:39 +02:00
0fda2c6d4b Fix some bugs 2026-03-17 22:26:02 +02:00
80dfd74ee5 Add very basic docs 2026-03-17 22:16:27 +02:00
f55f9be345 Add floats 2026-03-17 22:10:40 +02:00
8c0220c0c2 Allow dead code in token_stream 2026-03-17 21:57:42 +02:00
e30ecd865a Remove a bunch of warnings 2026-03-17 21:56:42 +02:00
0fcd00864b Libraryfy usage 2026-03-17 21:51:45 +02:00
e259bae865 Fix or and and operators 2026-03-17 21:11:17 +02:00
f6548019e3 Add keywords nil, true, false 2026-03-17 21:05:51 +02:00
1e2feb9c3c Add booleans 2026-03-17 20:58:15 +02:00
9 changed files with 410 additions and 238 deletions

View File

@ -20,10 +20,11 @@ global sometable = {}
sometable["hello"] = {} sometable["hello"] = {}
sometable["hello"].there = "my dude" sometable["hello"].there = "my dude"
print(max(11, 9)) print(max(11.12345, 9))
print(add(10)(15)) print(add(10)(15))
print(add(10)(15)) print(add(10)(15))
print(b) print(b)
print(min(11, 9)) print(min(11, 9))
print(10 - 15) print(10 - 15)
print("hello there!") print("hello there!")
print(true or 0)

65
examples/test.rs Normal file
View File

@ -0,0 +1,65 @@
use ferrite_lua::{
compile,
vm::{self, RuntimeError, RustFunction, VirtualMachine},
};
static TEST: &str = include_str!("../examples/test.lua");
#[derive(Debug, PartialEq, Eq)]
pub struct Max;
impl RustFunction for Max {
fn execute(&self, parameters: Vec<vm::Value>) -> Result<Vec<vm::Value>, RuntimeError> {
let lhs = parameters.get(0).cloned().unwrap_or(vm::Value::Nil);
let rhs = parameters.get(1).cloned().unwrap_or(vm::Value::Nil);
match lhs.lt(&rhs)? {
vm::Value::Boolean(value) => Ok(vec![if value.0 { rhs } else { lhs }]),
_ => Ok(vec![vm::Value::Nil]),
}
}
fn as_indexable(&self) -> String {
"std::max".to_owned()
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct Print;
impl RustFunction for Print {
fn execute(&self, parameters: Vec<vm::Value>) -> Result<Vec<vm::Value>, RuntimeError> {
println!("{:?}", parameters);
Ok(Vec::new())
}
fn as_indexable(&self) -> String {
"std::print".to_owned()
}
}
fn main() {
let compilation_unit = compile(TEST, None).unwrap();
let mut vm = VirtualMachine::default();
vm.set_global("max".into(), Max.into());
vm.set_global("print".into(), Print.into());
dbg!(&compilation_unit);
let mut runner = compilation_unit.with_virtual_machine(&mut vm).execute();
while runner.next().unwrap().is_none() {
// let inner = compile("print(b)", Some(&compilation_unit)).unwrap();
// let mut inner_runner = runner.execute(&inner);
// while {
// match inner_runner.next() {
// Ok(Some(_)) => false,
// Ok(None) => true,
// Err(e) => {
// println!("Error: {}", e);
// false
// }
// }
// } {}
}
dbg!(&vm.get_globals());
}

View File

@ -134,7 +134,7 @@ pub struct Function {
pub name: Option<Node<String>>, pub name: Option<Node<String>>,
pub params: Vec<Node<String>>, pub params: Vec<Node<String>>,
pub block: Block, pub block: Block,
pub meta: Metadata, pub _meta: Metadata,
} }
impl Parse for Function { impl Parse for Function {
@ -165,7 +165,7 @@ impl Parse for Function {
name, name,
params, params,
block, block,
meta: Metadata::produce(&mut stream, pre), _meta: Metadata::produce(&mut stream, pre),
}) })
} }
} }
@ -183,7 +183,7 @@ impl Parse for String {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Block { pub struct Block {
pub statements: Vec<Node<Statement>>, pub statements: Vec<Node<Statement>>,
pub meta: Metadata, pub _meta: Metadata,
} }
impl Parse for Block { impl Parse for Block {
@ -200,7 +200,7 @@ impl Parse for Block {
Ok(Block { Ok(Block {
statements, statements,
meta: Metadata::produce(&mut stream, pre), _meta: Metadata::produce(&mut stream, pre),
}) })
} }
} }
@ -465,12 +465,34 @@ impl Parse for PrimaryExpression {
Expression::FunctionDefinition(function.kind.params, function.kind.block) Expression::FunctionDefinition(function.kind.params, function.kind.block)
} else if let Some(Token::DecimalValue(value)) = peeked { } else if let Some(Token::DecimalValue(value)) = peeked {
stream.next(); // Consume decimal value stream.next(); // Consume decimal value
Expression::Literal(Literal::Integer(LuaInteger(
i64::from_str_radix(&value, 10).unwrap(), let initial_value = i64::from_str_radix(&value, 10).unwrap();
if stream.peek() == Some(Token::Symbol('.')) {
stream.next(); // consume dot
if let Some(Token::DecimalValue(decimal_string)) = stream.next() {
let decimal_value = u64::from_str_radix(&decimal_string, 10).unwrap();
let log10 = decimal_value.ilog10();
let decimal_value = decimal_value as f64 / (10u64.pow(log10 + 1) as f64);
Expression::Literal(Literal::Float(LuaFloat(
initial_value as f64 + decimal_value,
))) )))
} else {
return Err(stream.expected_err("number"));
}
} else {
Expression::Literal(Literal::Integer(LuaInteger(initial_value)))
}
} else if let Some(Token::StringLit(value)) = peeked { } else if let Some(Token::StringLit(value)) = peeked {
stream.next(); // Consume string-literal stream.next(); // Consume string-literal
Expression::Literal(Literal::String(value)) Expression::Literal(Literal::String(value))
} else if let Some(Token::Keyword(Keyword::True) | Token::Keyword(Keyword::False)) = peeked
{
let value = Token::Keyword(Keyword::True) == stream.next().unwrap();
Expression::Literal(Literal::Bool(value))
} else if let Some(Token::Keyword(Keyword::Nil)) = peeked {
stream.next(); // Consume nil
Expression::Literal(Literal::Nil)
} else if let Some(Token::Symbol('{')) = peeked { } else if let Some(Token::Symbol('{')) = peeked {
stream.next(); stream.next();
stream.expect_symbol('}')?; stream.expect_symbol('}')?;
@ -556,4 +578,6 @@ pub enum Literal {
Float(LuaFloat), Float(LuaFloat),
Integer(LuaInteger), Integer(LuaInteger),
String(String), String(String),
Bool(bool),
Nil,
} }

View File

@ -2,9 +2,10 @@ use std::collections::{HashMap, HashSet};
use crate::{ use crate::{
ast::{AccessModifier, BinaryOperator, Block, Expression, Literal, Statement, UnaryOperator}, ast::{AccessModifier, BinaryOperator, Block, Expression, Literal, Statement, UnaryOperator},
vm::{Constant, Instruction, VMFloat}, vm::{Constant, Instruction, LuaBool},
}; };
#[derive(Clone, Debug)]
pub struct State { pub struct State {
pub constants: Vec<Constant>, pub constants: Vec<Constant>,
pub prototypes: Vec<Vec<Instruction>>, pub prototypes: Vec<Vec<Instruction>>,
@ -15,7 +16,7 @@ impl State {
self.constants self.constants
.iter() .iter()
.enumerate() .enumerate()
.find(|(i, c)| *c == value) .find(|(_, c)| *c == value)
.unwrap() .unwrap()
.0 as u32 .0 as u32
} }
@ -73,7 +74,11 @@ impl LocalCounter {
} }
impl Block { impl Block {
pub fn find_constants(&self, scope: &mut Scope, constants: Vec<Constant>) -> HashSet<Constant> { pub(crate) fn find_constants(
&self,
scope: &mut Scope,
constants: Vec<Constant>,
) -> HashSet<Constant> {
let mut constants = constants.iter().cloned().collect::<HashSet<_>>(); let mut constants = constants.iter().cloned().collect::<HashSet<_>>();
let mut inner_scope = scope.clone(); let mut inner_scope = scope.clone();
@ -85,7 +90,7 @@ impl Block {
constants constants
} }
pub fn compile(&self, state: &mut State, scope: &mut Scope) -> Vec<Instruction> { pub(crate) fn compile(&self, state: &mut State, scope: &mut Scope) -> Vec<Instruction> {
let mut instructions = Vec::new(); let mut instructions = Vec::new();
let mut inner_scope = scope.clone(); let mut inner_scope = scope.clone();
@ -98,7 +103,7 @@ impl Block {
} }
impl Statement { impl Statement {
pub fn find_constants(&self, scope: &mut Scope) -> HashSet<Constant> { fn find_constants(&self, scope: &mut Scope) -> HashSet<Constant> {
match self { match self {
Statement::Assignment(access, names, expr_list) => { Statement::Assignment(access, names, expr_list) => {
let mut constants = HashSet::new(); let mut constants = HashSet::new();
@ -148,7 +153,7 @@ impl Statement {
} }
} }
pub fn compile(&self, state: &mut State, scope: &mut Scope) -> Vec<Instruction> { fn compile(&self, state: &mut State, scope: &mut Scope) -> Vec<Instruction> {
let mut instructions = Vec::new(); let mut instructions = Vec::new();
match self { match self {
@ -213,8 +218,7 @@ impl Statement {
None => { None => {
for (i, (name, indexes)) in names.iter().enumerate() { for (i, (name, indexes)) in names.iter().enumerate() {
if indexes.len() > 0 { if indexes.len() > 0 {
let mut table_reg = if let Some(reg) = scope.locals.get(&name.kind) let table_reg = if let Some(reg) = scope.locals.get(&name.kind) {
{
*reg *reg
} else if let Some(upval_reg) = scope.upvalues.get(&name.kind) { } else if let Some(upval_reg) = scope.upvalues.get(&name.kind) {
let local = scope.register_counter.next(); let local = scope.register_counter.next();
@ -345,7 +349,7 @@ impl Statement {
} }
impl Expression { impl Expression {
pub fn find_constants(&self, scope: &mut Scope) -> HashSet<Constant> { fn find_constants(&self, scope: &mut Scope) -> HashSet<Constant> {
match self { match self {
Expression::ValueRef(name) => { Expression::ValueRef(name) => {
let mut constants = HashSet::new(); let mut constants = HashSet::new();
@ -386,6 +390,16 @@ impl Expression {
constants.insert(Constant::String(value.clone())); constants.insert(Constant::String(value.clone()));
constants constants
} }
Literal::Bool(value) => {
let mut constants = HashSet::new();
constants.insert(Constant::Bool(LuaBool(*value)));
constants
}
Literal::Nil => {
let mut constants = HashSet::new();
constants.insert(Constant::Nil);
constants
}
}, },
Expression::TableConstructor => { Expression::TableConstructor => {
let constants = HashSet::new(); let constants = HashSet::new();
@ -394,7 +408,7 @@ impl Expression {
} }
} }
pub fn compile( fn compile(
&self, &self,
state: &mut State, state: &mut State,
scope: &mut Scope, scope: &mut Scope,
@ -474,15 +488,15 @@ impl Expression {
BinaryOperator::And => { BinaryOperator::And => {
instructions.push(Instruction::And( instructions.push(Instruction::And(
reg, reg,
*rhs.get(0).unwrap(),
*lhs.get(0).unwrap(), *lhs.get(0).unwrap(),
*rhs.get(0).unwrap(),
)); ));
} }
BinaryOperator::Or => { BinaryOperator::Or => {
instructions.push(Instruction::Or( instructions.push(Instruction::Or(
reg, reg,
*rhs.get(0).unwrap(),
*lhs.get(0).unwrap(), *lhs.get(0).unwrap(),
*rhs.get(0).unwrap(),
)); ));
} }
}; };
@ -604,6 +618,8 @@ impl Expression {
Literal::Float(value) => Constant::Float(value.vm_number()), Literal::Float(value) => Constant::Float(value.vm_number()),
Literal::String(value) => Constant::String(value.clone()), Literal::String(value) => Constant::String(value.clone()),
Literal::Integer(lua_integer) => Constant::Integer(*lua_integer), Literal::Integer(lua_integer) => Constant::Integer(*lua_integer),
Literal::Bool(value) => Constant::Bool(LuaBool(*value)),
Literal::Nil => Constant::Nil,
}), }),
)); ));
(instructions, vec![reg]) (instructions, vec![reg])

143
src/lib.rs Normal file
View File

@ -0,0 +1,143 @@
//! Usage example:
//! ```rust
//! use ferrite_lua::{compile, vm};
//!
//! #[derive(Debug, PartialEq, Eq)]
//! pub struct Print;
//! impl vm::RustFunction for Print {
//! fn execute(&self, parameters: Vec<vm::Value>) -> Result<Vec<vm::Value>, vm::RuntimeError> {
//! println!("{:?}", parameters);
//! Ok(Vec::new())
//! }
//!
//! fn as_indexable(&self) -> String {
//! "std::print".to_owned()
//! }
//! }
//!
//! let compilation_unit = compile("print(\"hello\")", None).unwrap();
//!
//! let mut vm = vm::VirtualMachine::default();
//! vm.set_global("print".into(), Print.into());
//!
//! let mut runner = compilation_unit.with_virtual_machine(&mut vm).execute();
//!
//! while runner.next().unwrap().is_none() { }
//! ```
use std::{fmt::Debug, path::PathBuf};
use thiserror::Error;
use crate::{
ast::Block,
token_stream::{
TokenStream, TokenStreamError,
lexer::{Error as TokenizerError, Token, tokenize},
},
vm::{ClosureRunner, VirtualMachine},
};
mod ast;
mod compile;
mod token_stream;
pub mod vm;
#[derive(Error, Debug)]
pub enum CompilationError {
#[error(transparent)]
TokenStreamError(#[from] TokenStreamError),
#[error(transparent)]
TokenizationError(#[from] TokenizerError),
}
#[derive(Clone)]
pub struct CompilationUnit {
pub instructions: Vec<vm::Instruction>,
pub state: compile::State,
pub constants: Vec<vm::Constant>,
}
impl Debug for CompilationUnit {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("CompilationUnit")
.field("instructions", &self.instructions)
.field("state", &self.state)
.field("constants", &self.constants)
.finish()
}
}
impl CompilationUnit {
pub fn with_virtual_machine<'a>(&self, vm: &'a mut VirtualMachine) -> ExecutionUnit<'a> {
let chunk_id = vm.new_prototype(self.instructions.clone());
for prototype in &self.state.prototypes {
vm.new_prototype(prototype.clone());
}
vm.constants = self.constants.clone();
ExecutionUnit { chunk_id, vm }
}
}
pub struct ExecutionUnit<'a> {
chunk_id: u32,
vm: &'a mut VirtualMachine,
}
impl<'a> ExecutionUnit<'a> {
pub fn execute(self) -> ClosureRunner {
let closure = self.vm.create_closure(self.chunk_id);
closure.run(Vec::new())
}
}
pub fn compile(
text: &str,
unit: Option<&CompilationUnit>,
) -> Result<CompilationUnit, CompilationError> {
let file_path = PathBuf::from("../examples/test.lua");
let tokens = tokenize(text)?;
let mut stream = TokenStream::from(&file_path, &tokens);
// dbg!(&tokens);
let chunk = stream.parse::<Block>()?;
stream.expect(Token::Eof)?;
// dbg!(&chunk);
let constants = chunk
.find_constants(
&mut Default::default(),
if let Some(unit) = unit {
unit.constants.clone()
} else {
Vec::new()
},
)
.into_iter()
.collect::<Vec<_>>();
let mut state = compile::State {
constants: constants.clone(),
prototypes: if let Some(unit) = unit {
unit.state.prototypes.clone()
} else {
Vec::new()
},
};
let mut scope = Default::default();
let instructions = chunk.compile(&mut state, &mut scope);
// dbg!(&instructions);
// dbg!(&constants);
Ok(CompilationUnit {
instructions,
state,
constants,
})
}

View File

@ -1,131 +0,0 @@
use std::{cell::RefCell, path::PathBuf, rc::Rc};
use crate::{
ast::{Block, Function},
token_stream::{
TokenStream,
lexer::{Token, tokenize},
},
vm::{LuaFloat, RuntimeError, RustFunction, VirtualMachine},
};
mod ast;
mod compile;
mod token_stream;
mod vm;
static TEST: &str = include_str!("../examples/test.lua");
#[derive(Debug, PartialEq, Eq)]
pub struct Max;
impl RustFunction for Max {
fn execute(&self, parameters: Vec<vm::Value>) -> Result<Vec<vm::Value>, RuntimeError> {
let lhs = parameters.get(0).cloned().unwrap_or(vm::Value::Nil);
let rhs = parameters.get(1).cloned().unwrap_or(vm::Value::Nil);
match lhs.lt(&rhs)? {
vm::Value::Integer(value) => Ok(vec![if value.0 > 0 { rhs } else { lhs }]),
_ => Ok(vec![vm::Value::Nil]),
}
}
fn as_indexable(&self) -> String {
"std::max".to_owned()
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct Print;
impl RustFunction for Print {
fn execute(&self, parameters: Vec<vm::Value>) -> Result<Vec<vm::Value>, RuntimeError> {
println!("{:?}", parameters);
Ok(Vec::new())
}
fn as_indexable(&self) -> String {
"std::print".to_owned()
}
}
fn compile(
text: &str,
constants: Vec<vm::Constant>,
prototypes: Vec<Vec<vm::Instruction>>,
) -> (Vec<vm::Instruction>, compile::State, Vec<vm::Constant>) {
let file_path = PathBuf::from("../examples/test.lua");
let tokens = tokenize(text).unwrap();
let mut stream = TokenStream::from(&file_path, &tokens);
dbg!(&tokens);
let chunk = stream.parse::<Block>().unwrap();
stream.expect(Token::Eof).unwrap();
dbg!(&chunk);
let constants = chunk
.find_constants(&mut Default::default(), constants)
.into_iter()
.collect::<Vec<_>>();
let mut state = compile::State {
constants: constants.clone(),
prototypes,
};
let mut scope = Default::default();
let instructions = chunk.compile(&mut state, &mut scope);
(instructions, state, constants)
}
fn main() {
let (instructions, state, constants) = compile(TEST, Vec::new(), Vec::new());
dbg!(&instructions);
dbg!(&constants);
let mut vm = VirtualMachine {
environment: Default::default(),
constants: constants.clone(),
prototypes: Default::default(),
proto_counter: 0,
};
let chunk_id = vm.new_prototype(instructions);
for prototype in &state.prototypes {
vm.new_prototype(prototype.clone());
}
dbg!(&vm.prototypes);
vm.environment.borrow_mut().set_global(
vm::Constant::String("max".to_owned()),
vm::StackValue::Value(vm::Value::RustFunction(Rc::new(RefCell::new(Max)))),
);
vm.environment.borrow_mut().set_global(
vm::Constant::String("print".to_owned()),
vm::StackValue::Value(vm::Value::RustFunction(Rc::new(RefCell::new(Print)))),
);
let closure = vm.create_closure(chunk_id);
let mut run = closure.run(Vec::new());
while run.next().unwrap().is_none() {
// let (instructions, state, constants) =
// compile("print(b)", constants.clone(), state.prototypes.clone());
// // dbg!(&instructions);
// let mut new_run = run.execute(instructions, state, constants);
// while {
// match new_run.next() {
// Ok(Some(_)) => false,
// Ok(None) => true,
// Err(e) => {
// print!("Error: {}", e);
// false
// }
// }
// } {}
}
dbg!(&vm.environment.borrow().globals);
}

View File

@ -16,6 +16,10 @@ pub enum Keyword {
Return, Return,
If, If,
Then, Then,
True,
False,
Nil,
Not,
} }
impl Keyword { impl Keyword {
@ -28,6 +32,10 @@ impl Keyword {
"return" => Keyword::Return, "return" => Keyword::Return,
"if" => Keyword::If, "if" => Keyword::If,
"then" => Keyword::Then, "then" => Keyword::Then,
"true" => Keyword::True,
"false" => Keyword::False,
"nil" => Keyword::Nil,
"not" => Keyword::Not,
_ => None?, _ => None?,
}) })
} }
@ -43,6 +51,10 @@ impl ToString for Keyword {
Keyword::Return => "return", Keyword::Return => "return",
Keyword::If => "if", Keyword::If => "if",
Keyword::Then => "then", Keyword::Then => "then",
Keyword::True => "true",
Keyword::False => "false",
Keyword::Nil => "nil",
Keyword::Not => "not",
} }
.to_string() .to_string()
} }

View File

@ -1,6 +1,6 @@
//! Contains relevant code for parsing tokens received from //! Contains relevant code for parsing tokens received from
//! Lexing/Tokenizing-stage. //! Lexing/Tokenizing-stage.
#![allow(dead_code)]
use std::{cell::RefCell, path::PathBuf, rc::Rc}; use std::{cell::RefCell, path::PathBuf, rc::Rc};
pub mod lexer; pub mod lexer;

212
src/vm.rs
View File

@ -1,16 +1,10 @@
use thiserror::Error; use thiserror::Error;
use std::{ use std::{cell::RefCell, collections::HashMap, fmt::Debug, hash::Hash, rc::Rc};
cell::{RefCell, RefMut},
collections::HashMap,
fmt::Debug,
hash::Hash,
rc::Rc,
};
use crate::{ use crate::{
CompilationUnit,
ast::{BinaryOperator, UnaryOperator}, ast::{BinaryOperator, UnaryOperator},
compile,
}; };
#[derive(Clone, Hash, PartialEq, Eq, Copy)] #[derive(Clone, Hash, PartialEq, Eq, Copy)]
@ -64,19 +58,50 @@ impl From<&LuaInteger> for LuaFloat {
} }
} }
#[derive(Clone, Copy, Hash, PartialEq, Eq)]
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 {
String(String), String(String),
Float(VMFloat), Float(VMFloat),
Integer(LuaInteger), Integer(LuaInteger),
Bool(LuaBool),
Nil,
}
impl From<&str> for Constant {
fn from(value: &str) -> Self {
Constant::String(value.to_owned())
}
} }
impl Debug for Constant { impl Debug for Constant {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { match self {
Self::String(arg0) => f.debug_tuple("String").field(arg0).finish(), Self::String(arg0) => f.debug_tuple("String").field(arg0).finish(),
Self::Float(arg0) => f.debug_tuple("Number").field(&arg0.lua_number()).finish(), Self::Float(arg0) => f.debug_tuple("Float").field(&arg0.lua_number()).finish(),
Self::Integer(arg0) => f.debug_tuple("Integer").field(arg0).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(),
} }
} }
} }
@ -189,36 +214,30 @@ pub enum RuntimeError {
} }
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone, Default)]
pub struct Environment { pub(crate) struct Environment {
pub globals: HashMap<Constant, Rc<RefCell<Value>>>, pub globals: HashMap<Constant, Rc<RefCell<Value>>>,
} }
impl Environment { impl Environment {
pub fn get_global(&mut self, key: &Constant) -> Option<StackValue> { fn get_global(&mut self, key: &Constant) -> Option<StackValue> {
let value = self.globals.get_mut(key)?; let value = self.globals.get_mut(key)?;
Some(match &*value.borrow() { Some(match &*value.borrow() {
_ => StackValue::Value(value.borrow().clone()), _ => StackValue::Value(value.borrow().clone()),
}) })
} }
pub fn set_global(&mut self, key: Constant, value: StackValue) { fn set_global(&mut self, key: Constant, value: StackValue) {
if let Some(existing) = self.globals.get_mut(&key) { if let Some(existing) = self.globals.get_mut(&key) {
match value { match value {
StackValue::Value(value) => { StackValue::Value(value) => {
*existing.borrow_mut() = value; *existing.borrow_mut() = value;
} }
StackValue::Ref(reference) => {
*existing = reference;
}
} }
} else { } else {
match value { match value {
StackValue::Value(value) => { StackValue::Value(value) => {
self.globals.insert(key, Rc::new(RefCell::new(value))); self.globals.insert(key, Rc::new(RefCell::new(value)));
} }
StackValue::Ref(reference) => {
self.globals.insert(key, reference);
}
} }
} }
} }
@ -229,6 +248,7 @@ pub enum Value {
String(String), String(String),
Float(VMFloat), Float(VMFloat),
Integer(LuaInteger), Integer(LuaInteger),
Boolean(LuaBool),
RustFunction(Rc<RefCell<dyn RustFunction>>), RustFunction(Rc<RefCell<dyn RustFunction>>),
Function(Closure), Function(Closure),
Nil, Nil,
@ -241,6 +261,7 @@ impl Value {
Value::String(value) => Ok(IndexableValue::String(value)), Value::String(value) => Ok(IndexableValue::String(value)),
Value::Float(value) => Ok(IndexableValue::Number(value)), Value::Float(value) => Ok(IndexableValue::Number(value)),
Value::Integer(value) => Ok(IndexableValue::Integer(value)), Value::Integer(value) => Ok(IndexableValue::Integer(value)),
Value::Boolean(value) => Ok(IndexableValue::Bool(value)),
Value::RustFunction(value) => { Value::RustFunction(value) => {
Ok(IndexableValue::RustFunction(value.borrow().as_indexable())) Ok(IndexableValue::RustFunction(value.borrow().as_indexable()))
} }
@ -254,6 +275,15 @@ impl Value {
match self { match self {
Value::Float(vmfloat) => Ok(vmfloat.lua_number()), Value::Float(vmfloat) => Ok(vmfloat.lua_number()),
Value::Integer(lua_integer) => Ok(lua_integer.into()), 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())), _ => Err(RuntimeError::NotFloatable(self.clone())),
} }
} }
@ -264,6 +294,7 @@ pub enum IndexableValue {
String(String), String(String),
Number(VMFloat), Number(VMFloat),
Integer(LuaInteger), Integer(LuaInteger),
Bool(LuaBool),
RustFunction(String), RustFunction(String),
Function(u32), Function(u32),
} }
@ -271,11 +302,14 @@ pub enum IndexableValue {
impl Value { impl Value {
pub fn add(&self, other: &Value) -> Result<Value, RuntimeError> { pub fn add(&self, other: &Value) -> Result<Value, RuntimeError> {
match (self, other) { match (self, other) {
(Value::Integer(lhs), Value::Integer(rhs)) => { (Value::Integer(_) | Value::Boolean(_), Value::Integer(_) | Value::Boolean(_)) => {
let res = LuaInteger(lhs.0 + rhs.0); let res = LuaInteger(self.as_integer()?.0 + other.as_integer()?.0);
Ok(Value::Integer(res)) Ok(Value::Integer(res))
} }
(Value::Float(_) | Value::Integer(_), Value::Float(_) | Value::Integer(_)) => { (
Value::Float(_) | Value::Integer(_) | Value::Boolean(_),
Value::Float(_) | Value::Integer(_) | Value::Boolean(_),
) => {
let res = LuaFloat(self.as_float()?.0 + other.as_float()?.0); let res = LuaFloat(self.as_float()?.0 + other.as_float()?.0);
Ok(Value::Float(res.vm_number())) Ok(Value::Float(res.vm_number()))
} }
@ -290,12 +324,11 @@ impl Value {
pub fn eq(&self, other: &Value) -> Result<Value, RuntimeError> { pub fn eq(&self, other: &Value) -> Result<Value, RuntimeError> {
match (self, other) { match (self, other) {
(Value::Integer(lhs), Value::Integer(rhs)) => { (Value::Integer(lhs), Value::Integer(rhs)) => {
let res = LuaInteger((lhs.0 == rhs.0) as i64); Ok(Value::Boolean(LuaBool(lhs.0 == rhs.0)))
Ok(Value::Integer(res))
} }
(Value::Float(_) | Value::Integer(_), Value::Float(_) | Value::Integer(_)) => { (Value::Float(_) | Value::Integer(_), Value::Float(_) | Value::Integer(_)) => {
let res = LuaInteger((self.as_float()?.0 == other.as_float()?.0) as i64); let res = LuaBool(self.as_float()?.0 == other.as_float()?.0);
Ok(Value::Integer(res)) Ok(Value::Boolean(res))
} }
_ => Err(RuntimeError::InvalidOperands( _ => Err(RuntimeError::InvalidOperands(
BinaryOperator::Equal, BinaryOperator::Equal,
@ -308,12 +341,11 @@ impl Value {
pub fn lt(&self, other: &Value) -> Result<Value, RuntimeError> { pub fn lt(&self, other: &Value) -> Result<Value, RuntimeError> {
match (self, other) { match (self, other) {
(Value::Integer(lhs), Value::Integer(rhs)) => { (Value::Integer(lhs), Value::Integer(rhs)) => {
let res = LuaInteger((lhs.0 < rhs.0) as i64); Ok(Value::Boolean(LuaBool(lhs.0 < rhs.0)))
Ok(Value::Integer(res))
} }
(Value::Float(_) | Value::Integer(_), Value::Float(_) | Value::Integer(_)) => { (Value::Float(_) | Value::Integer(_), Value::Float(_) | Value::Integer(_)) => {
let res = LuaInteger((self.as_float()?.0 < other.as_float()?.0) as i64); let res = LuaBool(self.as_float()?.0 < other.as_float()?.0);
Ok(Value::Integer(res)) Ok(Value::Boolean(res))
} }
_ => Err(RuntimeError::InvalidOperands( _ => Err(RuntimeError::InvalidOperands(
BinaryOperator::LessThan, BinaryOperator::LessThan,
@ -326,12 +358,11 @@ impl Value {
pub fn lte(&self, other: &Value) -> Result<Value, RuntimeError> { pub fn lte(&self, other: &Value) -> Result<Value, RuntimeError> {
match (self, other) { match (self, other) {
(Value::Integer(lhs), Value::Integer(rhs)) => { (Value::Integer(lhs), Value::Integer(rhs)) => {
let res = LuaInteger((lhs.0 <= rhs.0) as i64); Ok(Value::Boolean(LuaBool(lhs.0 <= rhs.0)))
Ok(Value::Integer(res))
} }
(Value::Float(_) | Value::Integer(_), Value::Float(_) | Value::Integer(_)) => { (Value::Float(_) | Value::Integer(_), Value::Float(_) | Value::Integer(_)) => {
let res = LuaInteger((self.as_float()?.0 <= other.as_float()?.0) as i64); let res = LuaBool(self.as_float()?.0 <= other.as_float()?.0);
Ok(Value::Integer(res)) Ok(Value::Boolean(res))
} }
_ => Err(RuntimeError::InvalidOperands( _ => Err(RuntimeError::InvalidOperands(
BinaryOperator::LessThanOrEqual, BinaryOperator::LessThanOrEqual,
@ -351,6 +382,10 @@ impl Value {
let res = LuaFloat(-lhs.lua_number().0); let res = LuaFloat(-lhs.lua_number().0);
Ok(Value::Float(res.vm_number())) Ok(Value::Float(res.vm_number()))
} }
Value::Boolean(val) => {
let res = LuaBool(!val.0);
Ok(Value::Boolean(res))
}
_ => Err(RuntimeError::InvalidOperand( _ => Err(RuntimeError::InvalidOperand(
UnaryOperator::Negation, UnaryOperator::Negation,
self.clone(), self.clone(),
@ -359,18 +394,10 @@ impl Value {
} }
pub fn and(&self, other: &Value) -> Result<Value, RuntimeError> { pub fn and(&self, other: &Value) -> Result<Value, RuntimeError> {
match (self, other) { if self.is_truthy() {
(Value::Float(_) | Value::Integer(_), Value::Float(_) | Value::Integer(_)) => Ok( Ok(self.clone())
Value::Integer(LuaInteger((self.is_truthy() && other.is_truthy()) as i64)), } else {
), Ok(other.clone())
(Value::Float(_) | Value::Integer(_), _) => {
Ok(Value::Integer(LuaInteger(self.is_truthy() as i64)))
}
(_, Value::Float(_) | Value::Integer(_)) => {
Ok(Value::Integer(LuaInteger(other.is_truthy() as i64)))
}
(Value::Nil, _) | (_, Value::Nil) => Ok(Value::Nil),
_ => Ok(Value::Nil),
} }
} }
@ -384,13 +411,14 @@ impl Value {
pub fn is_truthy(&self) -> bool { pub fn is_truthy(&self) -> bool {
match self { match self {
Value::String(value) => value.len() > 0, Value::String(_) => true,
Value::Float(vmfloat) => vmfloat.lua_number().0 > 0., Value::Float(_) => true,
Value::Integer(lua_integer) => lua_integer.0 > 0, Value::Integer(_) => true,
Value::Boolean(lua_bool) => lua_bool.0,
Value::RustFunction(_) => true, Value::RustFunction(_) => true,
Value::Function(_) => true, Value::Function(_) => true,
Value::Nil => false, Value::Nil => false,
Value::Table(value) => value.borrow().len() > 0, Value::Table(_) => true,
} }
} }
} }
@ -398,8 +426,9 @@ impl Value {
impl Debug for Value { impl Debug for Value {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { match self {
Value::Float(arg0) => f.debug_tuple("Number").field(&arg0.lua_number()).finish(), Value::Float(arg0) => f.debug_tuple("Float").field(&arg0.lua_number()).finish(),
Value::Integer(arg0) => f.debug_tuple("Integer").field(arg0).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::String(value) => f.debug_tuple("String").field(value).finish(),
Value::RustFunction(arg0) => f.debug_tuple("RustFunction").field(arg0).finish(), Value::RustFunction(arg0) => f.debug_tuple("RustFunction").field(arg0).finish(),
Value::Function(closure) => f Value::Function(closure) => f
@ -422,12 +451,18 @@ pub trait RustFunction: Debug {
fn as_indexable(&self) -> String; fn as_indexable(&self) -> String;
} }
#[derive(Debug, Clone)] impl<T: RustFunction + 'static> From<T> for Value {
fn from(value: T) -> Self {
Self::RustFunction(Rc::new(RefCell::new(value)))
}
}
#[derive(Debug, Clone, Default)]
pub struct VirtualMachine { pub struct VirtualMachine {
pub environment: Rc<RefCell<Environment>>, pub(super) environment: Rc<RefCell<Environment>>,
pub constants: Vec<Constant>, pub(super) constants: Vec<Constant>,
pub prototypes: HashMap<u32, Vec<Instruction>>, pub(super) prototypes: HashMap<u32, Vec<Instruction>>,
pub proto_counter: u32, pub(super) proto_counter: u32,
} }
impl VirtualMachine { impl VirtualMachine {
@ -440,7 +475,7 @@ impl VirtualMachine {
} }
impl VirtualMachine { impl VirtualMachine {
pub fn create_closure(&self, prototype: u32) -> Closure { pub(crate) fn create_closure(&self, prototype: u32) -> Closure {
Closure { Closure {
vm: self.clone(), vm: self.clone(),
prototype, prototype,
@ -448,14 +483,29 @@ impl VirtualMachine {
upvalues: HashMap::new(), upvalues: HashMap::new(),
} }
} }
pub fn set_global(&mut self, key: Constant, value: Value) {
self.environment
.borrow_mut()
.set_global(key, StackValue::Value(value));
}
pub fn get_globals(&self) -> HashMap<Constant, Value> {
let mut values = HashMap::new();
for (key, value) in &self.environment.borrow().globals {
values.insert(key.clone(), value.borrow().clone());
}
values
}
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Closure { pub struct Closure {
pub vm: VirtualMachine, vm: VirtualMachine,
pub prototype: u32, prototype: u32,
pub environment: Rc<RefCell<Environment>>, environment: Rc<RefCell<Environment>>,
pub upvalues: HashMap<u16, Rc<RefCell<Value>>>, upvalues: HashMap<u16, Rc<RefCell<Value>>>,
} }
impl Closure { impl Closure {
@ -488,43 +538,38 @@ impl Closure {
} }
pub struct ClosureRunner { pub struct ClosureRunner {
pub closure: Closure, closure: Closure,
pub program_counter: usize, program_counter: usize,
pub stack: HashMap<u16, Rc<RefCell<Value>>>, stack: HashMap<u16, Rc<RefCell<Value>>>,
pub inner: Option<Box<ClosureRunner>>, inner: Option<Box<ClosureRunner>>,
pub function_register: u16, function_register: u16,
pub return_registers: Vec<u16>, return_registers: Vec<u16>,
pub top: u16, top: u16,
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum StackValue { enum StackValue {
Value(Value), Value(Value),
Ref(Rc<RefCell<Value>>),
} }
impl ClosureRunner { impl ClosureRunner {
pub fn set_stack(&mut self, idx: u16, value: StackValue) { fn set_stack(&mut self, idx: u16, value: StackValue) {
if let Some(stack_slot) = self.stack.get_mut(&idx) { if let Some(stack_slot) = self.stack.get_mut(&idx) {
match value { match value {
StackValue::Value(value) => { StackValue::Value(value) => {
*stack_slot.borrow_mut() = value; *stack_slot.borrow_mut() = value;
} }
StackValue::Ref(ref_cell) => *stack_slot = ref_cell,
} }
} else { } else {
match value { match value {
StackValue::Value(value) => { StackValue::Value(value) => {
self.stack.insert(idx, Rc::new(RefCell::new(value))); self.stack.insert(idx, Rc::new(RefCell::new(value)));
} }
StackValue::Ref(reference) => {
self.stack.insert(idx, reference);
}
} }
} }
} }
pub fn get_stack(&mut self, idx: u16) -> StackValue { fn get_stack(&mut self, idx: u16) -> StackValue {
let value = self.stack.get(&idx); let value = self.stack.get(&idx);
if let Some(value) = value { if let Some(value) = value {
match &*value.borrow() { match &*value.borrow() {
@ -551,17 +596,12 @@ impl ClosureRunner {
upvalues upvalues
} }
pub fn execute( pub fn execute(&mut self, unit: &CompilationUnit) -> ClosureRunner {
&mut self,
instructions: Vec<Instruction>,
state: compile::State,
constants: Vec<Constant>,
) -> ClosureRunner {
let mut vm = self.closure.vm.clone(); let mut vm = self.closure.vm.clone();
vm.constants = constants; vm.constants = unit.constants.clone();
let proto_id = vm.new_prototype(instructions); let proto_id = vm.new_prototype(unit.instructions.clone());
for prototype in state.prototypes { for prototype in &unit.state.prototypes {
vm.new_prototype(prototype); vm.new_prototype(prototype.clone());
} }
let closure = Closure { let closure = Closure {
@ -626,6 +666,8 @@ impl ClosureRunner {
Constant::String(value) => Value::String(value.clone()), Constant::String(value) => Value::String(value.clone()),
Constant::Float(value) => Value::Float(*value), Constant::Float(value) => Value::Float(*value),
Constant::Integer(value) => Value::Integer(*value), Constant::Integer(value) => Value::Integer(*value),
Constant::Bool(lua_bool) => Value::Boolean(*lua_bool),
Constant::Nil => Value::Nil,
}), }),
); );
} }