Modified Machine to take a State, partially implemented metamethods, refactored for adopting freelists. Fails build; in progress.
This commit is contained in:
parent
60c097518a
commit
db0ff1348c
13 changed files with 632 additions and 871 deletions
|
|
@ -4,15 +4,15 @@ use crate::parser::{
|
|||
Args, Block, Call, Chunk, Constructor, Expression, Function, LoadError, Name, Piece, Prefix,
|
||||
Statement, VarArg, Variable,
|
||||
};
|
||||
use crate::table::{Equivalent, Hashed, Hashes, Map, TableEntry};
|
||||
use crate::vm::{Code, Index, Machine, Offset, Prototype, Reference, Value};
|
||||
use std::cmp::PartialEq;
|
||||
use std::ops::Deref;
|
||||
use std::rc::Rc;
|
||||
use deimos::map::{Equivalent, Hashed, Hashes, Map, TableEntry};
|
||||
|
||||
pub(crate) struct Traversal<'a> {
|
||||
pub(crate) struct Traversal<'a,S> {
|
||||
// A 'cursor' of where we are in assumed execution
|
||||
machine: &'a mut Machine,
|
||||
machine: &'a mut Machine<S>,
|
||||
contexts: Vec<Context>, // Stack of function prototypes being compiled
|
||||
}
|
||||
|
||||
|
|
@ -70,7 +70,7 @@ pub(crate) struct Scope {
|
|||
upvalue: bool,
|
||||
}
|
||||
|
||||
impl Traversal<'_> {
|
||||
impl<S> Traversal<'_,S> {
|
||||
fn compile<T>(&mut self, it: &T) -> Result<(), LoadError>
|
||||
where
|
||||
T: Compile,
|
||||
|
|
@ -132,7 +132,7 @@ impl Traversal<'_> {
|
|||
| Code::RangeLoop(_)
|
||||
| Code::RangeBegin
|
||||
| Code::IteratorLoop(_)
|
||||
| Code::Comment(_) => {}
|
||||
| Code::Message(_) => {}
|
||||
}
|
||||
let prototype = &mut self.contexts.last_mut().unwrap().prototype;
|
||||
prototype.code.push(it.it);
|
||||
|
|
@ -247,7 +247,7 @@ impl Traversal<'_> {
|
|||
}
|
||||
Ok(context.prototype)
|
||||
}
|
||||
pub(crate) fn new(machine: &mut Machine) -> Traversal {
|
||||
pub(crate) fn new(machine: &mut Machine<S>) -> Traversal<S> {
|
||||
Traversal {
|
||||
machine,
|
||||
contexts: vec![Context {
|
||||
|
|
@ -277,12 +277,12 @@ impl Traversal<'_> {
|
|||
|
||||
// todo: not all impl Compile for Piece<T> needs to be &T
|
||||
trait Compile {
|
||||
fn compile(&self, traversal: &mut Traversal) -> Result<(), LoadError>;
|
||||
fn compile<S>(&self, traversal: &mut Traversal<S>) -> Result<(), LoadError>;
|
||||
}
|
||||
|
||||
fn compile_function(
|
||||
fn compile_function<S>(
|
||||
this: &Piece<&Function>,
|
||||
traversal: &mut Traversal,
|
||||
traversal: &mut Traversal<S>,
|
||||
method: bool,
|
||||
) -> Result<(), LoadError> {
|
||||
let mut self_arguments = Vec::new();
|
||||
|
|
@ -341,9 +341,9 @@ enum Access {
|
|||
Get,
|
||||
}
|
||||
|
||||
fn compile_identifier(
|
||||
fn compile_identifier<S>(
|
||||
name: Piece<String>,
|
||||
traversal: &mut Traversal,
|
||||
traversal: &mut Traversal<S>,
|
||||
access: Access,
|
||||
) -> Result<(), LoadError> {
|
||||
let contexts_len = traversal.contexts.len();
|
||||
|
|
@ -416,7 +416,7 @@ fn compile_identifier(
|
|||
}
|
||||
}
|
||||
}
|
||||
// break 'search avoids this use of the _G table
|
||||
// At this point there were no returns so we must use _G table
|
||||
traversal.push_string(name.it.clone(), name.unit())?;
|
||||
traversal.push_code(name.swap(Code::UpvalueGet(0)));
|
||||
traversal.push_code(name.swap(match access {
|
||||
|
|
@ -426,9 +426,9 @@ fn compile_identifier(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn compile_variable(
|
||||
fn compile_variable<S>(
|
||||
variable: Piece<&Variable>,
|
||||
traversal: &mut Traversal,
|
||||
traversal: &mut Traversal<S>,
|
||||
access: Access,
|
||||
) -> Result<(), LoadError> {
|
||||
match &variable.it {
|
||||
|
|
@ -455,13 +455,13 @@ fn compile_variable(
|
|||
}
|
||||
|
||||
impl Compile for Piece<&Function> {
|
||||
fn compile(&self, traversal: &mut Traversal) -> Result<(), LoadError> {
|
||||
fn compile<S>(&self, traversal: &mut Traversal<S>) -> Result<(), LoadError> {
|
||||
compile_function(self, traversal, false)
|
||||
}
|
||||
}
|
||||
|
||||
impl Compile for Piece<&Args> {
|
||||
fn compile(&self, traversal: &mut Traversal) -> Result<(), LoadError> {
|
||||
fn compile<S>(&self, traversal: &mut Traversal<S>) -> Result<(), LoadError> {
|
||||
let mut expressions_iter = self.it.0.iter().rev();
|
||||
if let Some(expression) = expressions_iter.next() {
|
||||
let piece = expression.unit();
|
||||
|
|
@ -491,7 +491,7 @@ impl Compile for Piece<&Args> {
|
|||
}
|
||||
|
||||
impl Compile for Piece<&Call> {
|
||||
fn compile(&self, traversal: &mut Traversal) -> Result<(), LoadError> {
|
||||
fn compile<S>(&self, traversal: &mut Traversal<S>) -> Result<(), LoadError> {
|
||||
let index = traversal.index().map(|it| it + 1).unwrap_or(0); // there will always be at least the callable
|
||||
traversal.compile(&self.it.args.inner_ref())?; // arguments
|
||||
traversal.compile(&self.it.prefix.as_ref().inner_ref())?; // call prefix
|
||||
|
|
@ -508,7 +508,7 @@ impl Compile for Piece<&Call> {
|
|||
}
|
||||
|
||||
impl Compile for Piece<&Prefix> {
|
||||
fn compile<'a, 'b>(&self, traversal: &mut Traversal) -> Result<(), LoadError> {
|
||||
fn compile<'a, 'b, S>(&self, traversal: &mut Traversal<S>) -> Result<(), LoadError> {
|
||||
match &self.it {
|
||||
Prefix::Call(call) => {
|
||||
let index = traversal.index().unwrap();
|
||||
|
|
@ -528,7 +528,7 @@ impl Compile for Piece<&Prefix> {
|
|||
|
||||
// for the sake of erroring alone
|
||||
impl Compile for Piece<&VarArg> {
|
||||
fn compile<'a, 'b>(&self, traversal: &mut Traversal) -> Result<(), LoadError> {
|
||||
fn compile<'a, 'b, S>(&self, traversal: &mut Traversal<S>) -> Result<(), LoadError> {
|
||||
match &traversal.contexts.last().unwrap().vararg {
|
||||
Some(piece) => match &piece.it {
|
||||
Some(that_name) => match &self.it.0 {
|
||||
|
|
@ -576,7 +576,7 @@ impl Compile for Piece<&VarArg> {
|
|||
}
|
||||
|
||||
impl Compile for Piece<&Constructor> {
|
||||
fn compile<'a, 'b>(&self, traversal: &mut Traversal) -> Result<(), LoadError> {
|
||||
fn compile<'a, 'b, S>(&self, traversal: &mut Traversal<S>) -> Result<(), LoadError> {
|
||||
traversal.push_code(self.swap(Code::TableNew));
|
||||
let index_table = traversal.index().unwrap();
|
||||
for field in self.it.0.iter() {
|
||||
|
|
@ -626,7 +626,7 @@ impl Compile for Piece<&Constructor> {
|
|||
}
|
||||
|
||||
impl Compile for Piece<&Expression> {
|
||||
fn compile<'a>(&self, traversal: &mut Traversal) -> Result<(), LoadError> {
|
||||
fn compile<'a, S>(&self, traversal: &mut Traversal<S>) -> Result<(), LoadError> {
|
||||
match self.it {
|
||||
Expression::Nil => {
|
||||
traversal.push_literal(Value::Nil, self.unit());
|
||||
|
|
@ -684,7 +684,7 @@ fn jump_from(here: usize, there: usize) -> Result<Offset, LoadError> {
|
|||
Ok((there as i64 - here as i64) as Offset)
|
||||
}
|
||||
|
||||
fn resolve_break(traversal: &mut Traversal, begin: usize, end: usize) -> Result<(), LoadError> {
|
||||
fn resolve_break<S>(traversal: &mut Traversal<S>, begin: usize, end: usize) -> Result<(), LoadError> {
|
||||
for jump in std::mem::replace(&mut traversal.last_scope().jumps, Vec::new()) {
|
||||
// ?
|
||||
match jump {
|
||||
|
|
@ -700,8 +700,8 @@ fn resolve_break(traversal: &mut Traversal, begin: usize, end: usize) -> Result<
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn compile_assignment(
|
||||
traversal: &mut Traversal,
|
||||
fn compile_assignment<S>(
|
||||
traversal: &mut Traversal<S>,
|
||||
variables: &Vec<Piece<Variable>>,
|
||||
expressions: &Vec<Piece<Expression>>,
|
||||
) -> Result<(), LoadError> {
|
||||
|
|
@ -709,7 +709,7 @@ fn compile_assignment(
|
|||
let mut expressions_iter = expressions.iter().peekable();
|
||||
while let Some(expression) = expressions_iter.next() {
|
||||
let compile_variable_expression = |// Compile a variable normally with one value from an expression
|
||||
traversal: &mut Traversal,
|
||||
traversal: &mut Traversal<S>,
|
||||
variable: Option<&Piece<Variable>>,
|
||||
expression: &Piece<&Expression>|
|
||||
-> Result<(), LoadError> {
|
||||
|
|
@ -784,7 +784,7 @@ fn compile_assignment(
|
|||
}
|
||||
|
||||
impl Compile for Piece<&Block> {
|
||||
fn compile(&self, traversal: &mut Traversal) -> Result<(), LoadError> {
|
||||
fn compile<S>(&self, traversal: &mut Traversal<S>) -> Result<(), LoadError> {
|
||||
let last_scope_index = traversal.index().unwrap_or(0);
|
||||
let mut depth = 0; // Counter for 'dangling' scopes produced by any declarations to be closed.
|
||||
// todo: for some reason, register indices are called "<thing>_index" and code indices "index_<thing>"? fix it
|
||||
|
|
@ -1016,7 +1016,7 @@ impl Compile for Piece<&Block> {
|
|||
Statement::Declaration(names, expressions) => {
|
||||
let mut expressions_iter = expressions.iter().peekable();
|
||||
let mut names_iter = names.iter().peekable();
|
||||
let compile_declaration = |traversal: &mut Traversal,
|
||||
let compile_declaration = |traversal: &mut Traversal<S>,
|
||||
expression: &Piece<Expression>,
|
||||
name: &Piece<Name>|
|
||||
-> Result<(), LoadError> {
|
||||
|
|
@ -1125,11 +1125,15 @@ impl Compile for Piece<&Block> {
|
|||
}
|
||||
}
|
||||
// Any 'dangling' scopes that weren't closed (declarations) will be at the end of this block
|
||||
let return_index = traversal.index().unwrap_or(0);
|
||||
if let Some(return_expressions) = &self.it.return_exp {
|
||||
// arguments are backwards
|
||||
for return_expression in return_expressions.iter().rev() {
|
||||
traversal.compile(&return_expression.inner_ref())?;
|
||||
}
|
||||
if !return_expressions.is_empty() {
|
||||
traversal.push_code(return_expressions.first().unwrap().swap(Code::Return(return_index)));
|
||||
}
|
||||
}
|
||||
for _ in 0..depth {
|
||||
traversal.close_scope();
|
||||
|
|
@ -1140,12 +1144,12 @@ impl Compile for Piece<&Block> {
|
|||
}
|
||||
|
||||
impl Compile for Chunk {
|
||||
fn compile(&self, traversal: &mut Traversal) -> Result<(), LoadError> {
|
||||
fn compile<S>(&self, traversal: &mut Traversal<S>) -> Result<(), LoadError> {
|
||||
traversal.compile(&self.0.inner_ref())
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn compile(chunk: &Chunk, machine: &mut Machine) -> Result<Prototype, LoadError> {
|
||||
pub(crate) fn compile<S>(chunk: &Chunk, machine: &mut Machine<S>) -> Result<Prototype, LoadError> {
|
||||
let mut traversal = Traversal::new(machine);
|
||||
traversal.compile(chunk)?;
|
||||
traversal.close_context()
|
||||
|
|
|
|||
18
src/core.rs
Normal file
18
src/core.rs
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
use crate::vm::{Machine, ForeignFunction, Needle, RunError};
|
||||
pub(crate) fn load<S>(machine: &mut Machine<S>) -> Result<(),RunError> {
|
||||
machine.set_global_function("print",|machine: &mut Machine<S>, mut needle: Needle| {
|
||||
while let Some(value) = needle.pop() {
|
||||
print!("{} ", machine.meta_to_string(needle.dispatch, value)?.unwrap_str())
|
||||
}
|
||||
println!();
|
||||
Ok(())
|
||||
})?;
|
||||
machine.set_global_function("debug",|_machine: &mut Machine<S>, mut needle: Needle| {
|
||||
while let Some(value) = needle.pop() {
|
||||
print!("{:?} ", value)
|
||||
}
|
||||
println!();
|
||||
Ok(())
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
154
src/gc.rs
154
src/gc.rs
|
|
@ -1,154 +0,0 @@
|
|||
use std::alloc::{Layout, alloc, dealloc};
|
||||
use std::any::Any;
|
||||
use std::cell::{Ref, RefCell, RefMut};
|
||||
use std::fmt::Debug;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::ptr::NonNull;
|
||||
// A simple 'stop the world' 'mark and sweep' garbage collector
|
||||
// This approach is used since game engine frames provide a nice 'resting' interval for the interpreter to clean up faster
|
||||
// todo! generational and naive reference counted collection
|
||||
/* naive reference counting could be done as:
|
||||
keep a traditional reference count =>
|
||||
- field count: u8 (>256 references -> a cycle will prevent premature deallocation anyway)
|
||||
if the count reaches zero, prematurely deallocate by using an optional soft 'drop' method in the VM =>
|
||||
- field location: usize (location of this pointer in the big 'objects' array of the allocator)
|
||||
- drop(&self,allocator) {
|
||||
if count == 0 {
|
||||
allocator[self.pointer->location] = nullptr; // make allocator forget this
|
||||
dealloc(self.pointer); // deallocate now
|
||||
}
|
||||
}
|
||||
in the allocator ensure location is consistent
|
||||
|
||||
*/
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Gc<T: ?Sized + Traverse> {
|
||||
it: NonNull<RefCell<Header<T>>>,
|
||||
}
|
||||
|
||||
pub struct Agc {}
|
||||
|
||||
impl<T: ?Sized + Traverse> Clone for Gc<T> {
|
||||
fn clone(&self) -> Self {
|
||||
Gc { it: self.it }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + Traverse> Traverse for Gc<T> {
|
||||
fn traverse(&self) {
|
||||
self.borrow().traverse()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + Traverse> PartialEq for Gc<T> {
|
||||
fn eq(&self, other: &Gc<T>) -> bool {
|
||||
self.it == other.it
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + Traverse> Gc<T> {
|
||||
pub fn borrow(&self) -> impl Deref<Target = T> {
|
||||
Ref::map(
|
||||
unsafe {
|
||||
// ???
|
||||
(*self.it.as_ptr()).borrow()
|
||||
},
|
||||
|it| &it.data,
|
||||
)
|
||||
}
|
||||
pub fn borrow_mut(&mut self) -> impl DerefMut<Target = T> {
|
||||
RefMut::map(unsafe { (*self.it.as_mut()).borrow_mut() }, |it| {
|
||||
&mut it.data
|
||||
})
|
||||
}
|
||||
|
||||
pub fn replace(&mut self, new: T)
|
||||
where
|
||||
T: Sized,
|
||||
{
|
||||
unsafe {
|
||||
(*self.it.as_mut()).borrow_mut().data = new;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn addr(&self) -> usize {
|
||||
self.it.as_ptr().addr()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Copy)]
|
||||
enum Color {
|
||||
White,
|
||||
Black,
|
||||
}
|
||||
|
||||
struct Header<T: ?Sized + Traverse> {
|
||||
layout: Layout,
|
||||
color: Color, // NOT for incremental tri-color right now; white == mark-free, black == mark-keep
|
||||
data: T,
|
||||
}
|
||||
|
||||
pub struct Allocator {
|
||||
// An array of pointers is used here since I did not want to use a compactor and larger types
|
||||
// held on the GC (game objects) will most likely be massive anyway.
|
||||
objects: Vec<Option<NonNull<RefCell<Header<dyn Traverse>>>>>,
|
||||
}
|
||||
|
||||
impl Allocator {
|
||||
pub fn new() -> Allocator {
|
||||
Allocator {
|
||||
objects: Vec::new(),
|
||||
}
|
||||
}
|
||||
pub fn alloc<T: Traverse>(&mut self, it: T) -> Gc<T> {
|
||||
let layout = Layout::new::<RefCell<Header<T>>>();
|
||||
let pointer = unsafe {
|
||||
let pointer = alloc(layout) as *mut RefCell<Header<T>>;
|
||||
assert!(!pointer.is_null()); // lol idk how to fix this rn (gah use a result!!!)
|
||||
pointer.write(RefCell::new(Header {
|
||||
layout,
|
||||
color: Color::Black,
|
||||
data: it,
|
||||
}));
|
||||
pointer
|
||||
}; // todo: maybe use a compacting GC
|
||||
self.objects.push(NonNull::new(pointer));
|
||||
Gc {
|
||||
it: NonNull::new(pointer).unwrap(),
|
||||
}
|
||||
}
|
||||
fn mark_white(&mut self) {
|
||||
for i in 0..self.objects.len() {
|
||||
if let Some(object) = self.objects[i] {
|
||||
unsafe { (*object.as_ptr()).borrow_mut().color = Color::White };
|
||||
}
|
||||
}
|
||||
}
|
||||
fn collect(&mut self) {
|
||||
self.mark_white();
|
||||
self.objects.retain(|object| {
|
||||
if let Some(object) = object {
|
||||
let header = unsafe { (*object.as_ptr()).borrow_mut() };
|
||||
match header.color {
|
||||
Color::White => {
|
||||
unsafe {
|
||||
dealloc(object.as_ptr() as *mut u8, header.layout);
|
||||
}
|
||||
true
|
||||
}
|
||||
Color::Black => false,
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Traverse: Any + Debug {
|
||||
fn traverse(&self) {
|
||||
// mark or grey
|
||||
()
|
||||
}
|
||||
}
|
||||
12
src/lib.rs
12
src/lib.rs
|
|
@ -1,17 +1,23 @@
|
|||
#![warn(clippy::cast_lossless)]
|
||||
|
||||
pub use crate::vm::*;
|
||||
pub use deimos;
|
||||
|
||||
mod compiler;
|
||||
mod gc;
|
||||
mod parser;
|
||||
mod table;
|
||||
mod vm;
|
||||
mod core;
|
||||
mod tasks;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::vm::Machine;
|
||||
use deimos::gc::Allocates;
|
||||
|
||||
#[test]
|
||||
fn simple() {
|
||||
let mut machine = match Machine::new() {
|
||||
let mut machine = match Machine::new(|_| ()) {
|
||||
Err(error) => panic!("{}", error),
|
||||
Ok(machine) => machine,
|
||||
};
|
||||
|
|
@ -20,7 +26,7 @@ mod tests {
|
|||
Ok(closure) => closure,
|
||||
_ => panic!(),
|
||||
};
|
||||
println!("{:?}", closure.borrow().prototype);
|
||||
println!("{:?}", machine.freelist().get(&closure).prototype);
|
||||
match machine.enter(closure) {
|
||||
Err(error) => panic!("{}", error),
|
||||
_ => {}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
// PARSER
|
||||
|
||||
use crate::vm::{Index, RunError};
|
||||
use colored::*;
|
||||
use std::cmp::{max, min};
|
||||
use std::fmt::Formatter;
|
||||
// TODO: Vec<Piece<T>> is good but sometimes we either need at least one or none, current fix is just to check if there is one and if not skip it, this takes extra performance
|
||||
|
|
|
|||
313
src/table.rs
313
src/table.rs
|
|
@ -1,56 +1,6 @@
|
|||
use crate::gc::{Gc, Traverse};
|
||||
use crate::vm::{Callable, RunError, Value};
|
||||
use std::rc::Rc;
|
||||
|
||||
// I've already spent 2 months on this interpreter, and I'm tired, so I've cut a few corners...
|
||||
/* See some of Lua's notes on this table: https://www.lua.org/source/5.5/ltable.c.html
|
||||
This table uses an 'open-addressed' hashmap https://en.wikipedia.org/wiki/Open_addressing
|
||||
where collisions are handled by 'linear-probing' and something I call 'displacement'.
|
||||
|
||||
Motivation:
|
||||
Afaik, without having some mechanism that records or ensures known proximity between a displaced element
|
||||
that collided with other(s) in the hashmap and it's original home (at element's hash's index),
|
||||
the entire array may have to be checked just to see if an element is present. How can we fix this?
|
||||
|
||||
Solution:
|
||||
Lua uses Brent's method, which I couldn't find a concrete explanation of, here I'm just recording
|
||||
the maximum displacement that will need to be linearly-probed from the home entry to be certain of
|
||||
any 'home' element's presence in the greater array.
|
||||
|
||||
Notes:
|
||||
- If this value becomes large, the load factor is probably high and there would be a resize.
|
||||
- Values would only need to be shuffled around closer to their 'home' during a resize.
|
||||
- It is unlikely the displacement would large value without a resize amending that problem.
|
||||
*/
|
||||
|
||||
type Displacement = u8;
|
||||
|
||||
#[cfg(feature = "bighash")]
|
||||
pub type Hashed = u64;
|
||||
#[cfg(not(feature = "bighash"))]
|
||||
pub type Hashed = u32;
|
||||
|
||||
pub trait Equivalent<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;
|
||||
}
|
||||
use deimos::gc::{Allocates, Id, MaybeId, Traverse};
|
||||
use crate::vm::{Callable, MarsAllocator, RunError, Value};
|
||||
use deimos::map::{Equivalent, Hashed, Hashes, TableEntry, Map, Entry};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct KeyValue {
|
||||
|
|
@ -88,221 +38,6 @@ impl TableEntry for KeyValue {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct Entry<E> {
|
||||
entry: E,
|
||||
home: usize,
|
||||
displacement: Displacement,
|
||||
}
|
||||
|
||||
#[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> {
|
||||
fn exchange_table(&mut self, len: usize) {
|
||||
let old = std::mem::replace(
|
||||
&mut self.map,
|
||||
vec![
|
||||
Entry {
|
||||
entry: E::new_vacant(),
|
||||
home: 0,
|
||||
displacement: 0,
|
||||
};
|
||||
len
|
||||
],
|
||||
);
|
||||
self.map_bounds = self.table_lower()..self.table_upper();
|
||||
for Entry { entry, .. } in old {
|
||||
if !entry.is_vacant() {
|
||||
self.set_table(entry)
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn resize_table(&mut self, len: usize) {
|
||||
self.exchange_table(len.max(self.map_count + (self.map_count / 3)));
|
||||
self.map_bounds = 0..self.table_upper();
|
||||
}
|
||||
fn table_upper(&self) -> usize {
|
||||
(self.map.len() / 4) * 3
|
||||
}
|
||||
fn table_lower(&self) -> usize {
|
||||
self.map.len() / 3
|
||||
}
|
||||
fn ensure_table(&mut self) {
|
||||
if self.map_count + 1 > self.table_upper() {
|
||||
// free space is short
|
||||
self.exchange_table((self.map.len() + 4) * 2);
|
||||
} else if self.map_count < self.table_lower() {
|
||||
// too much free space
|
||||
self.exchange_table(self.map.len() / 2)
|
||||
}
|
||||
}
|
||||
pub(crate) fn set_table(&mut self, entry: E) {
|
||||
#[cfg(feature = "assertions")]
|
||||
assert!(!entry.is_vacant());
|
||||
if !self.map_bounds.contains(&(self.map_count + 1)) {
|
||||
// make sure the table is appropriately sized
|
||||
self.ensure_table();
|
||||
}
|
||||
let range = self.map.len();
|
||||
let home = entry.hashed() as usize % range;
|
||||
if self.map[home].entry.is_vacant() {
|
||||
// attempt to place it directly in an empty space
|
||||
// INSERT
|
||||
self.map[home].home = home;
|
||||
self.map[home].entry = entry;
|
||||
self.map[home].displacement = self.map[home].displacement.max(0);
|
||||
self.map_count += 1;
|
||||
} else if self.map[home].entry.matches(&entry) {
|
||||
// attempt to replace it directly
|
||||
// REPLACE
|
||||
self.map[home].entry = entry; // home.item.soft_drop()
|
||||
} else {
|
||||
// attempt to replace it in a collided neighbour location
|
||||
for i in 1..self.map[home].displacement + 1 {
|
||||
let neighbour = &mut self.map[(home + i as usize) % range]; // todo: is mod expensive?
|
||||
if neighbour.entry.matches(&entry) {
|
||||
// found where it was displaced to
|
||||
// REPLACE
|
||||
neighbour.entry = entry;
|
||||
return;
|
||||
}
|
||||
}
|
||||
// at this point it must be added, probe to place in an empty space
|
||||
for j in self.map[home].displacement + 1..Displacement::MAX - 1 {
|
||||
let neighbour = &mut self.map[(home + j as usize) % range];
|
||||
if neighbour.entry.is_vacant() {
|
||||
// new empty slot hooray!
|
||||
// INSERT
|
||||
neighbour.entry = entry;
|
||||
neighbour.home = home;
|
||||
self.map[home].displacement = j;
|
||||
self.map_count += 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
// impossible but this still needs to be complete
|
||||
#[cfg(feature = "messages")]
|
||||
eprintln!("Large table collision, are the hashes ok?\n\tBrute-force probing...");
|
||||
let mut free: Option<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]; // todo: maybe mod slows this down
|
||||
if neighbour.entry.matches(&index) {
|
||||
neighbour.entry = E::new_vacant();
|
||||
self.map_count -= 1;
|
||||
if i == self.map[home].displacement {
|
||||
self.map[home].displacement = largest
|
||||
}
|
||||
return;
|
||||
} else if !neighbour.entry.is_vacant() && neighbour.home == home {
|
||||
largest = i;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let mut largest = 0;
|
||||
let mut finished = false; // what
|
||||
for k in 0..self.map.len() {
|
||||
let neighbour = &mut self.map[k];
|
||||
if neighbour.entry.matches(&index) {
|
||||
neighbour.entry = E::new_vacant();
|
||||
self.map_count -= 1;
|
||||
#[cfg(feature = "assertions")]
|
||||
assert!(!finished);
|
||||
finished = true;
|
||||
}
|
||||
if neighbour.home == home && !neighbour.entry.is_vacant() {
|
||||
largest = largest.max(if k < home {
|
||||
k + self.map.len() - home - 1 // ?
|
||||
} else {
|
||||
k - home
|
||||
})
|
||||
}
|
||||
}
|
||||
self.map[home].displacement = if largest > Displacement::MAX as usize {
|
||||
Displacement::MAX
|
||||
} else {
|
||||
largest as Displacement
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pub(crate) fn get_table<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) % self.map.len()];
|
||||
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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Hashes for Value {
|
||||
fn hashed(&self) -> Hashed {
|
||||
match self {
|
||||
|
|
@ -317,8 +52,8 @@ impl Hashes for Value {
|
|||
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,
|
||||
Callable::Rust(native) => native.hashed(),
|
||||
Callable::Mars(closure) => closure.hashed(),
|
||||
},
|
||||
Value::Integer(integer) => *integer as Hashed,
|
||||
Value::Number(number) => {
|
||||
|
|
@ -328,8 +63,8 @@ impl Hashes for Value {
|
|||
number.to_bits() as Hashed
|
||||
}
|
||||
}
|
||||
Value::Table(table) => table.addr() as Hashed,
|
||||
Value::Object(object) => object.addr() as Hashed,
|
||||
Value::Table(table) => table.hashed(),
|
||||
Value::Object(object,..) => object.hashed(),
|
||||
#[cfg(feature = "vector3")]
|
||||
Value::Vector(vec) => {
|
||||
let mut hasher = DefaultHasher::new();
|
||||
|
|
@ -341,23 +76,25 @@ impl Hashes for Value {
|
|||
}
|
||||
|
||||
// todo: displaced items don't ever get shuffled closer to their homes unless the table is resized
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug)]
|
||||
pub struct Table {
|
||||
table: Map<KeyValue>,
|
||||
array: Vec<Value>,
|
||||
pub meta: Option<Gc<Table>>,
|
||||
pub meta: MaybeId<Table>,
|
||||
}
|
||||
|
||||
impl Traverse for Table {
|
||||
fn traverse(&self) {
|
||||
for Entry { entry, .. } in self.table.map.iter() {
|
||||
impl<C> Traverse<C> for Table where C: MarsAllocator {
|
||||
fn traverse(id: &Id<Table>, context: &mut C) {
|
||||
for i in 0..context.freelist().get(id).table.map.len() {
|
||||
let entry = context.freelist().get(id).table.map[i].entry.clone();
|
||||
if !entry.is_vacant() {
|
||||
entry.index.traverse();
|
||||
entry.value.traverse();
|
||||
entry.index.traverse(context);
|
||||
entry.value.traverse(context);
|
||||
}
|
||||
}
|
||||
for item in self.array.iter() {
|
||||
item.traverse()
|
||||
for i in 0..context.freelist().get(id).array.len() {
|
||||
let item = context.freelist().get(id).array[i].clone();
|
||||
item.traverse(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -378,21 +115,21 @@ impl Table {
|
|||
// This is an integer index, try the array first
|
||||
match value {
|
||||
Value::Nil => {
|
||||
if (0..self.array.len() + 1).contains(&(index as usize)) {
|
||||
if self.array.len() == index as usize {
|
||||
if (0..self.array.len() + 1).contains(&(index as usize - 1)) {
|
||||
if self.array.len() == index as usize - 1 {
|
||||
self.array.pop();
|
||||
} else {
|
||||
self.array[index as usize + 1] = Value::Nil
|
||||
self.array[index as usize - 1] = Value::Nil
|
||||
}
|
||||
}
|
||||
self.table.rem_table(Value::Integer(index));
|
||||
}
|
||||
item => {
|
||||
if (0..self.array.len() + 1).contains(&(index as usize)) {
|
||||
if self.array.len() == index as usize {
|
||||
if (0..self.array.len() + 1).contains(&(index as usize - 1)) {
|
||||
if self.array.len() == index as usize - 1 {
|
||||
self.array.push(item)
|
||||
} else {
|
||||
self.array[index as usize + 1] = item;
|
||||
self.array[index as usize - 1] = item;
|
||||
}
|
||||
} else {
|
||||
self.table.set_table(KeyValue {
|
||||
|
|
@ -439,7 +176,7 @@ impl Table {
|
|||
Table {
|
||||
table: Map::new(),
|
||||
array: Vec::new(),
|
||||
meta: None,
|
||||
meta: MaybeId::NULL,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
0
src/tasks.rs
Normal file
0
src/tasks.rs
Normal file
|
|
@ -1,9 +1,5 @@
|
|||
local var = 2
|
||||
local function thing()
|
||||
print(var)
|
||||
var = 3
|
||||
local tab = {}
|
||||
for i = 1, 1000000 do
|
||||
tab[i] = true
|
||||
end
|
||||
|
||||
print(var)
|
||||
thing()
|
||||
thing()
|
||||
debug(tab)
|
||||
Loading…
Add table
Add a link
Reference in a new issue