Libraryfy usage
This commit is contained in:
parent
e259bae865
commit
0fcd00864b
66
examples/test.rs
Normal file
66
examples/test.rs
Normal file
@ -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<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();
|
||||
|
||||
runner.execute(&inner);
|
||||
while {
|
||||
match runner.next() {
|
||||
Ok(Some(_)) => false,
|
||||
Ok(None) => true,
|
||||
Err(e) => {
|
||||
print!("Error: {}", e);
|
||||
false
|
||||
}
|
||||
}
|
||||
} {}
|
||||
}
|
||||
|
||||
dbg!(&vm.get_globals());
|
||||
}
|
||||
@ -5,6 +5,7 @@ use crate::{
|
||||
vm::{Constant, Instruction, LuaBool, VMFloat},
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct State {
|
||||
pub constants: Vec<Constant>,
|
||||
pub prototypes: Vec<Vec<Instruction>>,
|
||||
@ -73,7 +74,11 @@ impl LocalCounter {
|
||||
}
|
||||
|
||||
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 inner_scope = scope.clone();
|
||||
@ -85,7 +90,7 @@ impl Block {
|
||||
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 inner_scope = scope.clone();
|
||||
@ -98,7 +103,7 @@ impl Block {
|
||||
}
|
||||
|
||||
impl Statement {
|
||||
pub fn find_constants(&self, scope: &mut Scope) -> HashSet<Constant> {
|
||||
fn find_constants(&self, scope: &mut Scope) -> HashSet<Constant> {
|
||||
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<Instruction> {
|
||||
fn compile(&self, state: &mut State, scope: &mut Scope) -> Vec<Instruction> {
|
||||
let mut instructions = Vec::new();
|
||||
|
||||
match self {
|
||||
@ -345,7 +350,7 @@ impl Statement {
|
||||
}
|
||||
|
||||
impl Expression {
|
||||
pub fn find_constants(&self, scope: &mut Scope) -> HashSet<Constant> {
|
||||
fn find_constants(&self, scope: &mut Scope) -> HashSet<Constant> {
|
||||
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,
|
||||
|
||||
116
src/lib.rs
Normal file
116
src/lib.rs
Normal file
@ -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<vm::Instruction>,
|
||||
state: compile::State,
|
||||
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,
|
||||
})
|
||||
}
|
||||
131
src/main.rs
131
src/main.rs
@ -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::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 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);
|
||||
}
|
||||
55
src/vm.rs
55
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<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 environment: Rc<RefCell<Environment>>,
|
||||
pub constants: Vec<Constant>,
|
||||
pub prototypes: HashMap<u32, Vec<Instruction>>,
|
||||
pub proto_counter: u32,
|
||||
pub(super) environment: Rc<RefCell<Environment>>,
|
||||
pub(super) constants: Vec<Constant>,
|
||||
pub(super) prototypes: HashMap<u32, Vec<Instruction>>,
|
||||
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<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)]
|
||||
@ -586,17 +614,12 @@ impl ClosureRunner {
|
||||
upvalues
|
||||
}
|
||||
|
||||
pub fn execute(
|
||||
&mut self,
|
||||
instructions: Vec<Instruction>,
|
||||
state: compile::State,
|
||||
constants: Vec<Constant>,
|
||||
) -> 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 {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user