diff --git a/.gitignore b/.gitignore index af73913..c3881aa 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .idea target -Cargo.lock \ No newline at end of file +Cargo.lock +src/tests/main.lua \ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..50c2ed5 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "deimos"] + path = deimos + url = ssh://forgejo@git.mallard.gdn/PSx/Deimos.git diff --git a/.luarc.json b/.luarc.json new file mode 100644 index 0000000..0d7584e --- /dev/null +++ b/.luarc.json @@ -0,0 +1,5 @@ +{ + "diagnostics.disable": [ + "undefined-global" + ] +} \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index bbe798c..aaa1cec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,12 +3,11 @@ version = 4 [[package]] -name = "colored" -version = "3.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34" +name = "deimos" +version = "0.1.0" dependencies = [ - "windows-sys", + "macros", + "map", ] [[package]] @@ -17,25 +16,21 @@ version = "0.33.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7360bd2cd76e0cd9032d42cf2922155cecea2685b0cfa4630c3246df030bcfd6" +[[package]] +name = "macros" +version = "0.0.0" +dependencies = [ + "map", +] + +[[package]] +name = "map" +version = "0.1.0" + [[package]] name = "mars" version = "0.1.0" dependencies = [ - "colored", + "deimos", "glam", ] - -[[package]] -name = "windows-link" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" - -[[package]] -name = "windows-sys" -version = "0.61.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" -dependencies = [ - "windows-link", -] diff --git a/Cargo.toml b/Cargo.toml index af6a241..1174d14 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,11 +4,11 @@ version = "0.1.0" edition = "2024" [features] -bighash = [] +freelist = [] vector3 = ["dep:glam"] messages = [] assertions = [] [dependencies] glam = { version = "0.33.3", optional = true } -colored = { version = "3.1.1" } \ No newline at end of file +deimos = { path = "./deimos" } diff --git a/deimos b/deimos new file mode 160000 index 0000000..385a08b --- /dev/null +++ b/deimos @@ -0,0 +1 @@ +Subproject commit 385a08b094e43ff12b60a41a90ab99b4b18cea0c diff --git a/doc/.objects.autosave.xopp b/doc/.objects.autosave.xopp new file mode 100644 index 0000000..6caf4f8 Binary files /dev/null and b/doc/.objects.autosave.xopp differ diff --git a/doc/objects.xopp b/doc/objects.xopp new file mode 100644 index 0000000..6caf4f8 Binary files /dev/null and b/doc/objects.xopp differ diff --git a/src/compiler.rs b/src/compiler.rs index 66e4c08..557d23f 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -4,15 +4,15 @@ use crate::parser::{ Args, Block, Call, Chunk, Constructor, Expression, Function, LoadError, Name, Piece, Prefix, Statement, VarArg, Variable, }; -use crate::table::{Equivalent, Hashed, Hashes, Map, TableEntry}; use crate::vm::{Code, Index, Machine, Offset, Prototype, Reference, Value}; +use deimos::map::Map; use std::cmp::PartialEq; use std::ops::Deref; use std::rc::Rc; -pub(crate) struct Traversal<'a> { +pub(crate) struct Traversal<'a, S> { // A 'cursor' of where we are in assumed execution - machine: &'a mut Machine, + machine: &'a mut Machine, contexts: Vec, // Stack of function prototypes being compiled } @@ -20,40 +20,10 @@ pub(crate) struct Context { // Everything known about a closure at a time scopes: Vec, vararg: Option>>, - constants: Map, // map some constant value to an index for not repeating constants + constants: Map, // map some constant value to an index for not repeating constants prototype: Prototype, } -#[derive(Clone, Debug)] -struct ConstantEntry(Value, Index); - -impl Equivalent for ConstantEntry { - fn matches(&self, other: &Self) -> bool { - self.0 == other.0 - } -} - -impl Equivalent for ConstantEntry { - fn matches(&self, other: &Value) -> bool { - self.0 == *other - } -} - -impl Hashes for ConstantEntry { - fn hashed(&self) -> Hashed { - self.0.hashed() - } -} - -impl TableEntry for ConstantEntry { - fn new_vacant() -> Self { - ConstantEntry(Value::Nil, 0) - } - fn is_vacant(&self) -> bool { - matches!(self.0, Value::Nil) - } -} - #[derive(Clone, Debug)] enum BlindJump { Break(usize), @@ -70,7 +40,7 @@ pub(crate) struct Scope { upvalue: bool, } -impl Traversal<'_> { +impl Traversal<'_, S> { fn compile(&mut self, it: &T) -> Result<(), LoadError> where T: Compile, @@ -132,7 +102,7 @@ impl Traversal<'_> { | Code::RangeLoop(_) | Code::RangeBegin | Code::IteratorLoop(_) - | Code::Comment(_) => {} + | Code::Message(_) => {} } let prototype = &mut self.contexts.last_mut().unwrap().prototype; prototype.code.push(it.it); @@ -147,14 +117,8 @@ impl Traversal<'_> { fn push_constant(&mut self, it: Value) -> Index { // todo: constants need pieces too!!! // don't use constants that already exist - if let Some(entry) = &mut self - .contexts - .last_mut() - .unwrap() - .constants - .get_table(it.clone()) - { - entry.1 + if let Some(entry) = self.contexts.last_mut().unwrap().constants.get(it.clone()) { + *entry } else { let index = { let constants = &mut self.contexts.last_mut().unwrap().prototype.constants; @@ -163,11 +127,7 @@ impl Traversal<'_> { } .try_into() .unwrap(); - self.contexts - .last_mut() - .unwrap() - .constants - .set_table(ConstantEntry(it, index)); + self.contexts.last_mut().unwrap().constants.set(it, index); index } } @@ -247,7 +207,7 @@ impl Traversal<'_> { } Ok(context.prototype) } - pub(crate) fn new(machine: &mut Machine) -> Traversal { + pub(crate) fn new(machine: &mut Machine) -> Traversal { Traversal { machine, contexts: vec![Context { @@ -277,12 +237,12 @@ impl Traversal<'_> { // todo: not all impl Compile for Piece needs to be &T trait Compile { - fn compile(&self, traversal: &mut Traversal) -> Result<(), LoadError>; + fn compile(&self, traversal: &mut Traversal) -> Result<(), LoadError>; } -fn compile_function( +fn compile_function( this: &Piece<&Function>, - traversal: &mut Traversal, + traversal: &mut Traversal, method: bool, ) -> Result<(), LoadError> { let mut self_arguments = Vec::new(); @@ -341,9 +301,9 @@ enum Access { Get, } -fn compile_identifier( +fn compile_identifier( name: Piece, - traversal: &mut Traversal, + traversal: &mut Traversal, access: Access, ) -> Result<(), LoadError> { let contexts_len = traversal.contexts.len(); @@ -416,7 +376,7 @@ fn compile_identifier( } } } - // break 'search avoids this use of the _G table + // At this point there were no returns so we must use _G table traversal.push_string(name.it.clone(), name.unit())?; traversal.push_code(name.swap(Code::UpvalueGet(0))); traversal.push_code(name.swap(match access { @@ -426,9 +386,9 @@ fn compile_identifier( Ok(()) } -fn compile_variable( +fn compile_variable( variable: Piece<&Variable>, - traversal: &mut Traversal, + traversal: &mut Traversal, access: Access, ) -> Result<(), LoadError> { match &variable.it { @@ -455,13 +415,13 @@ fn compile_variable( } impl Compile for Piece<&Function> { - fn compile(&self, traversal: &mut Traversal) -> Result<(), LoadError> { + fn compile(&self, traversal: &mut Traversal) -> Result<(), LoadError> { compile_function(self, traversal, false) } } impl Compile for Piece<&Args> { - fn compile(&self, traversal: &mut Traversal) -> Result<(), LoadError> { + fn compile(&self, traversal: &mut Traversal) -> Result<(), LoadError> { let mut expressions_iter = self.it.0.iter().rev(); if let Some(expression) = expressions_iter.next() { let piece = expression.unit(); @@ -491,7 +451,7 @@ impl Compile for Piece<&Args> { } impl Compile for Piece<&Call> { - fn compile(&self, traversal: &mut Traversal) -> Result<(), LoadError> { + fn compile(&self, traversal: &mut Traversal) -> Result<(), LoadError> { let index = traversal.index().map(|it| it + 1).unwrap_or(0); // there will always be at least the callable traversal.compile(&self.it.args.inner_ref())?; // arguments traversal.compile(&self.it.prefix.as_ref().inner_ref())?; // call prefix @@ -508,7 +468,7 @@ impl Compile for Piece<&Call> { } impl Compile for Piece<&Prefix> { - fn compile<'a, 'b>(&self, traversal: &mut Traversal) -> Result<(), LoadError> { + fn compile<'a, 'b, S>(&self, traversal: &mut Traversal) -> Result<(), LoadError> { match &self.it { Prefix::Call(call) => { let index = traversal.index().unwrap(); @@ -528,7 +488,7 @@ impl Compile for Piece<&Prefix> { // for the sake of erroring alone impl Compile for Piece<&VarArg> { - fn compile<'a, 'b>(&self, traversal: &mut Traversal) -> Result<(), LoadError> { + fn compile<'a, 'b, S>(&self, traversal: &mut Traversal) -> Result<(), LoadError> { match &traversal.contexts.last().unwrap().vararg { Some(piece) => match &piece.it { Some(that_name) => match &self.it.0 { @@ -576,7 +536,7 @@ impl Compile for Piece<&VarArg> { } impl Compile for Piece<&Constructor> { - fn compile<'a, 'b>(&self, traversal: &mut Traversal) -> Result<(), LoadError> { + fn compile<'a, 'b, S>(&self, traversal: &mut Traversal) -> Result<(), LoadError> { traversal.push_code(self.swap(Code::TableNew)); let index_table = traversal.index().unwrap(); for field in self.it.0.iter() { @@ -626,7 +586,7 @@ impl Compile for Piece<&Constructor> { } impl Compile for Piece<&Expression> { - fn compile<'a>(&self, traversal: &mut Traversal) -> Result<(), LoadError> { + fn compile<'a, S>(&self, traversal: &mut Traversal) -> Result<(), LoadError> { match self.it { Expression::Nil => { traversal.push_literal(Value::Nil, self.unit()); @@ -684,7 +644,11 @@ fn jump_from(here: usize, there: usize) -> Result { Ok((there as i64 - here as i64) as Offset) } -fn resolve_break(traversal: &mut Traversal, begin: usize, end: usize) -> Result<(), LoadError> { +fn resolve_break( + traversal: &mut Traversal, + begin: usize, + end: usize, +) -> Result<(), LoadError> { for jump in std::mem::replace(&mut traversal.last_scope().jumps, Vec::new()) { // ? match jump { @@ -700,8 +664,8 @@ fn resolve_break(traversal: &mut Traversal, begin: usize, end: usize) -> Result< Ok(()) } -fn compile_assignment( - traversal: &mut Traversal, +fn compile_assignment( + traversal: &mut Traversal, variables: &Vec>, expressions: &Vec>, ) -> Result<(), LoadError> { @@ -709,7 +673,7 @@ fn compile_assignment( let mut expressions_iter = expressions.iter().peekable(); while let Some(expression) = expressions_iter.next() { let compile_variable_expression = |// Compile a variable normally with one value from an expression - traversal: &mut Traversal, + traversal: &mut Traversal, variable: Option<&Piece>, expression: &Piece<&Expression>| -> Result<(), LoadError> { @@ -784,7 +748,7 @@ fn compile_assignment( } impl Compile for Piece<&Block> { - fn compile(&self, traversal: &mut Traversal) -> Result<(), LoadError> { + fn compile(&self, traversal: &mut Traversal) -> Result<(), LoadError> { let last_scope_index = traversal.index().unwrap_or(0); let mut depth = 0; // Counter for 'dangling' scopes produced by any declarations to be closed. // todo: for some reason, register indices are called "_index" and code indices "index_"? fix it @@ -1016,7 +980,7 @@ impl Compile for Piece<&Block> { Statement::Declaration(names, expressions) => { let mut expressions_iter = expressions.iter().peekable(); let mut names_iter = names.iter().peekable(); - let compile_declaration = |traversal: &mut Traversal, + let compile_declaration = |traversal: &mut Traversal, expression: &Piece, name: &Piece| -> Result<(), LoadError> { @@ -1125,11 +1089,20 @@ impl Compile for Piece<&Block> { } } // Any 'dangling' scopes that weren't closed (declarations) will be at the end of this block + let return_index = traversal.index().unwrap_or(0); if let Some(return_expressions) = &self.it.return_exp { // arguments are backwards for return_expression in return_expressions.iter().rev() { traversal.compile(&return_expression.inner_ref())?; } + if !return_expressions.is_empty() { + traversal.push_code( + return_expressions + .first() + .unwrap() + .swap(Code::Return(return_index)), + ); + } } for _ in 0..depth { traversal.close_scope(); @@ -1140,12 +1113,12 @@ impl Compile for Piece<&Block> { } impl Compile for Chunk { - fn compile(&self, traversal: &mut Traversal) -> Result<(), LoadError> { + fn compile(&self, traversal: &mut Traversal) -> Result<(), LoadError> { traversal.compile(&self.0.inner_ref()) } } -pub(crate) fn compile(chunk: &Chunk, machine: &mut Machine) -> Result { +pub(crate) fn compile(chunk: &Chunk, machine: &mut Machine) -> Result { let mut traversal = Traversal::new(machine); traversal.compile(chunk)?; traversal.close_context() diff --git a/src/core.rs b/src/core.rs new file mode 100644 index 0000000..b2a2548 --- /dev/null +++ b/src/core.rs @@ -0,0 +1,18 @@ +use crate::vm::{Machine, ForeignFunction, Needle, RunError}; +pub(crate) fn load(machine: &mut Machine) -> Result<(),RunError> { + machine.set_global_function("print",|machine: &mut Machine, mut needle: Needle| { + while let Some(value) = needle.pop() { + print!("{} ", machine.meta_to_string(needle.dispatch, value)?.unwrap_str()) + } + println!(); + Ok(()) + })?; + machine.set_global_function("debug",|_machine: &mut Machine, mut needle: Needle| { + while let Some(value) = needle.pop() { + print!("{:?} ", value) + } + println!(); + Ok(()) + })?; + Ok(()) +} \ No newline at end of file diff --git a/src/gc.rs b/src/gc.rs deleted file mode 100644 index 3562c33..0000000 --- a/src/gc.rs +++ /dev/null @@ -1,154 +0,0 @@ -use std::alloc::{Layout, alloc, dealloc}; -use std::any::Any; -use std::cell::{Ref, RefCell, RefMut}; -use std::fmt::Debug; -use std::ops::{Deref, DerefMut}; -use std::ptr::NonNull; -// A simple 'stop the world' 'mark and sweep' garbage collector -// This approach is used since game engine frames provide a nice 'resting' interval for the interpreter to clean up faster -// todo! generational and naive reference counted collection -/* naive reference counting could be done as: -keep a traditional reference count => - - field count: u8 (>256 references -> a cycle will prevent premature deallocation anyway) -if the count reaches zero, prematurely deallocate by using an optional soft 'drop' method in the VM => - - field location: usize (location of this pointer in the big 'objects' array of the allocator) - - drop(&self,allocator) { - if count == 0 { - allocator[self.pointer->location] = nullptr; // make allocator forget this - dealloc(self.pointer); // deallocate now - } - } -in the allocator ensure location is consistent - - */ - -#[derive(Debug)] -pub struct Gc { - it: NonNull>>, -} - -pub struct Agc {} - -impl Clone for Gc { - fn clone(&self) -> Self { - Gc { it: self.it } - } -} - -impl Traverse for Gc { - fn traverse(&self) { - self.borrow().traverse() - } -} - -impl PartialEq for Gc { - fn eq(&self, other: &Gc) -> bool { - self.it == other.it - } -} - -impl Gc { - pub fn borrow(&self) -> impl Deref { - Ref::map( - unsafe { - // ??? - (*self.it.as_ptr()).borrow() - }, - |it| &it.data, - ) - } - pub fn borrow_mut(&mut self) -> impl DerefMut { - RefMut::map(unsafe { (*self.it.as_mut()).borrow_mut() }, |it| { - &mut it.data - }) - } - - pub fn replace(&mut self, new: T) - where - T: Sized, - { - unsafe { - (*self.it.as_mut()).borrow_mut().data = new; - } - } - - pub fn addr(&self) -> usize { - self.it.as_ptr().addr() - } -} - -#[derive(Clone, Debug, Copy)] -enum Color { - White, - Black, -} - -struct Header { - layout: Layout, - color: Color, // NOT for incremental tri-color right now; white == mark-free, black == mark-keep - data: T, -} - -pub struct Allocator { - // An array of pointers is used here since I did not want to use a compactor and larger types - // held on the GC (game objects) will most likely be massive anyway. - objects: Vec>>>>, -} - -impl Allocator { - pub fn new() -> Allocator { - Allocator { - objects: Vec::new(), - } - } - pub fn alloc(&mut self, it: T) -> Gc { - let layout = Layout::new::>>(); - let pointer = unsafe { - let pointer = alloc(layout) as *mut RefCell>; - assert!(!pointer.is_null()); // lol idk how to fix this rn (gah use a result!!!) - pointer.write(RefCell::new(Header { - layout, - color: Color::Black, - data: it, - })); - pointer - }; // todo: maybe use a compacting GC - self.objects.push(NonNull::new(pointer)); - Gc { - it: NonNull::new(pointer).unwrap(), - } - } - fn mark_white(&mut self) { - for i in 0..self.objects.len() { - if let Some(object) = self.objects[i] { - unsafe { (*object.as_ptr()).borrow_mut().color = Color::White }; - } - } - } - fn collect(&mut self) { - self.mark_white(); - self.objects.retain(|object| { - if let Some(object) = object { - let header = unsafe { (*object.as_ptr()).borrow_mut() }; - match header.color { - Color::White => { - unsafe { - dealloc(object.as_ptr() as *mut u8, header.layout); - } - true - } - Color::Black => false, - } - } else { - false - } - }); - } -} - -pub trait Traverse: Any + Debug { - fn traverse(&self) { - // mark or grey - () - } -} diff --git a/src/lib.rs b/src/lib.rs index 84db274..97ac9f1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,29 +1,52 @@ #![warn(clippy::cast_lossless)] +pub use crate::vm::*; +pub use deimos; + mod compiler; -mod gc; mod parser; mod table; mod vm; +mod core; +mod tasks; #[cfg(test)] mod tests { use crate::vm::Machine; - #[test] - fn simple() { - let mut machine = match Machine::new() { - Err(error) => panic!("{}", error), - Ok(machine) => machine, - }; - let closure = match machine.load(include_str!("tests/script.lua")) { - Err(error) => panic!("{}", error), - Ok(closure) => closure, - _ => panic!(), - }; - println!("{:?}", closure.borrow().prototype); - match machine.enter(closure) { - Err(error) => panic!("{}", error), - _ => {} + use crate::{RunError, Needle}; + + macro_rules! unwrap { + ($e:expr) => { + match $e { + Err(error) => panic!("{}", error), + Ok(result) => result, + } }; } + + fn output(machine: &mut Machine, mut needle: Needle) -> Result<(),RunError> { + let arg = needle.pop().ok_or(RunError::new("Expected string, got nil."))?; + let str = arg.try_string().ok_or(RunError::new("Expected string."))?; + machine.state.push_str(str.as_str()); + println!("{}",str.as_str()); + Ok(()) + } + + fn output_test(file: &str) -> Result,Box> { + let mut machine = Machine::new(|_| String::new())?; + machine.set_global_function("output",output)?; + let closure = machine.load(file)?; + machine.enter(closure)?; + Ok(machine) + } + + #[test] + fn hello() { + assert_eq!(unwrap!(output_test(include_str!("tests/hello.lua"))).state,"hello") + } + + #[test] + fn many() { + println!("{}",unwrap!(output_test(include_str!("tests/script.lua"))).state) + } } diff --git a/src/parser.rs b/src/parser.rs index eef2a63..6a750c1 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1,7 +1,6 @@ // PARSER use crate::vm::{Index, RunError}; -use colored::*; use std::cmp::{max, min}; use std::fmt::Formatter; // TODO: Vec> is good but sometimes we either need at least one or none, current fix is just to check if there is one and if not skip it, this takes extra performance diff --git a/src/table.rs b/src/table.rs index 64902d0..9359720 100644 --- a/src/table.rs +++ b/src/table.rs @@ -1,56 +1,6 @@ -use crate::gc::{Gc, Traverse}; -use crate::vm::{Callable, RunError, Value}; -use std::rc::Rc; - -// I've already spent 2 months on this interpreter, and I'm tired, so I've cut a few corners... -/* See some of Lua's notes on this table: https://www.lua.org/source/5.5/ltable.c.html -This table uses an 'open-addressed' hashmap https://en.wikipedia.org/wiki/Open_addressing -where collisions are handled by 'linear-probing' and something I call 'displacement'. - -Motivation: -Afaik, without having some mechanism that records or ensures known proximity between a displaced element -that collided with other(s) in the hashmap and it's original home (at element's hash's index), -the entire array may have to be checked just to see if an element is present. How can we fix this? - -Solution: -Lua uses Brent's method, which I couldn't find a concrete explanation of, here I'm just recording -the maximum displacement that will need to be linearly-probed from the home entry to be certain of -any 'home' element's presence in the greater array. - -Notes: -- If this value becomes large, the load factor is probably high and there would be a resize. -- Values would only need to be shuffled around closer to their 'home' during a resize. -- It is unlikely the displacement would large value without a resize amending that problem. - */ - -type Displacement = u8; - -#[cfg(feature = "bighash")] -pub type Hashed = u64; -#[cfg(not(feature = "bighash"))] -pub type Hashed = u32; - -pub trait Equivalent: Sized { - fn matches(&self, other: &T) -> bool; -} - -impl Equivalent for T -where - T: PartialEq, -{ - fn matches(&self, other: &T) -> bool { - self == other - } -} - -pub trait Hashes { - fn hashed(&self) -> Hashed; -} - -pub trait TableEntry: Equivalent + Hashes { - fn new_vacant() -> Self; - fn is_vacant(&self) -> bool; -} +use crate::vm::{Callable, MarsAllocator, RunError, Value}; +use deimos::gc::{Id, MaybeId, Traverse}; +use deimos::map::{DisplacementSet, Equivalent, Hashed, Hashes, TableEntry}; #[derive(Debug, Clone)] struct KeyValue { @@ -71,12 +21,10 @@ impl Hashes for KeyValue { } impl TableEntry for KeyValue { - fn new_vacant() -> Self { - KeyValue { - index: Value::Nil, - value: Value::Nil, - } - } + const VACANT: Self = KeyValue { + index: Value::Nil, + value: Value::Nil, + }; fn is_vacant(&self) -> bool { matches!( self, @@ -88,221 +36,6 @@ impl TableEntry for KeyValue { } } -#[derive(Debug, Clone)] -struct Entry { - entry: E, - home: usize, - displacement: Displacement, -} - -#[derive(Clone, Debug)] -pub struct Map { - // default representing vacancy! - map: Vec>, - map_bounds: std::ops::Range, - map_count: usize, // number of elements in table -} - -impl Map { - fn exchange_table(&mut self, len: usize) { - let old = std::mem::replace( - &mut self.map, - vec![ - Entry { - entry: E::new_vacant(), - home: 0, - displacement: 0, - }; - len - ], - ); - self.map_bounds = self.table_lower()..self.table_upper(); - for Entry { entry, .. } in old { - if !entry.is_vacant() { - self.set_table(entry) - } - } - } - pub fn resize_table(&mut self, len: usize) { - self.exchange_table(len.max(self.map_count + (self.map_count / 3))); - self.map_bounds = 0..self.table_upper(); - } - fn table_upper(&self) -> usize { - (self.map.len() / 4) * 3 - } - fn table_lower(&self) -> usize { - self.map.len() / 3 - } - fn ensure_table(&mut self) { - if self.map_count + 1 > self.table_upper() { - // free space is short - self.exchange_table((self.map.len() + 4) * 2); - } else if self.map_count < self.table_lower() { - // too much free space - self.exchange_table(self.map.len() / 2) - } - } - pub(crate) fn set_table(&mut self, entry: E) { - #[cfg(feature = "assertions")] - assert!(!entry.is_vacant()); - if !self.map_bounds.contains(&(self.map_count + 1)) { - // make sure the table is appropriately sized - self.ensure_table(); - } - let range = self.map.len(); - let home = entry.hashed() as usize % range; - if self.map[home].entry.is_vacant() { - // attempt to place it directly in an empty space - // INSERT - self.map[home].home = home; - self.map[home].entry = entry; - self.map[home].displacement = self.map[home].displacement.max(0); - self.map_count += 1; - } else if self.map[home].entry.matches(&entry) { - // attempt to replace it directly - // REPLACE - self.map[home].entry = entry; // home.item.soft_drop() - } else { - // attempt to replace it in a collided neighbour location - for i in 1..self.map[home].displacement + 1 { - let neighbour = &mut self.map[(home + i as usize) % range]; // todo: is mod expensive? - if neighbour.entry.matches(&entry) { - // found where it was displaced to - // REPLACE - neighbour.entry = entry; - return; - } - } - // at this point it must be added, probe to place in an empty space - for j in self.map[home].displacement + 1..Displacement::MAX - 1 { - let neighbour = &mut self.map[(home + j as usize) % range]; - if neighbour.entry.is_vacant() { - // new empty slot hooray! - // INSERT - neighbour.entry = entry; - neighbour.home = home; - self.map[home].displacement = j; - self.map_count += 1; - return; - } - } - // impossible but this still needs to be complete - #[cfg(feature = "messages")] - eprintln!("Large table collision, are the hashes ok?\n\tBrute-force probing..."); - let mut free: Option = None; - for k in 0..self.map.len() { - let neighbour = &mut self.map[k]; - if neighbour.entry.is_vacant() && free.is_none() { - free = Some(k); - } else if neighbour.entry.matches(&entry) { - // REPLACE - neighbour.entry = entry; - return; - } - } - if let Some(k) = free { - // INSERT - self.map[k].home = home; - self.map[k].entry = entry; - self.map[home].displacement = Displacement::MAX; - self.map_count += 1; - return; - } - // must resize - #[cfg(feature = "messages")] - eprintln!("\n\tResizing..."); - self.resize_table((self.map.len() + 4) * 2); - self.set_table(entry); - } - } - pub(crate) fn rem_table(&mut self, index: K) - where - E: Equivalent, - K: Hashes, - { - if self.map.len() == 0 { - return; - } - let range = self.map.len(); - let home = index.hashed() as usize % range; - if self.map[home].entry.matches(&index) { - self.map[home].entry = E::new_vacant(); - self.map_count -= 1; - } else { - if self.map[home].displacement != Displacement::MAX { - let mut largest = 0; - for i in 1..self.map[home].displacement { - let neighbour = &mut self.map[(home + i as usize) % range]; // todo: maybe mod slows this down - if neighbour.entry.matches(&index) { - neighbour.entry = E::new_vacant(); - self.map_count -= 1; - if i == self.map[home].displacement { - self.map[home].displacement = largest - } - return; - } else if !neighbour.entry.is_vacant() && neighbour.home == home { - largest = i; - } - } - } else { - let mut largest = 0; - let mut finished = false; // what - for k in 0..self.map.len() { - let neighbour = &mut self.map[k]; - if neighbour.entry.matches(&index) { - neighbour.entry = E::new_vacant(); - self.map_count -= 1; - #[cfg(feature = "assertions")] - assert!(!finished); - finished = true; - } - if neighbour.home == home && !neighbour.entry.is_vacant() { - largest = largest.max(if k < home { - k + self.map.len() - home - 1 // ? - } else { - k - home - }) - } - } - self.map[home].displacement = if largest > Displacement::MAX as usize { - Displacement::MAX - } else { - largest as Displacement - } - } - } - } - pub(crate) fn get_table(&mut self, index: K) -> Option<&E> - where - E: Equivalent, - K: Hashes, - { - if self.map.len() == 0 { - return None; - } - let location = index.hashed() as usize % self.map.len(); - let home = &self.map[location]; - if home.entry.matches(&index) { - Some(&home.entry) - } else { - for i in 1..home.displacement as usize { - let neighbour = &self.map[(location + i) % self.map.len()]; - if neighbour.entry.matches(&index) { - return Some(&neighbour.entry); - } - } - None - } - } - pub(crate) fn new() -> Map { - Map { - map: Vec::new(), - map_count: 0, - map_bounds: (0..0).into(), - } - } -} - impl Hashes for Value { fn hashed(&self) -> Hashed { match self { @@ -314,11 +47,11 @@ impl Hashes for Value { 0 } } - Value::CachedString(cached) => cached.1, - Value::String(_string, hash) => *hash, + Value::CachedString(hashed) => hashed.1, + Value::String(hashed) => hashed.1, Value::Function(callable) => match callable { - Callable::Rust(native) => Rc::as_ptr(native).addr() as Hashed, - Callable::Mars(closure) => closure.addr() as Hashed, + Callable::Rust(native) => native.hashed(), + Callable::Mars(closure) => closure.hashed(), }, Value::Integer(integer) => *integer as Hashed, Value::Number(number) => { @@ -328,8 +61,8 @@ impl Hashes for Value { number.to_bits() as Hashed } } - Value::Table(table) => table.addr() as Hashed, - Value::Object(object) => object.addr() as Hashed, + Value::Table(table) => table.hashed(), + Value::Object(object, ..) => object.hashed(), #[cfg(feature = "vector3")] Value::Vector(vec) => { let mut hasher = DefaultHasher::new(); @@ -341,23 +74,28 @@ impl Hashes for Value { } // todo: displaced items don't ever get shuffled closer to their homes unless the table is resized -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct Table { - table: Map, + table: DisplacementSet, array: Vec, - pub meta: Option>, + pub meta: MaybeId, } -impl Traverse for Table { - fn traverse(&self) { - for Entry { entry, .. } in self.table.map.iter() { +impl Traverse for Table +where + C: MarsAllocator, +{ + fn traverse(id: &Id
, context: &mut C) { + for i in 0..context.freelist().get(id).table.map.len() { + let entry = context.freelist().get(id).table.map[i].entry.clone(); if !entry.is_vacant() { - entry.index.traverse(); - entry.value.traverse(); + entry.index.traverse(context); + entry.value.traverse(context); } } - for item in self.array.iter() { - item.traverse() + for i in 0..context.freelist().get(id).array.len() { + let item = context.freelist().get(id).array[i].clone(); + item.traverse(context); } } } @@ -378,24 +116,24 @@ impl Table { // This is an integer index, try the array first match value { Value::Nil => { - if (0..self.array.len() + 1).contains(&(index as usize)) { - if self.array.len() == index as usize { + if (0..self.array.len() + 1).contains(&(index as usize - 1)) { + if self.array.len() == index as usize - 1 { self.array.pop(); } else { - self.array[index as usize + 1] = Value::Nil + self.array[index as usize - 1] = Value::Nil } } - self.table.rem_table(Value::Integer(index)); + self.table.remove(Value::Integer(index)); } item => { - if (0..self.array.len() + 1).contains(&(index as usize)) { - if self.array.len() == index as usize { + if (0..self.array.len() + 1).contains(&(index as usize - 1)) { + if self.array.len() == index as usize - 1 { self.array.push(item) } else { - self.array[index as usize + 1] = item; + self.array[index as usize - 1] = item; } } else { - self.table.set_table(KeyValue { + self.table.set(KeyValue { index: Value::Integer(index), value: item, }) @@ -407,7 +145,7 @@ impl Table { Value::Nil => Err(RunError( "Attempt to set new index of table with key: nil".to_string(), )), - index => Ok(self.table.set_table(KeyValue { index, value })), + index => Ok(self.table.set(KeyValue { index, value })), } } pub fn get(&mut self, index: Value) -> Result { @@ -422,11 +160,8 @@ impl Table { // todo: move this into the Map Ok(self .table - .get_table(index) - .unwrap_or(&KeyValue { - index: Value::Nil, - value: Value::Nil, - }) + .get(index) + .unwrap_or(&KeyValue::VACANT) .value .clone()) } @@ -437,9 +172,9 @@ impl Table { } pub fn new() -> Self { Table { - table: Map::new(), + table: DisplacementSet::new(), array: Vec::new(), - meta: None, + meta: MaybeId::NULL, } } } diff --git a/src/tasks.rs b/src/tasks.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/tests/hello.lua b/src/tests/hello.lua new file mode 100644 index 0000000..4835649 --- /dev/null +++ b/src/tests/hello.lua @@ -0,0 +1 @@ +output "hello" \ No newline at end of file diff --git a/src/tests/script.lua b/src/tests/script.lua index a854da9..8535bbd 100644 --- a/src/tests/script.lua +++ b/src/tests/script.lua @@ -1,9 +1,5 @@ -local var = 2 -local function thing() - print(var) - var = 3 +local tab = {} +for i = 1, 1000000 do + tab[i] = true end - -print(var) -thing() -thing() +output(#tab) \ No newline at end of file diff --git a/src/vm.rs b/src/vm.rs index f2067a0..fe3cbc9 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -1,16 +1,14 @@ // MACHINE use crate::parser::{BinaryOp, Piece, UnaryOp, parse}; -use crate::table::{Equivalent, Hashed, Hashes, Map, Table, TableEntry}; -use std::any::Any; -use std::fmt::{Display, Formatter}; -use std::hash::{DefaultHasher, Hash, Hasher}; -use std::ops::{Deref, DerefMut}; +use deimos::gc::{Allocates, AnyId, FreeList, Id, MaybeId, Traverse}; +use deimos::map::{DisplacementSet, Equivalent, Hashed, Hashes, MapEntry, TableEntry}; +use std::fmt::{Debug, Display, Formatter}; +use std::ops::Deref; use std::rc::Rc; // Rust function to call // todo: make needles more private -pub struct Native(fn(&mut Machine, &mut Needle, usize) -> Result<(), RunError>); // A native function in Rust #[derive(Clone, Debug, Copy, PartialEq)] pub(crate) enum Reference { @@ -65,7 +63,7 @@ enum Upvalue { pub struct Closure { // An actual callable function pub(crate) prototype: Rc, - upvalues: Vec>, // List of upvalues associated with this function + upvalues: Vec>, // List of upvalues associated with this function } impl std::fmt::Debug for Closure { @@ -73,41 +71,55 @@ impl std::fmt::Debug for Closure { writeln!(f, "Closure:")?; writeln!(f, " Upvalues:")?; for (index, upvalue) in self.upvalues.iter().enumerate() { - writeln!(f, " [{}] {:?}", index, upvalue.borrow().deref())?; + writeln!(f, " [{}] {:?}", index, upvalue)?; } write!(f, "{:?}", self.prototype) } } -impl Traverse for Upvalue { - fn traverse(&self) { - if let Upvalue::Heap(object) = self { - object.traverse() +pub trait MarsAllocator: + Allocates
+ Allocates + Allocates + Allocates +{ +} + +impl Traverse for Upvalue +where + C: MarsAllocator, +{ + fn traverse(id: &Id, context: &mut C) { + if let Upvalue::Heap(object) = context.freelist().get(id).clone() { + object.traverse(context); } } } #[derive(Clone)] pub enum Callable { - Rust(Rc), - Mars(Gc), + Rust(AnyId), + Mars(Id), } -impl Traverse for Callable { - fn traverse(&self) { +impl Callable { + fn traverse(&self, context: &mut C) + where + C: MarsAllocator, + { match self { Callable::Rust(_) => {} Callable::Mars(closure) => { - closure.traverse(); + Closure::traverse(closure, context); } } } } -impl Traverse for Closure { - fn traverse(&self) { - for upvalue in self.upvalues.iter() { - upvalue.traverse() +impl Traverse for Closure +where + C: MarsAllocator, +{ + fn traverse(id: &Id, context: &mut C) { + for i in 0..context.freelist().get(id).upvalues.len() { + Upvalue::traverse(&context.freelist().get(id).upvalues[i].clone(), context); } } } @@ -116,7 +128,7 @@ impl PartialEq for Callable { fn eq(&self, other: &Self) -> bool { match self { Callable::Rust(a) => match other { - Callable::Rust(b) => Rc::ptr_eq(a, b), + Callable::Rust(b) => a == b, Callable::Mars(_) => false, }, Callable::Mars(a) => match other { @@ -131,13 +143,25 @@ impl std::fmt::Debug for Callable { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { Callable::Rust(_) => f.write_str("Native"), - Callable::Mars(closure) => write!(f, "{:?}", closure.borrow().deref()), + Callable::Mars(closure) => write!(f, "{:?}", closure), } } } #[derive(Debug)] -pub struct RunError(pub(crate) String); +pub struct RunError(pub String); + +impl RunError { + pub fn new(string: &str) -> RunError { + RunError(string.to_string()) + } +} + +impl From for RunError { + fn from(value: String) -> Self { + RunError(value) + } +} impl Display for RunError { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { @@ -145,67 +169,38 @@ impl Display for RunError { } } -pub trait Object: std::fmt::Debug { - fn meta_string(&mut self) -> Result { - Ok(Value::Nil) - } - fn meta_number(&mut self) -> Result { - Ok(Value::Nil) - } - fn meta_new_index(&mut self, index: Value, item: Value) -> Result<(), RunError> { - Err(RunError(format!("Cannot set index in {:?}", &self))) - } - fn meta_index(&mut self, index: Value) -> Result { - Err(RunError(format!("Cannot get index in {:?}", &self))) - } - fn meta_eq(&mut self, other: &Value) -> Result { - Ok(Value::Nil) - } -} - -impl Value {} - -pub trait Userdata: Traverse + std::fmt::Debug + Any + Object {} - -pub struct Anything(dyn Userdata); // can't impl PartialEq for Rc - -impl std::fmt::Debug for Anything { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - self.0.fmt(f) - } -} - -impl Traverse for Anything { - fn traverse(&self) { - self.0.traverse() - } -} +impl std::error::Error for RunError {} use crate::compiler::compile; -use crate::gc::{Allocator, Gc, Traverse}; +use crate::core::load; +use crate::table::Table; #[cfg(feature = "vector3")] use glam::Vec3; -#[derive(Clone, Debug, PartialEq)] -struct Phrase { - bytes: [u8; PHRASE_LEN], - hash: usize, -} - -// Any value in the language const SHORT_STRING_LEN: usize = 40; #[derive(Clone, Debug)] -pub(crate) struct CachedString(Rc, pub(crate) Hashed); -#[derive(Clone, Debug)] -struct CachedStringEntry(Option); -impl Hashes for &str { +pub struct HashedString(Rc, pub(crate) Hashed); + +impl Hashes for HashedString { fn hashed(&self) -> Hashed { - let mut hasher = DefaultHasher::new(); - self[..self.len().min(6)].hash(&mut hasher); - hasher.finish() as Hashed + self.1 } } +impl PartialEq for HashedString { + fn eq(&self, other: &Self) -> bool { + self.1 == other.1 && self.0 == other.0 + } +} + +impl HashedString { + pub fn as_str(&self) -> &str { + self.0.deref() + } +} + +#[derive(Clone, Debug)] +struct CachedStringEntry(Option); impl Hashes for CachedStringEntry { fn hashed(&self) -> Hashed { self.0.as_ref().unwrap().0.deref().hashed() @@ -226,39 +221,59 @@ impl Equivalent for CachedStringEntry { } impl TableEntry for CachedStringEntry { - fn new_vacant() -> Self { - CachedStringEntry(None) - } + const VACANT: Self = CachedStringEntry(None); fn is_vacant(&self) -> bool { self.0.is_none() } } impl Equivalent<&str> for CachedStringEntry { - fn matches(&self, string: &&str) -> bool { - match self.0 { - Some(ref entry) => entry.0.deref() == *string, - None => false, + fn matches(&self, other: &&str) -> bool { + if let Some(ref string) = self.0 { + string.as_str() == *other + } else { + false } } } -const PHRASE_LEN: usize = 16; -#[derive(Debug, Clone)] +#[derive(Clone)] pub enum Value { Nil, // Empty value Bool(bool), Number(f64), Integer(u64), // 64 maybe we should use signed integers... - CachedString(CachedString), - String(Rc, Hashed), - Table(Gc
), - Object(Gc), + CachedString(HashedString), + String(HashedString), + Table(Id
), + Object(AnyId, AnyId), Function(Callable), #[cfg(feature = "vector3")] Vector(Vec3), } +impl Default for Value { + fn default() -> Self { + Value::Nil + } +} + +impl Debug for Value { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + Value::Nil => write!(f, "nil"), + Value::Bool(boolean) => write!(f, "{}", boolean), + Value::Number(number) => write!(f, "{}", number), + Value::Integer(integer) => write!(f, "{}", integer), + Value::CachedString(string) => write!(f, "{}", string.0.deref()), + Value::String(string) => write!(f, "{}", string.0.deref()), + Value::Table(it) => write!(f, "{:?}", it), + Value::Object(it, meta) => write!(f, "{:?}:{:?}", it, meta), + Value::Function(it) => write!(f, "{:?}", it), + } + } +} + impl Value { fn bool(&mut self) -> bool { match self { @@ -267,13 +282,30 @@ impl Value { _ => true, } } - fn unwrap_str(&mut self) -> Rc { + pub fn unwrap_str(&mut self) -> Rc { match self { - Value::CachedString(cached) => cached.0.clone(), - Value::String(string, _hash) => string.clone(), + Value::CachedString(hashed) => hashed.0.clone(), + Value::String(hashed) => hashed.0.clone(), _ => panic!(), } } + pub fn try_string(self) -> Option { + match self { + Value::CachedString(hashed) => Some(hashed), + Value::String(hashed) => Some(hashed), + _ => None, + } + } + pub(crate) fn traverse(&self, context: &mut C) + where + C: MarsAllocator, + { + match self { + Value::Table(id) => {} + Value::Object(id, meta_id) => {} + _ => {} + } + } } impl PartialEq for Value { @@ -297,17 +329,17 @@ impl PartialEq for Value { Value::Integer(other) => this == other, _ => false, }, - Value::String(string, hash) => match other { + Value::String(string) => match other { Value::CachedString(other) => { - if *hash == other.1 { - other.0.deref() == string.deref() + if string.1 == other.1 { + other.0.deref() == string.0.deref() } else { false } } - Value::String(other_string, other_hash) => { - if hash == other_hash { - string == other_string + Value::String(other) => { + if string.1 == other.1 { + string.0.deref() == other.0.deref() } else { false } @@ -316,9 +348,9 @@ impl PartialEq for Value { }, Value::CachedString(string) => match other { Value::CachedString(other) => other.0 == string.0, // pointer equality - Value::String(other_string, other_hash) => { - if string.1 == *other_hash { - string.0.deref() == other_string.deref() + Value::String(other) => { + if string.1 == other.1 { + string.0.deref() == other.0.deref() } else { false } @@ -329,8 +361,8 @@ impl PartialEq for Value { Value::Table(other) => other == this, _ => false, }, - Value::Object(this) => match other { - Value::Object(other) => other == this, + Value::Object(this, ..) => match other { + Value::Object(other, ..) => other == this, _ => false, }, Value::Function(this) => match other { @@ -341,34 +373,90 @@ impl PartialEq for Value { } } -impl Traverse for Value { - fn traverse(&self) { - match self { - Value::Table(table) => { - table.traverse(); - } - Value::Object(anything) => { - anything.traverse(); - } - Value::Function(callable) => callable.traverse(), - _ => {} - } - } -} - // Frame for calling a function +#[derive(Clone)] pub struct Frame { vararg: usize, // Where varargs start offset: usize, // Where arguments start counter: usize, // Which instruction we are at - closure: Gc, // The function that was called on this frame + closure: Id, // The function that was called on this frame } +impl Equivalent for MapEntry<&str,V> { // For the constant maps + fn matches(&self, other: &HashedString) -> bool { + self.0.as_ref().unwrap().0 == other.as_str() + } +} + +// Metas are a faster alternative to metatables for foreign objects +pub type MetaGetFn = fn(&mut Machine, AnyId, Value, Needle) -> Result; +pub type MetaSetFn = fn(&mut Machine, AnyId, Value, Value, Needle) -> Result<(), RunError>; +pub type MetaInFn = fn(&mut Machine, AnyId, Value, Needle) -> Result<(), RunError>; +pub type MetaOutFn = fn(&mut Machine, AnyId, Needle) -> Result; +pub type MetaTraverseFn = fn(&mut Machine, AnyId) -> Result<(), RunError>; +pub struct Meta { + pub string: MetaOutFn, + pub number: MetaOutFn, + pub get: MetaGetFn, + pub set: MetaSetFn, + pub traverse: MetaTraverseFn, +} +impl Default for Meta { + fn default() -> Self { + Meta { + string: |_machine, _id, _needle| Err(RunError::new("Type does not implement to string")), + number: |_machine, _id, _needle| Err(RunError::new("Type does not implement to number")), + get: |_machine, _id, _index, _needle| Err(RunError::new("Type does not implement get")), + set: |_machine, _id, _index, _value, _needle| Err(RunError::new("Type does not implement set")), + traverse: |_machine, _id| Err(RunError::new("Type did not subscribe to traversal")), + } + } +} + +pub type ForeignFunction = fn(&mut Machine, needle: Needle) -> Result<(), RunError>; + // Machine for executing functions -pub struct Machine { - global: Gc
, - allocator: Allocator, - cache: Map, // todo: better to use a generic version of Map +pub struct Machine { + pub state: S, + global: Id
, + tables: FreeList
, + closures: FreeList, + upvalues: FreeList, + natives: FreeList>, + metas: FreeList>, + cache: DisplacementSet, + meta_strings: MetaStrings, +} + +impl MarsAllocator for Machine {} + +impl Allocates> for Machine { + fn freelist(&mut self) -> &mut FreeList> { + &mut self.natives + } +} + +impl Traverse> for ForeignFunction +where + Machine: MarsAllocator, +{ + fn traverse(id: &Id>, _context: &mut Machine) {} +} + +impl Allocates
for Machine { + fn freelist(&mut self) -> &mut FreeList
{ + &mut self.tables + } +} +impl Allocates for Machine { + fn freelist(&mut self) -> &mut FreeList { + &mut self.closures + } +} +impl Allocates for Machine { + fn freelist(&mut self) -> &mut FreeList { + &mut self.upvalues + } } /* @@ -380,40 +468,76 @@ Also, I had a premonition I may need this for multithreading later on! #[derive(Clone)] pub struct Register { value: Value, - upvalue: Option>, + upvalue: MaybeId, } impl Register { const NIL: Register = Register { value: Value::Nil, - upvalue: None, + upvalue: MaybeId::NULL, }; } -pub struct Needle { +pub struct Dispatch { frames: Vec, // Delineation of the stack and execution state for individual functions stack: Vec, // Temporary storage for programs } -impl Needle { +impl Dispatch { fn pop(&mut self) -> Value { self.stack.pop().unwrap().value } fn push(&mut self, value: Value) { self.stack.push(Register { value, - upvalue: None, + upvalue: MaybeId::NULL, }) } fn index(&self, index: Index, frame: &Frame) -> &Register { &self.stack[frame.offset + index as usize] } + fn needle(&mut self) -> Needle<'_> { + Needle { + index: self.stack.len(), + dispatch: self, + } + } + fn new() -> Dispatch { + Dispatch { + frames: Vec::new(), + stack: Vec::new(), + } + } +} + +pub struct Needle<'a> { + pub dispatch: &'a mut Dispatch, + index: usize, +} + +impl Needle<'_> { + pub fn pop(&mut self) -> Option { + if self.dispatch.stack.len() == self.index { + None + } else { + Some(self.dispatch.stack.pop().unwrap().value) + } + } + pub fn push(&mut self, value: Value) { + self.dispatch.stack.push(Register { + value, + upvalue: MaybeId::NULL, + }) + } } pub(crate) type Index = u16; // Vector index -type Count = u16; // Number of arguments +pub(crate) type Stack = u16; // Stack index; +pub(crate) type Count = u16; // Number of arguments pub(crate) type Offset = i16; // Jump to offset +const COMMENTS: [&'static str; 1] = ["comment"]; + // Bytecode for execution in a procedure #[derive(Debug, Clone, Copy)] #[repr(u32)] @@ -421,26 +545,26 @@ pub(crate) enum Code { TableNew, TableSet, // consume: value, index, table, TableGet, // consume: index, table; push: result - TableInsert(Index), // consume: table, value + TableInsert(Stack), // consume: table, value(s) UpvalueGet(Index), // push: U[I] UpvalueSet(Index), // consume: value - RegisterGet(Index), // push: R[I] - RegisterSet(Index), // consume: value + RegisterGet(Stack), // push: R[I] + RegisterSet(Stack), // consume: value Constant(Index), // push: C[I] - Call(Index), // consume: R[I...] push: ... (all results) - Discard(Index), // consume: R[I...] (used with above to cull results or pad nil) + Call(Stack), // consume: R[I...] push: ... (all results) + Discard(Stack), // consume: R[I...] (used with above to cull results or pad nil) VarArg(Count), // push: VA[1 -> count] OR VA[all] Jump(Offset), // PC += offset - Close(Index), // Raise R[I...].upvalue from Upvalue::Stack to Upvalue::Heap, 'closing' the register, same effect as Discard for None upvalues + Close(Stack), // Raise R[I...].upvalue from Upvalue::Stack to Upvalue::Heap, 'closing' the register, same effect as Discard for None upvalues Skip, // consume: condition; if condition then PC++ (and naturally ++ again) - Return(Index), // consume: R[I...] (move arguments around and finish) + Return(Stack), // consume: R[I...] (move arguments back to start of frame and finish) Binary(BinaryOp), // consume: fst, snd; push: result TODO: just use direct instructions? Unary(UnaryOp), // consume: value; push: result Closure(Index), // push: closure constructed from prototypes[I] IteratorLoop(Offset), - RangeBegin, // + RangeBegin, RangeLoop(Offset), - Comment(Index), + Message(Index), } impl Code { @@ -448,32 +572,143 @@ impl Code { pub(crate) const UNSURE: Code = Code::Jump(0); } -impl Machine { - pub(crate) fn new_string(&mut self, string: &str) -> Result { +#[derive(Default)] +pub struct MetaStrings { + number: Value, + string: Value, + call: Value, + property: Value, +} + +impl MetaStrings { + const TO_NUMBER: &'static str = "__tonumber"; + const TO_STRING: &'static str = "__tostring"; + const CALL: &'static str = "__call"; +} + +impl MetaStrings { + fn new() -> MetaStrings { + MetaStrings { + ..Default::default() + } + } +} + +impl Machine { + fn init_meta_strings(&mut self) -> Result<(), RunError> { + self.meta_strings = MetaStrings { + number: self.new_string(MetaStrings::TO_NUMBER)?, + string: self.new_string(MetaStrings::TO_STRING)?, + call: self.new_string(MetaStrings::CALL)?, + property: Default::default(), + }; + Ok(()) + } + pub fn new_bool(&mut self, bool: bool) -> Value { + Value::Bool(bool) + } + pub fn new_number(&mut self, number: f64) -> Value { + Value::Number(number) + } + pub fn new_table(&mut self) -> Result { + Ok(Value::Table(self.tables.make(Table::new()).id)) + } + // todo: maybe use a trait to make S provide Id> through T, e.g. 'State' + pub fn new_object(&mut self, id: Id, meta: Id>) -> Value { + Value::Object(id.any(), meta.any()) + } + pub fn new_string(&mut self, string: &str) -> Result { + // todo: why Result of RunError? if string.len() < SHORT_STRING_LEN { - let existing = self.cache.get_table(string); - match existing { - Some(ref entry) => Ok(Value::CachedString(entry.0.clone().unwrap())), + match self.cache.get(string) { + Some(ref entry) => Ok(Value::CachedString(entry.0.as_ref().unwrap().clone())), None => { - let cached = CachedString(Rc::from(string), string.hashed()); - self.cache - .set_table(CachedStringEntry(Some(cached.clone()))); + let cached = HashedString(Rc::from(string), string.hashed()); + self.cache.set(CachedStringEntry(Some(cached.clone()))); Ok(Value::CachedString(cached)) } } } else { - Ok(Value::String(Rc::from(string), string.hashed())) + Ok(Value::String(HashedString(Rc::from(string), string.hashed()))) } } - fn try_number(&mut self, value: &mut Value) -> Result { + fn meta_call( + &mut self, + dispatch: &mut Dispatch, + value: Value, + index: usize, + ) -> Result<(), RunError> { + match value { + Value::Function(callable) => { + self.call(dispatch, callable, index)?; + } + Value::Object(_, meta) => match self + .tables + .get(&meta.into()) + .get(self.meta_strings.call.clone())? + { + Value::Function(callable) => self.call(dispatch, callable, index)?, + _ => { + return Err(RunError( + "Expected function for __call metatable entry".to_string(), + )); + } + }, + Value::Table(table) => { + let mut table = self.tables.get(&table); + if let Some(meta) = table.meta.exists() { + match self.tables.get(&meta).get(self.meta_strings.call.clone())? { + Value::Function(callable) => self.call(dispatch, callable, index)?, + _ => { + return Err(RunError( + "Expected function for __call metatable entry".to_string(), + )); + } + } + } else { + return Err(RunError("Expected metatable to call table".to_string())); + }; + } + _ => return Err(RunError("Cannot call primitive".to_string())), + } + Ok(()) + } + fn meta_to_number(&mut self, dispatch: &mut Dispatch, value: Value) -> Result { + let clone = value.clone(); match value { integer @ Value::Integer(_) => Ok(integer.clone()), number @ Value::Number(_) => Ok(number.clone()), - Value::Object(object) => object.borrow_mut().0.meta_number(), + Value::Object(_, meta) => { + match self + .tables + .get(&meta.into()) + .get(self.meta_strings.number.clone())? + { + Value::Function(callable) => { + let mut needle = dispatch.needle(); + needle.push(clone); + self.call(needle.dispatch, callable, 1)?; + Ok(needle.pop().unwrap_or(Value::Nil)) + } + it => Ok(it), + } + } Value::Table(table) => { - let mut table = table.borrow_mut(); - if let Some(_metatable) = &table.meta { - Err(RunError("Metatables unimplemented".to_string())) + let table = self.tables.get(&table); + if let Some(meta) = table.meta.exists() { + match self + .tables + .get(&meta) + .get(self.meta_strings.number.clone())? + { + Value::Function(callable) => { + let mut needle = dispatch.needle(); + needle.push(clone); + self.call(needle.dispatch, callable, 1)?; + Ok(needle.pop().unwrap_or(Value::Nil)) + } + it => Ok(it), + } } else { Ok(Value::Nil) } @@ -481,7 +716,12 @@ impl Machine { _ => Ok(Value::Nil), } } - fn try_string(&mut self, value: &mut Value) -> Result { + pub(crate) fn meta_to_string( + &mut self, + dispatch: &mut Dispatch, + value: Value, + ) -> Result { + let cloned = value.clone(); Ok(match value { Value::Nil => self.new_string("nil".to_string().as_str())?, Value::Bool(bool) => self.new_string(format!("{}", bool).as_str())?, @@ -492,128 +732,94 @@ impl Machine { #[cfg(feature = "vector3")] Value::Vector(vector) => format!("{}", vector), Value::Table(table) => { - let mut table = table.borrow_mut(); - if let Some(_metatable) = &table.meta { + let mut table = self.tables.get(&table); + if let Some(metatable) = table.meta.exists() { return Err(RunError("Metatables unimplemented".to_string())); } else { self.new_string("{...}".to_string().as_str())? } } - Value::Object(object) => match object.borrow_mut().0.meta_string()? { - string @ Value::String(..) => return Ok(string), - it => return Err(RunError(format!("Expected string but got {:?}", it))), - }, + Value::Object(_, meta) => { + match self + .tables + .get(&meta.into()) + .get(self.meta_strings.number.clone())? + { + Value::Function(callable) => { + let mut needle = dispatch.needle(); + needle.push(cloned); + self.call(needle.dispatch, callable, 1)?; + needle.pop().unwrap_or(Value::Nil) + } + it => it, + } + } Value::Function(callable) => self.new_string(format!("{:?}", callable).as_str())?, }) } - fn set(&mut self, subject: Value, index: Value, value: Value) -> Result<(), RunError> { + fn meta_set( + &mut self, + dispatch: &mut Dispatch, + subject: Value, + index: Value, + value: Value, + ) -> Result<(), RunError> { match subject { - Value::Nil => Err(RunError("Cannot get from nil".to_string())), - Value::Bool(bool) => Err(RunError(format!("Cannot index {:?}", bool))), - Value::Integer(_integer) => Err(RunError("Integer index unimplemented".to_string())), - Value::Number(_number) => Err(RunError("Number index unimplemented".to_string())), + Value::Nil => Err(RunError("Cannot set to nil".to_string())), + Value::Bool(bool) => Err(RunError("Cannot set to boolean".to_string())), + Value::Integer(_integer) => Err(RunError("Cannot set to integer".to_string())), + Value::Number(_number) => Err(RunError("Cannot set to number".to_string())), Value::String(..) | Value::CachedString(..) => { Err(RunError("String index unimplemented".to_string())) } #[cfg(feature = "vector3")] Value::Vector(_vector) => Err(RunError("Vector index unimplemented".to_string())), - Value::Table(mut table) => { - let mut table = table.borrow_mut(); - if let Some(_metatable) = &table.meta { + Value::Table(ref table) => { + let mut table = self.tables.get(table); + if let Some(ref _metatable) = table.meta.exists() { Err(RunError("Metatables unimplemented".to_string())) } else { table.set(index, value) } } - Value::Object(mut object) => object.borrow_mut().0.meta_new_index(index, value), + Value::Object(id, meta) => (self.metas.get(&meta.into()).set)(self, id, index, value, dispatch.needle()), Value::Function(_callable) => Err(RunError("Function index unimplemented".to_string())), } } - fn get(&mut self, subject: Value, index: Value) -> Result { + fn meta_get( + &mut self, + dispatch: &mut Dispatch, + mut subject: Value, + index: Value, + ) -> Result { match subject { Value::Nil => Err(RunError("Cannot set to nil".to_string())), - Value::Bool(bool) => Err(RunError(format!("Cannot index {:?}", bool))), - Value::Integer(_integer) => Err(RunError("Integer index unimplemented".to_string())), - Value::Number(_number) => Err(RunError("Number index unimplemented".to_string())), + Value::Bool(bool) => Err(RunError("Cannot index boolean".to_string())), + Value::Integer(_integer) => Err(RunError("Cannot index integer".to_string())), + Value::Number(_number) => Err(RunError("Number index number".to_string())), Value::String(..) | Value::CachedString(..) => { Err(RunError("String index unimplemented".to_string())) } #[cfg(feature = "vector3")] Value::Vector(_vector) => Err(RunError("Vector index unimplemented".to_string())), - Value::Table(mut table) => { - let mut table = table.borrow_mut(); - if let Some(_metatable) = &table.meta { + Value::Table(ref mut table) => { + let mut table = self.tables.get(table); + if let Some(ref _metatable) = table.meta.exists() { Err(RunError("Metatables unimplemented".to_string())) } else { table.get(index.clone()) } } - Value::Object(mut object) => object.borrow_mut().0.meta_index(index.clone()), + Value::Object(id, meta) => (self.metas.get(&meta.into()).get)(self, id, index, dispatch.needle()), Value::Function(_callable) => Err(RunError("Function index unimplemented".to_string())), } } - /*fn equal(&mut self, lhs: &mut Value, rhs: &mut Value) -> Result { // with respect to metamethods - Ok(match lhs { - Value::Nil => match rhs { - Value::Nil => true, - Value::Object(object) => object.borrow_mut().0.meta_eq(rhs)?.bool(), - _ => false, - } - Value::Bool(this) => match rhs { - Value::Bool(other) => this == other, - _ => false, - } - Value::Number(this) => match rhs { - Value::Number(other) => this == other, - Value::Integer(other) => *this == *other as f64, - _ => false, - } - Value::Integer(this) => match rhs { - Value::Number(other) => *this as f64 == *other, // are these conversions ok? - Value::Integer(other) => this == other, - _ => false, - } - Value::String(string, hash) => match rhs { - Value::CachedString(other) => - if *hash == other.1 { - other.0.deref() == (*string).deref() - } else { - false - } - Value::String(other_string, other_hash) => - if hash == other_hash { - string == other_string - } else { - false - } - _ => false - } - Value::CachedString(string) => match rhs { - Value::CachedString(other) => other.0 == string.0, // pointer equality - Value::String(other_string, other_hash) => { - if string.1 == *other_hash { - string.0.deref() == (*other_string).deref() - } else { - false - } - } - _ => false - } - Value::Table(this) => match rhs { - Value::Table(other) => other == this, - _ => false - } - Value::Object(this) => match rhs { - Value::Object(other) => other == this, - _ => false - } - Value::Function(this) => match rhs { - Value::Function(other) => other == this, - _ => false - } - }) - }*/ - pub(crate) fn load(&mut self, source: &str) -> Result, MarsError> { + // todo: object-table equality + fn meta_equal(&mut self, lhs: &mut Value, rhs: &mut Value) -> Result { + // with respect to metamethods + Ok(false) + } + pub fn load(&mut self, source: &str) -> Result, MarsError> { let chunk = match parse(source) { Ok(parsed) => parsed, Err(error) => return Err(MarsError(error.msg(source))), @@ -627,54 +833,54 @@ impl Machine { }; let global = self.global.clone(); - let upvalues = vec![self.allocator.alloc(Upvalue::Heap(Value::Table(global)))]; - Ok(self.allocator.alloc(Closure { - prototype: Rc::new(prototype), - upvalues, - })) + let upvalues = vec![self.freelist().make(Upvalue::Heap(Value::Table(global))).id]; + Ok(self + .freelist() + .make(Closure { + prototype: Rc::new(prototype), + upvalues, + }) + .id) } - fn dispatch(&mut self, needle: &mut Needle) -> Result<(), RunError> { - let mut frame: Frame = needle - .frames - .pop() - .ok_or(RunError("No frames in needle!".to_string()))?; - let mut call = |machine: &mut Machine, - needle: &mut Needle, - mut frame: Frame, - index: Index| - -> Result { - frame.counter += 1; // return to this frame at the next instruction - let offset = frame.offset; - match needle.pop() { - Value::Function(callable) => { - match callable { - Callable::Rust(function) => { - function.0(machine, needle, offset + index as usize)?; - Ok(frame) // nothing changes - } - Callable::Mars(closure) => { - needle.frames.push(frame); - let nargs = closure.borrow().prototype.args; - needle - .stack - .resize(offset + index as usize + nargs as usize, Register::NIL); - Ok(Frame { - // where to find the function, what about missing arguments?, - vararg: offset + index as usize, // start of frame points to the register after the function - offset: offset + index as usize + nargs as usize, // points to first argument (or last vararg if nargs = zero) - counter: 0, - closure, - }) - } - } - } - _ => { - return Err(RunError( - "Calling non-function types are unimplemented".to_string(), - )); - } + fn call( + &mut self, + dispatch: &mut Dispatch, + callable: Callable, + n_args: usize, + ) -> Result<(), RunError> { + self.dispatch_call(dispatch, callable, dispatch.stack.len() - n_args - 1)?; + self.dispatch(dispatch)?; + Ok(()) + } + // Call a callable in a dispatch. + // For Rust functions, this calls it immediately. + // For Mars functions it will set up the dispatch to execute this function, so there must be a dispatch in progress already. + fn dispatch_call( + &mut self, + dispatch: &mut Dispatch, + callable: Callable, + index: usize, + ) -> Result<(), RunError> { + match callable { + Callable::Rust(function) => { + self.natives.get(&function.into())(self, Needle { dispatch, index })?; } - }; + Callable::Mars(closure) => { + let nargs = self.freelist().get(&closure).prototype.args; + dispatch.stack.resize(index + nargs as usize, Register::NIL); + dispatch.frames.push(Frame { + // where to find the function, what about missing arguments?, + vararg: index, // start of frame points to the register after the function + offset: index + nargs as usize, // points to first argument (or last vararg if nargs = zero) + counter: 0, + closure, + }); + } + } + Ok(()) + } + fn dispatch(&mut self, dispatch: &mut Dispatch) -> Result<(), RunError> { + let frame_index = dispatch.frames.len(); let jump = |frame: &mut Frame, offset: Offset| { if offset < 0 { frame.counter -= -(offset - 1) as usize @@ -683,12 +889,17 @@ impl Machine { } }; loop { - if needle.stack.len() > 65536 { + let frame = dispatch + .frames + .last_mut() + .ok_or(RunError("No frames in needle!".to_string()))? + .clone(); + if dispatch.stack.len() > Stack::MAX as usize { return Err(RunError("Soft stack limit of 65536 reached".to_string())); } - let code = frame - .closure - .borrow_mut() + let code = self + .freelist() + .get(&frame.closure) .prototype .code .get(frame.counter) @@ -696,35 +907,43 @@ impl Machine { let code = match code { Some(code) => code, None => { - if let Some(upper) = needle.frames.pop() { - frame = upper; - } else { + if dispatch.frames.len() == frame_index { + dispatch.frames.pop(); break; + } else { + dispatch.frames.pop(); + if dispatch.frames.last_mut().is_none() { + break; + } else { + continue; + } } - continue; } }; match code { - Code::Comment(number) => { - eprintln!("{}", number) + Code::Message(number) => { + eprintln!("{}", COMMENTS[number as usize]) + } + Code::TableNew => { + dispatch.push(Value::Table(self.freelist().make(Table::new()).id)) } - Code::TableNew => needle.push(Value::Table(self.allocator.alloc(Table::new()))), Code::TableSet => { - let table = needle.pop(); - let index = needle.pop(); - let value = needle.pop(); - self.set(table, index, value)?; + let table = dispatch.pop(); + let index = dispatch.pop(); + let value = dispatch.pop(); + self.meta_set(dispatch, table, index, value)?; } Code::TableGet => { - let table = needle.pop(); - let index = needle.pop(); - needle.push(self.get(table, index)?); + let table = dispatch.pop(); + let index = dispatch.pop(); + let value = self.meta_get(dispatch, table, index)?; + dispatch.push(value); } Code::TableInsert(index) => { - match needle.pop() { - Value::Table(mut table) => { - for i in frame.offset + index as usize..needle.stack.len() { - table.borrow_mut().append(needle.pop()) + match dispatch.pop() { + Value::Table(ref mut table) => { + for i in frame.offset + index as usize..dispatch.stack.len() { + self.tables.get(table).append(dispatch.pop()) } } _ => { @@ -735,54 +954,61 @@ impl Machine { } } Code::UpvalueGet(index) => { - match frame.closure.borrow_mut().upvalues[index as usize] - .borrow_mut() - .deref() + match self + .upvalues + .get(&self.closures.get(&frame.closure).upvalues[index as usize]) { - Upvalue::Stack(stack) => needle.push(needle.stack[*stack].value.clone()), - Upvalue::Heap(object) => needle.push(object.clone()), + Upvalue::Stack(stack) => { + dispatch.push(dispatch.stack[*stack].value.clone()) + } + Upvalue::Heap(object) => dispatch.push(object.clone()), } } Code::UpvalueSet(index) => { - match frame.closure.borrow_mut().upvalues[index as usize] - .borrow_mut() - .deref_mut() + match self + .upvalues + .get(&self.closures.get(&frame.closure).upvalues[index as usize]) { - Upvalue::Stack(register) => needle.stack[*register].value = needle.pop(), - Upvalue::Heap(it) => *it = needle.pop(), + Upvalue::Stack(register) => { + dispatch.stack[*register].value = dispatch.pop() + } + Upvalue::Heap(it) => *it = dispatch.pop(), } } Code::RegisterGet(index) => { - needle.push(needle.stack[frame.vararg + index as usize].value.clone()); + dispatch.push(dispatch.stack[frame.vararg + index as usize].value.clone()); } Code::RegisterSet(index) => { - needle.stack[frame.vararg + index as usize].value = needle.pop(); + dispatch.stack[frame.vararg + index as usize].value = dispatch.pop(); } Code::Constant(constant) => { - needle.push( - frame.closure.borrow_mut().prototype.constants[constant as usize].clone(), + dispatch.push( + self.closures.get(&frame.closure).prototype.constants[constant as usize] + .clone(), ); } - Code::Call(index) => { - frame = call(self, needle, frame, index)?; - continue; - } + Code::Call(index) => match dispatch.pop() { + Value::Function(callable) => { + self.dispatch_call(dispatch, callable, frame.offset + index as usize)?; + } + _ => return Err(RunError("Could not call value".to_string())), + }, Code::Discard(index) => { - needle + dispatch .stack .resize(frame.offset + index as usize + 1, Register::NIL); } Code::VarArg(count) => { if count == 0 { for i in frame.vararg..frame.offset { - needle.push(needle.stack[i].value.clone()); + dispatch.push(dispatch.stack[i].value.clone()); } } else { for i in 0..count as usize { if frame.offset - i - 1 < frame.vararg { - needle.push(Value::Nil); + dispatch.push(Value::Nil); } else { - needle.push(needle.stack[frame.offset - i - 1].value.clone()); + dispatch.push(dispatch.stack[frame.offset - i - 1].value.clone()); } } } @@ -790,24 +1016,24 @@ impl Machine { Code::Jump(offset) => { #[cfg(feature = "assertions")] assert_ne!(offset, 0); - jump(&mut frame, offset); + jump(dispatch.frames.last_mut().unwrap(), offset); } Code::Skip => { - if needle.pop().bool() { - frame.counter += 1; + if dispatch.pop().bool() { + dispatch.frames.last_mut().unwrap().counter += 1; } } Code::Return(index) => { // Settle the return arguments to where this frame was started originally - for i in frame.offset + index as usize..needle.stack.len() { - needle.stack[frame.vararg + i - 1] = needle.stack[i].clone(); + for i in frame.offset + index as usize..dispatch.stack.len() { + dispatch.stack[frame.vararg + i - 1] = dispatch.stack[i].clone(); } - if let Some(upper) = needle.frames.pop() { - frame = upper; + dispatch.frames.pop(); + if dispatch.frames.last_mut().is_some() { + continue; } else { break; } - continue; // todo: is this needed? } Code::Binary(op) => { return Err(RunError( @@ -818,62 +1044,69 @@ impl Machine { return Err(RunError("Unary operations are not implemented".to_string())); } Code::Close(index) => { - for i in frame.offset + index as usize..needle.stack.len() { - match &mut needle.stack[i] { - Register { - value, - upvalue: Some(upvalue), - } => { - if matches!(upvalue.borrow().deref(), Upvalue::Stack(_)) { - upvalue.replace(Upvalue::Heap(value.clone())) - } else { - panic!() // Because it's on the stack since it's right here! + for i in frame.offset + index as usize..dispatch.stack.len() { + if let Some(upvalue) = dispatch.stack[i].upvalue.exists() { + match self.upvalues.get(&upvalue) { + it @ Upvalue::Stack(_) => { + *it = Upvalue::Heap(dispatch.stack[i].value.clone()) } + _ => panic!(), } - Register { upvalue: None, .. } => {} } } - needle + dispatch .stack .resize(frame.offset + index as usize, Register::NIL); // As with Code::Discard } Code::Closure(index) => { - let prototype = - frame.closure.borrow().prototype.prototypes[index as usize].clone(); - let mut upvalues = Vec::new(); + // Make a new closure from a prototype inside this closure and push to the stack + let prototype = self.freelist().get(&frame.closure).prototype.prototypes + [index as usize] + .clone(); // Get the prototype of this closure that the instruction is referring to + let mut upvalues = Vec::new(); // List of upvalues for this new closure for reference in prototype.upvalues.iter() { + // For every reference in it upvalues.push(match reference { + // Push it based on what the reference was Reference::Closing(index) => { - let index = frame.offset + *index as usize; - match &mut needle.stack[index].upvalue { + // It's actually alive inside this function already + let index = frame.offset + *index as usize; // Get its actual index + match &mut dispatch.stack[index].upvalue.exists() { + // Does the upvalue in that register exist? Some(upvalue) => upvalue.clone(), - it => { - let upvalue = self.allocator.alloc(Upvalue::Stack(index)); - *it = Some(upvalue.clone()); - upvalue + _ => { + let upvalue = self.freelist().make(Upvalue::Stack(index)); + dispatch.stack[index].upvalue = upvalue.id.clone().maybe(); + upvalue.id } } } Reference::Upvalue(index) => { - frame.closure.borrow_mut().upvalues[*index as usize].clone() + self.freelist().get(&frame.closure).upvalues[*index as usize] + .clone() } }); } - needle.push(Value::Function(Callable::Mars(self.allocator.alloc( - Closure { - prototype, - upvalues, - }, - )))); + dispatch.push(Value::Function(Callable::Mars( + self.freelist() + .make(Closure { + prototype, + upvalues, + }) + .id, + ))); } Code::RangeBegin => { - let len = needle.stack.len(); + let len = dispatch.stack.len(); let init = len - 3; let limit = len - 2; let step = len - 1; - let init_value = self.try_number(&mut needle.stack[init].value)?; - let limit_value = self.try_number(&mut needle.stack[limit].value)?; - let step_value = self.try_number(&mut needle.stack[step].value)?; + let init_value = + self.meta_to_number(dispatch, dispatch.stack[init].value.clone())?; + let limit_value = + self.meta_to_number(dispatch, dispatch.stack[limit].value.clone())?; + let step_value = + self.meta_to_number(dispatch, dispatch.stack[step].value.clone())?; if matches!(init_value, Value::Integer(_)) && matches!(limit_value, Value::Integer(_)) && matches!(step_value, Value::Integer(_)) @@ -883,9 +1116,9 @@ impl Machine { _ => panic!(), }; // All integers so we can carry this out nicely - needle.stack[init].value = init_value; - needle.stack[limit].value = limit_value; // todo: is this actually correct? - needle.stack[step].value = step_value; + dispatch.stack[init].value = init_value; + dispatch.stack[limit].value = limit_value; // todo: is this actually correct? + dispatch.stack[step].value = step_value; } else { // Make them all as floats let cast = |value: Value| match value { @@ -893,21 +1126,21 @@ impl Machine { number @ Value::Number(_) => number, _ => panic!(), }; - needle.stack[init].value = cast(init_value); - needle.stack[limit].value = cast(limit_value); - needle.stack[step].value = cast(step_value); + dispatch.stack[init].value = cast(init_value); + dispatch.stack[limit].value = cast(limit_value); + dispatch.stack[step].value = cast(step_value); } - needle.push(needle.stack[init].value.clone()); + dispatch.push(dispatch.stack[init].value.clone()); } Code::RangeLoop(offset) => { - needle.pop(); // get rid of temp - let len = needle.stack.len(); + dispatch.pop(); // get rid of temp + let len = dispatch.stack.len(); let init_index = len - 3; let limit_index = len - 2; let step_index = len - 1; - let init_value = self.try_number(&mut needle.stack[init_index].value)?; - let limit_value = self.try_number(&mut needle.stack[limit_index].value)?; - let step_value = self.try_number(&mut needle.stack[step_index].value)?; + let init_value = dispatch.stack[init_index].value.clone(); + let limit_value = dispatch.stack[limit_index].value.clone(); + let step_value = dispatch.stack[step_index].value.clone(); match init_value { Value::Integer(init) => { let step = match step_value { @@ -919,13 +1152,13 @@ impl Machine { _ => panic!(), }; if init < limit { - needle.stack[init_index].value = Value::Integer(init + step); - needle.push(Value::Integer(init + step)); // owned variable for loop block - jump(&mut frame, offset); + dispatch.stack[init_index].value = Value::Integer(init + step); + dispatch.push(Value::Integer(init + step)); // owned variable for loop block + jump(dispatch.frames.last_mut().unwrap(), offset); } else { - needle.pop(); - needle.pop(); - needle.pop(); + dispatch.pop(); + dispatch.pop(); + dispatch.pop(); } } Value::Number(init) => { @@ -938,47 +1171,50 @@ impl Machine { _ => panic!(), }; if (step >= 0.0 && init > limit) || (step < 0.0 && init < limit) { - needle.stack[init_index].value = Value::Number(init + step); - needle.push(Value::Number(init)); // owned variable for loop block - jump(&mut frame, offset); + dispatch.stack[init_index].value = Value::Number(init + step); + dispatch.push(Value::Number(init)); // owned variable for loop block + jump(dispatch.frames.last_mut().unwrap(), offset); } else { - needle.pop(); - needle.pop(); - needle.pop(); + dispatch.pop(); + dispatch.pop(); + dispatch.pop(); } } _ => panic!(), } } Code::IteratorLoop(offset) => { - let len = needle.stack.len(); - let function = needle.stack[len - 2].value.clone(); - let state = needle.stack[len - 1].value.clone(); - let var = needle.stack[len].value.clone(); - needle.push(function); - needle.push(state); - needle.push(var); + let len = dispatch.stack.len(); + let callable = match dispatch.stack[len - 2].value.clone() { + Value::Function(callable) => callable, + _ => return Err(RunError("Could not call value".to_string())), + }; + let state = dispatch.stack[len - 1].value.clone(); + let var = dispatch.stack[len].value.clone(); + dispatch.push(var); + dispatch.push(state); let frame_offset = frame.offset; - frame = call( - self, - needle, - frame, - Index::try_from(needle.stack.len() - frame_offset - 2).unwrap(), + self.dispatch_call( + dispatch, + callable, + frame.offset + + Index::try_from(dispatch.stack.len() - frame_offset - 2).unwrap() + as usize, )?; if matches!( - needle.stack.get(len + 1).unwrap_or(&Register::NIL).value, + dispatch.stack.get(len + 1).unwrap_or(&Register::NIL).value, Value::Nil, ) { - jump(&mut frame, offset); + jump(dispatch.frames.last_mut().unwrap(), offset); }; } } - frame.counter += 1; + dispatch.frames.last_mut().unwrap().counter += 1; } Ok(()) } - pub(crate) fn enter(&mut self, closure: Gc) -> Result<(), RunError> { - let mut needle = Needle { + pub fn enter(&mut self, closure: Id) -> Result<(), RunError> { + let mut needle = Dispatch { frames: vec![Frame { vararg: 0, offset: 0, @@ -989,55 +1225,60 @@ impl Machine { }; self.dispatch(&mut needle) } - pub fn register_global(&mut self, name: &str, native: Native) -> Result<(), RunError> { + pub fn set_global(&mut self, name: &str, value: Value) -> Result<(), RunError> { let name = self.new_string(name)?; - self.global - .borrow_mut() - .set(name, Value::Function(Callable::Rust(Rc::from(native)))) + self.tables.get(&self.global).set(name, value) } - pub fn new() -> Result { - let mut allocator = Allocator::new(); + pub fn set_global_object( + &mut self, + name: &str, + object: Id, + meta: Id>, + ) -> Result<(), RunError> { + let value = self.new_object(object, meta); + self.set_global(name, value) + } + pub fn set_global_function( + &mut self, + name: &str, + function: ForeignFunction, + ) -> Result<(), RunError> { + let fun = self.new_foreign(function)?; + self.set_global(name, fun) + } + pub fn new_foreign(&mut self, function: ForeignFunction) -> Result { + Ok(Value::Function(Callable::Rust( + self.natives.make(function).id.any(), + ))) + } + pub fn new(state: impl FnOnce(&mut FreeList>) -> S) -> Result, RunError> { + let mut tables = FreeList::
::new(); + let mut metas = FreeList::new(); + let state = state(&mut metas); let mut machine = Machine { - global: allocator.alloc(Table::new()), - allocator, - cache: Map::new(), + state, + global: tables.make(Table::new()).id, + tables, + metas, + closures: FreeList::new(), + upvalues: FreeList::new(), + natives: FreeList::new(), + meta_strings: MetaStrings::new(), + cache: DisplacementSet::new(), }; - machine.register_global( - "print", - Native( - |machine: &mut Machine, - needle: &mut Needle, - index: usize| - -> Result<(), RunError> { - for i in index..needle.stack.len() { - print!("{} ", machine.try_string(&mut needle.pop())?.unwrap_str()) - } - println!(); - Ok(()) - }, - ), - )?; - machine.register_global( - "debug", - Native( - |machine: &mut Machine, - needle: &mut Needle, - index: usize| - -> Result<(), RunError> { - for i in index..needle.stack.len() { - print!("{:?} ", machine.try_string(&mut needle.pop())?.unwrap_str()) - } - println!(); - Ok(()) - }, - ), - )?; + machine.init_meta_strings()?; + load(&mut machine)?; Ok(machine) } } #[derive(Debug, Clone)] pub struct MarsError(String); + +impl std::error::Error for MarsError { + +} + impl Display for MarsError { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.0)