//! 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) -> Result, 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, pub state: compile::State, pub 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(vm::Prototype { instructions: self.instructions.clone(), parameters: 0, }); 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, }) }