diff --git a/Cargo.toml b/Cargo.toml index a734020..55a8c8a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,7 @@ version = "0.1.0" edition = "2024" [features] +bighash = [] vector3 = ["dep:glam"] messages = [] assertions = [] diff --git a/mars-49d70575ae6f08.core b/mars-49d70575ae6f08.core deleted file mode 100644 index 40e21fd..0000000 Binary files a/mars-49d70575ae6f08.core and /dev/null differ diff --git a/src/lib.rs b/src/lib.rs index 2a18593..195bd8e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,8 +18,6 @@ struct Reader<'a> { white: bool, // Is the previous 'thing' whitespace? } -type Error = String; - pub struct LoadError(String, Piece<()>); // Any error returned by a machine loading code impl LoadError { @@ -52,6 +50,9 @@ impl LoadError { } roll } + fn from_runtime(piece: Piece<()>, error: RunError) -> LoadError { + LoadError(error.0,piece) + } } impl std::fmt::Debug for LoadError { @@ -1233,8 +1234,9 @@ use std::any::Any; use std::cmp::PartialEq; use std::rc::Rc; -struct Traversal { +struct Traversal<'a> { // A 'cursor' of where we are in assumed execution + machine: &'a mut Machine, contexts: Vec, // Stack of function prototypes being compiled } @@ -1242,9 +1244,40 @@ 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 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), @@ -1261,7 +1294,7 @@ struct Scope { upvalue: bool, } -impl Traversal { +impl Traversal<'_> { fn compile(&mut self, it: &T) -> Result<(), LoadError> where T: Compile, @@ -1333,20 +1366,29 @@ impl Traversal { } fn push_constant(&mut self, it: Value) -> Index { // todo: constants need pieces too!!! - let constants = &mut self.contexts.last_mut().unwrap().prototype.constants; - let index; - if let Some(position) = constants.iter().position(|found| it.eq(found)) { - index = position; + // don't use constants that already exist + if let Some(entry) = &mut self.contexts.last_mut().unwrap().constants.get_table(it.clone()) { + entry.1 } else { - index = constants.len(); + let index = { + let constants = &mut self.contexts.last_mut().unwrap().prototype.constants; + constants.push(it.clone()); + constants.len() - 1 + }.try_into().unwrap(); + self.contexts.last_mut().unwrap().constants.set_table(ConstantEntry(it,index)); + index } - constants.push(it); - index as Index } fn push_literal(&mut self, it: Value, piece: Piece<()>) { let constant = self.push_constant(it); self.push_code(piece.swap(Code::Constant(constant))); } + fn push_string(&mut self, string: String, piece: Piece<()>) -> Result<(),LoadError> { + match self.machine.new_string(string.clone().as_str()) { + Err(error) => Err(LoadError(error.0,piece)), + Ok(string) => Ok(self.push_literal(string, piece)), + } + } fn new_scope(&mut self) { let context = &mut self.contexts.last_mut().unwrap(); let scopes = &mut context.scopes; @@ -1449,6 +1491,7 @@ fn compile_function( pieces: vec![], code: vec![], }, + constants: Map::new(), }); traversal.compile(&this.it.2.inner_ref())?; // compile the block let prototype = traversal.close_context()?; @@ -1547,7 +1590,7 @@ fn compile_identifier( } } // break 'search avoids this use of the _G table - traversal.push_literal(name.it.clone().into(), name.unit()); + traversal.push_string(name.it.clone(),name.unit())?; traversal.push_code(name.swap(Code::UpvalueGet(0))); traversal.push_code(name.swap(match access { Access::Set => Code::TableSet, @@ -1625,7 +1668,7 @@ impl Compile for Piece<&Call> { traversal.compile(&self.it.0.as_ref().inner_ref())?; // call prefix match &self.it.1 { Some(name) => { - traversal.push_literal(name.it.0.clone().into(), name.unit()); + traversal.push_string(name.it.0.clone(),name.unit())?; traversal.push_code(name.swap(Code::TableGet)); } None => {} @@ -1759,7 +1802,7 @@ impl Compile for Piece<&Expression> { traversal.push_literal(Value::Number(number), self.unit()); } Expression::String(string) => { - traversal.push_literal(string.clone().into(), self.unit()); + traversal.push_string(string.clone(),self.unit())?; } Expression::VarArg(vararg) => { traversal.compile(&self.swap(vararg))?; @@ -2121,10 +2164,10 @@ impl Compile for Piece<&Block> { while let Some(name) = names.next() { if !names.peek().is_some() { compile_function(&self.swap(function), traversal, is_method)?; - traversal.push_literal(name.it.0.clone().into(), name.unit()); + traversal.push_string(name.it.0.clone(),name.unit())?; traversal.push_code(name.unit().end().swap(Code::TableSet)); } else { - traversal.push_literal(name.it.0.clone().into(), name.unit()); + traversal.push_string(name.it.0.clone(),name.unit())?; traversal.push_code(name.unit().end().swap(Code::TableGet)); } } @@ -2174,10 +2217,10 @@ impl Compile for Chunk { // MACHINE -use crate::table::{Hashed, Table}; +use crate::table::{Equivalent, Hashed, Hashes, Map, Table, TableEntry}; // Rust function to call -struct Native(dyn Fn(&mut Machine, &mut Needle) -> Result<(), RunError>); // A native function in Rust +struct Native(fn(&mut Machine, &mut Needle, usize) -> Result<(), RunError>); // A native function in Rust #[derive(Clone, Debug, Copy, PartialEq)] enum Reference { @@ -2303,7 +2346,8 @@ impl std::fmt::Debug for Callable { } } -struct RunError(String); +#[derive(Debug)] +pub struct RunError(String); pub trait Object: std::fmt::Debug { fn meta_string(&mut self) -> Result { @@ -2316,109 +2360,15 @@ pub trait Object: std::fmt::Debug { Err(RunError(format!("Cannot set index in {:?}", &self))) } fn meta_index(&mut self, index: Value) -> Result { - Ok(Value::Nil) + Err(RunError(format!("Cannot get index in {:?}", &self))) } fn meta_eq(&mut self, other: &Value) -> Result { Ok(Value::Nil) } } -impl From for Value { - fn from(string: String) -> Self { - let mut hasher = DefaultHasher::new(); - string.hash(&mut hasher); - Value::String(Rc::from(string.as_str()), hasher.finish() as Hashed) - } -} - impl Value { - fn number(&mut self) -> Result { - match self { - integer @ Value::Integer(_) => Ok(integer.clone()), - number @ Value::Number(_) => Ok(number.clone()), - Value::Object(object) => object.borrow_mut().0.meta_number(), - Value::Table(table) => { - let mut table = table.borrow_mut(); - if let Some(_metatable) = &table.meta { - Err(RunError("Metatables unimplemented".to_string())) - } else { - Ok(Value::Nil) - } - } - _ => Ok(Value::Nil), - } - } - fn string(&mut self) -> Result { - Ok(match self { - Value::Nil => "nil".to_string(), - Value::Bool(bool) => format!("{}", bool), - Value::Integer(integer) => format!("{}", integer), - Value::Number(number) => format!("{}", number), - it @ Value::String(..) => return Ok(it.clone()), - #[cfg(feature = "vector3")] - Value::Vector(vector) => format!("{}", vector), - Value::Table(table) => { - let mut table = table.borrow_mut(); - if let Some(_metatable) = &table.meta { - return Err(RunError("Metatables unimplemented".to_string())); - } else { - "{...}".to_string() - } - } - 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) => format!("{:?}", callable), - } - .into()) - } - fn set(&mut self, index: Value, item: Value) -> Result<(), RunError> { - match self { - Value::Nil => Err(RunError("Cannot index 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(_string, _hash) => { - Err(RunError("String index unimplemented".to_string())) - } - #[cfg(feature = "vector3")] - Value::Vector(_vector) => Err(RunError("Vector index unimplemented".to_string())), - Value::Table(table) => { - let mut table = table.borrow_mut(); - if let Some(_metatable) = &table.meta { - Err(RunError("Metatables unimplemented".to_string())) - } else { - table.set(index, item) - } - } - Value::Object(object) => object.borrow_mut().0.meta_new_index(index, item), - Value::Function(_callable) => Err(RunError("Function index unimplemented".to_string())), - } - } - fn get(&mut self, index: Value) -> Result { - match self { - Value::Nil => Err(RunError("Cannot index 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(_string, _hash) => { - Err(RunError("String index unimplemented".to_string())) - } - #[cfg(feature = "vector3")] - Value::Vector(_vector) => Err(RunError("Vector index unimplemented".to_string())), - Value::Table(table) => { - let table = table.borrow_mut(); - if let Some(_metatable) = &table.meta { - Err(RunError("Metatables unimplemented".to_string())) - } else { - table.get(index) - } - } - Value::Object(object) => object.borrow_mut().0.meta_index(index), - Value::Function(_callable) => Err(RunError("Function index unimplemented".to_string())), - } - } + } pub trait Userdata: Traverse + std::fmt::Debug + Any + Object {} @@ -2447,15 +2397,63 @@ struct Phrase { } // Any value in the language +const SHORT_STRING_LEN: usize = 40; +#[derive(Clone, Debug)] +struct CachedString(Rc, Hashed); +#[derive(Clone, Debug)] +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() + } +} + +impl Equivalent for CachedStringEntry { // redundant? + fn matches(&self, other: &Self) -> bool { + match self.0 { + Some(ref entry) => match other.0 { + Some(ref other) => entry.0.deref() == other.0.deref(), + None => false, + }, + None => false, + } + } +} + +impl TableEntry for CachedStringEntry { + fn new_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 + } + } +} + const PHRASE_LEN: usize = 16; #[derive(Debug, Clone)] -// 120 - 8 - 64 -> 56 pub enum Value { - // 32 bytes!!! 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), @@ -2464,6 +2462,78 @@ pub enum Value { Vector(Vec3), } +impl Value { + fn bool(&mut self) -> bool { + match self { + Value::Nil => false, + Value::Bool(false) => false, + _ => true, + } + } +} + +impl PartialEq for Value { + fn eq(&self, other: &Self) -> bool { + match self { + Value::Nil => match other { + Value::Nil => true, + _ => false, + } + Value::Bool(this) => match other { + Value::Bool(other) => this == other, + _ => false, + } + Value::Number(this) => match other { + Value::Number(other) => this == other, + Value::Integer(other) => *this == *other as f64, + _ => false, + } + Value::Integer(this) => match other { + Value::Number(other) => *this as f64 == *other, // are these conversions ok? + Value::Integer(other) => this == other, + _ => false, + } + Value::String(string, hash) => match other { + 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 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() + } else { + false + } + _ => false + } + Value::Table(this) => match other { + Value::Table(other) => other == this, + _ => false + } + Value::Object(this) => match other { + Value::Object(other) => other == this, + _ => false + } + Value::Function(this) => match other { + Value::Function(other) => other == this, + _ => false + } + } + } +} + impl Traverse for Value { fn traverse(&self) { match self { @@ -2491,6 +2561,7 @@ pub struct Frame { pub struct Machine { global: Gc
, allocator: Allocator, + cache: Map // todo: better to use a generic version of Map } /* @@ -2570,15 +2641,181 @@ impl Code { } impl Machine { - fn load(&mut self, source: &str) -> Result { + fn new_string(&mut self, string: &str) -> Result { + 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())) + }, + None => { + let cached = CachedString(Rc::from(string),string.hashed()); + self.cache.set_table(CachedStringEntry(Some(cached.clone()))); + Ok(Value::CachedString(cached)) + } + } + } else { + Ok(Value::String(Rc::from(string), string.hashed())) + } + } + 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(object) => object.borrow_mut().0.meta_number(), + Value::Table(table) => { + let mut table = table.borrow_mut(); + if let Some(_metatable) = &table.meta { + Err(RunError("Metatables unimplemented".to_string())) + } else { + Ok(Value::Nil) + } + } + _ => Ok(Value::Nil), + } + } + /*fn try_string(&mut self, machine: &mut Machine) -> Result { + Ok(match self { + Value::Nil => machine.new_string("nil".to_string().as_str())?, + Value::Bool(bool) => machine.new_string(format!("{}", bool).as_str())?, + Value::Integer(integer) => machine.new_string(format!("{}", integer).as_str())?, + Value::Number(number) => machine.new_string(format!("{}", number).as_str())?, + it @ Value::String(..) => return Ok(it.clone()), + it @ Value::CachedString(..) => return Ok(it.clone()), + #[cfg(feature = "vector3")] + Value::Vector(vector) => format!("{}", vector), + Value::Table(table) => { + let mut table = table.borrow_mut(); + if let Some(_metatable) = &table.meta { + return Err(RunError("Metatables unimplemented".to_string())); + } else { + machine.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::Function(callable) => machine.new_string(format!("{:?}", callable).as_str())?, + }) + }*/ + fn set(&mut self, 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::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 { + 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::Function(_callable) => Err(RunError("Function index unimplemented".to_string())), + } + } + 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(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(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(mut object) => object.borrow_mut().0.meta_index(index.clone()), + 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 + } + }) + }*/ + fn load(&mut self, source: &str) -> Result { let parsed = match parse(source) { Ok(parsed) => parsed, - Err(error) => return Err(error.msg(source)), + Err(error) => return Err(MarsError(error.msg(source))), }; println!("{:?}", parsed); let mut traversal = Traversal { + machine: self, contexts: vec![Context { scopes: vec![Scope { // Dummy initial scope so the rest can start from index = 0 @@ -2589,6 +2826,7 @@ impl Machine { upvalue: false, }], vararg: Some(Piece::::null().swap(None)), + constants: Map::new(), prototype: Prototype { args: 0, vararg: true, @@ -2603,12 +2841,12 @@ impl Machine { match parsed.compile(&mut traversal) { Ok(compiled) => compiled, - Err(error) => return Err(error.msg(source)), + Err(error) => return Err(MarsError(error.msg(source))), }; let prototype = match traversal.close_context() { Ok(compiled) => compiled, - Err(error) => return Err(error.msg(source)), + Err(error) => return Err(MarsError(error.msg(source))), }; let global = self.global.clone(); @@ -2634,7 +2872,7 @@ impl Machine { Value::Function(callable) => { match callable { Callable::Rust(function) => { - function.0(machine, needle)?; + function.0(machine, needle, offset + index as usize + 1)?; Ok(frame) // nothing changes } Callable::Mars(closure) => { @@ -2655,7 +2893,7 @@ impl Machine { } _ => { return Err(RunError( - "Calling non-function types are not yet implemented".to_string(), + "Calling non-function types are unimplemented".to_string(), )); } } @@ -2671,21 +2909,32 @@ impl Machine { if needle.stack.len() > 65536 { return Err(RunError("Soft stack limit of 65536 reached".to_string())); } - let code = frame.closure.borrow_mut().prototype.code[frame.counter]; + let code = frame.closure.borrow_mut().prototype.code.get(frame.counter).cloned(); + let code = match code { + Some(code) => code, + None => { + if let Some(upper) = needle.frames.pop() { + frame = upper; + } else { + break; + } + continue; + } + }; match code { Code::Comment(number) => { eprintln!("{}", number) } Code::TableSet => { - let value = needle.pop(); + let table = needle.pop(); let index = needle.pop(); - let mut table = needle.pop(); - table.set(index, value)?; + let value = needle.pop(); + self.set(table, index, value)?; } Code::TableGet => { + let table = needle.pop(); let index = needle.pop(); - let mut table = needle.pop(); - needle.push(table.get(index)?); + needle.push(self.get(table, index)?); } Code::TableInsert(index) => { match needle.pop() { @@ -2758,9 +3007,10 @@ impl Machine { #[cfg(feature = "assertions")] assert_ne!(offset, 0); jump(&mut frame, offset); + continue; // Skip the extra offset++ } Code::Skip => { - if matches!(needle.pop(), Value::Nil | Value::Bool(false)) { + if needle.pop().bool() { frame.counter += 1; } } @@ -2774,6 +3024,7 @@ impl Machine { } else { break; } + continue; // todo: is this needed? } Code::Binary(op) => { return Err(RunError( @@ -2837,9 +3088,9 @@ impl Machine { let init = len - 2; let limit = len - 1; let step = len; - let init_value = needle.stack[init].value.number()?; - let limit_value = needle.stack[limit].value.number()?; - let step_value = needle.stack[step].value.number()?; + 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(_)) @@ -2874,9 +3125,9 @@ impl Machine { let init_index = len - 2; let limit_index = len - 1; let step_index = len; - let init_value = needle.stack[init_index].value.number()?; - let limit_value = needle.stack[limit_index].value.number()?; - let step_value = needle.stack[step_index].value.number()?; + 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 { @@ -2953,25 +3204,48 @@ impl Machine { }; self.dispatch(&mut needle) } - fn new() -> Machine { + fn new() -> Result { let mut allocator = Allocator::new(); - Machine { + let mut machine = Machine { global: allocator.alloc(Table::new()), allocator, - } + cache: Map::new(), + }; + let string = machine.new_string("print").unwrap(); + machine.global.borrow_mut().set( + string, + Value::Function(Callable::Rust(Rc::from(Native(|machine: &mut Machine, needle: &mut Needle, index: usize| -> Result<(), RunError> { + for i in index..needle.stack.len() { + println!("{:?}",needle.pop()) + } + Ok(()) + })))) + )?; + Ok(machine) } } +#[derive(Debug, Clone)] +pub struct MarsError(String); + #[cfg(test)] mod tests { use super::*; #[test] fn simple() { - let mut machine = Machine::new(); - let result = machine.load(include_str!("tests/script.lua")); - match result { - Err(error) => println!("{}", error), - Ok(result) => println!("{:?}", result), - } + 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(Callable::Mars(closure)) => closure, + _ => panic!() + }; + println!("{:?}",closure.borrow().prototype); + match machine.enter(closure) { + Err(error) => panic!("{:?}",error), + _ => {} + }; } } diff --git a/src/table.rs b/src/table.rs index 6df39b0..c5ae287 100644 --- a/src/table.rs +++ b/src/table.rs @@ -1,5 +1,5 @@ use crate::gc::{Gc, Traverse}; -use crate::{Callable, RunError, Value}; +use crate::{Callable, Object, 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... @@ -24,57 +24,298 @@ Notes: */ type Displacement = u8; -pub type Hashed = usize; + +#[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 Entry { +struct KeyValue { index: Value, - item: Value, + value: Value, +} + +impl Equivalent for KeyValue { + fn matches(&self, other: &Self) -> bool { + self.index == other.index + } +} + +impl Hashes for KeyValue { + fn hashed(&self) -> Hashed { + self.index.hashed() + } +} + +impl TableEntry for KeyValue { + fn new_vacant() -> Self { + KeyValue { + index: Value::Nil, + value: Value::Nil, + } + } + fn is_vacant(&self) -> bool { + matches!(self,KeyValue { index: Value::Nil, .. }) + } +} + +#[derive(Debug, Clone)] +struct Entry { + entry: E, home: usize, displacement: Displacement, } -impl Default for Entry { - fn default() -> Self { - Entry { - index: Value::Nil, - item: Value::Nil, +#[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 { + pub(crate) fn string_cache() { // todo: this isn't a clean way, maybe make a generic Map and use a specific one for the cache and the rest of the language + + } + 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]); + 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) + } + self.map_bounds = self.table_lower()..self.table_upper() + } + 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]; + 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]; + 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(), } } } -pub fn hash(value: &Value) -> Hashed { - match value { - Value::Nil => 0, - Value::Bool(boolean) => { - if *boolean { - 1 - } else { - 0 +impl Hashes for Value { + fn hashed(&self) -> Hashed { + match self { + Value::Nil => 0, + Value::Bool(boolean) => { + if *boolean { + 1 + } else { + 0 + } } - } - Value::String(_string, hash) => *hash, - Value::Function(callable) => match callable { - Callable::Rust(native) => Rc::as_ptr(native).addr(), - Callable::Mars(closure) => closure.addr(), - }, - Value::Integer(integer) => *integer as usize, - Value::Number(number) => { - if number.is_nan() { - 0 - } else { - number.to_bits() as usize + Value::CachedString(cached) => cached.1, + Value::String(_string, hash) => *hash, + Value::Function(callable) => match callable { + 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) => { + if number.is_nan() { + 0 + } else { + number.to_bits() as 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(); + vec.as_u64vec3().hash(&mut hasher); + hasher.finish() as usize } - } - Value::Table(table) => table.addr(), - Value::Object(object) => object.addr(), - #[cfg(feature = "vector3")] - Value::Vector(vec) => { - let mut hasher = DefaultHasher::new(); - vec.as_u64vec3().hash(&mut hasher); - hasher.finish() as usize } } } @@ -82,19 +323,17 @@ pub fn hash(value: &Value) -> Hashed { // todo: displaced items don't ever get shuffled closer to their homes unless the table is resized #[derive(Debug, Clone)] pub struct Table { - table: Vec, + table: Map, array: Vec, - table_bounds: std::ops::Range, - table_count: usize, // number of elements in table pub meta: Option>, } impl Traverse for Table { fn traverse(&self) { - for Entry { index, item, .. } in self.table.iter() { - if !matches!(index, Value::Nil) { - index.traverse(); - item.traverse(); + for Entry { entry, .. } in self.table.map.iter() { + if !entry.is_vacant() { + entry.index.traverse(); + entry.value.traverse(); } } for item in self.array.iter() { @@ -103,174 +342,21 @@ impl Traverse for Table { } } +impl Equivalent for KeyValue { + fn matches(&self, other: &Value) -> bool { + self.index == *other + } +} + impl Table { - fn exchange_table(&mut self, len: usize) { - let old = std::mem::replace(&mut self.table, vec![Entry::default(); len]); - for Entry { index, item, .. } in old { - if index != Value::Nil { - self.set_table(index, item) - } - } - } - pub fn resize_table(&mut self, len: usize) { - self.exchange_table(len.max(self.table_count + (self.table_count / 3))); - self.table_bounds = 0..self.table_upper(); - } pub fn resize_array(&mut self, len: usize) { self.array.resize(len, Value::Nil) } - fn table_upper(&self) -> usize { - (self.table.len() / 4) * 3 - } - fn table_lower(&self) -> usize { - self.table.len() / 3 - } - fn ensure_table(&mut self) { - if self.table_count > self.table_upper() { - // free space is short - self.exchange_table((self.table.len() + 4) * 2); - } else if self.table_count < self.table_lower() { - // too much free space - self.exchange_table(self.table.len() / 2) - } - self.table_bounds = self.table_lower()..self.table_upper() - } - fn set_table(&mut self, index: Value, item: Value) { - #[cfg(feature = "assertions")] - assert_ne!(index, Value::Nil); - #[cfg(feature = "assertions")] - assert_ne!(item, Value::Nil); - if !self.table_bounds.contains(&self.table_count) { - // make sure the table is appropriately sized - self.ensure_table(); - } - let range = self.table.len(); - let home = hash(&index) % range; - if self.table[home].index == Value::Nil { - // attempt to place it directly in an empty space - // INSERT - self.table[home].home = home; - self.table[home].item = item; // home.item.soft_drop() - self.table[home].index = index; - self.table[home].displacement = self.table[home].displacement.max(0); - self.table_count += 1; - } else if self.table[home].index == index { - // attempt to replace it directly - // REPLACE - self.table[home].item = item; // home.item.soft_drop() - } else { - // attempt to replace it in a collided neighbour location - for i in 1..self.table[home].displacement + 1 { - let neighbour = &mut self.table[(home + i as usize) % range]; // todo: is mod expensive? - if neighbour.index == index { - // found where it was displaced to - // REPLACE - neighbour.item = item; - return; - } - } - // at this point it must be added, probe to place in an empty space - for j in self.table[home].displacement + 1..Displacement::MAX - 1 { - let neighbour = &mut self.table[(home + j as usize) % range]; - if neighbour.index == Value::Nil { - // new empty slot hooray! - // INSERT - neighbour.home = home; - neighbour.item = item; - neighbour.index = index; - self.table[home].displacement = j; - self.table_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.table.len() { - let neighbour = &mut self.table[k]; - if neighbour.index == Value::Nil && free.is_none() { - free = Some(k); - } else if neighbour.index == index { - // REPLACE - neighbour.item = item; - return; - } - } - if let Some(k) = free { - // INSERT - self.table[k].home = home; - self.table[k].index = index; - self.table[k].item = item; - self.table[home].displacement = Displacement::MAX; - self.table_count += 1; - return; - } - // must resize - #[cfg(feature = "messages")] - eprintln!("\n\tResizing..."); - self.resize_table((self.table.len() + 4) * 2); - self.set_table(index, item); - } - } - fn rem_table(&mut self, index: Value) { - let range = self.table.len(); - let home = hash(&index) % range; - if self.table[home].index == index { - self.table[home].index = Value::Nil; - self.table[home].item = Value::Nil; // is this necessary? - self.table_count -= 1; - } else { - if self.table[home].displacement != Displacement::MAX { - let mut largest = 0; - for i in 1..self.table[home].displacement { - let neighbour = &mut self.table[(home + i as usize) % range]; - if neighbour.index == index { - neighbour.index = Value::Nil; - neighbour.item = Value::Nil; - self.table_count -= 1; - if i == self.table[home].displacement { - self.table[home].displacement = largest - } - return; - } else if neighbour.index != Value::Nil && neighbour.home == home { - largest = i; - } - } - } else { - let mut largest = 0; - let mut finished = false; // what - for k in 0..self.table.len() { - let neighbour = &mut self.table[k]; - if neighbour.index == index { - neighbour.index = Value::Nil; - neighbour.item = Value::Nil; - self.table_count -= 1; - #[cfg(feature = "assertions")] - assert!(!finished); - finished = true; - } - if neighbour.home == home && neighbour.index != Value::Nil { - largest = largest.max(if k < home { - k + self.table.len() - home - 1 // ? - } else { - k - home - }) - } - } - self.table[home].displacement = if largest > Displacement::MAX as usize { - Displacement::MAX - } else { - largest as Displacement - } - } - } - } - pub fn set(&mut self, index: Value, item: Value) -> Result<(), RunError> { + pub fn set(&mut self, index: Value, value: Value) -> Result<(), RunError> { match index { Value::Integer(index) => { // This is an integer index, try the array first - match item { + match value { Value::Nil => { if (0..self.array.len() + 1).contains(&(index as usize)) { if self.array.len() == index as usize { @@ -279,7 +365,7 @@ impl Table { self.array[index as usize + 1] = Value::Nil } } - self.rem_table(Value::Integer(index)); + self.table.rem_table(Value::Integer(index)); } item => { if (0..self.array.len() + 1).contains(&(index as usize)) { @@ -289,19 +375,25 @@ impl Table { self.array[index as usize + 1] = item; } } else { - self.set_table(Value::Integer(index), item) + self.table.set_table(KeyValue { + index: Value::Integer(index), + value: item + }) } } } Ok(()) } Value::Nil => Err(RunError( - "Attempt to set new index of tabel with key: nil".to_string(), + "Attempt to set new index of table with key: nil".to_string(), )), - index => Ok(self.set_table(index, item)), + index => Ok(self.table.set_table(KeyValue { + index, + value + })), } } - pub fn get(&self, index: Value) -> Result { + pub fn get(&mut self, index: Value) -> Result { match index { Value::Integer(index) => Ok(self .array @@ -309,20 +401,11 @@ impl Table { .unwrap_or(&Value::Nil) .clone()), Value::Nil => Err(RunError("Attempt to index table with key: nil".to_string())), - index => { - let location = hash(&index) % self.table.len(); - let home = &self.table[location]; - if home.index == index { - Ok(home.item.clone()) - } else { - for i in 1..home.displacement as usize { - let neighbour = &self.table[location + i]; - if neighbour.index == index { - return Ok(neighbour.item.clone()); - } - } - Ok(Value::Nil) - } + index => { // todo: move this into the Map + Ok(self.table.get_table(index).unwrap_or(&KeyValue { + index: Value::Nil, + value: Value::Nil, + }).value.clone()) } } } @@ -331,11 +414,9 @@ impl Table { } pub fn new() -> Self { Table { - table: Vec::new(), + table: Map::new(), array: Vec::new(), - table_count: 0, - table_bounds: (0..0).into(), meta: None, } } -} +} \ No newline at end of file diff --git a/src/tests/script.lua b/src/tests/script.lua index 36d8fd4..6b01721 100644 --- a/src/tests/script.lua +++ b/src/tests/script.lua @@ -2,4 +2,4 @@ if true then print("whats good") else print("oh no") -end +end \ No newline at end of file