diff --git a/examples/test.rs b/examples/test.rs new file mode 100644 index 0000000..2f0648f --- /dev/null +++ b/examples/test.rs @@ -0,0 +1,66 @@ +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) -> Result, 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) -> Result, 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(); + + runner.execute(&inner); + while { + match runner.next() { + Ok(Some(_)) => false, + Ok(None) => true, + Err(e) => { + print!("Error: {}", e); + false + } + } + } {} + } + + dbg!(&vm.get_globals()); +} diff --git a/src/compile.rs b/src/compile.rs index 4d19e58..d6540d8 100644 --- a/src/compile.rs +++ b/src/compile.rs @@ -5,6 +5,7 @@ use crate::{ vm::{Constant, Instruction, LuaBool, VMFloat}, }; +#[derive(Clone, Debug)] pub struct State { pub constants: Vec, pub prototypes: Vec>, @@ -73,7 +74,11 @@ impl LocalCounter { } impl Block { - pub fn find_constants(&self, scope: &mut Scope, constants: Vec) -> HashSet { + pub(crate) fn find_constants( + &self, + scope: &mut Scope, + constants: Vec, + ) -> HashSet { let mut constants = constants.iter().cloned().collect::>(); let mut inner_scope = scope.clone(); @@ -85,7 +90,7 @@ impl Block { constants } - pub fn compile(&self, state: &mut State, scope: &mut Scope) -> Vec { + pub(crate) fn compile(&self, state: &mut State, scope: &mut Scope) -> Vec { let mut instructions = Vec::new(); let mut inner_scope = scope.clone(); @@ -98,7 +103,7 @@ impl Block { } impl Statement { - pub fn find_constants(&self, scope: &mut Scope) -> HashSet { + fn find_constants(&self, scope: &mut Scope) -> HashSet { match self { Statement::Assignment(access, names, expr_list) => { let mut constants = HashSet::new(); @@ -148,7 +153,7 @@ impl Statement { } } - pub fn compile(&self, state: &mut State, scope: &mut Scope) -> Vec { + fn compile(&self, state: &mut State, scope: &mut Scope) -> Vec { let mut instructions = Vec::new(); match self { @@ -345,7 +350,7 @@ impl Statement { } impl Expression { - pub fn find_constants(&self, scope: &mut Scope) -> HashSet { + fn find_constants(&self, scope: &mut Scope) -> HashSet { match self { Expression::ValueRef(name) => { let mut constants = HashSet::new(); @@ -404,7 +409,7 @@ impl Expression { } } - pub fn compile( + fn compile( &self, state: &mut State, scope: &mut Scope, diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..2519ccb --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,116 @@ +use std::{fmt::Debug, marker::PhantomData, path::PathBuf}; + +use thiserror::Error; + +use crate::{ + ast::Block, + token_stream::{ + TokenStream, TokenStreamError, + lexer::{Error as TokenizerError, Token, tokenize}, + }, + vm::{ClosureRunner, VirtualMachine}, +}; + +pub mod ast; +pub mod compile; +pub 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 { + instructions: Vec, + state: compile::State, + constants: Vec, +} + +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 { + 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::()?; + 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::>(); + + 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, + }) +} diff --git a/src/main.rs b/src/main.rs deleted file mode 100644 index cd58060..0000000 --- a/src/main.rs +++ /dev/null @@ -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) -> Result, 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) -> Result, RuntimeError> { - println!("{:?}", parameters); - Ok(Vec::new()) - } - - fn as_indexable(&self) -> String { - "std::print".to_owned() - } -} - -fn compile( - text: &str, - constants: Vec, - prototypes: Vec>, -) -> (Vec, compile::State, Vec) { - 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::().unwrap(); - stream.expect(Token::Eof).unwrap(); - - dbg!(&chunk); - - let constants = chunk - .find_constants(&mut Default::default(), constants) - .into_iter() - .collect::>(); - - 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); -} diff --git a/src/vm.rs b/src/vm.rs index 852657c..c5f284e 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -9,6 +9,7 @@ use std::{ }; use crate::{ + CompilationUnit, ast::{BinaryOperator, UnaryOperator}, compile, }; @@ -94,6 +95,12 @@ pub enum Constant { Nil, } +impl From<&str> for Constant { + fn from(value: &str) -> Self { + Constant::String(value.to_owned()) + } +} + impl Debug for Constant { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -457,12 +464,18 @@ pub trait RustFunction: Debug { fn as_indexable(&self) -> String; } -#[derive(Debug, Clone)] +impl From for Value { + fn from(value: T) -> Self { + Self::RustFunction(Rc::new(RefCell::new(value))) + } +} + +#[derive(Debug, Clone, Default)] pub struct VirtualMachine { - pub environment: Rc>, - pub constants: Vec, - pub prototypes: HashMap>, - pub proto_counter: u32, + pub(super) environment: Rc>, + pub(super) constants: Vec, + pub(super) prototypes: HashMap>, + pub(super) proto_counter: u32, } impl VirtualMachine { @@ -475,7 +488,7 @@ impl VirtualMachine { } impl VirtualMachine { - pub fn create_closure(&self, prototype: u32) -> Closure { + pub(crate) fn create_closure(&self, prototype: u32) -> Closure { Closure { vm: self.clone(), prototype, @@ -483,6 +496,21 @@ impl VirtualMachine { upvalues: HashMap::new(), } } + + pub fn set_global(&mut self, key: Constant, value: Value) { + self.environment + .borrow_mut() + .set_global(key, StackValue::Value(value)); + } + + pub fn get_globals(&self) -> HashMap { + let mut values = HashMap::new(); + for (key, value) in &self.environment.borrow().globals { + values.insert(key.clone(), value.borrow().clone()); + } + + values + } } #[derive(Debug, Clone)] @@ -586,17 +614,12 @@ impl ClosureRunner { upvalues } - pub fn execute( - &mut self, - instructions: Vec, - state: compile::State, - constants: Vec, - ) -> ClosureRunner { + pub fn execute(&mut self, unit: &CompilationUnit) -> ClosureRunner { let mut vm = self.closure.vm.clone(); - vm.constants = constants; - let proto_id = vm.new_prototype(instructions); - for prototype in state.prototypes { - vm.new_prototype(prototype); + vm.constants = unit.constants.clone(); + let proto_id = vm.new_prototype(unit.instructions.clone()); + for prototype in &unit.state.prototypes { + vm.new_prototype(prototype.clone()); } let closure = Closure {