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"
|
edition = "2024"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
bighash = []
|
||||||
vector3 = ["dep:glam"]
|
vector3 = ["dep:glam"]
|
||||||
messages = []
|
messages = []
|
||||||
assertions = []
|
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?
|
white: bool, // Is the previous 'thing' whitespace?
|
||||||
}
|
}
|
||||||
|
|
||||||
type Error = String;
|
|
||||||
|
|
||||||
pub struct LoadError(String, Piece<()>); // Any error returned by a machine loading code
|
pub struct LoadError(String, Piece<()>); // Any error returned by a machine loading code
|
||||||
|
|
||||||
impl LoadError {
|
impl LoadError {
|
||||||
|
|
@ -52,6 +50,9 @@ impl LoadError {
|
||||||
}
|
}
|
||||||
roll
|
roll
|
||||||
}
|
}
|
||||||
|
fn from_runtime(piece: Piece<()>, error: RunError) -> LoadError {
|
||||||
|
LoadError(error.0,piece)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Debug for LoadError {
|
impl std::fmt::Debug for LoadError {
|
||||||
|
|
@ -1233,8 +1234,9 @@ use std::any::Any;
|
||||||
use std::cmp::PartialEq;
|
use std::cmp::PartialEq;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
struct Traversal {
|
struct Traversal<'a> {
|
||||||
// A 'cursor' of where we are in assumed execution
|
// A 'cursor' of where we are in assumed execution
|
||||||
|
machine: &'a mut Machine,
|
||||||
contexts: Vec<Context>, // Stack of function prototypes being compiled
|
contexts: Vec<Context>, // Stack of function prototypes being compiled
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1242,9 +1244,40 @@ struct Context {
|
||||||
// Everything known about a closure at a time
|
// Everything known about a closure at a time
|
||||||
scopes: Vec<Scope>,
|
scopes: Vec<Scope>,
|
||||||
vararg: Option<Piece<Option<String>>>,
|
vararg: Option<Piece<Option<String>>>,
|
||||||
|
constants: Map<ConstantEntry>, // map some constant value to an index for not repeating constants
|
||||||
prototype: Prototype,
|
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)]
|
#[derive(Clone, Debug)]
|
||||||
enum BlindJump {
|
enum BlindJump {
|
||||||
Break(usize),
|
Break(usize),
|
||||||
|
|
@ -1261,7 +1294,7 @@ struct Scope {
|
||||||
upvalue: bool,
|
upvalue: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Traversal {
|
impl Traversal<'_> {
|
||||||
fn compile<T>(&mut self, it: &T) -> Result<(), LoadError>
|
fn compile<T>(&mut self, it: &T) -> Result<(), LoadError>
|
||||||
where
|
where
|
||||||
T: Compile,
|
T: Compile,
|
||||||
|
|
@ -1333,20 +1366,29 @@ impl Traversal {
|
||||||
}
|
}
|
||||||
fn push_constant(&mut self, it: Value) -> Index {
|
fn push_constant(&mut self, it: Value) -> Index {
|
||||||
// todo: constants need pieces too!!!
|
// todo: constants need pieces too!!!
|
||||||
let constants = &mut self.contexts.last_mut().unwrap().prototype.constants;
|
// don't use constants that already exist
|
||||||
let index;
|
if let Some(entry) = &mut self.contexts.last_mut().unwrap().constants.get_table(it.clone()) {
|
||||||
if let Some(position) = constants.iter().position(|found| it.eq(found)) {
|
entry.1
|
||||||
index = position;
|
|
||||||
} else {
|
} 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<()>) {
|
fn push_literal(&mut self, it: Value, piece: Piece<()>) {
|
||||||
let constant = self.push_constant(it);
|
let constant = self.push_constant(it);
|
||||||
self.push_code(piece.swap(Code::Constant(constant)));
|
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) {
|
fn new_scope(&mut self) {
|
||||||
let context = &mut self.contexts.last_mut().unwrap();
|
let context = &mut self.contexts.last_mut().unwrap();
|
||||||
let scopes = &mut context.scopes;
|
let scopes = &mut context.scopes;
|
||||||
|
|
@ -1449,6 +1491,7 @@ fn compile_function(
|
||||||
pieces: vec![],
|
pieces: vec![],
|
||||||
code: vec![],
|
code: vec![],
|
||||||
},
|
},
|
||||||
|
constants: Map::new(),
|
||||||
});
|
});
|
||||||
traversal.compile(&this.it.2.inner_ref())?; // compile the block
|
traversal.compile(&this.it.2.inner_ref())?; // compile the block
|
||||||
let prototype = traversal.close_context()?;
|
let prototype = traversal.close_context()?;
|
||||||
|
|
@ -1547,7 +1590,7 @@ fn compile_identifier(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// break 'search avoids this use of the _G table
|
// 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(Code::UpvalueGet(0)));
|
||||||
traversal.push_code(name.swap(match access {
|
traversal.push_code(name.swap(match access {
|
||||||
Access::Set => Code::TableSet,
|
Access::Set => Code::TableSet,
|
||||||
|
|
@ -1625,7 +1668,7 @@ impl Compile for Piece<&Call> {
|
||||||
traversal.compile(&self.it.0.as_ref().inner_ref())?; // call prefix
|
traversal.compile(&self.it.0.as_ref().inner_ref())?; // call prefix
|
||||||
match &self.it.1 {
|
match &self.it.1 {
|
||||||
Some(name) => {
|
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));
|
traversal.push_code(name.swap(Code::TableGet));
|
||||||
}
|
}
|
||||||
None => {}
|
None => {}
|
||||||
|
|
@ -1759,7 +1802,7 @@ impl Compile for Piece<&Expression> {
|
||||||
traversal.push_literal(Value::Number(number), self.unit());
|
traversal.push_literal(Value::Number(number), self.unit());
|
||||||
}
|
}
|
||||||
Expression::String(string) => {
|
Expression::String(string) => {
|
||||||
traversal.push_literal(string.clone().into(), self.unit());
|
traversal.push_string(string.clone(),self.unit())?;
|
||||||
}
|
}
|
||||||
Expression::VarArg(vararg) => {
|
Expression::VarArg(vararg) => {
|
||||||
traversal.compile(&self.swap(vararg))?;
|
traversal.compile(&self.swap(vararg))?;
|
||||||
|
|
@ -2121,10 +2164,10 @@ impl Compile for Piece<&Block> {
|
||||||
while let Some(name) = names.next() {
|
while let Some(name) = names.next() {
|
||||||
if !names.peek().is_some() {
|
if !names.peek().is_some() {
|
||||||
compile_function(&self.swap(function), traversal, is_method)?;
|
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));
|
traversal.push_code(name.unit().end().swap(Code::TableSet));
|
||||||
} else {
|
} 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));
|
traversal.push_code(name.unit().end().swap(Code::TableGet));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2174,10 +2217,10 @@ impl Compile for Chunk {
|
||||||
|
|
||||||
// MACHINE
|
// MACHINE
|
||||||
|
|
||||||
use crate::table::{Hashed, Table};
|
use crate::table::{Equivalent, Hashed, Hashes, Map, Table, TableEntry};
|
||||||
|
|
||||||
// Rust function to call
|
// 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)]
|
#[derive(Clone, Debug, Copy, PartialEq)]
|
||||||
enum Reference {
|
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 {
|
pub trait Object: std::fmt::Debug {
|
||||||
fn meta_string(&mut self) -> Result<Value, RunError> {
|
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)))
|
Err(RunError(format!("Cannot set index in {:?}", &self)))
|
||||||
}
|
}
|
||||||
fn meta_index(&mut self, index: Value) -> Result<Value, RunError> {
|
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> {
|
fn meta_eq(&mut self, other: &Value) -> Result<Value, RunError> {
|
||||||
Ok(Value::Nil)
|
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 {
|
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 {}
|
pub trait Userdata: Traverse + std::fmt::Debug + Any + Object {}
|
||||||
|
|
@ -2447,15 +2397,63 @@ struct Phrase {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Any value in the language
|
// 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;
|
const PHRASE_LEN: usize = 16;
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
// 120 - 8 - 64 -> 56
|
|
||||||
pub enum Value {
|
pub enum Value {
|
||||||
// 32 bytes!!!
|
|
||||||
Nil, // Empty value
|
Nil, // Empty value
|
||||||
Bool(bool),
|
Bool(bool),
|
||||||
Number(f64),
|
Number(f64),
|
||||||
Integer(u64), // 64 maybe we should use signed integers...
|
Integer(u64), // 64 maybe we should use signed integers...
|
||||||
|
CachedString(CachedString),
|
||||||
String(Rc<str>, Hashed),
|
String(Rc<str>, Hashed),
|
||||||
Table(Gc<Table>),
|
Table(Gc<Table>),
|
||||||
Object(Gc<Anything>),
|
Object(Gc<Anything>),
|
||||||
|
|
@ -2464,6 +2462,78 @@ pub enum Value {
|
||||||
Vector(Vec3),
|
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 {
|
impl Traverse for Value {
|
||||||
fn traverse(&self) {
|
fn traverse(&self) {
|
||||||
match self {
|
match self {
|
||||||
|
|
@ -2491,6 +2561,7 @@ pub struct Frame {
|
||||||
pub struct Machine {
|
pub struct Machine {
|
||||||
global: Gc<Table>,
|
global: Gc<Table>,
|
||||||
allocator: Allocator,
|
allocator: Allocator,
|
||||||
|
cache: Map<CachedStringEntry> // todo: better to use a generic version of Map
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -2570,15 +2641,181 @@ impl Code {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Machine {
|
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) {
|
let parsed = match parse(source) {
|
||||||
Ok(parsed) => parsed,
|
Ok(parsed) => parsed,
|
||||||
Err(error) => return Err(error.msg(source)),
|
Err(error) => return Err(MarsError(error.msg(source))),
|
||||||
};
|
};
|
||||||
|
|
||||||
println!("{:?}", parsed);
|
println!("{:?}", parsed);
|
||||||
|
|
||||||
let mut traversal = Traversal {
|
let mut traversal = Traversal {
|
||||||
|
machine: self,
|
||||||
contexts: vec![Context {
|
contexts: vec![Context {
|
||||||
scopes: vec![Scope {
|
scopes: vec![Scope {
|
||||||
// Dummy initial scope so the rest can start from index = 0
|
// Dummy initial scope so the rest can start from index = 0
|
||||||
|
|
@ -2589,6 +2826,7 @@ impl Machine {
|
||||||
upvalue: false,
|
upvalue: false,
|
||||||
}],
|
}],
|
||||||
vararg: Some(Piece::<Name>::null().swap(None)),
|
vararg: Some(Piece::<Name>::null().swap(None)),
|
||||||
|
constants: Map::new(),
|
||||||
prototype: Prototype {
|
prototype: Prototype {
|
||||||
args: 0,
|
args: 0,
|
||||||
vararg: true,
|
vararg: true,
|
||||||
|
|
@ -2603,12 +2841,12 @@ impl Machine {
|
||||||
|
|
||||||
match parsed.compile(&mut traversal) {
|
match parsed.compile(&mut traversal) {
|
||||||
Ok(compiled) => compiled,
|
Ok(compiled) => compiled,
|
||||||
Err(error) => return Err(error.msg(source)),
|
Err(error) => return Err(MarsError(error.msg(source))),
|
||||||
};
|
};
|
||||||
|
|
||||||
let prototype = match traversal.close_context() {
|
let prototype = match traversal.close_context() {
|
||||||
Ok(compiled) => compiled,
|
Ok(compiled) => compiled,
|
||||||
Err(error) => return Err(error.msg(source)),
|
Err(error) => return Err(MarsError(error.msg(source))),
|
||||||
};
|
};
|
||||||
|
|
||||||
let global = self.global.clone();
|
let global = self.global.clone();
|
||||||
|
|
@ -2634,7 +2872,7 @@ impl Machine {
|
||||||
Value::Function(callable) => {
|
Value::Function(callable) => {
|
||||||
match callable {
|
match callable {
|
||||||
Callable::Rust(function) => {
|
Callable::Rust(function) => {
|
||||||
function.0(machine, needle)?;
|
function.0(machine, needle, offset + index as usize + 1)?;
|
||||||
Ok(frame) // nothing changes
|
Ok(frame) // nothing changes
|
||||||
}
|
}
|
||||||
Callable::Mars(closure) => {
|
Callable::Mars(closure) => {
|
||||||
|
|
@ -2655,7 +2893,7 @@ impl Machine {
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
return Err(RunError(
|
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 {
|
if needle.stack.len() > 65536 {
|
||||||
return Err(RunError("Soft stack limit of 65536 reached".to_string()));
|
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 {
|
match code {
|
||||||
Code::Comment(number) => {
|
Code::Comment(number) => {
|
||||||
eprintln!("{}", number)
|
eprintln!("{}", number)
|
||||||
}
|
}
|
||||||
Code::TableSet => {
|
Code::TableSet => {
|
||||||
let value = needle.pop();
|
let table = needle.pop();
|
||||||
let index = needle.pop();
|
let index = needle.pop();
|
||||||
let mut table = needle.pop();
|
let value = needle.pop();
|
||||||
table.set(index, value)?;
|
self.set(table, index, value)?;
|
||||||
}
|
}
|
||||||
Code::TableGet => {
|
Code::TableGet => {
|
||||||
|
let table = needle.pop();
|
||||||
let index = needle.pop();
|
let index = needle.pop();
|
||||||
let mut table = needle.pop();
|
needle.push(self.get(table, index)?);
|
||||||
needle.push(table.get(index)?);
|
|
||||||
}
|
}
|
||||||
Code::TableInsert(index) => {
|
Code::TableInsert(index) => {
|
||||||
match needle.pop() {
|
match needle.pop() {
|
||||||
|
|
@ -2758,9 +3007,10 @@ impl Machine {
|
||||||
#[cfg(feature = "assertions")]
|
#[cfg(feature = "assertions")]
|
||||||
assert_ne!(offset, 0);
|
assert_ne!(offset, 0);
|
||||||
jump(&mut frame, offset);
|
jump(&mut frame, offset);
|
||||||
|
continue; // Skip the extra offset++
|
||||||
}
|
}
|
||||||
Code::Skip => {
|
Code::Skip => {
|
||||||
if matches!(needle.pop(), Value::Nil | Value::Bool(false)) {
|
if needle.pop().bool() {
|
||||||
frame.counter += 1;
|
frame.counter += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2774,6 +3024,7 @@ impl Machine {
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
continue; // todo: is this needed?
|
||||||
}
|
}
|
||||||
Code::Binary(op) => {
|
Code::Binary(op) => {
|
||||||
return Err(RunError(
|
return Err(RunError(
|
||||||
|
|
@ -2837,9 +3088,9 @@ impl Machine {
|
||||||
let init = len - 2;
|
let init = len - 2;
|
||||||
let limit = len - 1;
|
let limit = len - 1;
|
||||||
let step = len;
|
let step = len;
|
||||||
let init_value = needle.stack[init].value.number()?;
|
let init_value = self.try_number(&mut needle.stack[init].value)?;
|
||||||
let limit_value = needle.stack[limit].value.number()?;
|
let limit_value = self.try_number(&mut needle.stack[limit].value)?;
|
||||||
let step_value = needle.stack[step].value.number()?;
|
let step_value = self.try_number(&mut needle.stack[step].value)?;
|
||||||
if matches!(init_value, Value::Integer(_))
|
if matches!(init_value, Value::Integer(_))
|
||||||
&& matches!(limit_value, Value::Integer(_))
|
&& matches!(limit_value, Value::Integer(_))
|
||||||
&& matches!(step_value, Value::Integer(_))
|
&& matches!(step_value, Value::Integer(_))
|
||||||
|
|
@ -2874,9 +3125,9 @@ impl Machine {
|
||||||
let init_index = len - 2;
|
let init_index = len - 2;
|
||||||
let limit_index = len - 1;
|
let limit_index = len - 1;
|
||||||
let step_index = len;
|
let step_index = len;
|
||||||
let init_value = needle.stack[init_index].value.number()?;
|
let init_value = self.try_number(&mut needle.stack[init_index].value)?;
|
||||||
let limit_value = needle.stack[limit_index].value.number()?;
|
let limit_value = self.try_number(&mut needle.stack[limit_index].value)?;
|
||||||
let step_value = needle.stack[step_index].value.number()?;
|
let step_value = self.try_number(&mut needle.stack[step_index].value)?;
|
||||||
match init_value {
|
match init_value {
|
||||||
Value::Integer(init) => {
|
Value::Integer(init) => {
|
||||||
let step = match step_value {
|
let step = match step_value {
|
||||||
|
|
@ -2953,25 +3204,48 @@ impl Machine {
|
||||||
};
|
};
|
||||||
self.dispatch(&mut needle)
|
self.dispatch(&mut needle)
|
||||||
}
|
}
|
||||||
fn new() -> Machine {
|
fn new() -> Result<Machine, RunError> {
|
||||||
let mut allocator = Allocator::new();
|
let mut allocator = Allocator::new();
|
||||||
Machine {
|
let mut machine = Machine {
|
||||||
global: allocator.alloc(Table::new()),
|
global: allocator.alloc(Table::new()),
|
||||||
allocator,
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
#[test]
|
#[test]
|
||||||
fn simple() {
|
fn simple() {
|
||||||
let mut machine = Machine::new();
|
let mut machine = match Machine::new() {
|
||||||
let result = machine.load(include_str!("tests/script.lua"));
|
Err(error) => panic!("{:?}",error),
|
||||||
match result {
|
Ok(machine) => machine,
|
||||||
Err(error) => println!("{}", error),
|
};
|
||||||
Ok(result) => println!("{:?}", result),
|
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::gc::{Gc, Traverse};
|
||||||
use crate::{Callable, RunError, Value};
|
use crate::{Callable, Object, RunError, Value};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
// I've already spent 2 months on this interpreter, and I'm tired, so I've cut a few corners...
|
// 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;
|
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)]
|
#[derive(Debug, Clone)]
|
||||||
struct Entry {
|
struct KeyValue {
|
||||||
index: Value,
|
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,
|
home: usize,
|
||||||
displacement: Displacement,
|
displacement: Displacement,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Entry {
|
#[derive(Clone, Debug)]
|
||||||
fn default() -> Self {
|
pub struct Map<E: TableEntry + Clone> { // default representing vacancy!
|
||||||
Entry {
|
map: Vec<Entry<E>>,
|
||||||
index: Value::Nil,
|
map_bounds: std::ops::Range<usize>,
|
||||||
item: Value::Nil,
|
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,
|
home: 0,
|
||||||
displacement: 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 {
|
impl Hashes for Value {
|
||||||
match value {
|
fn hashed(&self) -> Hashed {
|
||||||
Value::Nil => 0,
|
match self {
|
||||||
Value::Bool(boolean) => {
|
Value::Nil => 0,
|
||||||
if *boolean {
|
Value::Bool(boolean) => {
|
||||||
1
|
if *boolean {
|
||||||
} else {
|
1
|
||||||
0
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
Value::CachedString(cached) => cached.1,
|
||||||
Value::String(_string, hash) => *hash,
|
Value::String(_string, hash) => *hash,
|
||||||
Value::Function(callable) => match callable {
|
Value::Function(callable) => match callable {
|
||||||
Callable::Rust(native) => Rc::as_ptr(native).addr(),
|
Callable::Rust(native) => Rc::as_ptr(native).addr() as Hashed,
|
||||||
Callable::Mars(closure) => closure.addr(),
|
Callable::Mars(closure) => closure.addr() as Hashed,
|
||||||
},
|
},
|
||||||
Value::Integer(integer) => *integer as usize,
|
Value::Integer(integer) => *integer as Hashed,
|
||||||
Value::Number(number) => {
|
Value::Number(number) => {
|
||||||
if number.is_nan() {
|
if number.is_nan() {
|
||||||
0
|
0
|
||||||
} else {
|
} else {
|
||||||
number.to_bits() as usize
|
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
|
// todo: displaced items don't ever get shuffled closer to their homes unless the table is resized
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Table {
|
pub struct Table {
|
||||||
table: Vec<Entry>,
|
table: Map<KeyValue>,
|
||||||
array: Vec<Value>,
|
array: Vec<Value>,
|
||||||
table_bounds: std::ops::Range<usize>,
|
|
||||||
table_count: usize, // number of elements in table
|
|
||||||
pub meta: Option<Gc<Table>>,
|
pub meta: Option<Gc<Table>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Traverse for Table {
|
impl Traverse for Table {
|
||||||
fn traverse(&self) {
|
fn traverse(&self) {
|
||||||
for Entry { index, item, .. } in self.table.iter() {
|
for Entry { entry, .. } in self.table.map.iter() {
|
||||||
if !matches!(index, Value::Nil) {
|
if !entry.is_vacant() {
|
||||||
index.traverse();
|
entry.index.traverse();
|
||||||
item.traverse();
|
entry.value.traverse();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for item in self.array.iter() {
|
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 {
|
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) {
|
pub fn resize_array(&mut self, len: usize) {
|
||||||
self.array.resize(len, Value::Nil)
|
self.array.resize(len, Value::Nil)
|
||||||
}
|
}
|
||||||
fn table_upper(&self) -> usize {
|
pub fn set(&mut self, index: Value, value: Value) -> Result<(), RunError> {
|
||||||
(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> {
|
|
||||||
match index {
|
match index {
|
||||||
Value::Integer(index) => {
|
Value::Integer(index) => {
|
||||||
// This is an integer index, try the array first
|
// This is an integer index, try the array first
|
||||||
match item {
|
match value {
|
||||||
Value::Nil => {
|
Value::Nil => {
|
||||||
if (0..self.array.len() + 1).contains(&(index as usize)) {
|
if (0..self.array.len() + 1).contains(&(index as usize)) {
|
||||||
if self.array.len() == 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.array[index as usize + 1] = Value::Nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.rem_table(Value::Integer(index));
|
self.table.rem_table(Value::Integer(index));
|
||||||
}
|
}
|
||||||
item => {
|
item => {
|
||||||
if (0..self.array.len() + 1).contains(&(index as usize)) {
|
if (0..self.array.len() + 1).contains(&(index as usize)) {
|
||||||
|
|
@ -289,19 +375,25 @@ impl Table {
|
||||||
self.array[index as usize + 1] = item;
|
self.array[index as usize + 1] = item;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.set_table(Value::Integer(index), item)
|
self.table.set_table(KeyValue {
|
||||||
|
index: Value::Integer(index),
|
||||||
|
value: item
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Value::Nil => Err(RunError(
|
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 {
|
match index {
|
||||||
Value::Integer(index) => Ok(self
|
Value::Integer(index) => Ok(self
|
||||||
.array
|
.array
|
||||||
|
|
@ -309,20 +401,11 @@ impl Table {
|
||||||
.unwrap_or(&Value::Nil)
|
.unwrap_or(&Value::Nil)
|
||||||
.clone()),
|
.clone()),
|
||||||
Value::Nil => Err(RunError("Attempt to index table with key: nil".to_string())),
|
Value::Nil => Err(RunError("Attempt to index table with key: nil".to_string())),
|
||||||
index => {
|
index => { // todo: move this into the Map
|
||||||
let location = hash(&index) % self.table.len();
|
Ok(self.table.get_table(index).unwrap_or(&KeyValue {
|
||||||
let home = &self.table[location];
|
index: Value::Nil,
|
||||||
if home.index == index {
|
value: Value::Nil,
|
||||||
Ok(home.item.clone())
|
}).value.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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -331,11 +414,9 @@ impl Table {
|
||||||
}
|
}
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Table {
|
Table {
|
||||||
table: Vec::new(),
|
table: Map::new(),
|
||||||
array: Vec::new(),
|
array: Vec::new(),
|
||||||
table_count: 0,
|
|
||||||
table_bounds: (0..0).into(),
|
|
||||||
meta: None,
|
meta: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2,4 +2,4 @@ if true then
|
||||||
print("whats good")
|
print("whats good")
|
||||||
else
|
else
|
||||||
print("oh no")
|
print("oh no")
|
||||||
end
|
end
|
||||||
Loading…
Add table
Add a link
Reference in a new issue