Fixed table indexing and construction, declarations and some instructions. Builds and passes indexing test.
This commit is contained in:
parent
93599ff5d7
commit
1e288b3948
5 changed files with 2984 additions and 2773 deletions
263
src/compiler.rs
263
src/compiler.rs
|
|
@ -1,10 +1,14 @@
|
||||||
// COMPILER
|
// COMPILER
|
||||||
|
|
||||||
|
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::cmp::PartialEq;
|
||||||
|
use std::ops::Deref;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use crate::parser::{Piece,Expression,Statement,Name,Block,Chunk,Variable,VarArg,Call,Args,Function,Prefix,Constructor,LoadError,Number};
|
|
||||||
use crate::vm::{Machine, Prototype, Code, Value, Index, Reference, Offset, MarsError};
|
|
||||||
use crate::table::{Equivalent,Hashes,Map,Hashed,TableEntry};
|
|
||||||
|
|
||||||
pub(crate) struct Traversal<'a> {
|
pub(crate) struct Traversal<'a> {
|
||||||
// A 'cursor' of where we are in assumed execution
|
// A 'cursor' of where we are in assumed execution
|
||||||
|
|
@ -21,7 +25,7 @@ pub(crate) struct Context {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
struct ConstantEntry(Value,Index);
|
struct ConstantEntry(Value, Index);
|
||||||
|
|
||||||
impl Equivalent<Self> for ConstantEntry {
|
impl Equivalent<Self> for ConstantEntry {
|
||||||
fn matches(&self, other: &Self) -> bool {
|
fn matches(&self, other: &Self) -> bool {
|
||||||
|
|
@ -43,10 +47,10 @@ impl Hashes for ConstantEntry {
|
||||||
|
|
||||||
impl TableEntry for ConstantEntry {
|
impl TableEntry for ConstantEntry {
|
||||||
fn new_vacant() -> Self {
|
fn new_vacant() -> Self {
|
||||||
ConstantEntry(Value::Nil,0)
|
ConstantEntry(Value::Nil, 0)
|
||||||
}
|
}
|
||||||
fn is_vacant(&self) -> bool {
|
fn is_vacant(&self) -> bool {
|
||||||
matches!(self.0,Value::Nil)
|
matches!(self.0, Value::Nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -137,20 +141,33 @@ impl Traversal<'_> {
|
||||||
prototype.code.len() - 1
|
prototype.code.len() - 1
|
||||||
}
|
}
|
||||||
fn insert_code(&mut self, location: usize, it: Code) {
|
fn insert_code(&mut self, location: usize, it: Code) {
|
||||||
|
// slightly dangerous since it doesn't consider scopes
|
||||||
self.last_context().prototype.code[location] = it;
|
self.last_context().prototype.code[location] = it;
|
||||||
}
|
}
|
||||||
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!!!
|
||||||
// don't use constants that already exist
|
// don't use constants that already exist
|
||||||
if let Some(entry) = &mut self.contexts.last_mut().unwrap().constants.get_table(it.clone()) {
|
if let Some(entry) = &mut self
|
||||||
|
.contexts
|
||||||
|
.last_mut()
|
||||||
|
.unwrap()
|
||||||
|
.constants
|
||||||
|
.get_table(it.clone())
|
||||||
|
{
|
||||||
entry.1
|
entry.1
|
||||||
} else {
|
} else {
|
||||||
let index = {
|
let index = {
|
||||||
let constants = &mut self.contexts.last_mut().unwrap().prototype.constants;
|
let constants = &mut self.contexts.last_mut().unwrap().prototype.constants;
|
||||||
constants.push(it.clone());
|
constants.push(it.clone());
|
||||||
constants.len() - 1
|
constants.len() - 1
|
||||||
}.try_into().unwrap();
|
}
|
||||||
self.contexts.last_mut().unwrap().constants.set_table(ConstantEntry(it,index));
|
.try_into()
|
||||||
|
.unwrap();
|
||||||
|
self.contexts
|
||||||
|
.last_mut()
|
||||||
|
.unwrap()
|
||||||
|
.constants
|
||||||
|
.set_table(ConstantEntry(it, index));
|
||||||
index
|
index
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -158,13 +175,14 @@ impl Traversal<'_> {
|
||||||
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> {
|
fn push_string(&mut self, string: String, piece: Piece<()>) -> Result<(), LoadError> {
|
||||||
match self.machine.new_string(string.clone().as_str()) {
|
match self.machine.new_string(string.clone().as_str()) {
|
||||||
Err(error) => Err(LoadError(error.0,piece)),
|
Err(error) => Err(LoadError(error.0, piece)),
|
||||||
Ok(string) => Ok(self.push_literal(string, piece)),
|
Ok(string) => Ok(self.push_literal(string, piece)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn new_scope(&mut self) { // Make some new scope
|
fn new_scope(&mut self) {
|
||||||
|
// Make some new scope
|
||||||
let index = self.last_scope().index;
|
let index = self.last_scope().index;
|
||||||
self.contexts.last_mut().unwrap().scopes.push(Scope {
|
self.contexts.last_mut().unwrap().scopes.push(Scope {
|
||||||
index,
|
index,
|
||||||
|
|
@ -174,9 +192,11 @@ impl Traversal<'_> {
|
||||||
upvalue: false,
|
upvalue: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
fn temp_scope(&mut self) -> Index { // Make a new scope with index++
|
fn temp_scope(&mut self) -> Index {
|
||||||
|
// Make a new scope with index++
|
||||||
self.new_scope();
|
self.new_scope();
|
||||||
if self.last_context().scopes.len() > 2 { // For the dummy scope
|
if self.last_context().scopes.len() > 2 {
|
||||||
|
// For the dummy scope
|
||||||
self.last_scope().index += 1;
|
self.last_scope().index += 1;
|
||||||
}
|
}
|
||||||
self.last_scope().index
|
self.last_scope().index
|
||||||
|
|
@ -218,8 +238,12 @@ impl Traversal<'_> {
|
||||||
}
|
}
|
||||||
pub(crate) fn close_context(&mut self) -> Result<Prototype, LoadError> {
|
pub(crate) fn close_context(&mut self) -> Result<Prototype, LoadError> {
|
||||||
let mut context = self.contexts.pop().unwrap();
|
let mut context = self.contexts.pop().unwrap();
|
||||||
for jump in context.scopes.pop().unwrap().jumps { // Get back the dummy one and error any jumps
|
for jump in context.scopes.pop().unwrap().jumps {
|
||||||
return Err(LoadError::from_str(format!("{:?}",jump).as_str(), &Piece::<()>::null())) // todo: we need Vec or some append in LoadError and also BlindJumps need pieces!
|
// Get back the dummy one and error any jumps
|
||||||
|
return Err(LoadError::from_str(
|
||||||
|
format!("{:?}", jump).as_str(),
|
||||||
|
&Piece::<()>::null(),
|
||||||
|
)); // todo: we need Vec or some append in LoadError and also BlindJumps need pieces!
|
||||||
}
|
}
|
||||||
Ok(context.prototype)
|
Ok(context.prototype)
|
||||||
}
|
}
|
||||||
|
|
@ -227,7 +251,8 @@ impl Traversal<'_> {
|
||||||
Traversal {
|
Traversal {
|
||||||
machine,
|
machine,
|
||||||
contexts: vec![Context {
|
contexts: vec![Context {
|
||||||
scopes: vec![Scope { // Dummy scope
|
scopes: vec![Scope {
|
||||||
|
// Dummy scope
|
||||||
index: 0,
|
index: 0,
|
||||||
jumps: vec![],
|
jumps: vec![],
|
||||||
labels: vec![],
|
labels: vec![],
|
||||||
|
|
@ -264,7 +289,7 @@ fn compile_function(
|
||||||
if method {
|
if method {
|
||||||
self_arguments.push("self".to_string());
|
self_arguments.push("self".to_string());
|
||||||
}
|
}
|
||||||
self_arguments.append(&mut this.it.0.iter().map(|it| it.it.0.clone()).collect());
|
self_arguments.append(&mut this.it.args.iter().map(|it| it.it.0.clone()).collect());
|
||||||
let mut argument_scopes = vec![Scope {
|
let mut argument_scopes = vec![Scope {
|
||||||
// Dummy initial scope so the rest can start from index = 0 (since there can be zero arguments!!)
|
// Dummy initial scope so the rest can start from index = 0 (since there can be zero arguments!!)
|
||||||
index: 0,
|
index: 0,
|
||||||
|
|
@ -287,13 +312,13 @@ fn compile_function(
|
||||||
traversal.contexts.push(Context {
|
traversal.contexts.push(Context {
|
||||||
vararg: this
|
vararg: this
|
||||||
.it
|
.it
|
||||||
.1
|
.vararg
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|it| it.swap(it.clone().it.0.map(|string| string.0.clone()))),
|
.map(|it| it.swap(it.clone().it.0.map(|string| string.0.clone()))),
|
||||||
scopes: argument_scopes,
|
scopes: argument_scopes,
|
||||||
prototype: Prototype {
|
prototype: Prototype {
|
||||||
args: this.it.0.len().try_into().unwrap(),
|
args: this.it.args.len().try_into().unwrap(),
|
||||||
vararg: this.it.1.is_some(),
|
vararg: this.it.vararg.is_some(),
|
||||||
prototypes: vec![],
|
prototypes: vec![],
|
||||||
constants: vec![],
|
constants: vec![],
|
||||||
upvalues: vec![Reference::Upvalue(0)], // _ENV
|
upvalues: vec![Reference::Upvalue(0)], // _ENV
|
||||||
|
|
@ -302,7 +327,7 @@ fn compile_function(
|
||||||
},
|
},
|
||||||
constants: Map::new(),
|
constants: Map::new(),
|
||||||
});
|
});
|
||||||
traversal.compile(&this.it.2.inner_ref())?; // compile the block
|
traversal.compile(&this.it.block.inner_ref())?; // compile the block
|
||||||
let prototype = traversal.close_context()?;
|
let prototype = traversal.close_context()?;
|
||||||
let prototypes = &mut traversal.last_context().prototype.prototypes; // add this to the upper function's prototypes list
|
let prototypes = &mut traversal.last_context().prototype.prototypes; // add this to the upper function's prototypes list
|
||||||
let index = prototypes.len().try_into().unwrap();
|
let index = prototypes.len().try_into().unwrap();
|
||||||
|
|
@ -399,7 +424,7 @@ fn compile_identifier(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// break 'search avoids this use of the _G table
|
// break 'search avoids this use of the _G table
|
||||||
traversal.push_string(name.it.clone(),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,
|
||||||
|
|
@ -474,17 +499,17 @@ impl Compile for Piece<&Args> {
|
||||||
|
|
||||||
impl Compile for Piece<&Call> {
|
impl Compile for Piece<&Call> {
|
||||||
fn compile(&self, traversal: &mut Traversal) -> Result<(), LoadError> {
|
fn compile(&self, traversal: &mut Traversal) -> Result<(), LoadError> {
|
||||||
traversal.compile(&self.it.0.as_ref().inner_ref())?; // call prefix
|
traversal.compile(&self.it.prefix.as_ref().inner_ref())?; // call prefix
|
||||||
match &self.it.1 {
|
match &self.it.method {
|
||||||
Some(name) => {
|
Some(name) => {
|
||||||
traversal.push_string(name.it.0.clone(),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 => {}
|
||||||
}
|
}
|
||||||
let index = traversal.index().unwrap();
|
let index = traversal.index().unwrap();
|
||||||
traversal.compile(&self.it.2.inner_ref())?; // arguments
|
traversal.compile(&self.it.args.inner_ref())?; // arguments
|
||||||
traversal.push_code(self.it.0.unit().end().swap(Code::Call(index))); // Our responsibility to discard here
|
traversal.push_code(self.it.prefix.unit().end().swap(Code::Call(index))); // Our responsibility to discard here
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -562,20 +587,26 @@ impl Compile for Piece<&Constructor> {
|
||||||
traversal.push_code(self.swap(Code::TableNew));
|
traversal.push_code(self.swap(Code::TableNew));
|
||||||
let index_table = traversal.index().unwrap();
|
let index_table = traversal.index().unwrap();
|
||||||
for field in self.it.0.iter() {
|
for field in self.it.0.iter() {
|
||||||
match &field.it.0 {
|
match &field.it.index {
|
||||||
Some(index) => {
|
Some(index) => {
|
||||||
traversal.compile(&index.inner_ref())?;
|
traversal.compile(&index.inner_ref())?;
|
||||||
traversal.push_code(field.it.1.swap(Code::RegisterGet(index_table)).start());
|
traversal.push_code(
|
||||||
traversal.push_code(field.it.1.swap(Code::TableSet));
|
field
|
||||||
|
.it
|
||||||
|
.expression
|
||||||
|
.swap(Code::RegisterGet(index_table))
|
||||||
|
.start(),
|
||||||
|
);
|
||||||
|
traversal.push_code(field.it.expression.swap(Code::TableSet));
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
match &field.it.1.it {
|
match &field.it.expression.it {
|
||||||
Expression::VarArg(vararg) => {
|
Expression::VarArg(vararg) => {
|
||||||
traversal.compile(&field.it.1.swap(vararg))?;
|
traversal.compile(&field.it.expression.swap(vararg))?;
|
||||||
traversal.push_code(field.it.1.swap(Code::VarArg(0)));
|
traversal.push_code(field.it.expression.swap(Code::VarArg(0)));
|
||||||
}
|
}
|
||||||
it => {
|
it => {
|
||||||
traversal.compile(&field.it.1.swap(it))?;
|
traversal.compile(&field.it.expression.swap(it))?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let index = traversal
|
let index = traversal
|
||||||
|
|
@ -586,8 +617,14 @@ impl Compile for Piece<&Constructor> {
|
||||||
.last()
|
.last()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.index;
|
.index;
|
||||||
traversal.push_code(field.it.1.swap(Code::RegisterGet(index_table)).start());
|
traversal.push_code(
|
||||||
traversal.push_code(field.it.1.swap(Code::TableInsert(index)));
|
field
|
||||||
|
.it
|
||||||
|
.expression
|
||||||
|
.swap(Code::RegisterGet(index_table))
|
||||||
|
.start(),
|
||||||
|
);
|
||||||
|
traversal.push_code(field.it.expression.swap(Code::TableInsert(index)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -609,13 +646,19 @@ impl Compile for Piece<&Expression> {
|
||||||
}
|
}
|
||||||
Expression::Number(number) => {
|
Expression::Number(number) => {
|
||||||
let number = number
|
let number = number
|
||||||
|
.0
|
||||||
|
.parse::<u64>()
|
||||||
|
.map(|it| Value::Integer(it))
|
||||||
|
.unwrap_or(Value::Number(
|
||||||
|
number
|
||||||
.0
|
.0
|
||||||
.parse::<f64>()
|
.parse::<f64>()
|
||||||
.map_err(|it| LoadError(it.to_string(), self.unit()))?;
|
.map_err(|it| LoadError(it.to_string(), self.unit()))?,
|
||||||
traversal.push_literal(Value::Number(number), self.unit());
|
));
|
||||||
|
traversal.push_literal(number, self.unit());
|
||||||
}
|
}
|
||||||
Expression::String(string) => {
|
Expression::String(string) => {
|
||||||
traversal.push_string(string.clone(),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))?;
|
||||||
|
|
@ -649,7 +692,7 @@ fn jump_from(here: usize, there: usize) -> Result<Offset, LoadError> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_break(traversal: &mut Traversal, begin: usize, end: usize) -> Result<(), LoadError> {
|
fn resolve_break(traversal: &mut Traversal, begin: usize, end: usize) -> Result<(), LoadError> {
|
||||||
for jump in std::mem::replace(&mut traversal.last_scope().jumps,Vec::new()) {
|
for jump in std::mem::replace(&mut traversal.last_scope().jumps, Vec::new()) {
|
||||||
// ?
|
// ?
|
||||||
match jump {
|
match jump {
|
||||||
BlindJump::Continue(index) => {
|
BlindJump::Continue(index) => {
|
||||||
|
|
@ -672,11 +715,10 @@ fn compile_assignment(
|
||||||
let mut variables_iter = variables.iter().peekable();
|
let mut variables_iter = variables.iter().peekable();
|
||||||
let mut expressions_iter = expressions.iter().peekable();
|
let mut expressions_iter = expressions.iter().peekable();
|
||||||
while let Some(expression) = expressions_iter.next() {
|
while let Some(expression) = expressions_iter.next() {
|
||||||
let compile_variable_expression = | // Compile a variable normally with one value from an expression
|
let compile_variable_expression = |// Compile a variable normally with one value from an expression
|
||||||
traversal: &mut Traversal,
|
traversal: &mut Traversal,
|
||||||
variable: Option<&Piece<Variable>>,
|
variable: Option<&Piece<Variable>>,
|
||||||
expression: &Piece<&Expression>
|
expression: &Piece<&Expression>|
|
||||||
|
|
|
||||||
-> Result<(), LoadError> {
|
-> Result<(), LoadError> {
|
||||||
let index = traversal.index().unwrap();
|
let index = traversal.index().unwrap();
|
||||||
traversal.compile(expression)?;
|
traversal.compile(expression)?;
|
||||||
|
|
@ -691,8 +733,8 @@ fn compile_assignment(
|
||||||
// If this is the last expression it CAN have multiplicity
|
// If this is the last expression it CAN have multiplicity
|
||||||
match &expression.it {
|
match &expression.it {
|
||||||
Expression::Prefix(prefix) => {
|
Expression::Prefix(prefix) => {
|
||||||
match &**prefix {
|
match **prefix {
|
||||||
Prefix::Call(call) => {
|
Prefix::Call(ref call) => {
|
||||||
// multiple
|
// multiple
|
||||||
let mut index = traversal.index().unwrap();
|
let mut index = traversal.index().unwrap();
|
||||||
traversal.compile(&expression.swap(call))?;
|
traversal.compile(&expression.swap(call))?;
|
||||||
|
|
@ -701,9 +743,12 @@ fn compile_assignment(
|
||||||
index += 1; // The index we would need to pad nil to or restrict to
|
index += 1; // The index we would need to pad nil to or restrict to
|
||||||
compile_variable(variable.inner_ref(), traversal, Access::Set)?; // Pops each return value off the stack
|
compile_variable(variable.inner_ref(), traversal, Access::Set)?; // Pops each return value off the stack
|
||||||
}
|
}
|
||||||
traversal.insert_code(index_discard, Code::Discard(index.try_into().unwrap()));
|
traversal.insert_code(
|
||||||
|
index_discard,
|
||||||
|
Code::Discard(index.try_into().unwrap()),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
prefix => {
|
ref prefix => {
|
||||||
if let Some(variable) = variables_iter.next() {
|
if let Some(variable) = variables_iter.next() {
|
||||||
traversal.compile(&expression.swap(prefix))?;
|
traversal.compile(&expression.swap(prefix))?;
|
||||||
compile_variable(variable.inner_ref(), traversal, Access::Set)?
|
compile_variable(variable.inner_ref(), traversal, Access::Set)?
|
||||||
|
|
@ -720,7 +765,8 @@ fn compile_assignment(
|
||||||
count += 1;
|
count += 1;
|
||||||
compile_variable(variable.inner_ref(), traversal, Access::Set)?;
|
compile_variable(variable.inner_ref(), traversal, Access::Set)?;
|
||||||
}
|
}
|
||||||
traversal.insert_code(index_vararg, Code::VarArg(count.try_into().unwrap()));
|
traversal
|
||||||
|
.insert_code(index_vararg, Code::VarArg(count.try_into().unwrap()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
|
|
@ -755,7 +801,8 @@ impl Compile for Piece<&Block> {
|
||||||
index
|
index
|
||||||
};
|
};
|
||||||
// todo: for some reason, register indices are called "<thing>_index" and code indices "index_<thing>"? fix it
|
// todo: for some reason, register indices are called "<thing>_index" and code indices "index_<thing>"? fix it
|
||||||
for statement in self.it.0.iter() {
|
// todo: maybe refactor this so that there are less indents
|
||||||
|
for statement in self.it.statements.iter() {
|
||||||
let piece = statement.unit();
|
let piece = statement.unit();
|
||||||
match &statement.it {
|
match &statement.it {
|
||||||
Statement::Semicolon => {}
|
Statement::Semicolon => {}
|
||||||
|
|
@ -932,7 +979,9 @@ impl Compile for Piece<&Block> {
|
||||||
let index = traversal.index().unwrap();
|
let index = traversal.index().unwrap();
|
||||||
traversal.push_code(
|
traversal.push_code(
|
||||||
block
|
block
|
||||||
.swap(Code::Discard((index as usize + names.len()).try_into().unwrap()))
|
.swap(Code::Discard(
|
||||||
|
(index as usize + names.len()).try_into().unwrap(),
|
||||||
|
))
|
||||||
.start(),
|
.start(),
|
||||||
);
|
);
|
||||||
for name in names.iter() {
|
for name in names.iter() {
|
||||||
|
|
@ -976,10 +1025,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_string(name.it.0.clone(),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_string(name.it.0.clone(),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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -992,19 +1041,107 @@ impl Compile for Piece<&Block> {
|
||||||
traversal.push_code(name.swap(Code::RegisterSet(index)).end());
|
traversal.push_code(name.swap(Code::RegisterSet(index)).end());
|
||||||
}
|
}
|
||||||
Statement::Declaration(names, expressions) => {
|
Statement::Declaration(names, expressions) => {
|
||||||
let variables = &names
|
let mut expressions_iter = expressions.iter().peekable();
|
||||||
.iter()
|
let mut names_iter = names.iter().peekable();
|
||||||
.map(|name| {
|
let compile_declaration = |traversal: &mut Traversal,
|
||||||
var_scope(traversal, name.it.0.clone());
|
expression: &Piece<Expression>,
|
||||||
name.swap(Variable::Name(name.it.clone()))
|
name: &Piece<Name>|
|
||||||
})
|
-> Result<(), LoadError> {
|
||||||
.collect();
|
traversal.compile(&expression.inner_ref())?;
|
||||||
compile_assignment(traversal, variables, expressions)?;
|
traversal.last_scope().symbol = Some(name.it.0.clone());
|
||||||
|
Ok(())
|
||||||
|
};
|
||||||
|
while names_iter.peek().is_some() {
|
||||||
|
match (expressions_iter.next(), expressions_iter.peek().is_none()) {
|
||||||
|
(Some(expression), true) => {
|
||||||
|
// expression is last, has multiplicity
|
||||||
|
match expression.it {
|
||||||
|
Expression::VarArg(ref vararg) => {
|
||||||
|
let names: Vec<&Piece<Name>> = names_iter.collect();
|
||||||
|
traversal.compile(&expression.swap(vararg))?; // for error checking only
|
||||||
|
traversal.push_code(
|
||||||
|
expression.swap(Code::VarArg(
|
||||||
|
names.len().try_into().unwrap(),
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
let mut scopes =
|
||||||
|
traversal.last_context().scopes.iter_mut().rev();
|
||||||
|
for name in names.iter().rev() {
|
||||||
|
scopes.next().unwrap().symbol = Some(name.it.0.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Expression::Prefix(ref prefix) => {
|
||||||
|
match prefix.deref() {
|
||||||
|
Prefix::Call(call) => {
|
||||||
|
let names: Vec<&Piece<Name>> = names_iter.collect();
|
||||||
|
traversal.compile(&expression.swap(call))?;
|
||||||
|
let index = (traversal.index().unwrap_or(0)
|
||||||
|
as usize
|
||||||
|
+ names.len())
|
||||||
|
.try_into()
|
||||||
|
.unwrap();
|
||||||
|
traversal.push_code(
|
||||||
|
expression.swap(Code::Discard(index)).end(),
|
||||||
|
); // discard and extend to names
|
||||||
|
let mut scopes = traversal
|
||||||
|
.last_context()
|
||||||
|
.scopes
|
||||||
|
.iter_mut()
|
||||||
|
.rev();
|
||||||
|
for name in names.iter().rev() {
|
||||||
|
scopes.next().unwrap().symbol =
|
||||||
|
Some(name.it.0.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => compile_declaration(
|
||||||
|
traversal,
|
||||||
|
expression,
|
||||||
|
names_iter.next().unwrap(),
|
||||||
|
)?,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => compile_declaration(
|
||||||
|
traversal,
|
||||||
|
expression,
|
||||||
|
names_iter.next().unwrap(),
|
||||||
|
)?,
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
(Some(expression), false) => compile_declaration(
|
||||||
|
traversal,
|
||||||
|
expression,
|
||||||
|
names_iter.next().unwrap(),
|
||||||
|
)?, // expression is not last, just use it
|
||||||
|
(None, ..) => {
|
||||||
|
// no more expressions
|
||||||
|
let index = traversal.index().unwrap_or(0) as usize;
|
||||||
|
traversal.push_code(
|
||||||
|
names_iter
|
||||||
|
.next()
|
||||||
|
.unwrap()
|
||||||
|
.swap(Code::Discard(
|
||||||
|
(index + names_iter.count()).try_into().unwrap(),
|
||||||
|
))
|
||||||
|
.end(),
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let index = traversal.index().unwrap_or(0);
|
||||||
|
// clear up the remaining expressions and discard if they existed
|
||||||
|
if expressions_iter.peek().is_some() {
|
||||||
|
while let Some(expression) = expressions_iter.next() {
|
||||||
|
traversal.compile(&expression.inner_ref())?
|
||||||
|
}
|
||||||
|
traversal.push_code(Piece::<()>::null().swap(Code::Discard(index)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Any 'dangling' scopes that weren't closed (declarations) will be at the end of this block
|
// Any 'dangling' scopes that weren't closed (declarations) will be at the end of this block
|
||||||
if let Some(return_expressions) = &self.it.1 {
|
if let Some(return_expressions) = &self.it.return_exp {
|
||||||
// arguments are backwards
|
// arguments are backwards
|
||||||
for return_expression in return_expressions.iter().rev() {
|
for return_expression in return_expressions.iter().rev() {
|
||||||
traversal.compile(&return_expression.inner_ref())?;
|
traversal.compile(&return_expression.inner_ref())?;
|
||||||
|
|
@ -1024,7 +1161,7 @@ impl Compile for Chunk {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn compile(chunk: &Chunk, machine: &mut Machine) -> Result<Prototype,LoadError> {
|
pub(crate) fn compile(chunk: &Chunk, machine: &mut Machine) -> Result<Prototype, LoadError> {
|
||||||
let mut traversal = Traversal::new(machine);
|
let mut traversal = Traversal::new(machine);
|
||||||
traversal.compile(chunk)?;
|
traversal.compile(chunk)?;
|
||||||
traversal.close_context()
|
traversal.close_context()
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
// PARSER
|
// PARSER
|
||||||
|
|
||||||
use std::fmt::Formatter;
|
|
||||||
use std::hash::{DefaultHasher, Hash, Hasher};
|
|
||||||
use std::ops::{Add, Deref, DerefMut};
|
|
||||||
use crate::vm::{Index, RunError};
|
use crate::vm::{Index, RunError};
|
||||||
|
use std::fmt::Formatter;
|
||||||
|
use std::ops::Add;
|
||||||
// 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
|
// 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
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
|
|
@ -47,7 +46,7 @@ impl LoadError {
|
||||||
roll
|
roll
|
||||||
}
|
}
|
||||||
fn from_runtime(piece: Piece<()>, error: RunError) -> LoadError {
|
fn from_runtime(piece: Piece<()>, error: RunError) -> LoadError {
|
||||||
LoadError(error.0,piece)
|
LoadError(error.0, piece)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -335,12 +334,9 @@ impl Parse for Number {
|
||||||
if end == 0 {
|
if end == 0 {
|
||||||
reader.error("Could not parse number".into())
|
reader.error("Could not parse number".into())
|
||||||
} else {
|
} else {
|
||||||
|
let number = reader.slice[..end].to_string();
|
||||||
reader.slice = &reader.slice[end..];
|
reader.slice = &reader.slice[end..];
|
||||||
Ok(Piece::of(
|
Ok(Piece::of(Number(number), reader, end))
|
||||||
Number(reader.slice[..end].to_string()),
|
|
||||||
reader,
|
|
||||||
end,
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -426,7 +422,10 @@ impl Parse for String {
|
||||||
|
|
||||||
// AST
|
// AST
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct Block(pub(crate) Vec<Piece<Statement>>, pub(crate) Option<Vec<Piece<Expression>>>); //
|
pub(crate) struct Block {
|
||||||
|
pub(crate) statements: Vec<Piece<Statement>>,
|
||||||
|
pub(crate) return_exp: Option<Vec<Piece<Expression>>>,
|
||||||
|
} //
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct Chunk(pub(crate) Piece<Block>);
|
pub(crate) struct Chunk(pub(crate) Piece<Block>);
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
@ -494,13 +493,24 @@ pub(crate) enum Prefix {
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub(crate) struct VarArg(pub(crate) Option<Name>);
|
pub(crate) struct VarArg(pub(crate) Option<Name>);
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct Call(pub(crate) Box<Piece<Prefix>>, pub(crate) Option<Piece<Name>>, pub(crate) Piece<Args>);
|
pub(crate) struct Call {
|
||||||
|
pub(crate) prefix: Box<Piece<Prefix>>,
|
||||||
|
pub(crate) method: Option<Piece<Name>>,
|
||||||
|
pub(crate) args: Piece<Args>,
|
||||||
|
}
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct Args(pub(crate) Vec<Piece<Expression>>);
|
pub(crate) struct Args(pub(crate) Vec<Piece<Expression>>);
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct Function(pub(crate) Vec<Piece<Name>>, pub(crate) Option<Piece<VarArg>>, pub(crate) Piece<Block>);
|
pub(crate) struct Function {
|
||||||
|
pub(crate) args: Vec<Piece<Name>>,
|
||||||
|
pub(crate) vararg: Option<Piece<VarArg>>,
|
||||||
|
pub(crate) block: Piece<Block>,
|
||||||
|
}
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct Field(pub(crate) Option<Piece<Expression>>, pub(crate) Piece<Expression>);
|
pub(crate) struct Field {
|
||||||
|
pub(crate) index: Option<Piece<Expression>>,
|
||||||
|
pub(crate) expression: Piece<Expression>,
|
||||||
|
}
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) enum FieldSep {
|
pub(crate) enum FieldSep {
|
||||||
Comma,
|
Comma,
|
||||||
|
|
@ -701,7 +711,11 @@ impl Parse for Function {
|
||||||
let block = reader.take::<Block>()?;
|
let block = reader.take::<Block>()?;
|
||||||
reader.white_consume("end")?;
|
reader.white_consume("end")?;
|
||||||
Ok(Piece::from(
|
Ok(Piece::from(
|
||||||
Function(pre_parameters, vararg, block),
|
Function {
|
||||||
|
args: pre_parameters,
|
||||||
|
vararg: vararg,
|
||||||
|
block: block,
|
||||||
|
},
|
||||||
&start,
|
&start,
|
||||||
&end,
|
&end,
|
||||||
))
|
))
|
||||||
|
|
@ -857,14 +871,23 @@ impl Parse for Field {
|
||||||
reader.consume_white("=")?;
|
reader.consume_white("=")?;
|
||||||
let value = reader.take::<Expression>()?;
|
let value = reader.take::<Expression>()?;
|
||||||
reader.consume_white("]")?;
|
reader.consume_white("]")?;
|
||||||
Field(Some(index), value)
|
Field {
|
||||||
|
index: Some(index),
|
||||||
|
expression: value,
|
||||||
|
}
|
||||||
} else if let Ok(name) = reader.take::<Name>() {
|
} else if let Ok(name) = reader.take::<Name>() {
|
||||||
reader.consume_white("=")?;
|
reader.consume_white("=")?;
|
||||||
let index = name.map(|it| Expression::String(it.0));
|
let index = name.map(|it| Expression::String(it.0));
|
||||||
let value = reader.take::<Expression>()?;
|
let value = reader.take::<Expression>()?;
|
||||||
Field(Some(index), value)
|
Field {
|
||||||
|
index: Some(index),
|
||||||
|
expression: value,
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Field(None, reader.take::<Expression>()?)
|
Field {
|
||||||
|
index: None,
|
||||||
|
expression: reader.take::<Expression>()?,
|
||||||
|
}
|
||||||
};
|
};
|
||||||
Ok(Piece::from(result, &start, &reader.marker()))
|
Ok(Piece::from(result, &start, &reader.marker()))
|
||||||
}
|
}
|
||||||
|
|
@ -940,9 +963,17 @@ impl Parse for Prefix {
|
||||||
} else if reader.consume_white(":").is_ok() {
|
} else if reader.consume_white(":").is_ok() {
|
||||||
let field = reader.take::<Name>()?;
|
let field = reader.take::<Name>()?;
|
||||||
let args = reader.take::<Args>()?;
|
let args = reader.take::<Args>()?;
|
||||||
Prefix::Call(Call(current, Some(field), args))
|
Prefix::Call(Call {
|
||||||
|
prefix: current,
|
||||||
|
method: Some(field),
|
||||||
|
args: args,
|
||||||
|
})
|
||||||
} else if let Ok(args) = reader.take::<Args>() {
|
} else if let Ok(args) = reader.take::<Args>() {
|
||||||
Prefix::Call(Call(current, None, args))
|
Prefix::Call(Call {
|
||||||
|
prefix: current,
|
||||||
|
method: None,
|
||||||
|
args: args,
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
};
|
};
|
||||||
|
|
@ -1180,7 +1211,10 @@ impl Parse for Block {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
Ok(Piece::from(
|
Ok(Piece::from(
|
||||||
Block(statements, expressions),
|
Block {
|
||||||
|
statements: statements,
|
||||||
|
return_exp: expressions,
|
||||||
|
},
|
||||||
&start,
|
&start,
|
||||||
&reader.marker(),
|
&reader.marker(),
|
||||||
))
|
))
|
||||||
|
|
@ -1208,7 +1242,10 @@ impl Parse for Chunk {
|
||||||
};
|
};
|
||||||
Ok(Piece::from(
|
Ok(Piece::from(
|
||||||
Chunk(Piece::from(
|
Chunk(Piece::from(
|
||||||
Block(statements, expressions),
|
Block {
|
||||||
|
statements: statements,
|
||||||
|
return_exp: expressions,
|
||||||
|
},
|
||||||
&start,
|
&start,
|
||||||
&reader.marker(),
|
&reader.marker(),
|
||||||
)),
|
)),
|
||||||
|
|
|
||||||
65
src/table.rs
65
src/table.rs
|
|
@ -25,16 +25,19 @@ Notes:
|
||||||
|
|
||||||
type Displacement = u8;
|
type Displacement = u8;
|
||||||
|
|
||||||
#[cfg(feature="bighash")]
|
#[cfg(feature = "bighash")]
|
||||||
pub type Hashed = u64;
|
pub type Hashed = u64;
|
||||||
#[cfg(not(feature="bighash"))]
|
#[cfg(not(feature = "bighash"))]
|
||||||
pub type Hashed = u32;
|
pub type Hashed = u32;
|
||||||
|
|
||||||
pub trait Equivalent<T>: Sized {
|
pub trait Equivalent<T>: Sized {
|
||||||
fn matches(&self, other: &T) -> bool;
|
fn matches(&self, other: &T) -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Equivalent<T> for T where T: PartialEq {
|
impl<T> Equivalent<T> for T
|
||||||
|
where
|
||||||
|
T: PartialEq,
|
||||||
|
{
|
||||||
fn matches(&self, other: &T) -> bool {
|
fn matches(&self, other: &T) -> bool {
|
||||||
self == other
|
self == other
|
||||||
}
|
}
|
||||||
|
|
@ -75,7 +78,13 @@ impl TableEntry for KeyValue {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn is_vacant(&self) -> bool {
|
fn is_vacant(&self) -> bool {
|
||||||
matches!(self,KeyValue { index: Value::Nil, .. })
|
matches!(
|
||||||
|
self,
|
||||||
|
KeyValue {
|
||||||
|
index: Value::Nil,
|
||||||
|
..
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -87,7 +96,8 @@ struct Entry<E> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Map<E: TableEntry + Clone> { // default representing vacancy!
|
pub struct Map<E: TableEntry + Clone> {
|
||||||
|
// default representing vacancy!
|
||||||
map: Vec<Entry<E>>,
|
map: Vec<Entry<E>>,
|
||||||
map_bounds: std::ops::Range<usize>,
|
map_bounds: std::ops::Range<usize>,
|
||||||
map_count: usize, // number of elements in table
|
map_count: usize, // number of elements in table
|
||||||
|
|
@ -95,14 +105,19 @@ pub struct Map<E: TableEntry + Clone> { // default representing vacancy!
|
||||||
|
|
||||||
impl<E: TableEntry + Clone> Map<E> {
|
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
|
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) {
|
fn exchange_table(&mut self, len: usize) {
|
||||||
let old = std::mem::replace(&mut self.map, vec![Entry {
|
let old = std::mem::replace(
|
||||||
|
&mut self.map,
|
||||||
|
vec![
|
||||||
|
Entry {
|
||||||
entry: E::new_vacant(),
|
entry: E::new_vacant(),
|
||||||
home: 0,
|
home: 0,
|
||||||
displacement: 0,
|
displacement: 0,
|
||||||
}; len]);
|
};
|
||||||
|
len
|
||||||
|
],
|
||||||
|
);
|
||||||
for Entry { entry, .. } in old {
|
for Entry { entry, .. } in old {
|
||||||
if entry.is_vacant() {
|
if entry.is_vacant() {
|
||||||
self.set_table(entry)
|
self.set_table(entry)
|
||||||
|
|
@ -202,7 +217,11 @@ impl<E: TableEntry + Clone> Map<E> {
|
||||||
self.set_table(entry);
|
self.set_table(entry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub(crate) fn rem_table<K>(&mut self, index: K) where E: Equivalent<K>, K: Hashes {
|
pub(crate) fn rem_table<K>(&mut self, index: K)
|
||||||
|
where
|
||||||
|
E: Equivalent<K>,
|
||||||
|
K: Hashes,
|
||||||
|
{
|
||||||
if self.map.len() == 0 {
|
if self.map.len() == 0 {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -256,9 +275,12 @@ impl<E: TableEntry + Clone> Map<E> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub(crate) fn get_table<K>(&mut self, index: K) -> Option<&E>
|
pub(crate) fn get_table<K>(&mut self, index: K) -> Option<&E>
|
||||||
where E: Equivalent<K>, K: Hashes {
|
where
|
||||||
|
E: Equivalent<K>,
|
||||||
|
K: Hashes,
|
||||||
|
{
|
||||||
if self.map.len() == 0 {
|
if self.map.len() == 0 {
|
||||||
return None
|
return None;
|
||||||
}
|
}
|
||||||
let location = index.hashed() as usize % self.map.len();
|
let location = index.hashed() as usize % self.map.len();
|
||||||
let home = &self.map[location];
|
let home = &self.map[location];
|
||||||
|
|
@ -377,7 +399,7 @@ impl Table {
|
||||||
} else {
|
} else {
|
||||||
self.table.set_table(KeyValue {
|
self.table.set_table(KeyValue {
|
||||||
index: Value::Integer(index),
|
index: Value::Integer(index),
|
||||||
value: item
|
value: item,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -387,25 +409,28 @@ impl Table {
|
||||||
Value::Nil => Err(RunError(
|
Value::Nil => Err(RunError(
|
||||||
"Attempt to set new index of table with key: nil".to_string(),
|
"Attempt to set new index of table with key: nil".to_string(),
|
||||||
)),
|
)),
|
||||||
index => Ok(self.table.set_table(KeyValue {
|
index => Ok(self.table.set_table(KeyValue { index, value })),
|
||||||
index,
|
|
||||||
value
|
|
||||||
})),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn get(&mut 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
|
||||||
.get(index as usize)
|
.get(index as usize - 1)
|
||||||
.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 => { // todo: move this into the Map
|
index => {
|
||||||
Ok(self.table.get_table(index).unwrap_or(&KeyValue {
|
// todo: move this into the Map
|
||||||
|
Ok(self
|
||||||
|
.table
|
||||||
|
.get_table(index)
|
||||||
|
.unwrap_or(&KeyValue {
|
||||||
index: Value::Nil,
|
index: Value::Nil,
|
||||||
value: Value::Nil,
|
value: Value::Nil,
|
||||||
}).value.clone())
|
})
|
||||||
|
.value
|
||||||
|
.clone())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1 +1,2 @@
|
||||||
local table = {}
|
local table = { "hello", "friend" }
|
||||||
|
print(table[1], table[2])
|
||||||
|
|
|
||||||
97
src/vm.rs
97
src/vm.rs
|
|
@ -1,12 +1,12 @@
|
||||||
// MACHINE
|
// MACHINE
|
||||||
|
|
||||||
|
use crate::parser::{BinaryOp, Piece, UnaryOp, parse};
|
||||||
|
use crate::table::{Equivalent, Hashed, Hashes, Map, Table, TableEntry};
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
use std::fmt::Formatter;
|
use std::fmt::Formatter;
|
||||||
use std::hash::{DefaultHasher, Hash, Hasher};
|
use std::hash::{DefaultHasher, Hash, Hasher};
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use crate::table::{Equivalent, Hashed, Hashes, Map, Table, TableEntry};
|
|
||||||
use crate::parser::{Piece, BinaryOp, UnaryOp, parse};
|
|
||||||
|
|
||||||
// Rust function to call
|
// Rust function to call
|
||||||
pub struct Native(fn(&mut Machine, &mut Needle, usize) -> Result<(), RunError>); // A native function in Rust
|
pub struct Native(fn(&mut Machine, &mut Needle, usize) -> Result<(), RunError>); // A native function in Rust
|
||||||
|
|
@ -156,9 +156,7 @@ pub trait Object: std::fmt::Debug {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Value {
|
impl Value {}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait Userdata: Traverse + std::fmt::Debug + Any + Object {}
|
pub trait Userdata: Traverse + std::fmt::Debug + Any + Object {}
|
||||||
|
|
||||||
|
|
@ -176,10 +174,10 @@ impl Traverse for Anything {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
use crate::compiler::compile;
|
||||||
|
use crate::gc::{Allocator, Gc, Traverse};
|
||||||
#[cfg(feature = "vector3")]
|
#[cfg(feature = "vector3")]
|
||||||
use glam::Vec3;
|
use glam::Vec3;
|
||||||
use crate::gc::{Allocator, Gc, Traverse};
|
|
||||||
use crate::compiler::{Traversal, Context, Scope, compile};
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
struct Phrase {
|
struct Phrase {
|
||||||
|
|
@ -207,7 +205,8 @@ impl Hashes for CachedStringEntry {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Equivalent<Self> for CachedStringEntry { // redundant?
|
impl Equivalent<Self> for CachedStringEntry {
|
||||||
|
// redundant?
|
||||||
fn matches(&self, other: &Self) -> bool {
|
fn matches(&self, other: &Self) -> bool {
|
||||||
match self.0 {
|
match self.0 {
|
||||||
Some(ref entry) => match other.0 {
|
Some(ref entry) => match other.0 {
|
||||||
|
|
@ -232,7 +231,7 @@ impl Equivalent<&str> for CachedStringEntry {
|
||||||
fn matches(&self, string: &&str) -> bool {
|
fn matches(&self, string: &&str) -> bool {
|
||||||
match self.0 {
|
match self.0 {
|
||||||
Some(ref entry) => entry.0.deref() == *string,
|
Some(ref entry) => entry.0.deref() == *string,
|
||||||
None => false
|
None => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -264,8 +263,8 @@ impl Value {
|
||||||
fn unwrap_str(&mut self) -> Rc<str> {
|
fn unwrap_str(&mut self) -> Rc<str> {
|
||||||
match self {
|
match self {
|
||||||
Value::CachedString(cached) => cached.0.clone(),
|
Value::CachedString(cached) => cached.0.clone(),
|
||||||
Value::String(string,_hash) => string.clone(),
|
Value::String(string, _hash) => string.clone(),
|
||||||
_ => panic!()
|
_ => panic!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -276,58 +275,61 @@ impl PartialEq for Value {
|
||||||
Value::Nil => match other {
|
Value::Nil => match other {
|
||||||
Value::Nil => true,
|
Value::Nil => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
},
|
||||||
Value::Bool(this) => match other {
|
Value::Bool(this) => match other {
|
||||||
Value::Bool(other) => this == other,
|
Value::Bool(other) => this == other,
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
},
|
||||||
Value::Number(this) => match other {
|
Value::Number(this) => match other {
|
||||||
Value::Number(other) => this == other,
|
Value::Number(other) => this == other,
|
||||||
Value::Integer(other) => *this == *other as f64,
|
Value::Integer(other) => *this == *other as f64,
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
},
|
||||||
Value::Integer(this) => match other {
|
Value::Integer(this) => match other {
|
||||||
Value::Number(other) => *this as f64 == *other, // are these conversions ok?
|
Value::Number(other) => *this as f64 == *other, // are these conversions ok?
|
||||||
Value::Integer(other) => this == other,
|
Value::Integer(other) => this == other,
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
},
|
||||||
Value::String(string, hash) => match other {
|
Value::String(string, hash) => match other {
|
||||||
Value::CachedString(other) =>
|
Value::CachedString(other) => {
|
||||||
if *hash == other.1 {
|
if *hash == other.1 {
|
||||||
other.0.deref() == string.deref()
|
other.0.deref() == string.deref()
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
Value::String(other_string, other_hash) =>
|
}
|
||||||
|
Value::String(other_string, other_hash) => {
|
||||||
if hash == other_hash {
|
if hash == other_hash {
|
||||||
string == other_string
|
string == other_string
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
_ => false
|
|
||||||
}
|
}
|
||||||
|
_ => false,
|
||||||
|
},
|
||||||
Value::CachedString(string) => match other {
|
Value::CachedString(string) => match other {
|
||||||
Value::CachedString(other) => other.0 == string.0, // pointer equality
|
Value::CachedString(other) => other.0 == string.0, // pointer equality
|
||||||
Value::String(other_string, other_hash) =>
|
Value::String(other_string, other_hash) => {
|
||||||
if string.1 == *other_hash {
|
if string.1 == *other_hash {
|
||||||
string.0.deref() == other_string.deref()
|
string.0.deref() == other_string.deref()
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
_ => false
|
|
||||||
}
|
}
|
||||||
|
_ => false,
|
||||||
|
},
|
||||||
Value::Table(this) => match other {
|
Value::Table(this) => match other {
|
||||||
Value::Table(other) => other == this,
|
Value::Table(other) => other == this,
|
||||||
_ => false
|
_ => false,
|
||||||
}
|
},
|
||||||
Value::Object(this) => match other {
|
Value::Object(this) => match other {
|
||||||
Value::Object(other) => other == this,
|
Value::Object(other) => other == this,
|
||||||
_ => false
|
_ => false,
|
||||||
}
|
},
|
||||||
Value::Function(this) => match other {
|
Value::Function(this) => match other {
|
||||||
Value::Function(other) => other == this,
|
Value::Function(other) => other == this,
|
||||||
_ => false
|
_ => false,
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -359,7 +361,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
|
cache: Map<CachedStringEntry>, // todo: better to use a generic version of Map
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -440,16 +442,15 @@ impl Code {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Machine {
|
impl Machine {
|
||||||
pub(crate) fn new_string(&mut self, string: &str) -> Result<Value,RunError> {
|
pub(crate) fn new_string(&mut self, string: &str) -> Result<Value, RunError> {
|
||||||
if string.len() < SHORT_STRING_LEN {
|
if string.len() < SHORT_STRING_LEN {
|
||||||
let existing = self.cache.get_table(string);
|
let existing = self.cache.get_table(string);
|
||||||
match existing {
|
match existing {
|
||||||
Some(ref entry) => {
|
Some(ref entry) => Ok(Value::CachedString(entry.0.clone().unwrap())),
|
||||||
Ok(Value::CachedString(entry.0.clone().unwrap()))
|
|
||||||
},
|
|
||||||
None => {
|
None => {
|
||||||
let cached = CachedString(Rc::from(string),string.hashed());
|
let cached = CachedString(Rc::from(string), string.hashed());
|
||||||
self.cache.set_table(CachedStringEntry(Some(cached.clone())));
|
self.cache
|
||||||
|
.set_table(CachedStringEntry(Some(cached.clone())));
|
||||||
Ok(Value::CachedString(cached))
|
Ok(Value::CachedString(cached))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -504,7 +505,7 @@ impl Machine {
|
||||||
Value::Bool(bool) => Err(RunError(format!("Cannot index {:?}", bool))),
|
Value::Bool(bool) => Err(RunError(format!("Cannot index {:?}", bool))),
|
||||||
Value::Integer(_integer) => Err(RunError("Integer index unimplemented".to_string())),
|
Value::Integer(_integer) => Err(RunError("Integer index unimplemented".to_string())),
|
||||||
Value::Number(_number) => Err(RunError("Number index unimplemented".to_string())),
|
Value::Number(_number) => Err(RunError("Number index unimplemented".to_string())),
|
||||||
Value::String(.. ) | Value::CachedString(.. ) => {
|
Value::String(..) | Value::CachedString(..) => {
|
||||||
Err(RunError("String index unimplemented".to_string()))
|
Err(RunError("String index unimplemented".to_string()))
|
||||||
}
|
}
|
||||||
#[cfg(feature = "vector3")]
|
#[cfg(feature = "vector3")]
|
||||||
|
|
@ -527,7 +528,7 @@ impl Machine {
|
||||||
Value::Bool(bool) => Err(RunError(format!("Cannot index {:?}", bool))),
|
Value::Bool(bool) => Err(RunError(format!("Cannot index {:?}", bool))),
|
||||||
Value::Integer(_integer) => Err(RunError("Integer index unimplemented".to_string())),
|
Value::Integer(_integer) => Err(RunError("Integer index unimplemented".to_string())),
|
||||||
Value::Number(_number) => Err(RunError("Number index unimplemented".to_string())),
|
Value::Number(_number) => Err(RunError("Number index unimplemented".to_string())),
|
||||||
Value::String(..) | Value::CachedString(.. ) => {
|
Value::String(..) | Value::CachedString(..) => {
|
||||||
Err(RunError("String index unimplemented".to_string()))
|
Err(RunError("String index unimplemented".to_string()))
|
||||||
}
|
}
|
||||||
#[cfg(feature = "vector3")]
|
#[cfg(feature = "vector3")]
|
||||||
|
|
@ -678,7 +679,13 @@ 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.get(frame.counter).cloned();
|
let code = frame
|
||||||
|
.closure
|
||||||
|
.borrow_mut()
|
||||||
|
.prototype
|
||||||
|
.code
|
||||||
|
.get(frame.counter)
|
||||||
|
.cloned();
|
||||||
let code = match code {
|
let code = match code {
|
||||||
Some(code) => code,
|
Some(code) => code,
|
||||||
None => {
|
None => {
|
||||||
|
|
@ -694,9 +701,7 @@ impl Machine {
|
||||||
Code::Comment(number) => {
|
Code::Comment(number) => {
|
||||||
eprintln!("{}", number)
|
eprintln!("{}", number)
|
||||||
}
|
}
|
||||||
Code::TableNew => {
|
Code::TableNew => needle.push(Value::Table(self.allocator.alloc(Table::new()))),
|
||||||
needle.push(Value::Table(self.allocator.alloc(Table::new())))
|
|
||||||
}
|
|
||||||
Code::TableSet => {
|
Code::TableSet => {
|
||||||
let table = needle.pop();
|
let table = needle.pop();
|
||||||
let index = needle.pop();
|
let index = needle.pop();
|
||||||
|
|
@ -712,7 +717,7 @@ impl Machine {
|
||||||
match needle.pop() {
|
match needle.pop() {
|
||||||
Value::Table(mut table) => {
|
Value::Table(mut table) => {
|
||||||
for i in frame.offset + index as usize..needle.stack.len() {
|
for i in frame.offset + index as usize..needle.stack.len() {
|
||||||
table.borrow_mut().append(needle.stack[i].value.clone())
|
table.borrow_mut().append(needle.pop())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
|
|
@ -986,12 +991,18 @@ impl Machine {
|
||||||
let string = machine.new_string("print").unwrap();
|
let string = machine.new_string("print").unwrap();
|
||||||
machine.global.borrow_mut().set(
|
machine.global.borrow_mut().set(
|
||||||
string,
|
string,
|
||||||
Value::Function(Callable::Rust(Rc::from(Native(|machine: &mut Machine, needle: &mut Needle, index: usize| -> Result<(), RunError> {
|
Value::Function(Callable::Rust(Rc::from(Native(
|
||||||
|
|machine: &mut Machine,
|
||||||
|
needle: &mut Needle,
|
||||||
|
index: usize|
|
||||||
|
-> Result<(), RunError> {
|
||||||
for i in index..needle.stack.len() {
|
for i in index..needle.stack.len() {
|
||||||
println!("{}",machine.try_string(&mut needle.pop())?.unwrap_str())
|
print!("{} ", machine.try_string(&mut needle.pop())?.unwrap_str())
|
||||||
}
|
}
|
||||||
|
println!();
|
||||||
Ok(())
|
Ok(())
|
||||||
}))))
|
},
|
||||||
|
)))),
|
||||||
)?;
|
)?;
|
||||||
Ok(machine)
|
Ok(machine)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue