ferrite-lua/src/lib.rs
2026-03-20 22:43:58 +02:00

146 lines
3.6 KiB
Rust

//! Usage example:
//! ```rust
//! use ferrite_lua::{compile, vm::{self, value}};
//!
//! #[derive(Debug, PartialEq, Eq)]
//! pub struct Print;
//! impl value::RustFunction for Print {
//! fn execute(&self, parameters: Vec<value::Value>) -> Result<Vec<value::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()).unwrap();
//!
//! 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,
compile::process_pre_instrs,
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::value::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(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<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)?;
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: process_pre_instrs(instructions),
state,
constants,
})
}