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:
parent
44b389a6a1
commit
c10d56c0f8
5 changed files with 730 additions and 374 deletions
|
|
@ -4,6 +4,7 @@ version = "0.1.0"
|
|||
edition = "2024"
|
||||
|
||||
[features]
|
||||
bighash = []
|
||||
vector3 = ["dep:glam"]
|
||||
messages = []
|
||||
assertions = []
|
||||
|
|
|
|||
Binary file not shown.
564
src/lib.rs
564
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<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),
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
537
src/table.rs
537
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<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,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,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,4 +2,4 @@ if true then
|
|||
print("whats good")
|
||||
else
|
||||
print("oh no")
|
||||
end
|
||||
end
|
||||
Loading…
Add table
Add a link
Reference in a new issue