diff --git a/.gitignore b/.gitignore index c3881aa..af73913 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ .idea target -Cargo.lock -src/tests/main.lua \ No newline at end of file +Cargo.lock \ No newline at end of file diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 50c2ed5..0000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "deimos"] - path = deimos - url = ssh://forgejo@git.mallard.gdn/PSx/Deimos.git diff --git a/.luarc.json b/.luarc.json deleted file mode 100644 index 0d7584e..0000000 --- a/.luarc.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "diagnostics.disable": [ - "undefined-global" - ] -} \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..bbe798c --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,41 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "colored" +version = "3.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "glam" +version = "0.33.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7360bd2cd76e0cd9032d42cf2922155cecea2685b0cfa4630c3246df030bcfd6" + +[[package]] +name = "mars" +version = "0.1.0" +dependencies = [ + "colored", + "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 1174d14..af6a241 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,11 +4,11 @@ version = "0.1.0" edition = "2024" [features] -freelist = [] +bighash = [] vector3 = ["dep:glam"] messages = [] assertions = [] [dependencies] glam = { version = "0.33.3", optional = true } -deimos = { path = "./deimos" } +colored = { version = "3.1.1" } \ No newline at end of file diff --git a/deimos b/deimos deleted file mode 160000 index 385a08b..0000000 --- a/deimos +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 385a08b094e43ff12b60a41a90ab99b4b18cea0c diff --git a/doc/.objects.autosave.xopp b/doc/.objects.autosave.xopp deleted file mode 100644 index 6caf4f8..0000000 Binary files a/doc/.objects.autosave.xopp and /dev/null differ diff --git a/doc/objects.xopp b/doc/objects.xopp deleted file mode 100644 index 6caf4f8..0000000 Binary files a/doc/objects.xopp and /dev/null differ diff --git a/src/compiler.rs b/src/compiler.rs index 557d23f..66e4c08 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, S> { +pub(crate) struct Traversal<'a> { // 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,10 +20,40 @@ 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), @@ -40,7 +70,7 @@ pub(crate) struct Scope { upvalue: bool, } -impl Traversal<'_, S> { +impl Traversal<'_> { fn compile(&mut self, it: &T) -> Result<(), LoadError> where T: Compile, @@ -102,7 +132,7 @@ impl Traversal<'_, S> { | Code::RangeLoop(_) | Code::RangeBegin | Code::IteratorLoop(_) - | Code::Message(_) => {} + | Code::Comment(_) => {} } let prototype = &mut self.contexts.last_mut().unwrap().prototype; prototype.code.push(it.it); @@ -117,8 +147,14 @@ impl Traversal<'_, S> { fn push_constant(&mut self, it: Value) -> Index { // todo: constants need pieces too!!! // don't use constants that already exist - if let Some(entry) = self.contexts.last_mut().unwrap().constants.get(it.clone()) { - *entry + if let Some(entry) = &mut self + .contexts + .last_mut() + .unwrap() + .constants + .get_table(it.clone()) + { + entry.1 } else { let index = { let constants = &mut self.contexts.last_mut().unwrap().prototype.constants; @@ -127,7 +163,11 @@ impl Traversal<'_, S> { } .try_into() .unwrap(); - self.contexts.last_mut().unwrap().constants.set(it, index); + self.contexts + .last_mut() + .unwrap() + .constants + .set_table(ConstantEntry(it, index)); index } } @@ -207,7 +247,7 @@ impl Traversal<'_, S> { } Ok(context.prototype) } - pub(crate) fn new(machine: &mut Machine) -> Traversal { + pub(crate) fn new(machine: &mut Machine) -> Traversal { Traversal { machine, contexts: vec![Context { @@ -237,12 +277,12 @@ impl Traversal<'_, S> { // 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(); @@ -301,9 +341,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(); @@ -376,7 +416,7 @@ fn compile_identifier( } } } - // At this point there were no returns so we must use _G table + // break 'search avoids this use of the _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 { @@ -386,9 +426,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 { @@ -415,13 +455,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(); @@ -451,7 +491,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 @@ -468,7 +508,7 @@ impl Compile for Piece<&Call> { } impl Compile for Piece<&Prefix> { - fn compile<'a, 'b, S>(&self, traversal: &mut Traversal) -> Result<(), LoadError> { + fn compile<'a, 'b>(&self, traversal: &mut Traversal) -> Result<(), LoadError> { match &self.it { Prefix::Call(call) => { let index = traversal.index().unwrap(); @@ -488,7 +528,7 @@ impl Compile for Piece<&Prefix> { // for the sake of erroring alone impl Compile for Piece<&VarArg> { - fn compile<'a, 'b, S>(&self, traversal: &mut Traversal) -> Result<(), LoadError> { + fn compile<'a, 'b>(&self, traversal: &mut Traversal) -> Result<(), LoadError> { match &traversal.contexts.last().unwrap().vararg { Some(piece) => match &piece.it { Some(that_name) => match &self.it.0 { @@ -536,7 +576,7 @@ impl Compile for Piece<&VarArg> { } impl Compile for Piece<&Constructor> { - fn compile<'a, 'b, S>(&self, traversal: &mut Traversal) -> Result<(), LoadError> { + fn compile<'a, 'b>(&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() { @@ -586,7 +626,7 @@ impl Compile for Piece<&Constructor> { } impl Compile for Piece<&Expression> { - fn compile<'a, S>(&self, traversal: &mut Traversal) -> Result<(), LoadError> { + fn compile<'a>(&self, traversal: &mut Traversal) -> Result<(), LoadError> { match self.it { Expression::Nil => { traversal.push_literal(Value::Nil, self.unit()); @@ -644,11 +684,7 @@ 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 { @@ -664,8 +700,8 @@ fn resolve_break( Ok(()) } -fn compile_assignment( - traversal: &mut Traversal, +fn compile_assignment( + traversal: &mut Traversal, variables: &Vec>, expressions: &Vec>, ) -> Result<(), LoadError> { @@ -673,7 +709,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> { @@ -748,7 +784,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 @@ -980,7 +1016,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> { @@ -1089,20 +1125,11 @@ 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(); @@ -1113,12 +1140,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 deleted file mode 100644 index b2a2548..0000000 --- a/src/core.rs +++ /dev/null @@ -1,18 +0,0 @@ -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 new file mode 100644 index 0000000..3562c33 --- /dev/null +++ b/src/gc.rs @@ -0,0 +1,154 @@ +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 97ac9f1..84db274 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,52 +1,29 @@ #![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; - use crate::{RunError, Needle}; - - macro_rules! unwrap { - ($e:expr) => { - match $e { - Err(error) => panic!("{}", error), - Ok(result) => result, - } + #[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), + _ => {} }; } - - 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 6a750c1..eef2a63 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1,6 +1,7 @@ // 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 9359720..64902d0 100644 --- a/src/table.rs +++ b/src/table.rs @@ -1,6 +1,56 @@ -use crate::vm::{Callable, MarsAllocator, RunError, Value}; -use deimos::gc::{Id, MaybeId, Traverse}; -use deimos::map::{DisplacementSet, Equivalent, Hashed, Hashes, TableEntry}; +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; +} #[derive(Debug, Clone)] struct KeyValue { @@ -21,10 +71,12 @@ impl Hashes for KeyValue { } impl TableEntry for KeyValue { - const VACANT: Self = KeyValue { - index: Value::Nil, - value: Value::Nil, - }; + fn new_vacant() -> Self { + KeyValue { + index: Value::Nil, + value: Value::Nil, + } + } fn is_vacant(&self) -> bool { matches!( self, @@ -36,6 +88,221 @@ 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 { @@ -47,11 +314,11 @@ impl Hashes for Value { 0 } } - Value::CachedString(hashed) => hashed.1, - Value::String(hashed) => hashed.1, + Value::CachedString(cached) => cached.1, + Value::String(_string, hash) => *hash, Value::Function(callable) => match callable { - Callable::Rust(native) => native.hashed(), - Callable::Mars(closure) => closure.hashed(), + Callable::Rust(native) => Rc::as_ptr(native).addr() as Hashed, + Callable::Mars(closure) => closure.addr() as Hashed, }, Value::Integer(integer) => *integer as Hashed, Value::Number(number) => { @@ -61,8 +328,8 @@ impl Hashes for Value { number.to_bits() as Hashed } } - Value::Table(table) => table.hashed(), - Value::Object(object, ..) => object.hashed(), + Value::Table(table) => table.addr() as Hashed, + Value::Object(object) => object.addr() as Hashed, #[cfg(feature = "vector3")] Value::Vector(vec) => { let mut hasher = DefaultHasher::new(); @@ -74,28 +341,23 @@ impl Hashes for Value { } // todo: displaced items don't ever get shuffled closer to their homes unless the table is resized -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Table { - table: DisplacementSet, + table: Map, array: Vec, - pub meta: MaybeId, + pub meta: Option>, } -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(); +impl Traverse for Table { + fn traverse(&self) { + for Entry { entry, .. } in self.table.map.iter() { if !entry.is_vacant() { - entry.index.traverse(context); - entry.value.traverse(context); + entry.index.traverse(); + entry.value.traverse(); } } - for i in 0..context.freelist().get(id).array.len() { - let item = context.freelist().get(id).array[i].clone(); - item.traverse(context); + for item in self.array.iter() { + item.traverse() } } } @@ -116,24 +378,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 - 1)) { - if self.array.len() == index as usize - 1 { + if (0..self.array.len() + 1).contains(&(index as usize)) { + if self.array.len() == index as usize { self.array.pop(); } else { - self.array[index as usize - 1] = Value::Nil + self.array[index as usize + 1] = Value::Nil } } - self.table.remove(Value::Integer(index)); + self.table.rem_table(Value::Integer(index)); } item => { - if (0..self.array.len() + 1).contains(&(index as usize - 1)) { - if self.array.len() == index as usize - 1 { + if (0..self.array.len() + 1).contains(&(index as usize)) { + if self.array.len() == index as usize { self.array.push(item) } else { - self.array[index as usize - 1] = item; + self.array[index as usize + 1] = item; } } else { - self.table.set(KeyValue { + self.table.set_table(KeyValue { index: Value::Integer(index), value: item, }) @@ -145,7 +407,7 @@ impl Table { Value::Nil => Err(RunError( "Attempt to set new index of table with key: nil".to_string(), )), - index => Ok(self.table.set(KeyValue { index, value })), + index => Ok(self.table.set_table(KeyValue { index, value })), } } pub fn get(&mut self, index: Value) -> Result { @@ -160,8 +422,11 @@ impl Table { // todo: move this into the Map Ok(self .table - .get(index) - .unwrap_or(&KeyValue::VACANT) + .get_table(index) + .unwrap_or(&KeyValue { + index: Value::Nil, + value: Value::Nil, + }) .value .clone()) } @@ -172,9 +437,9 @@ impl Table { } pub fn new() -> Self { Table { - table: DisplacementSet::new(), + table: Map::new(), array: Vec::new(), - meta: MaybeId::NULL, + meta: None, } } } diff --git a/src/tasks.rs b/src/tasks.rs deleted file mode 100644 index e69de29..0000000 diff --git a/src/tests/hello.lua b/src/tests/hello.lua deleted file mode 100644 index 4835649..0000000 --- a/src/tests/hello.lua +++ /dev/null @@ -1 +0,0 @@ -output "hello" \ No newline at end of file diff --git a/src/tests/script.lua b/src/tests/script.lua index 8535bbd..a854da9 100644 --- a/src/tests/script.lua +++ b/src/tests/script.lua @@ -1,5 +1,9 @@ -local tab = {} -for i = 1, 1000000 do - tab[i] = true +local var = 2 +local function thing() + print(var) + var = 3 end -output(#tab) \ No newline at end of file + +print(var) +thing() +thing() diff --git a/src/vm.rs b/src/vm.rs index fe3cbc9..f2067a0 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -1,14 +1,16 @@ // MACHINE use crate::parser::{BinaryOp, Piece, UnaryOp, parse}; -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 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 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 { @@ -63,7 +65,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 { @@ -71,55 +73,41 @@ impl std::fmt::Debug for Closure { writeln!(f, "Closure:")?; writeln!(f, " Upvalues:")?; for (index, upvalue) in self.upvalues.iter().enumerate() { - writeln!(f, " [{}] {:?}", index, upvalue)?; + writeln!(f, " [{}] {:?}", index, upvalue.borrow().deref())?; } write!(f, "{:?}", self.prototype) } } -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); +impl Traverse for Upvalue { + fn traverse(&self) { + if let Upvalue::Heap(object) = self { + object.traverse() } } } #[derive(Clone)] pub enum Callable { - Rust(AnyId), - Mars(Id), + Rust(Rc), + Mars(Gc), } -impl Callable { - fn traverse(&self, context: &mut C) - where - C: MarsAllocator, - { +impl Traverse for Callable { + fn traverse(&self) { match self { Callable::Rust(_) => {} Callable::Mars(closure) => { - Closure::traverse(closure, context); + closure.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); +impl Traverse for Closure { + fn traverse(&self) { + for upvalue in self.upvalues.iter() { + upvalue.traverse() } } } @@ -128,7 +116,7 @@ impl PartialEq for Callable { fn eq(&self, other: &Self) -> bool { match self { Callable::Rust(a) => match other { - Callable::Rust(b) => a == b, + Callable::Rust(b) => Rc::ptr_eq(a, b), Callable::Mars(_) => false, }, Callable::Mars(a) => match other { @@ -143,25 +131,13 @@ 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), + Callable::Mars(closure) => write!(f, "{:?}", closure.borrow().deref()), } } } #[derive(Debug)] -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) - } -} +pub struct RunError(pub(crate) String); impl Display for RunError { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { @@ -169,38 +145,67 @@ impl Display for RunError { } } -impl std::error::Error 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() + } +} use crate::compiler::compile; -use crate::core::load; -use crate::table::Table; +use crate::gc::{Allocator, Gc, Traverse}; #[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 struct HashedString(Rc, pub(crate) Hashed); - -impl Hashes for HashedString { - fn hashed(&self) -> 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() - } -} - +pub(crate) struct CachedString(Rc, pub(crate) Hashed); #[derive(Clone, Debug)] -struct CachedStringEntry(Option); +struct CachedStringEntry(Option); +impl Hashes for &str { + fn hashed(&self) -> Hashed { + let mut hasher = DefaultHasher::new(); + self[..self.len().min(6)].hash(&mut hasher); + hasher.finish() as Hashed + } +} + impl Hashes for CachedStringEntry { fn hashed(&self) -> Hashed { self.0.as_ref().unwrap().0.deref().hashed() @@ -221,59 +226,39 @@ impl Equivalent for CachedStringEntry { } impl TableEntry for CachedStringEntry { - const VACANT: Self = CachedStringEntry(None); + fn new_vacant() -> Self { + CachedStringEntry(None) + } fn is_vacant(&self) -> bool { self.0.is_none() } } impl Equivalent<&str> for CachedStringEntry { - fn matches(&self, other: &&str) -> bool { - if let Some(ref string) = self.0 { - string.as_str() == *other - } else { - false + fn matches(&self, string: &&str) -> bool { + match self.0 { + Some(ref entry) => entry.0.deref() == *string, + None => false, } } } -#[derive(Clone)] +const PHRASE_LEN: usize = 16; +#[derive(Debug, Clone)] pub enum Value { Nil, // Empty value Bool(bool), Number(f64), Integer(u64), // 64 maybe we should use signed integers... - CachedString(HashedString), - String(HashedString), - Table(Id
), - Object(AnyId, AnyId), + CachedString(CachedString), + String(Rc, Hashed), + Table(Gc
), + Object(Gc), 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 { @@ -282,30 +267,13 @@ impl Value { _ => true, } } - pub fn unwrap_str(&mut self) -> Rc { + fn unwrap_str(&mut self) -> Rc { match self { - Value::CachedString(hashed) => hashed.0.clone(), - Value::String(hashed) => hashed.0.clone(), + Value::CachedString(cached) => cached.0.clone(), + Value::String(string, _hash) => string.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 { @@ -329,17 +297,17 @@ impl PartialEq for Value { Value::Integer(other) => this == other, _ => false, }, - Value::String(string) => match other { + Value::String(string, hash) => match other { Value::CachedString(other) => { - if string.1 == other.1 { - other.0.deref() == string.0.deref() + if *hash == other.1 { + other.0.deref() == string.deref() } else { false } } - Value::String(other) => { - if string.1 == other.1 { - string.0.deref() == other.0.deref() + Value::String(other_string, other_hash) => { + if hash == other_hash { + string == other_string } else { false } @@ -348,9 +316,9 @@ impl PartialEq for Value { }, Value::CachedString(string) => match other { Value::CachedString(other) => other.0 == string.0, // pointer equality - Value::String(other) => { - if string.1 == other.1 { - string.0.deref() == other.0.deref() + Value::String(other_string, other_hash) => { + if string.1 == *other_hash { + string.0.deref() == other_string.deref() } else { false } @@ -361,8 +329,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 { @@ -373,90 +341,34 @@ impl PartialEq for Value { } } -// 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: 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")), +impl Traverse for Value { + fn traverse(&self) { + match self { + Value::Table(table) => { + table.traverse(); + } + Value::Object(anything) => { + anything.traverse(); + } + Value::Function(callable) => callable.traverse(), + _ => {} } } } -pub type ForeignFunction = fn(&mut Machine, needle: Needle) -> Result<(), RunError>; +// Frame for calling a function +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 +} // Machine for executing functions -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 - } +pub struct Machine { + global: Gc
, + allocator: Allocator, + cache: Map, // todo: better to use a generic version of Map } /* @@ -468,76 +380,40 @@ Also, I had a premonition I may need this for multithreading later on! #[derive(Clone)] pub struct Register { value: Value, - upvalue: MaybeId, + upvalue: Option>, } impl Register { const NIL: Register = Register { value: Value::Nil, - upvalue: MaybeId::NULL, + upvalue: None, }; } -pub struct Dispatch { +pub struct Needle { frames: Vec, // Delineation of the stack and execution state for individual functions stack: Vec, // Temporary storage for programs } -impl Dispatch { +impl Needle { fn pop(&mut self) -> Value { self.stack.pop().unwrap().value } fn push(&mut self, value: Value) { self.stack.push(Register { value, - upvalue: MaybeId::NULL, + upvalue: None, }) } 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 -pub(crate) type Stack = u16; // Stack index; -pub(crate) type Count = u16; // Number of arguments +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)] @@ -545,26 +421,26 @@ pub(crate) enum Code { TableNew, TableSet, // consume: value, index, table, TableGet, // consume: index, table; push: result - TableInsert(Stack), // consume: table, value(s) + TableInsert(Index), // consume: table, value UpvalueGet(Index), // push: U[I] UpvalueSet(Index), // consume: value - RegisterGet(Stack), // push: R[I] - RegisterSet(Stack), // consume: value + RegisterGet(Index), // push: R[I] + RegisterSet(Index), // consume: value Constant(Index), // push: C[I] - Call(Stack), // consume: R[I...] push: ... (all results) - Discard(Stack), // consume: R[I...] (used with above to cull results or pad nil) + Call(Index), // consume: R[I...] push: ... (all results) + Discard(Index), // 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(Stack), // Raise R[I...].upvalue from Upvalue::Stack to Upvalue::Heap, 'closing' the register, same effect as Discard for None upvalues + Close(Index), // 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(Stack), // consume: R[I...] (move arguments back to start of frame and finish) + Return(Index), // consume: R[I...] (move arguments around 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), - Message(Index), + Comment(Index), } impl Code { @@ -572,143 +448,32 @@ impl Code { pub(crate) const UNSURE: Code = Code::Jump(0); } -#[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? +impl Machine { + pub(crate) fn new_string(&mut self, string: &str) -> Result { if string.len() < SHORT_STRING_LEN { - match self.cache.get(string) { - Some(ref entry) => Ok(Value::CachedString(entry.0.as_ref().unwrap().clone())), + let existing = self.cache.get_table(string); + match existing { + Some(ref entry) => Ok(Value::CachedString(entry.0.clone().unwrap())), None => { - let cached = HashedString(Rc::from(string), string.hashed()); - self.cache.set(CachedStringEntry(Some(cached.clone()))); + let cached = CachedString(Rc::from(string), string.hashed()); + self.cache + .set_table(CachedStringEntry(Some(cached.clone()))); Ok(Value::CachedString(cached)) } } } else { - Ok(Value::String(HashedString(Rc::from(string), string.hashed()))) + Ok(Value::String(Rc::from(string), string.hashed())) } } - 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(); + fn try_number(&mut self, value: &mut Value) -> Result { match value { integer @ Value::Integer(_) => Ok(integer.clone()), number @ Value::Number(_) => Ok(number.clone()), - 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::Object(object) => object.borrow_mut().0.meta_number(), Value::Table(table) => { - 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), - } + let mut table = table.borrow_mut(); + if let Some(_metatable) = &table.meta { + Err(RunError("Metatables unimplemented".to_string())) } else { Ok(Value::Nil) } @@ -716,12 +481,7 @@ impl Machine { _ => Ok(Value::Nil), } } - pub(crate) fn meta_to_string( - &mut self, - dispatch: &mut Dispatch, - value: Value, - ) -> Result { - let cloned = value.clone(); + fn try_string(&mut self, value: &mut Value) -> Result { Ok(match value { Value::Nil => self.new_string("nil".to_string().as_str())?, Value::Bool(bool) => self.new_string(format!("{}", bool).as_str())?, @@ -732,94 +492,128 @@ impl Machine { #[cfg(feature = "vector3")] Value::Vector(vector) => format!("{}", vector), Value::Table(table) => { - let mut table = self.tables.get(&table); - if let Some(metatable) = table.meta.exists() { + let mut table = table.borrow_mut(); + if let Some(_metatable) = &table.meta { return Err(RunError("Metatables unimplemented".to_string())); } else { self.new_string("{...}".to_string().as_str())? } } - 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::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::Function(callable) => self.new_string(format!("{:?}", callable).as_str())?, }) } - fn meta_set( - &mut self, - dispatch: &mut Dispatch, - subject: Value, - index: Value, - value: Value, - ) -> Result<(), RunError> { + fn set(&mut self, subject: Value, index: Value, value: Value) -> Result<(), RunError> { match subject { - 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::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::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(ref table) => { - let mut table = self.tables.get(table); - if let Some(ref _metatable) = table.meta.exists() { + Value::Table(mut table) => { + let mut table = table.borrow_mut(); + if let Some(_metatable) = &table.meta { Err(RunError("Metatables unimplemented".to_string())) } else { table.set(index, value) } } - Value::Object(id, meta) => (self.metas.get(&meta.into()).set)(self, id, index, value, dispatch.needle()), + Value::Object(mut object) => object.borrow_mut().0.meta_new_index(index, value), Value::Function(_callable) => Err(RunError("Function index unimplemented".to_string())), } } - fn meta_get( - &mut self, - dispatch: &mut Dispatch, - mut subject: Value, - index: Value, - ) -> Result { + fn get(&mut self, subject: Value, index: Value) -> Result { match subject { Value::Nil => Err(RunError("Cannot set to nil".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::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::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(ref mut table) => { - let mut table = self.tables.get(table); - if let Some(ref _metatable) = table.meta.exists() { + Value::Table(mut table) => { + let mut table = table.borrow_mut(); + if let Some(_metatable) = &table.meta { Err(RunError("Metatables unimplemented".to_string())) } else { table.get(index.clone()) } } - Value::Object(id, meta) => (self.metas.get(&meta.into()).get)(self, id, index, dispatch.needle()), + Value::Object(mut object) => object.borrow_mut().0.meta_index(index.clone()), Value::Function(_callable) => Err(RunError("Function index unimplemented".to_string())), } } - // 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> { + /*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> { let chunk = match parse(source) { Ok(parsed) => parsed, Err(error) => return Err(MarsError(error.msg(source))), @@ -833,54 +627,54 @@ impl Machine { }; let global = self.global.clone(); - let upvalues = vec![self.freelist().make(Upvalue::Heap(Value::Table(global))).id]; - Ok(self - .freelist() - .make(Closure { - prototype: Rc::new(prototype), - upvalues, - }) - .id) + let upvalues = vec![self.allocator.alloc(Upvalue::Heap(Value::Table(global)))]; + Ok(self.allocator.alloc(Closure { + prototype: Rc::new(prototype), + upvalues, + })) } - 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 })?; + 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(), + )); + } } - 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 @@ -889,17 +683,12 @@ impl Machine { } }; loop { - let frame = dispatch - .frames - .last_mut() - .ok_or(RunError("No frames in needle!".to_string()))? - .clone(); - if dispatch.stack.len() > Stack::MAX as usize { + if needle.stack.len() > 65536 { return Err(RunError("Soft stack limit of 65536 reached".to_string())); } - let code = self - .freelist() - .get(&frame.closure) + let code = frame + .closure + .borrow_mut() .prototype .code .get(frame.counter) @@ -907,43 +696,35 @@ impl Machine { let code = match code { Some(code) => code, None => { - if dispatch.frames.len() == frame_index { - dispatch.frames.pop(); - break; + if let Some(upper) = needle.frames.pop() { + frame = upper; } else { - dispatch.frames.pop(); - if dispatch.frames.last_mut().is_none() { - break; - } else { - continue; - } + break; } + continue; } }; match code { - Code::Message(number) => { - eprintln!("{}", COMMENTS[number as usize]) - } - Code::TableNew => { - dispatch.push(Value::Table(self.freelist().make(Table::new()).id)) + Code::Comment(number) => { + eprintln!("{}", number) } + Code::TableNew => needle.push(Value::Table(self.allocator.alloc(Table::new()))), Code::TableSet => { - let table = dispatch.pop(); - let index = dispatch.pop(); - let value = dispatch.pop(); - self.meta_set(dispatch, table, index, value)?; + let table = needle.pop(); + let index = needle.pop(); + let value = needle.pop(); + self.set(table, index, value)?; } Code::TableGet => { - let table = dispatch.pop(); - let index = dispatch.pop(); - let value = self.meta_get(dispatch, table, index)?; - dispatch.push(value); + let table = needle.pop(); + let index = needle.pop(); + needle.push(self.get(table, index)?); } Code::TableInsert(index) => { - 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()) + match needle.pop() { + Value::Table(mut table) => { + for i in frame.offset + index as usize..needle.stack.len() { + table.borrow_mut().append(needle.pop()) } } _ => { @@ -954,61 +735,54 @@ impl Machine { } } Code::UpvalueGet(index) => { - match self - .upvalues - .get(&self.closures.get(&frame.closure).upvalues[index as usize]) + match frame.closure.borrow_mut().upvalues[index as usize] + .borrow_mut() + .deref() { - Upvalue::Stack(stack) => { - dispatch.push(dispatch.stack[*stack].value.clone()) - } - Upvalue::Heap(object) => dispatch.push(object.clone()), + Upvalue::Stack(stack) => needle.push(needle.stack[*stack].value.clone()), + Upvalue::Heap(object) => needle.push(object.clone()), } } Code::UpvalueSet(index) => { - match self - .upvalues - .get(&self.closures.get(&frame.closure).upvalues[index as usize]) + match frame.closure.borrow_mut().upvalues[index as usize] + .borrow_mut() + .deref_mut() { - Upvalue::Stack(register) => { - dispatch.stack[*register].value = dispatch.pop() - } - Upvalue::Heap(it) => *it = dispatch.pop(), + Upvalue::Stack(register) => needle.stack[*register].value = needle.pop(), + Upvalue::Heap(it) => *it = needle.pop(), } } Code::RegisterGet(index) => { - dispatch.push(dispatch.stack[frame.vararg + index as usize].value.clone()); + needle.push(needle.stack[frame.vararg + index as usize].value.clone()); } Code::RegisterSet(index) => { - dispatch.stack[frame.vararg + index as usize].value = dispatch.pop(); + needle.stack[frame.vararg + index as usize].value = needle.pop(); } Code::Constant(constant) => { - dispatch.push( - self.closures.get(&frame.closure).prototype.constants[constant as usize] - .clone(), + needle.push( + frame.closure.borrow_mut().prototype.constants[constant as usize].clone(), ); } - 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::Call(index) => { + frame = call(self, needle, frame, index)?; + continue; + } Code::Discard(index) => { - dispatch + needle .stack .resize(frame.offset + index as usize + 1, Register::NIL); } Code::VarArg(count) => { if count == 0 { for i in frame.vararg..frame.offset { - dispatch.push(dispatch.stack[i].value.clone()); + needle.push(needle.stack[i].value.clone()); } } else { for i in 0..count as usize { if frame.offset - i - 1 < frame.vararg { - dispatch.push(Value::Nil); + needle.push(Value::Nil); } else { - dispatch.push(dispatch.stack[frame.offset - i - 1].value.clone()); + needle.push(needle.stack[frame.offset - i - 1].value.clone()); } } } @@ -1016,24 +790,24 @@ impl Machine { Code::Jump(offset) => { #[cfg(feature = "assertions")] assert_ne!(offset, 0); - jump(dispatch.frames.last_mut().unwrap(), offset); + jump(&mut frame, offset); } Code::Skip => { - if dispatch.pop().bool() { - dispatch.frames.last_mut().unwrap().counter += 1; + if needle.pop().bool() { + frame.counter += 1; } } Code::Return(index) => { // Settle the return arguments to where this frame was started originally - for i in frame.offset + index as usize..dispatch.stack.len() { - dispatch.stack[frame.vararg + i - 1] = dispatch.stack[i].clone(); + for i in frame.offset + index as usize..needle.stack.len() { + needle.stack[frame.vararg + i - 1] = needle.stack[i].clone(); } - dispatch.frames.pop(); - if dispatch.frames.last_mut().is_some() { - continue; + if let Some(upper) = needle.frames.pop() { + frame = upper; } else { break; } + continue; // todo: is this needed? } Code::Binary(op) => { return Err(RunError( @@ -1044,69 +818,62 @@ impl Machine { return Err(RunError("Unary operations are not implemented".to_string())); } Code::Close(index) => { - 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()) + 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! } - _ => panic!(), } + Register { upvalue: None, .. } => {} } } - dispatch + needle .stack .resize(frame.offset + index as usize, Register::NIL); // As with Code::Discard } Code::Closure(index) => { - // 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 + let prototype = + frame.closure.borrow().prototype.prototypes[index as usize].clone(); + let mut upvalues = Vec::new(); 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) => { - // 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? + let index = frame.offset + *index as usize; + match &mut needle.stack[index].upvalue { Some(upvalue) => upvalue.clone(), - _ => { - let upvalue = self.freelist().make(Upvalue::Stack(index)); - dispatch.stack[index].upvalue = upvalue.id.clone().maybe(); - upvalue.id + it => { + let upvalue = self.allocator.alloc(Upvalue::Stack(index)); + *it = Some(upvalue.clone()); + upvalue } } } Reference::Upvalue(index) => { - self.freelist().get(&frame.closure).upvalues[*index as usize] - .clone() + frame.closure.borrow_mut().upvalues[*index as usize].clone() } }); } - dispatch.push(Value::Function(Callable::Mars( - self.freelist() - .make(Closure { - prototype, - upvalues, - }) - .id, - ))); + needle.push(Value::Function(Callable::Mars(self.allocator.alloc( + Closure { + prototype, + upvalues, + }, + )))); } Code::RangeBegin => { - let len = dispatch.stack.len(); + let len = needle.stack.len(); let init = len - 3; let limit = len - 2; let step = len - 1; - 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())?; + 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)?; if matches!(init_value, Value::Integer(_)) && matches!(limit_value, Value::Integer(_)) && matches!(step_value, Value::Integer(_)) @@ -1116,9 +883,9 @@ impl Machine { _ => panic!(), }; // All integers so we can carry this out nicely - dispatch.stack[init].value = init_value; - dispatch.stack[limit].value = limit_value; // todo: is this actually correct? - dispatch.stack[step].value = step_value; + needle.stack[init].value = init_value; + needle.stack[limit].value = limit_value; // todo: is this actually correct? + needle.stack[step].value = step_value; } else { // Make them all as floats let cast = |value: Value| match value { @@ -1126,21 +893,21 @@ impl Machine { number @ Value::Number(_) => number, _ => panic!(), }; - dispatch.stack[init].value = cast(init_value); - dispatch.stack[limit].value = cast(limit_value); - dispatch.stack[step].value = cast(step_value); + needle.stack[init].value = cast(init_value); + needle.stack[limit].value = cast(limit_value); + needle.stack[step].value = cast(step_value); } - dispatch.push(dispatch.stack[init].value.clone()); + needle.push(needle.stack[init].value.clone()); } Code::RangeLoop(offset) => { - dispatch.pop(); // get rid of temp - let len = dispatch.stack.len(); + needle.pop(); // get rid of temp + let len = needle.stack.len(); let init_index = len - 3; let limit_index = len - 2; let step_index = len - 1; - 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(); + 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)?; match init_value { Value::Integer(init) => { let step = match step_value { @@ -1152,13 +919,13 @@ impl Machine { _ => panic!(), }; if init < limit { - 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); + needle.stack[init_index].value = Value::Integer(init + step); + needle.push(Value::Integer(init + step)); // owned variable for loop block + jump(&mut frame, offset); } else { - dispatch.pop(); - dispatch.pop(); - dispatch.pop(); + needle.pop(); + needle.pop(); + needle.pop(); } } Value::Number(init) => { @@ -1171,50 +938,47 @@ impl Machine { _ => panic!(), }; if (step >= 0.0 && init > limit) || (step < 0.0 && init < limit) { - 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); + needle.stack[init_index].value = Value::Number(init + step); + needle.push(Value::Number(init)); // owned variable for loop block + jump(&mut frame, offset); } else { - dispatch.pop(); - dispatch.pop(); - dispatch.pop(); + needle.pop(); + needle.pop(); + needle.pop(); } } _ => panic!(), } } Code::IteratorLoop(offset) => { - 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 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 frame_offset = frame.offset; - self.dispatch_call( - dispatch, - callable, - frame.offset - + Index::try_from(dispatch.stack.len() - frame_offset - 2).unwrap() - as usize, + frame = call( + self, + needle, + frame, + Index::try_from(needle.stack.len() - frame_offset - 2).unwrap(), )?; if matches!( - dispatch.stack.get(len + 1).unwrap_or(&Register::NIL).value, + needle.stack.get(len + 1).unwrap_or(&Register::NIL).value, Value::Nil, ) { - jump(dispatch.frames.last_mut().unwrap(), offset); + jump(&mut frame, offset); }; } } - dispatch.frames.last_mut().unwrap().counter += 1; + frame.counter += 1; } Ok(()) } - pub fn enter(&mut self, closure: Id) -> Result<(), RunError> { - let mut needle = Dispatch { + pub(crate) fn enter(&mut self, closure: Gc) -> Result<(), RunError> { + let mut needle = Needle { frames: vec![Frame { vararg: 0, offset: 0, @@ -1225,60 +989,55 @@ impl Machine { }; self.dispatch(&mut needle) } - pub fn set_global(&mut self, name: &str, value: Value) -> Result<(), RunError> { + pub fn register_global(&mut self, name: &str, native: Native) -> Result<(), RunError> { let name = self.new_string(name)?; - self.tables.get(&self.global).set(name, value) + self.global + .borrow_mut() + .set(name, Value::Function(Callable::Rust(Rc::from(native)))) } - 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); + pub fn new() -> Result { + let mut allocator = Allocator::new(); let mut machine = Machine { - 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(), + global: allocator.alloc(Table::new()), + allocator, + cache: Map::new(), }; - machine.init_meta_strings()?; - load(&mut machine)?; + 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(()) + }, + ), + )?; 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)