Fixed loop instructions, local registers in other instructions, upvalues and tables. Builds and passes test.
This commit is contained in:
parent
110e035efd
commit
60c097518a
9 changed files with 229 additions and 151 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -1,2 +1,3 @@
|
||||||
.idea
|
.idea
|
||||||
target
|
target
|
||||||
|
Cargo.lock
|
||||||
25
Cargo.lock
generated
25
Cargo.lock
generated
|
|
@ -2,6 +2,15 @@
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 4
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "colored"
|
||||||
|
version = "3.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34"
|
||||||
|
dependencies = [
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "glam"
|
name = "glam"
|
||||||
version = "0.33.3"
|
version = "0.33.3"
|
||||||
|
|
@ -12,5 +21,21 @@ checksum = "7360bd2cd76e0cd9032d42cf2922155cecea2685b0cfa4630c3246df030bcfd6"
|
||||||
name = "mars"
|
name = "mars"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"colored",
|
||||||
"glam",
|
"glam",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-link"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.61.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
|
||||||
|
dependencies = [
|
||||||
|
"windows-link",
|
||||||
|
]
|
||||||
|
|
|
||||||
|
|
@ -10,4 +10,5 @@ messages = []
|
||||||
assertions = []
|
assertions = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
glam = { version = "0.33.3", optional = true }
|
glam = { version = "0.33.3", optional = true }
|
||||||
|
colored = { version = "3.1.1" }
|
||||||
126
src/compiler.rs
126
src/compiler.rs
|
|
@ -346,13 +346,13 @@ fn compile_identifier(
|
||||||
traversal: &mut Traversal,
|
traversal: &mut Traversal,
|
||||||
access: Access,
|
access: Access,
|
||||||
) -> Result<(), LoadError> {
|
) -> Result<(), LoadError> {
|
||||||
let length = traversal.contexts.len();
|
let contexts_len = traversal.contexts.len();
|
||||||
for i in 0..length {
|
for context_index in 0..contexts_len {
|
||||||
// For each context
|
// For each context
|
||||||
let i = length - i - 1; // From top to bottom
|
let context_index = contexts_len - context_index - 1; // From top to bottom
|
||||||
if i == length - 1 {
|
if context_index == contexts_len - 1 {
|
||||||
// If this is our context
|
// If this is our context
|
||||||
for scope in traversal.contexts[i].scopes.iter().rev() {
|
for scope in traversal.contexts[context_index].scopes.iter().rev() {
|
||||||
// Go through the scopes
|
// Go through the scopes
|
||||||
if scope.symbol.as_ref().is_some_and(|it| it.eq(&name.it)) {
|
if scope.symbol.as_ref().is_some_and(|it| it.eq(&name.it)) {
|
||||||
// Find it
|
// Find it
|
||||||
|
|
@ -366,36 +366,23 @@ fn compile_identifier(
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Otherwise it's another function's 'scope'(s)
|
// Otherwise it's another function's 'scope'(s)
|
||||||
let length = traversal.contexts[i].scopes.len();
|
let scopes_len = traversal.contexts[context_index].scopes.len();
|
||||||
for j in 0..length {
|
for scope_index in 0..scopes_len {
|
||||||
// For each scope in that foreign context
|
// For each scope in that foreign context
|
||||||
let j = length - j - 1; // From top to bottom
|
let scope_index = scopes_len - scope_index - 1; // From top to bottom
|
||||||
if traversal.contexts[i].scopes[j]
|
if traversal.contexts[context_index].scopes[scope_index]
|
||||||
.symbol
|
.symbol
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.is_some_and(|it| it.eq(&name.it))
|
.is_some_and(|it| it.eq(&name.it))
|
||||||
{
|
{
|
||||||
// Go through...
|
let register = traversal.contexts[context_index].scopes[scope_index].index;
|
||||||
// todo: fix this comment it's talking about something i ended up dropping
|
traversal.contexts[context_index].scopes[scope_index].upvalue = true;
|
||||||
/*
|
|
||||||
So here we know this 'traversal.contexts[i]' is the function holding the upvalue
|
|
||||||
in 'scopes[j]', if it has an upvalue already made, we use its 'prototype.closing'
|
|
||||||
index and set our initial rolling variable 'reference' to Reference::Register('index').
|
|
||||||
This is significant because this 'upvalue' MUST be made in this owning function's
|
|
||||||
prototype's closing list, but 'reference' also acts as a variable holding the upvalue
|
|
||||||
that the current function in the loop 'for k in j+1..length' produced so that the next
|
|
||||||
function context can reference it, this also means that the last 'reference' after the
|
|
||||||
loop is sort of vacuously a 'Reference::Upvalue' that is only storing our 'index'
|
|
||||||
for the eventual instruction itself.
|
|
||||||
*/
|
|
||||||
let register = traversal.contexts[i].scopes[j].index;
|
|
||||||
traversal.contexts[i].scopes[j].upvalue = true;
|
|
||||||
let mut reference = Reference::Closing(register);
|
let mut reference = Reference::Closing(register);
|
||||||
// its important that a few conditions are held here e.g. length > 2, j < length + 1...
|
// its important that a few conditions are held here e.g. length > 2, j < length + 1...
|
||||||
for k in j + 1..length {
|
for down_context_index in context_index + 1..contexts_len {
|
||||||
// Ok now going from every context to the one wanting the upvalue
|
// Ok now going from every context to the one wanting the upvalue
|
||||||
// set reference to what we found for the next context
|
// set reference to what we found for the next context
|
||||||
reference = match traversal.contexts[k]
|
reference = match traversal.contexts[down_context_index]
|
||||||
.prototype
|
.prototype
|
||||||
.upvalues
|
.upvalues
|
||||||
.iter()
|
.iter()
|
||||||
|
|
@ -404,8 +391,14 @@ fn compile_identifier(
|
||||||
Some(index) => Reference::Upvalue(index.try_into().unwrap()),
|
Some(index) => Reference::Upvalue(index.try_into().unwrap()),
|
||||||
None => {
|
None => {
|
||||||
// we don't even have a reference to this in our upvalues so add it
|
// we don't even have a reference to this in our upvalues so add it
|
||||||
let length = traversal.contexts[k].prototype.upvalues.len();
|
let length = traversal.contexts[down_context_index]
|
||||||
traversal.contexts[k].prototype.upvalues.push(reference);
|
.prototype
|
||||||
|
.upvalues
|
||||||
|
.len();
|
||||||
|
traversal.contexts[down_context_index]
|
||||||
|
.prototype
|
||||||
|
.upvalues
|
||||||
|
.push(reference);
|
||||||
Reference::Upvalue(length.try_into().unwrap()) // Reference (child) to this reference to that reference (parent) for next context
|
Reference::Upvalue(length.try_into().unwrap()) // Reference (child) to this reference to that reference (parent) for next context
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -499,6 +492,8 @@ 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> {
|
||||||
|
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
|
traversal.compile(&self.it.prefix.as_ref().inner_ref())?; // call prefix
|
||||||
match &self.it.method {
|
match &self.it.method {
|
||||||
Some(name) => {
|
Some(name) => {
|
||||||
|
|
@ -507,8 +502,6 @@ impl Compile for Piece<&Call> {
|
||||||
}
|
}
|
||||||
None => {}
|
None => {}
|
||||||
}
|
}
|
||||||
let index = traversal.index().unwrap();
|
|
||||||
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
|
traversal.push_code(self.it.prefix.unit().end().swap(Code::Call(index))); // Our responsibility to discard here
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -720,7 +713,7 @@ fn compile_assignment(
|
||||||
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_or(0);
|
||||||
traversal.compile(expression)?;
|
traversal.compile(expression)?;
|
||||||
if let Some(variable) = variable {
|
if let Some(variable) = variable {
|
||||||
compile_variable(variable.inner_ref(), traversal, Access::Set)?;
|
compile_variable(variable.inner_ref(), traversal, Access::Set)?;
|
||||||
|
|
@ -794,12 +787,6 @@ impl Compile for Piece<&Block> {
|
||||||
fn compile(&self, traversal: &mut Traversal) -> Result<(), LoadError> {
|
fn compile(&self, traversal: &mut Traversal) -> Result<(), LoadError> {
|
||||||
let last_scope_index = traversal.index().unwrap_or(0);
|
let last_scope_index = traversal.index().unwrap_or(0);
|
||||||
let mut depth = 0; // Counter for 'dangling' scopes produced by any declarations to be closed.
|
let mut depth = 0; // Counter for 'dangling' scopes produced by any declarations to be closed.
|
||||||
let mut var_scope = |traversal: &mut Traversal, symbol: String| -> Index {
|
|
||||||
let index = traversal.temp_scope();
|
|
||||||
traversal.last_scope().symbol = Some(symbol);
|
|
||||||
depth += 1;
|
|
||||||
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
|
||||||
// todo: maybe refactor this so that there are less indents
|
// todo: maybe refactor this so that there are less indents
|
||||||
for statement in self.it.statements.iter() {
|
for statement in self.it.statements.iter() {
|
||||||
|
|
@ -928,27 +915,16 @@ impl Compile for Piece<&Block> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Statement::Range(var, init, limit, step, block) => {
|
Statement::Range(var, init, limit, step, block) => {
|
||||||
let init_index = traversal.temp_scope(); // init
|
|
||||||
traversal.compile(&init.inner_ref())?;
|
traversal.compile(&init.inner_ref())?;
|
||||||
traversal.push_code(init.swap(Code::RegisterSet(init_index)));
|
|
||||||
|
|
||||||
let limit_index = traversal.temp_scope(); // limit
|
|
||||||
traversal.compile(&limit.inner_ref())?;
|
traversal.compile(&limit.inner_ref())?;
|
||||||
traversal.push_code(limit.swap(Code::RegisterSet(limit_index)));
|
|
||||||
|
|
||||||
let step_index = traversal.temp_scope(); // step;
|
|
||||||
match step {
|
match step {
|
||||||
Some(expression) => traversal.compile(&expression.inner_ref())?,
|
Some(expression) => traversal.compile(&expression.inner_ref())?,
|
||||||
None => traversal.push_literal(Value::Integer(1), limit.unit().end()),
|
None => traversal.push_literal(Value::Integer(1), limit.unit().end()),
|
||||||
};
|
};
|
||||||
let piece = match step {
|
|
||||||
Some(expression) => expression.unit().start(),
|
|
||||||
None => limit.unit().end(),
|
|
||||||
};
|
|
||||||
traversal.push_code(piece.swap(Code::RegisterSet(step_index)));
|
|
||||||
let index_loop =
|
let index_loop =
|
||||||
traversal.push_code(block.unit().start().swap(Code::RangeBegin)) + 1;
|
traversal.push_code(block.unit().start().swap(Code::RangeBegin)) + 1;
|
||||||
var_scope(traversal, var.it.0.clone());
|
traversal.temp_scope();
|
||||||
|
traversal.last_scope().symbol = Some(var.it.0.clone());
|
||||||
traversal.compile(&block.inner_ref())?;
|
traversal.compile(&block.inner_ref())?;
|
||||||
traversal.close_scope(); // temporary for v
|
traversal.close_scope(); // temporary for v
|
||||||
let index_jump = traversal.push_code(block.unit().start().swap(Code::UNSURE)); // Dummy NOP
|
let index_jump = traversal.push_code(block.unit().start().swap(Code::UNSURE)); // Dummy NOP
|
||||||
|
|
@ -957,9 +933,7 @@ impl Compile for Piece<&Block> {
|
||||||
Code::RangeLoop(jump_from(index_jump, index_loop)?),
|
Code::RangeLoop(jump_from(index_jump, index_loop)?),
|
||||||
); // Reinsert into above
|
); // Reinsert into above
|
||||||
resolve_break(traversal, index_loop, index_jump + 1)?;
|
resolve_break(traversal, index_loop, index_jump + 1)?;
|
||||||
traversal.close_scope();
|
traversal.consume_scope(3); // init, limit, step
|
||||||
traversal.close_scope();
|
|
||||||
traversal.close_scope(); // init, limit, step
|
|
||||||
}
|
}
|
||||||
Statement::Iterator(names, expressions, block) => {
|
Statement::Iterator(names, expressions, block) => {
|
||||||
let mut iterator_variables = Vec::new();
|
let mut iterator_variables = Vec::new();
|
||||||
|
|
@ -1035,10 +1009,9 @@ impl Compile for Piece<&Block> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Statement::LocalFunction(name, function) => {
|
Statement::LocalFunction(name, function) => {
|
||||||
var_scope(traversal, name.it.0.clone());
|
|
||||||
let index = traversal.index().unwrap();
|
|
||||||
traversal.compile(&self.swap(function))?;
|
traversal.compile(&self.swap(function))?;
|
||||||
traversal.push_code(name.swap(Code::RegisterSet(index)).end());
|
traversal.last_scope().symbol = Some(name.it.0.clone());
|
||||||
|
depth += 1;
|
||||||
}
|
}
|
||||||
Statement::Declaration(names, expressions) => {
|
Statement::Declaration(names, expressions) => {
|
||||||
let mut expressions_iter = expressions.iter().peekable();
|
let mut expressions_iter = expressions.iter().peekable();
|
||||||
|
|
@ -1068,6 +1041,7 @@ impl Compile for Piece<&Block> {
|
||||||
traversal.last_context().scopes.iter_mut().rev();
|
traversal.last_context().scopes.iter_mut().rev();
|
||||||
for name in names.iter().rev() {
|
for name in names.iter().rev() {
|
||||||
scopes.next().unwrap().symbol = Some(name.it.0.clone());
|
scopes.next().unwrap().symbol = Some(name.it.0.clone());
|
||||||
|
depth += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expression::Prefix(ref prefix) => {
|
Expression::Prefix(ref prefix) => {
|
||||||
|
|
@ -1091,28 +1065,38 @@ impl Compile for Piece<&Block> {
|
||||||
for name in names.iter().rev() {
|
for name in names.iter().rev() {
|
||||||
scopes.next().unwrap().symbol =
|
scopes.next().unwrap().symbol =
|
||||||
Some(name.it.0.clone());
|
Some(name.it.0.clone());
|
||||||
|
depth += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => compile_declaration(
|
_ => {
|
||||||
traversal,
|
compile_declaration(
|
||||||
expression,
|
traversal,
|
||||||
names_iter.next().unwrap(),
|
expression,
|
||||||
)?,
|
names_iter.next().unwrap(),
|
||||||
|
)?;
|
||||||
|
depth += 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => compile_declaration(
|
_ => {
|
||||||
traversal,
|
compile_declaration(
|
||||||
expression,
|
traversal,
|
||||||
names_iter.next().unwrap(),
|
expression,
|
||||||
)?,
|
names_iter.next().unwrap(),
|
||||||
|
)?;
|
||||||
|
depth += 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
(Some(expression), false) => compile_declaration(
|
(Some(expression), false) => {
|
||||||
traversal,
|
compile_declaration(
|
||||||
expression,
|
traversal,
|
||||||
names_iter.next().unwrap(),
|
expression,
|
||||||
)?, // expression is not last, just use it
|
names_iter.next().unwrap(),
|
||||||
|
)?;
|
||||||
|
depth += 1;
|
||||||
|
} // expression is not last, just use it
|
||||||
(None, ..) => {
|
(None, ..) => {
|
||||||
// no more expressions
|
// no more expressions
|
||||||
let index = traversal.index().unwrap_or(0) as usize;
|
let index = traversal.index().unwrap_or(0) as usize;
|
||||||
|
|
|
||||||
19
src/lib.rs
19
src/lib.rs
|
|
@ -1,29 +1,28 @@
|
||||||
#![warn(clippy::cast_lossless)]
|
#![warn(clippy::cast_lossless)]
|
||||||
|
|
||||||
mod parser;
|
|
||||||
mod compiler;
|
mod compiler;
|
||||||
mod vm;
|
|
||||||
mod table;
|
|
||||||
mod gc;
|
mod gc;
|
||||||
|
mod parser;
|
||||||
|
mod table;
|
||||||
|
mod vm;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::vm::{Callable, Machine};
|
use crate::vm::Machine;
|
||||||
use super::*;
|
|
||||||
#[test]
|
#[test]
|
||||||
fn simple() {
|
fn simple() {
|
||||||
let mut machine = match Machine::new() {
|
let mut machine = match Machine::new() {
|
||||||
Err(error) => panic!("{:?}",error),
|
Err(error) => panic!("{}", error),
|
||||||
Ok(machine) => machine,
|
Ok(machine) => machine,
|
||||||
};
|
};
|
||||||
let closure = match machine.load(include_str!("tests/script.lua")) {
|
let closure = match machine.load(include_str!("tests/script.lua")) {
|
||||||
Err(error) => panic!("{:?}",error),
|
Err(error) => panic!("{}", error),
|
||||||
Ok(closure) => closure,
|
Ok(closure) => closure,
|
||||||
_ => panic!()
|
_ => panic!(),
|
||||||
};
|
};
|
||||||
println!("{:?}",closure.borrow().prototype);
|
println!("{:?}", closure.borrow().prototype);
|
||||||
match machine.enter(closure) {
|
match machine.enter(closure) {
|
||||||
Err(error) => panic!("{:?}",error),
|
Err(error) => panic!("{}", error),
|
||||||
_ => {}
|
_ => {}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
// PARSER
|
// PARSER
|
||||||
|
|
||||||
use crate::vm::{Index, RunError};
|
use crate::vm::{Index, RunError};
|
||||||
|
use colored::*;
|
||||||
|
use std::cmp::{max, min};
|
||||||
use std::fmt::Formatter;
|
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)]
|
||||||
|
|
@ -23,27 +24,43 @@ impl LoadError {
|
||||||
LoadError(message.to_string(), piece.unit())
|
LoadError(message.to_string(), piece.unit())
|
||||||
}
|
}
|
||||||
pub(crate) fn msg(&self, reader: &str) -> String {
|
pub(crate) fn msg(&self, reader: &str) -> String {
|
||||||
let mut roll = format!("Error: {} {}", self.1.index, self.0);
|
fn put(it: &mut String, low: usize, high: usize, begin: usize, line: &String) {
|
||||||
let mut begin = self.1.index;
|
let bottom = min(max(low, begin), begin + line.len());
|
||||||
let mut end = self.1.index + self.1.width;
|
let top = min(max(high, begin), begin + line.len());
|
||||||
let mut count = 0;
|
let part = &line[bottom - begin..top - begin];
|
||||||
for line in reader.lines() {
|
if part.len() != 0 {
|
||||||
// todo: improve this
|
it.push_str(part);
|
||||||
count += 1;
|
|
||||||
if line.len() >= begin {
|
|
||||||
begin = 0;
|
|
||||||
roll = roll.add(format!("\n{}\t", count).as_str());
|
|
||||||
roll = roll.add(line);
|
|
||||||
} else {
|
|
||||||
begin -= line.len();
|
|
||||||
}
|
|
||||||
if line.len() > end {
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
end -= line.len()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
roll
|
let begin = self.1.index;
|
||||||
|
let end = self.1.index + self.1.width;
|
||||||
|
let mut line_index = 1;
|
||||||
|
let mut line_num = 0;
|
||||||
|
let mut col_num = 0;
|
||||||
|
let mut index;
|
||||||
|
let mut before = String::new();
|
||||||
|
let mut inner = String::new();
|
||||||
|
let mut after = String::new();
|
||||||
|
for line in reader.lines() {
|
||||||
|
index = line.as_ptr().addr() - reader.as_ptr().addr();
|
||||||
|
if index < begin {
|
||||||
|
line_num += 1;
|
||||||
|
}
|
||||||
|
if (index..index + line.len()).contains(&begin) {
|
||||||
|
col_num = begin - index;
|
||||||
|
}
|
||||||
|
let line = format!("{}\t{}\n", line_index, line);
|
||||||
|
if begin <= index + line.len() && end >= index {
|
||||||
|
put(&mut before, 0, begin, index, &line);
|
||||||
|
put(&mut inner, begin, end, index, &line);
|
||||||
|
put(&mut after, end, reader.len(), index, &line);
|
||||||
|
}
|
||||||
|
line_index += 1;
|
||||||
|
}
|
||||||
|
format!(
|
||||||
|
"Error: {}:{} \"{}\"\n{}{}{}",
|
||||||
|
line_num, col_num, self.0, before, inner, after
|
||||||
|
)
|
||||||
}
|
}
|
||||||
fn from_runtime(piece: Piece<()>, error: RunError) -> LoadError {
|
fn from_runtime(piece: Piece<()>, error: RunError) -> LoadError {
|
||||||
LoadError(error.0, piece)
|
LoadError(error.0, piece)
|
||||||
|
|
@ -913,9 +930,16 @@ impl Parse for Args {
|
||||||
reader.trim()?;
|
reader.trim()?;
|
||||||
let start = reader.marker();
|
let start = reader.marker();
|
||||||
let result = if reader.consume_white("(").is_ok() {
|
let result = if reader.consume_white("(").is_ok() {
|
||||||
let expressions = reader.take::<Vec<Piece<Expression>>>()?;
|
let mut expressions = Vec::new();
|
||||||
|
reader.trim()?;
|
||||||
|
if let Ok(first) = reader.take::<Expression>() {
|
||||||
|
expressions.push(first);
|
||||||
|
while reader.consume_white(",").is_ok() {
|
||||||
|
expressions.push(reader.take::<Expression>()?);
|
||||||
|
}
|
||||||
|
};
|
||||||
reader.consume_white(")")?;
|
reader.consume_white(")")?;
|
||||||
Args(expressions.it)
|
Args(expressions)
|
||||||
} else if let Ok(table) = reader.take::<Constructor>() {
|
} else if let Ok(table) = reader.take::<Constructor>() {
|
||||||
Args(vec![table.map(|it| Expression::Table(it))])
|
Args(vec![table.map(|it| Expression::Table(it))])
|
||||||
} else if let Ok(string) = reader.take::<String>() {
|
} else if let Ok(string) = reader.take::<String>() {
|
||||||
|
|
@ -1054,9 +1078,15 @@ impl Parse for Statement {
|
||||||
))
|
))
|
||||||
} else if reader.white_consume("for").is_ok() {
|
} else if reader.white_consume("for").is_ok() {
|
||||||
let names = reader.take::<Vec<Piece<Name>>>()?.it;
|
let names = reader.take::<Vec<Piece<Name>>>()?.it;
|
||||||
let result = if names.len() < 1 {
|
let before = reader.marker();
|
||||||
|
let result = if reader.consume_white("=").is_ok() {
|
||||||
|
if names.len() != 1 {
|
||||||
|
Err(LoadError::from_str(
|
||||||
|
"Expected exactly one name for range loop",
|
||||||
|
&before,
|
||||||
|
))?;
|
||||||
|
}
|
||||||
let name = names.first().unwrap();
|
let name = names.first().unwrap();
|
||||||
reader.consume_white("=")?;
|
|
||||||
let begin = reader.take::<Expression>()?;
|
let begin = reader.take::<Expression>()?;
|
||||||
reader.consume_white(",")?;
|
reader.consume_white(",")?;
|
||||||
let finish = reader.take::<Expression>()?;
|
let finish = reader.take::<Expression>()?;
|
||||||
|
|
|
||||||
10
src/table.rs
10
src/table.rs
|
|
@ -104,8 +104,6 @@ pub struct Map<E: TableEntry + Clone> {
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
fn exchange_table(&mut self, len: usize) {
|
fn exchange_table(&mut self, len: usize) {
|
||||||
let old = std::mem::replace(
|
let old = std::mem::replace(
|
||||||
&mut self.map,
|
&mut self.map,
|
||||||
|
|
@ -118,8 +116,9 @@ impl<E: TableEntry + Clone> Map<E> {
|
||||||
len
|
len
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
self.map_bounds = self.table_lower()..self.table_upper();
|
||||||
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -142,7 +141,6 @@ impl<E: TableEntry + Clone> Map<E> {
|
||||||
// too much free space
|
// too much free space
|
||||||
self.exchange_table(self.map.len() / 2)
|
self.exchange_table(self.map.len() / 2)
|
||||||
}
|
}
|
||||||
self.map_bounds = self.table_lower()..self.table_upper()
|
|
||||||
}
|
}
|
||||||
pub(crate) fn set_table(&mut self, entry: E) {
|
pub(crate) fn set_table(&mut self, entry: E) {
|
||||||
#[cfg(feature = "assertions")]
|
#[cfg(feature = "assertions")]
|
||||||
|
|
@ -234,7 +232,7 @@ impl<E: TableEntry + Clone> Map<E> {
|
||||||
if self.map[home].displacement != Displacement::MAX {
|
if self.map[home].displacement != Displacement::MAX {
|
||||||
let mut largest = 0;
|
let mut largest = 0;
|
||||||
for i in 1..self.map[home].displacement {
|
for i in 1..self.map[home].displacement {
|
||||||
let neighbour = &mut self.map[(home + i as usize) % range];
|
let neighbour = &mut self.map[(home + i as usize) % range]; // todo: maybe mod slows this down
|
||||||
if neighbour.entry.matches(&index) {
|
if neighbour.entry.matches(&index) {
|
||||||
neighbour.entry = E::new_vacant();
|
neighbour.entry = E::new_vacant();
|
||||||
self.map_count -= 1;
|
self.map_count -= 1;
|
||||||
|
|
@ -288,7 +286,7 @@ impl<E: TableEntry + Clone> Map<E> {
|
||||||
Some(&home.entry)
|
Some(&home.entry)
|
||||||
} else {
|
} else {
|
||||||
for i in 1..home.displacement as usize {
|
for i in 1..home.displacement as usize {
|
||||||
let neighbour = &self.map[location + i];
|
let neighbour = &self.map[(location + i) % self.map.len()];
|
||||||
if neighbour.entry.matches(&index) {
|
if neighbour.entry.matches(&index) {
|
||||||
return Some(&neighbour.entry);
|
return Some(&neighbour.entry);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,2 +1,9 @@
|
||||||
local table = { "hello", "friend" }
|
local var = 2
|
||||||
print(table[1], table[2])
|
local function thing()
|
||||||
|
print(var)
|
||||||
|
var = 3
|
||||||
|
end
|
||||||
|
|
||||||
|
print(var)
|
||||||
|
thing()
|
||||||
|
thing()
|
||||||
|
|
|
||||||
105
src/vm.rs
105
src/vm.rs
|
|
@ -3,12 +3,13 @@
|
||||||
use crate::parser::{BinaryOp, Piece, UnaryOp, parse};
|
use crate::parser::{BinaryOp, Piece, UnaryOp, parse};
|
||||||
use crate::table::{Equivalent, Hashed, Hashes, Map, Table, TableEntry};
|
use crate::table::{Equivalent, Hashed, Hashes, Map, Table, TableEntry};
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
use std::fmt::Formatter;
|
use std::fmt::{Display, 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;
|
||||||
|
|
||||||
// Rust function to call
|
// Rust function to call
|
||||||
|
// todo: make needles more private
|
||||||
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
|
||||||
|
|
||||||
#[derive(Clone, Debug, Copy, PartialEq)]
|
#[derive(Clone, Debug, Copy, PartialEq)]
|
||||||
|
|
@ -138,6 +139,12 @@ impl std::fmt::Debug for Callable {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct RunError(pub(crate) String);
|
pub struct RunError(pub(crate) String);
|
||||||
|
|
||||||
|
impl Display for RunError {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub trait Object: std::fmt::Debug {
|
pub trait Object: std::fmt::Debug {
|
||||||
fn meta_string(&mut self) -> Result<Value, RunError> {
|
fn meta_string(&mut self) -> Result<Value, RunError> {
|
||||||
Ok(Value::Nil)
|
Ok(Value::Nil)
|
||||||
|
|
@ -638,11 +645,11 @@ impl Machine {
|
||||||
-> Result<Frame, RunError> {
|
-> Result<Frame, RunError> {
|
||||||
frame.counter += 1; // return to this frame at the next instruction
|
frame.counter += 1; // return to this frame at the next instruction
|
||||||
let offset = frame.offset;
|
let offset = frame.offset;
|
||||||
match needle.stack[offset + index as usize].value.clone() {
|
match needle.pop() {
|
||||||
Value::Function(callable) => {
|
Value::Function(callable) => {
|
||||||
match callable {
|
match callable {
|
||||||
Callable::Rust(function) => {
|
Callable::Rust(function) => {
|
||||||
function.0(machine, needle, offset + index as usize + 1)?;
|
function.0(machine, needle, offset + index as usize)?;
|
||||||
Ok(frame) // nothing changes
|
Ok(frame) // nothing changes
|
||||||
}
|
}
|
||||||
Callable::Mars(closure) => {
|
Callable::Mars(closure) => {
|
||||||
|
|
@ -653,8 +660,8 @@ impl Machine {
|
||||||
.resize(offset + index as usize + nargs as usize, Register::NIL);
|
.resize(offset + index as usize + nargs as usize, Register::NIL);
|
||||||
Ok(Frame {
|
Ok(Frame {
|
||||||
// where to find the function, what about missing arguments?,
|
// where to find the function, what about missing arguments?,
|
||||||
vararg: offset + index as usize + 1, // start of frame points to the register after the function
|
vararg: offset + index as usize, // start of frame points to the register after the function
|
||||||
offset: offset + index as usize + 1 + nargs as usize, // points to first argument (or last vararg if nargs = zero)
|
offset: offset + index as usize + nargs as usize, // points to first argument (or last vararg if nargs = zero)
|
||||||
counter: 0,
|
counter: 0,
|
||||||
closure,
|
closure,
|
||||||
})
|
})
|
||||||
|
|
@ -668,9 +675,9 @@ impl Machine {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let mut jump = |frame: &mut Frame, offset: Offset| {
|
let jump = |frame: &mut Frame, offset: Offset| {
|
||||||
if offset < 0 {
|
if offset < 0 {
|
||||||
frame.counter -= -(offset) as usize
|
frame.counter -= -(offset - 1) as usize
|
||||||
} else {
|
} else {
|
||||||
frame.counter += offset as usize
|
frame.counter += offset as usize
|
||||||
}
|
}
|
||||||
|
|
@ -685,7 +692,7 @@ impl Machine {
|
||||||
.prototype
|
.prototype
|
||||||
.code
|
.code
|
||||||
.get(frame.counter)
|
.get(frame.counter)
|
||||||
.cloned();
|
.cloned(); // todo: this is probably slow for every instruction
|
||||||
let code = match code {
|
let code = match code {
|
||||||
Some(code) => code,
|
Some(code) => code,
|
||||||
None => {
|
None => {
|
||||||
|
|
@ -763,7 +770,7 @@ impl Machine {
|
||||||
Code::Discard(index) => {
|
Code::Discard(index) => {
|
||||||
needle
|
needle
|
||||||
.stack
|
.stack
|
||||||
.resize(frame.offset + index as usize, Register::NIL);
|
.resize(frame.offset + index as usize + 1, Register::NIL);
|
||||||
}
|
}
|
||||||
Code::VarArg(count) => {
|
Code::VarArg(count) => {
|
||||||
if count == 0 {
|
if count == 0 {
|
||||||
|
|
@ -784,7 +791,6 @@ impl Machine {
|
||||||
#[cfg(feature = "assertions")]
|
#[cfg(feature = "assertions")]
|
||||||
assert_ne!(offset, 0);
|
assert_ne!(offset, 0);
|
||||||
jump(&mut frame, offset);
|
jump(&mut frame, offset);
|
||||||
continue; // Skip the extra offset++
|
|
||||||
}
|
}
|
||||||
Code::Skip => {
|
Code::Skip => {
|
||||||
if needle.pop().bool() {
|
if needle.pop().bool() {
|
||||||
|
|
@ -862,9 +868,9 @@ impl Machine {
|
||||||
}
|
}
|
||||||
Code::RangeBegin => {
|
Code::RangeBegin => {
|
||||||
let len = needle.stack.len();
|
let len = needle.stack.len();
|
||||||
let init = len - 2;
|
let init = len - 3;
|
||||||
let limit = len - 1;
|
let limit = len - 2;
|
||||||
let step = len;
|
let step = len - 1;
|
||||||
let init_value = self.try_number(&mut needle.stack[init].value)?;
|
let init_value = self.try_number(&mut needle.stack[init].value)?;
|
||||||
let limit_value = self.try_number(&mut needle.stack[limit].value)?;
|
let limit_value = self.try_number(&mut needle.stack[limit].value)?;
|
||||||
let step_value = self.try_number(&mut needle.stack[step].value)?;
|
let step_value = self.try_number(&mut needle.stack[step].value)?;
|
||||||
|
|
@ -877,12 +883,8 @@ impl Machine {
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
};
|
};
|
||||||
// All integers so we can carry this out nicely
|
// All integers so we can carry this out nicely
|
||||||
needle.stack[init].value = init_value.clone();
|
needle.stack[init].value = init_value;
|
||||||
// https://www.lua.org/source/5.5/lvm.c.html#floatforloop
|
needle.stack[limit].value = limit_value; // todo: is this actually correct?
|
||||||
// but vmcase(OP_FORLOOP) actually preempts the number of iterations for integers!
|
|
||||||
needle.stack[limit].value = Value::Integer(
|
|
||||||
(get(limit_value) - get(init_value)) / get(step_value.clone()),
|
|
||||||
); // todo: is this actually correct?
|
|
||||||
needle.stack[step].value = step_value;
|
needle.stack[step].value = step_value;
|
||||||
} else {
|
} else {
|
||||||
// Make them all as floats
|
// Make them all as floats
|
||||||
|
|
@ -898,10 +900,11 @@ impl Machine {
|
||||||
needle.push(needle.stack[init].value.clone());
|
needle.push(needle.stack[init].value.clone());
|
||||||
}
|
}
|
||||||
Code::RangeLoop(offset) => {
|
Code::RangeLoop(offset) => {
|
||||||
|
needle.pop(); // get rid of temp
|
||||||
let len = needle.stack.len();
|
let len = needle.stack.len();
|
||||||
let init_index = len - 2;
|
let init_index = len - 3;
|
||||||
let limit_index = len - 1;
|
let limit_index = len - 2;
|
||||||
let step_index = len;
|
let step_index = len - 1;
|
||||||
let init_value = self.try_number(&mut needle.stack[init_index].value)?;
|
let init_value = self.try_number(&mut needle.stack[init_index].value)?;
|
||||||
let limit_value = self.try_number(&mut needle.stack[limit_index].value)?;
|
let limit_value = self.try_number(&mut needle.stack[limit_index].value)?;
|
||||||
let step_value = self.try_number(&mut needle.stack[step_index].value)?;
|
let step_value = self.try_number(&mut needle.stack[step_index].value)?;
|
||||||
|
|
@ -915,12 +918,14 @@ impl Machine {
|
||||||
Value::Integer(it) => it,
|
Value::Integer(it) => it,
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
};
|
};
|
||||||
if limit == 0 {
|
if init < limit {
|
||||||
|
needle.stack[init_index].value = Value::Integer(init + step);
|
||||||
|
needle.push(Value::Integer(init + step)); // owned variable for loop block
|
||||||
jump(&mut frame, offset);
|
jump(&mut frame, offset);
|
||||||
} else {
|
} else {
|
||||||
needle.stack[init_index].value = Value::Integer(init + step);
|
needle.pop();
|
||||||
needle.stack[limit_index].value = Value::Integer(limit - 1);
|
needle.pop();
|
||||||
needle.push(Value::Integer(init));
|
needle.pop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Value::Number(init) => {
|
Value::Number(init) => {
|
||||||
|
|
@ -933,10 +938,13 @@ impl Machine {
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
};
|
};
|
||||||
if (step >= 0.0 && init > limit) || (step < 0.0 && init < limit) {
|
if (step >= 0.0 && init > limit) || (step < 0.0 && init < limit) {
|
||||||
|
needle.stack[init_index].value = Value::Number(init + step);
|
||||||
|
needle.push(Value::Number(init)); // owned variable for loop block
|
||||||
jump(&mut frame, offset);
|
jump(&mut frame, offset);
|
||||||
} else {
|
} else {
|
||||||
needle.stack[init_index].value = Value::Number(init + step);
|
needle.pop();
|
||||||
needle.push(Value::Number(init));
|
needle.pop();
|
||||||
|
needle.pop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
|
|
@ -972,15 +980,21 @@ impl Machine {
|
||||||
pub(crate) fn enter(&mut self, closure: Gc<Closure>) -> Result<(), RunError> {
|
pub(crate) fn enter(&mut self, closure: Gc<Closure>) -> Result<(), RunError> {
|
||||||
let mut needle = Needle {
|
let mut needle = Needle {
|
||||||
frames: vec![Frame {
|
frames: vec![Frame {
|
||||||
vararg: 1, // Both 1 since there is a single NIL on the stack
|
vararg: 0,
|
||||||
offset: 1,
|
offset: 0,
|
||||||
counter: 0,
|
counter: 0,
|
||||||
closure,
|
closure,
|
||||||
}],
|
}],
|
||||||
stack: vec![Register::NIL], // When a function gets called, it expects to be able to place its return values over the value in the stack too
|
stack: vec![],
|
||||||
};
|
};
|
||||||
self.dispatch(&mut needle)
|
self.dispatch(&mut needle)
|
||||||
}
|
}
|
||||||
|
pub fn register_global(&mut self, name: &str, native: Native) -> Result<(), RunError> {
|
||||||
|
let name = self.new_string(name)?;
|
||||||
|
self.global
|
||||||
|
.borrow_mut()
|
||||||
|
.set(name, Value::Function(Callable::Rust(Rc::from(native))))
|
||||||
|
}
|
||||||
pub fn new() -> Result<Machine, RunError> {
|
pub fn new() -> Result<Machine, RunError> {
|
||||||
let mut allocator = Allocator::new();
|
let mut allocator = Allocator::new();
|
||||||
let mut machine = Machine {
|
let mut machine = Machine {
|
||||||
|
|
@ -988,10 +1002,9 @@ impl Machine {
|
||||||
allocator,
|
allocator,
|
||||||
cache: Map::new(),
|
cache: Map::new(),
|
||||||
};
|
};
|
||||||
let string = machine.new_string("print").unwrap();
|
machine.register_global(
|
||||||
machine.global.borrow_mut().set(
|
"print",
|
||||||
string,
|
Native(
|
||||||
Value::Function(Callable::Rust(Rc::from(Native(
|
|
||||||
|machine: &mut Machine,
|
|machine: &mut Machine,
|
||||||
needle: &mut Needle,
|
needle: &mut Needle,
|
||||||
index: usize|
|
index: usize|
|
||||||
|
|
@ -1002,7 +1015,22 @@ impl Machine {
|
||||||
println!();
|
println!();
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
},
|
||||||
)))),
|
),
|
||||||
|
)?;
|
||||||
|
machine.register_global(
|
||||||
|
"debug",
|
||||||
|
Native(
|
||||||
|
|machine: &mut Machine,
|
||||||
|
needle: &mut Needle,
|
||||||
|
index: usize|
|
||||||
|
-> Result<(), RunError> {
|
||||||
|
for i in index..needle.stack.len() {
|
||||||
|
print!("{:?} ", machine.try_string(&mut needle.pop())?.unwrap_str())
|
||||||
|
}
|
||||||
|
println!();
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
),
|
||||||
)?;
|
)?;
|
||||||
Ok(machine)
|
Ok(machine)
|
||||||
}
|
}
|
||||||
|
|
@ -1010,3 +1038,8 @@ impl Machine {
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct MarsError(String);
|
pub struct MarsError(String);
|
||||||
|
impl Display for MarsError {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue