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
237
src/compiler.rs
237
src/compiler.rs
|
|
@ -1,10 +1,14 @@
|
|||
// 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::ops::Deref;
|
||||
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> {
|
||||
// A 'cursor' of where we are in assumed execution
|
||||
|
|
@ -137,20 +141,33 @@ impl Traversal<'_> {
|
|||
prototype.code.len() - 1
|
||||
}
|
||||
fn insert_code(&mut self, location: usize, it: Code) {
|
||||
// slightly dangerous since it doesn't consider scopes
|
||||
self.last_context().prototype.code[location] = it;
|
||||
}
|
||||
fn push_constant(&mut self, it: Value) -> Index {
|
||||
// todo: constants need pieces too!!!
|
||||
// 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
|
||||
} else {
|
||||
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));
|
||||
}
|
||||
.try_into()
|
||||
.unwrap();
|
||||
self.contexts
|
||||
.last_mut()
|
||||
.unwrap()
|
||||
.constants
|
||||
.set_table(ConstantEntry(it, index));
|
||||
index
|
||||
}
|
||||
}
|
||||
|
|
@ -164,7 +181,8 @@ impl Traversal<'_> {
|
|||
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;
|
||||
self.contexts.last_mut().unwrap().scopes.push(Scope {
|
||||
index,
|
||||
|
|
@ -174,9 +192,11 @@ impl Traversal<'_> {
|
|||
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();
|
||||
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
|
||||
|
|
@ -218,8 +238,12 @@ impl Traversal<'_> {
|
|||
}
|
||||
pub(crate) fn close_context(&mut self) -> Result<Prototype, LoadError> {
|
||||
let mut context = self.contexts.pop().unwrap();
|
||||
for jump in context.scopes.pop().unwrap().jumps { // 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!
|
||||
for jump in context.scopes.pop().unwrap().jumps {
|
||||
// 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)
|
||||
}
|
||||
|
|
@ -227,7 +251,8 @@ impl Traversal<'_> {
|
|||
Traversal {
|
||||
machine,
|
||||
contexts: vec![Context {
|
||||
scopes: vec![Scope { // Dummy scope
|
||||
scopes: vec![Scope {
|
||||
// Dummy scope
|
||||
index: 0,
|
||||
jumps: vec![],
|
||||
labels: vec![],
|
||||
|
|
@ -264,7 +289,7 @@ fn compile_function(
|
|||
if method {
|
||||
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 {
|
||||
// Dummy initial scope so the rest can start from index = 0 (since there can be zero arguments!!)
|
||||
index: 0,
|
||||
|
|
@ -287,13 +312,13 @@ fn compile_function(
|
|||
traversal.contexts.push(Context {
|
||||
vararg: this
|
||||
.it
|
||||
.1
|
||||
.vararg
|
||||
.as_ref()
|
||||
.map(|it| it.swap(it.clone().it.0.map(|string| string.0.clone()))),
|
||||
scopes: argument_scopes,
|
||||
prototype: Prototype {
|
||||
args: this.it.0.len().try_into().unwrap(),
|
||||
vararg: this.it.1.is_some(),
|
||||
args: this.it.args.len().try_into().unwrap(),
|
||||
vararg: this.it.vararg.is_some(),
|
||||
prototypes: vec![],
|
||||
constants: vec![],
|
||||
upvalues: vec![Reference::Upvalue(0)], // _ENV
|
||||
|
|
@ -302,7 +327,7 @@ fn compile_function(
|
|||
},
|
||||
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 prototypes = &mut traversal.last_context().prototype.prototypes; // add this to the upper function's prototypes list
|
||||
let index = prototypes.len().try_into().unwrap();
|
||||
|
|
@ -474,8 +499,8 @@ impl Compile for Piece<&Args> {
|
|||
|
||||
impl Compile for Piece<&Call> {
|
||||
fn compile(&self, traversal: &mut Traversal) -> Result<(), LoadError> {
|
||||
traversal.compile(&self.it.0.as_ref().inner_ref())?; // call prefix
|
||||
match &self.it.1 {
|
||||
traversal.compile(&self.it.prefix.as_ref().inner_ref())?; // call prefix
|
||||
match &self.it.method {
|
||||
Some(name) => {
|
||||
traversal.push_string(name.it.0.clone(), name.unit())?;
|
||||
traversal.push_code(name.swap(Code::TableGet));
|
||||
|
|
@ -483,8 +508,8 @@ impl Compile for Piece<&Call> {
|
|||
None => {}
|
||||
}
|
||||
let index = traversal.index().unwrap();
|
||||
traversal.compile(&self.it.2.inner_ref())?; // arguments
|
||||
traversal.push_code(self.it.0.unit().end().swap(Code::Call(index))); // Our responsibility to discard here
|
||||
traversal.compile(&self.it.args.inner_ref())?; // arguments
|
||||
traversal.push_code(self.it.prefix.unit().end().swap(Code::Call(index))); // Our responsibility to discard here
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
@ -562,20 +587,26 @@ impl Compile for Piece<&Constructor> {
|
|||
traversal.push_code(self.swap(Code::TableNew));
|
||||
let index_table = traversal.index().unwrap();
|
||||
for field in self.it.0.iter() {
|
||||
match &field.it.0 {
|
||||
match &field.it.index {
|
||||
Some(index) => {
|
||||
traversal.compile(&index.inner_ref())?;
|
||||
traversal.push_code(field.it.1.swap(Code::RegisterGet(index_table)).start());
|
||||
traversal.push_code(field.it.1.swap(Code::TableSet));
|
||||
traversal.push_code(
|
||||
field
|
||||
.it
|
||||
.expression
|
||||
.swap(Code::RegisterGet(index_table))
|
||||
.start(),
|
||||
);
|
||||
traversal.push_code(field.it.expression.swap(Code::TableSet));
|
||||
}
|
||||
None => {
|
||||
match &field.it.1.it {
|
||||
match &field.it.expression.it {
|
||||
Expression::VarArg(vararg) => {
|
||||
traversal.compile(&field.it.1.swap(vararg))?;
|
||||
traversal.push_code(field.it.1.swap(Code::VarArg(0)));
|
||||
traversal.compile(&field.it.expression.swap(vararg))?;
|
||||
traversal.push_code(field.it.expression.swap(Code::VarArg(0)));
|
||||
}
|
||||
it => {
|
||||
traversal.compile(&field.it.1.swap(it))?;
|
||||
traversal.compile(&field.it.expression.swap(it))?;
|
||||
}
|
||||
}
|
||||
let index = traversal
|
||||
|
|
@ -586,8 +617,14 @@ impl Compile for Piece<&Constructor> {
|
|||
.last()
|
||||
.unwrap()
|
||||
.index;
|
||||
traversal.push_code(field.it.1.swap(Code::RegisterGet(index_table)).start());
|
||||
traversal.push_code(field.it.1.swap(Code::TableInsert(index)));
|
||||
traversal.push_code(
|
||||
field
|
||||
.it
|
||||
.expression
|
||||
.swap(Code::RegisterGet(index_table))
|
||||
.start(),
|
||||
);
|
||||
traversal.push_code(field.it.expression.swap(Code::TableInsert(index)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -609,10 +646,16 @@ impl Compile for Piece<&Expression> {
|
|||
}
|
||||
Expression::Number(number) => {
|
||||
let number = number
|
||||
.0
|
||||
.parse::<u64>()
|
||||
.map(|it| Value::Integer(it))
|
||||
.unwrap_or(Value::Number(
|
||||
number
|
||||
.0
|
||||
.parse::<f64>()
|
||||
.map_err(|it| LoadError(it.to_string(), self.unit()))?;
|
||||
traversal.push_literal(Value::Number(number), self.unit());
|
||||
.map_err(|it| LoadError(it.to_string(), self.unit()))?,
|
||||
));
|
||||
traversal.push_literal(number, self.unit());
|
||||
}
|
||||
Expression::String(string) => {
|
||||
traversal.push_string(string.clone(), self.unit())?;
|
||||
|
|
@ -675,8 +718,7 @@ fn compile_assignment(
|
|||
let compile_variable_expression = |// Compile a variable normally with one value from an expression
|
||||
traversal: &mut Traversal,
|
||||
variable: Option<&Piece<Variable>>,
|
||||
expression: &Piece<&Expression>
|
||||
|
|
||||
expression: &Piece<&Expression>|
|
||||
-> Result<(), LoadError> {
|
||||
let index = traversal.index().unwrap();
|
||||
traversal.compile(expression)?;
|
||||
|
|
@ -691,8 +733,8 @@ fn compile_assignment(
|
|||
// If this is the last expression it CAN have multiplicity
|
||||
match &expression.it {
|
||||
Expression::Prefix(prefix) => {
|
||||
match &**prefix {
|
||||
Prefix::Call(call) => {
|
||||
match **prefix {
|
||||
Prefix::Call(ref call) => {
|
||||
// multiple
|
||||
let mut index = traversal.index().unwrap();
|
||||
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
|
||||
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() {
|
||||
traversal.compile(&expression.swap(prefix))?;
|
||||
compile_variable(variable.inner_ref(), traversal, Access::Set)?
|
||||
|
|
@ -720,7 +765,8 @@ fn compile_assignment(
|
|||
count += 1;
|
||||
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
|
||||
};
|
||||
// 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();
|
||||
match &statement.it {
|
||||
Statement::Semicolon => {}
|
||||
|
|
@ -932,7 +979,9 @@ impl Compile for Piece<&Block> {
|
|||
let index = traversal.index().unwrap();
|
||||
traversal.push_code(
|
||||
block
|
||||
.swap(Code::Discard((index as usize + names.len()).try_into().unwrap()))
|
||||
.swap(Code::Discard(
|
||||
(index as usize + names.len()).try_into().unwrap(),
|
||||
))
|
||||
.start(),
|
||||
);
|
||||
for name in names.iter() {
|
||||
|
|
@ -992,19 +1041,107 @@ impl Compile for Piece<&Block> {
|
|||
traversal.push_code(name.swap(Code::RegisterSet(index)).end());
|
||||
}
|
||||
Statement::Declaration(names, expressions) => {
|
||||
let variables = &names
|
||||
.iter()
|
||||
.map(|name| {
|
||||
var_scope(traversal, name.it.0.clone());
|
||||
name.swap(Variable::Name(name.it.clone()))
|
||||
})
|
||||
.collect();
|
||||
compile_assignment(traversal, variables, expressions)?;
|
||||
let mut expressions_iter = expressions.iter().peekable();
|
||||
let mut names_iter = names.iter().peekable();
|
||||
let compile_declaration = |traversal: &mut Traversal,
|
||||
expression: &Piece<Expression>,
|
||||
name: &Piece<Name>|
|
||||
-> Result<(), LoadError> {
|
||||
traversal.compile(&expression.inner_ref())?;
|
||||
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
|
||||
if let Some(return_expressions) = &self.it.1 {
|
||||
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())?;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
// PARSER
|
||||
|
||||
use std::fmt::Formatter;
|
||||
use std::hash::{DefaultHasher, Hash, Hasher};
|
||||
use std::ops::{Add, Deref, DerefMut};
|
||||
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
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
|
|
@ -335,12 +334,9 @@ impl Parse for Number {
|
|||
if end == 0 {
|
||||
reader.error("Could not parse number".into())
|
||||
} else {
|
||||
let number = reader.slice[..end].to_string();
|
||||
reader.slice = &reader.slice[end..];
|
||||
Ok(Piece::of(
|
||||
Number(reader.slice[..end].to_string()),
|
||||
reader,
|
||||
end,
|
||||
))
|
||||
Ok(Piece::of(Number(number), reader, end))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -426,7 +422,10 @@ impl Parse for String {
|
|||
|
||||
// AST
|
||||
#[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)]
|
||||
pub(crate) struct Chunk(pub(crate) Piece<Block>);
|
||||
#[derive(Debug)]
|
||||
|
|
@ -494,13 +493,24 @@ pub(crate) enum Prefix {
|
|||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct VarArg(pub(crate) Option<Name>);
|
||||
#[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)]
|
||||
pub(crate) struct Args(pub(crate) Vec<Piece<Expression>>);
|
||||
#[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)]
|
||||
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)]
|
||||
pub(crate) enum FieldSep {
|
||||
Comma,
|
||||
|
|
@ -701,7 +711,11 @@ impl Parse for Function {
|
|||
let block = reader.take::<Block>()?;
|
||||
reader.white_consume("end")?;
|
||||
Ok(Piece::from(
|
||||
Function(pre_parameters, vararg, block),
|
||||
Function {
|
||||
args: pre_parameters,
|
||||
vararg: vararg,
|
||||
block: block,
|
||||
},
|
||||
&start,
|
||||
&end,
|
||||
))
|
||||
|
|
@ -857,14 +871,23 @@ impl Parse for Field {
|
|||
reader.consume_white("=")?;
|
||||
let value = reader.take::<Expression>()?;
|
||||
reader.consume_white("]")?;
|
||||
Field(Some(index), value)
|
||||
Field {
|
||||
index: Some(index),
|
||||
expression: value,
|
||||
}
|
||||
} else if let Ok(name) = reader.take::<Name>() {
|
||||
reader.consume_white("=")?;
|
||||
let index = name.map(|it| Expression::String(it.0));
|
||||
let value = reader.take::<Expression>()?;
|
||||
Field(Some(index), value)
|
||||
Field {
|
||||
index: Some(index),
|
||||
expression: value,
|
||||
}
|
||||
} else {
|
||||
Field(None, reader.take::<Expression>()?)
|
||||
Field {
|
||||
index: None,
|
||||
expression: reader.take::<Expression>()?,
|
||||
}
|
||||
};
|
||||
Ok(Piece::from(result, &start, &reader.marker()))
|
||||
}
|
||||
|
|
@ -940,9 +963,17 @@ impl Parse for Prefix {
|
|||
} else if reader.consume_white(":").is_ok() {
|
||||
let field = reader.take::<Name>()?;
|
||||
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>() {
|
||||
Prefix::Call(Call(current, None, args))
|
||||
Prefix::Call(Call {
|
||||
prefix: current,
|
||||
method: None,
|
||||
args: args,
|
||||
})
|
||||
} else {
|
||||
break;
|
||||
};
|
||||
|
|
@ -1180,7 +1211,10 @@ impl Parse for Block {
|
|||
None
|
||||
};
|
||||
Ok(Piece::from(
|
||||
Block(statements, expressions),
|
||||
Block {
|
||||
statements: statements,
|
||||
return_exp: expressions,
|
||||
},
|
||||
&start,
|
||||
&reader.marker(),
|
||||
))
|
||||
|
|
@ -1208,7 +1242,10 @@ impl Parse for Chunk {
|
|||
};
|
||||
Ok(Piece::from(
|
||||
Chunk(Piece::from(
|
||||
Block(statements, expressions),
|
||||
Block {
|
||||
statements: statements,
|
||||
return_exp: expressions,
|
||||
},
|
||||
&start,
|
||||
&reader.marker(),
|
||||
)),
|
||||
|
|
|
|||
61
src/table.rs
61
src/table.rs
|
|
@ -34,7 +34,10 @@ pub trait Equivalent<T>: Sized {
|
|||
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 {
|
||||
self == other
|
||||
}
|
||||
|
|
@ -75,7 +78,13 @@ impl TableEntry for KeyValue {
|
|||
}
|
||||
}
|
||||
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)]
|
||||
pub struct Map<E: TableEntry + Clone> { // default representing vacancy!
|
||||
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
|
||||
|
|
@ -95,14 +105,19 @@ pub struct Map<E: TableEntry + Clone> { // default representing vacancy!
|
|||
|
||||
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 {
|
||||
let old = std::mem::replace(
|
||||
&mut self.map,
|
||||
vec![
|
||||
Entry {
|
||||
entry: E::new_vacant(),
|
||||
home: 0,
|
||||
displacement: 0,
|
||||
}; len]);
|
||||
};
|
||||
len
|
||||
],
|
||||
);
|
||||
for Entry { entry, .. } in old {
|
||||
if entry.is_vacant() {
|
||||
self.set_table(entry)
|
||||
|
|
@ -202,7 +217,11 @@ impl<E: TableEntry + Clone> Map<E> {
|
|||
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 {
|
||||
return;
|
||||
}
|
||||
|
|
@ -256,9 +275,12 @@ impl<E: TableEntry + Clone> Map<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 {
|
||||
return None
|
||||
return None;
|
||||
}
|
||||
let location = index.hashed() as usize % self.map.len();
|
||||
let home = &self.map[location];
|
||||
|
|
@ -377,7 +399,7 @@ impl Table {
|
|||
} else {
|
||||
self.table.set_table(KeyValue {
|
||||
index: Value::Integer(index),
|
||||
value: item
|
||||
value: item,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -387,25 +409,28 @@ impl Table {
|
|||
Value::Nil => Err(RunError(
|
||||
"Attempt to set new index of table with key: nil".to_string(),
|
||||
)),
|
||||
index => Ok(self.table.set_table(KeyValue {
|
||||
index,
|
||||
value
|
||||
})),
|
||||
index => Ok(self.table.set_table(KeyValue { index, value })),
|
||||
}
|
||||
}
|
||||
pub fn get(&mut self, index: Value) -> Result<Value, RunError> {
|
||||
match index {
|
||||
Value::Integer(index) => Ok(self
|
||||
.array
|
||||
.get(index as usize)
|
||||
.get(index as usize - 1)
|
||||
.unwrap_or(&Value::Nil)
|
||||
.clone()),
|
||||
Value::Nil => Err(RunError("Attempt to index table with key: nil".to_string())),
|
||||
index => { // todo: move this into the Map
|
||||
Ok(self.table.get_table(index).unwrap_or(&KeyValue {
|
||||
index => {
|
||||
// todo: move this into the Map
|
||||
Ok(self
|
||||
.table
|
||||
.get_table(index)
|
||||
.unwrap_or(&KeyValue {
|
||||
index: Value::Nil,
|
||||
value: Value::Nil,
|
||||
}).value.clone())
|
||||
})
|
||||
.value
|
||||
.clone())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1 +1,2 @@
|
|||
local table = {}
|
||||
local table = { "hello", "friend" }
|
||||
print(table[1], table[2])
|
||||
|
|
|
|||
87
src/vm.rs
87
src/vm.rs
|
|
@ -1,12 +1,12 @@
|
|||
// MACHINE
|
||||
|
||||
use crate::parser::{BinaryOp, Piece, UnaryOp, parse};
|
||||
use crate::table::{Equivalent, Hashed, Hashes, Map, Table, TableEntry};
|
||||
use std::any::Any;
|
||||
use std::fmt::Formatter;
|
||||
use std::hash::{DefaultHasher, Hash, Hasher};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::rc::Rc;
|
||||
use crate::table::{Equivalent, Hashed, Hashes, Map, Table, TableEntry};
|
||||
use crate::parser::{Piece, BinaryOp, UnaryOp, parse};
|
||||
|
||||
// Rust function to call
|
||||
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 {}
|
||||
|
||||
|
|
@ -176,10 +174,10 @@ impl Traverse for Anything {
|
|||
}
|
||||
}
|
||||
|
||||
use crate::compiler::compile;
|
||||
use crate::gc::{Allocator, Gc, Traverse};
|
||||
#[cfg(feature = "vector3")]
|
||||
use glam::Vec3;
|
||||
use crate::gc::{Allocator, Gc, Traverse};
|
||||
use crate::compiler::{Traversal, Context, Scope, compile};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
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 {
|
||||
match self.0 {
|
||||
Some(ref entry) => match other.0 {
|
||||
|
|
@ -232,7 +231,7 @@ impl Equivalent<&str> for CachedStringEntry {
|
|||
fn matches(&self, string: &&str) -> bool {
|
||||
match self.0 {
|
||||
Some(ref entry) => entry.0.deref() == *string,
|
||||
None => false
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -265,7 +264,7 @@ impl Value {
|
|||
match self {
|
||||
Value::CachedString(cached) => cached.0.clone(),
|
||||
Value::String(string, _hash) => string.clone(),
|
||||
_ => panic!()
|
||||
_ => panic!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -276,58 +275,61 @@ impl PartialEq for Value {
|
|||
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) =>
|
||||
Value::CachedString(other) => {
|
||||
if *hash == other.1 {
|
||||
other.0.deref() == string.deref()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
Value::String(other_string, other_hash) =>
|
||||
}
|
||||
Value::String(other_string, other_hash) => {
|
||||
if hash == other_hash {
|
||||
string == other_string
|
||||
} else {
|
||||
false
|
||||
}
|
||||
_ => false
|
||||
}
|
||||
_ => false,
|
||||
},
|
||||
Value::CachedString(string) => match other {
|
||||
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 {
|
||||
string.0.deref() == other_string.deref()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
_ => false
|
||||
}
|
||||
_ => false,
|
||||
},
|
||||
Value::Table(this) => match other {
|
||||
Value::Table(other) => other == this,
|
||||
_ => false
|
||||
}
|
||||
_ => false,
|
||||
},
|
||||
Value::Object(this) => match other {
|
||||
Value::Object(other) => other == this,
|
||||
_ => false
|
||||
}
|
||||
_ => false,
|
||||
},
|
||||
Value::Function(this) => match other {
|
||||
Value::Function(other) => other == this,
|
||||
_ => false
|
||||
}
|
||||
_ => false,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -359,7 +361,7 @@ pub struct Frame {
|
|||
pub struct Machine {
|
||||
global: Gc<Table>,
|
||||
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
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -444,12 +446,11 @@ impl Machine {
|
|||
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()))
|
||||
},
|
||||
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())));
|
||||
self.cache
|
||||
.set_table(CachedStringEntry(Some(cached.clone())));
|
||||
Ok(Value::CachedString(cached))
|
||||
}
|
||||
}
|
||||
|
|
@ -678,7 +679,13 @@ impl Machine {
|
|||
if needle.stack.len() > 65536 {
|
||||
return Err(RunError("Soft stack limit of 65536 reached".to_string()));
|
||||
}
|
||||
let code = frame.closure.borrow_mut().prototype.code.get(frame.counter).cloned();
|
||||
let code = frame
|
||||
.closure
|
||||
.borrow_mut()
|
||||
.prototype
|
||||
.code
|
||||
.get(frame.counter)
|
||||
.cloned();
|
||||
let code = match code {
|
||||
Some(code) => code,
|
||||
None => {
|
||||
|
|
@ -694,9 +701,7 @@ impl Machine {
|
|||
Code::Comment(number) => {
|
||||
eprintln!("{}", number)
|
||||
}
|
||||
Code::TableNew => {
|
||||
needle.push(Value::Table(self.allocator.alloc(Table::new())))
|
||||
}
|
||||
Code::TableNew => needle.push(Value::Table(self.allocator.alloc(Table::new()))),
|
||||
Code::TableSet => {
|
||||
let table = needle.pop();
|
||||
let index = needle.pop();
|
||||
|
|
@ -712,7 +717,7 @@ impl Machine {
|
|||
match needle.pop() {
|
||||
Value::Table(mut table) => {
|
||||
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();
|
||||
machine.global.borrow_mut().set(
|
||||
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() {
|
||||
println!("{}",machine.try_string(&mut needle.pop())?.unwrap_str())
|
||||
print!("{} ", machine.try_string(&mut needle.pop())?.unwrap_str())
|
||||
}
|
||||
println!();
|
||||
Ok(())
|
||||
}))))
|
||||
},
|
||||
)))),
|
||||
)?;
|
||||
Ok(machine)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue