Refactored Table into Map for shared use in Compiler and VM. Refactored and modified direct equality for Values. Cleaned a core dump file. Builds and runs.

This commit is contained in:
paladin 2026-08-28 03:08:00 +01:00
parent 44b389a6a1
commit c10d56c0f8
5 changed files with 730 additions and 374 deletions

View file

@ -4,6 +4,7 @@ version = "0.1.0"
edition = "2024"
[features]
bighash = []
vector3 = ["dep:glam"]
messages = []
assertions = []

Binary file not shown.

View file

@ -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<Context>, // Stack of function prototypes being compiled
}
@ -1242,9 +1244,40 @@ struct Context {
// Everything known about a closure at a time
scopes: Vec<Scope>,
vararg: Option<Piece<Option<String>>>,
constants: Map<ConstantEntry>, // map some constant value to an index for not repeating constants
prototype: Prototype,
}
#[derive(Clone, Debug)]
struct ConstantEntry(Value,Index);
impl Equivalent<Self> for ConstantEntry {
fn matches(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl Equivalent<Value> 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<T>(&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<Value, RunError> {
@ -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<Value, RunError> {
Ok(Value::Nil)
Err(RunError(format!("Cannot get index in {:?}", &self)))
}
fn meta_eq(&mut self, other: &Value) -> Result<Value, RunError> {
Ok(Value::Nil)
}
}
impl From<String> 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<Value, RunError> {
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<Value, RunError> {
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<Value, 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 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<str>, Hashed);
#[derive(Clone, Debug)]
struct CachedStringEntry(Option<CachedString>);
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<Self> 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<str>, Hashed),
Table(Gc<Table>),
Object(Gc<Anything>),
@ -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<Table>,
allocator: Allocator,
cache: Map<CachedStringEntry> // todo: better to use a generic version of Map
}
/*
@ -2570,15 +2641,181 @@ impl Code {
}
impl Machine {
fn load(&mut self, source: &str) -> Result<Callable, Error> {
fn new_string(&mut self, string: &str) -> Result<Value,RunError> {
if string.len() < SHORT_STRING_LEN {
let existing = self.cache.get_table(string);
match existing {
Some(ref entry) => {
Ok(Value::CachedString(entry.0.clone().unwrap()))
},
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<Value, RunError> {
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<Value, RunError> {
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<Value, RunError> {
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<bool,RunError> { // 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<Callable, MarsError> {
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::<Name>::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<Machine, RunError> {
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),
_ => {}
};
}
}

View file

@ -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<T>: Sized {
fn matches(&self, other: &T) -> bool;
}
impl<T> Equivalent<T> for T where T: PartialEq {
fn matches(&self, other: &T) -> bool {
self == other
}
}
pub trait Hashes {
fn hashed(&self) -> Hashed;
}
pub trait TableEntry: Equivalent<Self> + 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<Self> 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<E> {
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<E: TableEntry + Clone> { // default representing vacancy!
map: Vec<Entry<E>>,
map_bounds: std::ops::Range<usize>,
map_count: usize, // number of elements in table
}
impl<E: TableEntry + Clone> Map<E> {
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<usize> = 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<K>(&mut self, index: K) where E: Equivalent<K>, 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<K>(&mut self, index: K) -> Option<&E>
where E: Equivalent<K>, 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<E> {
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<Entry>,
table: Map<KeyValue>,
array: Vec<Value>,
table_bounds: std::ops::Range<usize>,
table_count: usize, // number of elements in table
pub meta: Option<Gc<Table>>,
}
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<Value> 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<usize> = 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<Value, RunError> {
pub fn get(&mut self, index: Value) -> Result<Value, RunError> {
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,10 +414,8 @@ 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,
}
}