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
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1,2 +1,3 @@
|
|||
.idea
|
||||
target
|
||||
Cargo.lock
|
||||
25
Cargo.lock
generated
25
Cargo.lock
generated
|
|
@ -2,6 +2,15 @@
|
|||
# It is not intended for manual editing.
|
||||
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]]
|
||||
name = "glam"
|
||||
version = "0.33.3"
|
||||
|
|
@ -12,5 +21,21 @@ checksum = "7360bd2cd76e0cd9032d42cf2922155cecea2685b0cfa4630c3246df030bcfd6"
|
|||
name = "mars"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"colored",
|
||||
"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",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -11,3 +11,4 @@ assertions = []
|
|||
|
||||
[dependencies]
|
||||
glam = { version = "0.33.3", optional = true }
|
||||
colored = { version = "3.1.1" }
|
||||
108
src/compiler.rs
108
src/compiler.rs
|
|
@ -346,13 +346,13 @@ fn compile_identifier(
|
|||
traversal: &mut Traversal,
|
||||
access: Access,
|
||||
) -> Result<(), LoadError> {
|
||||
let length = traversal.contexts.len();
|
||||
for i in 0..length {
|
||||
let contexts_len = traversal.contexts.len();
|
||||
for context_index in 0..contexts_len {
|
||||
// For each context
|
||||
let i = length - i - 1; // From top to bottom
|
||||
if i == length - 1 {
|
||||
let context_index = contexts_len - context_index - 1; // From top to bottom
|
||||
if context_index == contexts_len - 1 {
|
||||
// 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
|
||||
if scope.symbol.as_ref().is_some_and(|it| it.eq(&name.it)) {
|
||||
// Find it
|
||||
|
|
@ -366,36 +366,23 @@ fn compile_identifier(
|
|||
}
|
||||
} else {
|
||||
// Otherwise it's another function's 'scope'(s)
|
||||
let length = traversal.contexts[i].scopes.len();
|
||||
for j in 0..length {
|
||||
let scopes_len = traversal.contexts[context_index].scopes.len();
|
||||
for scope_index in 0..scopes_len {
|
||||
// For each scope in that foreign context
|
||||
let j = length - j - 1; // From top to bottom
|
||||
if traversal.contexts[i].scopes[j]
|
||||
let scope_index = scopes_len - scope_index - 1; // From top to bottom
|
||||
if traversal.contexts[context_index].scopes[scope_index]
|
||||
.symbol
|
||||
.as_ref()
|
||||
.is_some_and(|it| it.eq(&name.it))
|
||||
{
|
||||
// Go through...
|
||||
// todo: fix this comment it's talking about something i ended up dropping
|
||||
/*
|
||||
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 register = traversal.contexts[context_index].scopes[scope_index].index;
|
||||
traversal.contexts[context_index].scopes[scope_index].upvalue = true;
|
||||
let mut reference = Reference::Closing(register);
|
||||
// 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
|
||||
// set reference to what we found for the next context
|
||||
reference = match traversal.contexts[k]
|
||||
reference = match traversal.contexts[down_context_index]
|
||||
.prototype
|
||||
.upvalues
|
||||
.iter()
|
||||
|
|
@ -404,8 +391,14 @@ fn compile_identifier(
|
|||
Some(index) => Reference::Upvalue(index.try_into().unwrap()),
|
||||
None => {
|
||||
// we don't even have a reference to this in our upvalues so add it
|
||||
let length = traversal.contexts[k].prototype.upvalues.len();
|
||||
traversal.contexts[k].prototype.upvalues.push(reference);
|
||||
let length = traversal.contexts[down_context_index]
|
||||
.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
|
||||
}
|
||||
}
|
||||
|
|
@ -499,6 +492,8 @@ impl Compile for Piece<&Args> {
|
|||
|
||||
impl Compile for Piece<&Call> {
|
||||
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
|
||||
match &self.it.method {
|
||||
Some(name) => {
|
||||
|
|
@ -507,8 +502,6 @@ impl Compile for Piece<&Call> {
|
|||
}
|
||||
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
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -720,7 +713,7 @@ fn compile_assignment(
|
|||
variable: Option<&Piece<Variable>>,
|
||||
expression: &Piece<&Expression>|
|
||||
-> Result<(), LoadError> {
|
||||
let index = traversal.index().unwrap();
|
||||
let index = traversal.index().unwrap_or(0);
|
||||
traversal.compile(expression)?;
|
||||
if let Some(variable) = variable {
|
||||
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> {
|
||||
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 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: maybe refactor this so that there are less indents
|
||||
for statement in self.it.statements.iter() {
|
||||
|
|
@ -928,27 +915,16 @@ impl Compile for Piece<&Block> {
|
|||
}
|
||||
}
|
||||
Statement::Range(var, init, limit, step, block) => {
|
||||
let init_index = traversal.temp_scope(); // init
|
||||
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.push_code(limit.swap(Code::RegisterSet(limit_index)));
|
||||
|
||||
let step_index = traversal.temp_scope(); // step;
|
||||
match step {
|
||||
Some(expression) => traversal.compile(&expression.inner_ref())?,
|
||||
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 =
|
||||
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.close_scope(); // temporary for v
|
||||
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)?),
|
||||
); // Reinsert into above
|
||||
resolve_break(traversal, index_loop, index_jump + 1)?;
|
||||
traversal.close_scope();
|
||||
traversal.close_scope();
|
||||
traversal.close_scope(); // init, limit, step
|
||||
traversal.consume_scope(3); // init, limit, step
|
||||
}
|
||||
Statement::Iterator(names, expressions, block) => {
|
||||
let mut iterator_variables = Vec::new();
|
||||
|
|
@ -1035,10 +1009,9 @@ impl Compile for Piece<&Block> {
|
|||
}
|
||||
}
|
||||
Statement::LocalFunction(name, function) => {
|
||||
var_scope(traversal, name.it.0.clone());
|
||||
let index = traversal.index().unwrap();
|
||||
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) => {
|
||||
let mut expressions_iter = expressions.iter().peekable();
|
||||
|
|
@ -1068,6 +1041,7 @@ impl Compile for Piece<&Block> {
|
|||
traversal.last_context().scopes.iter_mut().rev();
|
||||
for name in names.iter().rev() {
|
||||
scopes.next().unwrap().symbol = Some(name.it.0.clone());
|
||||
depth += 1;
|
||||
}
|
||||
}
|
||||
Expression::Prefix(ref prefix) => {
|
||||
|
|
@ -1091,28 +1065,38 @@ impl Compile for Piece<&Block> {
|
|||
for name in names.iter().rev() {
|
||||
scopes.next().unwrap().symbol =
|
||||
Some(name.it.0.clone());
|
||||
depth += 1;
|
||||
}
|
||||
}
|
||||
_ => compile_declaration(
|
||||
_ => {
|
||||
compile_declaration(
|
||||
traversal,
|
||||
expression,
|
||||
names_iter.next().unwrap(),
|
||||
)?,
|
||||
)?;
|
||||
depth += 1;
|
||||
}
|
||||
}
|
||||
_ => compile_declaration(
|
||||
}
|
||||
_ => {
|
||||
compile_declaration(
|
||||
traversal,
|
||||
expression,
|
||||
names_iter.next().unwrap(),
|
||||
)?,
|
||||
)?;
|
||||
depth += 1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
(Some(expression), false) => compile_declaration(
|
||||
(Some(expression), false) => {
|
||||
compile_declaration(
|
||||
traversal,
|
||||
expression,
|
||||
names_iter.next().unwrap(),
|
||||
)?, // expression is not last, just use it
|
||||
)?;
|
||||
depth += 1;
|
||||
} // expression is not last, just use it
|
||||
(None, ..) => {
|
||||
// no more expressions
|
||||
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)]
|
||||
|
||||
mod parser;
|
||||
mod compiler;
|
||||
mod vm;
|
||||
mod table;
|
||||
mod gc;
|
||||
mod parser;
|
||||
mod table;
|
||||
mod vm;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::vm::{Callable, Machine};
|
||||
use super::*;
|
||||
use crate::vm::Machine;
|
||||
#[test]
|
||||
fn simple() {
|
||||
let mut machine = match Machine::new() {
|
||||
Err(error) => panic!("{:?}",error),
|
||||
Err(error) => panic!("{}", error),
|
||||
Ok(machine) => machine,
|
||||
};
|
||||
let closure = match machine.load(include_str!("tests/script.lua")) {
|
||||
Err(error) => panic!("{:?}",error),
|
||||
Err(error) => panic!("{}", error),
|
||||
Ok(closure) => closure,
|
||||
_ => panic!()
|
||||
_ => panic!(),
|
||||
};
|
||||
println!("{:?}",closure.borrow().prototype);
|
||||
println!("{:?}", closure.borrow().prototype);
|
||||
match machine.enter(closure) {
|
||||
Err(error) => panic!("{:?}",error),
|
||||
Err(error) => panic!("{}", error),
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
// PARSER
|
||||
|
||||
use crate::vm::{Index, RunError};
|
||||
use colored::*;
|
||||
use std::cmp::{max, min};
|
||||
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)]
|
||||
|
|
@ -23,27 +24,43 @@ impl LoadError {
|
|||
LoadError(message.to_string(), piece.unit())
|
||||
}
|
||||
pub(crate) fn msg(&self, reader: &str) -> String {
|
||||
let mut roll = format!("Error: {} {}", self.1.index, self.0);
|
||||
let mut begin = self.1.index;
|
||||
let mut end = self.1.index + self.1.width;
|
||||
let mut count = 0;
|
||||
fn put(it: &mut String, low: usize, high: usize, begin: usize, line: &String) {
|
||||
let bottom = min(max(low, begin), begin + line.len());
|
||||
let top = min(max(high, begin), begin + line.len());
|
||||
let part = &line[bottom - begin..top - begin];
|
||||
if part.len() != 0 {
|
||||
it.push_str(part);
|
||||
}
|
||||
}
|
||||
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() {
|
||||
// todo: improve this
|
||||
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();
|
||||
index = line.as_ptr().addr() - reader.as_ptr().addr();
|
||||
if index < begin {
|
||||
line_num += 1;
|
||||
}
|
||||
if line.len() > end {
|
||||
break;
|
||||
} else {
|
||||
end -= line.len()
|
||||
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);
|
||||
}
|
||||
roll
|
||||
line_index += 1;
|
||||
}
|
||||
format!(
|
||||
"Error: {}:{} \"{}\"\n{}{}{}",
|
||||
line_num, col_num, self.0, before, inner, after
|
||||
)
|
||||
}
|
||||
fn from_runtime(piece: Piece<()>, error: RunError) -> LoadError {
|
||||
LoadError(error.0, piece)
|
||||
|
|
@ -913,9 +930,16 @@ impl Parse for Args {
|
|||
reader.trim()?;
|
||||
let start = reader.marker();
|
||||
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(")")?;
|
||||
Args(expressions.it)
|
||||
Args(expressions)
|
||||
} else if let Ok(table) = reader.take::<Constructor>() {
|
||||
Args(vec![table.map(|it| Expression::Table(it))])
|
||||
} else if let Ok(string) = reader.take::<String>() {
|
||||
|
|
@ -1054,9 +1078,15 @@ impl Parse for Statement {
|
|||
))
|
||||
} else if reader.white_consume("for").is_ok() {
|
||||
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();
|
||||
reader.consume_white("=")?;
|
||||
let begin = reader.take::<Expression>()?;
|
||||
reader.consume_white(",")?;
|
||||
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> {
|
||||
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,
|
||||
|
|
@ -118,8 +116,9 @@ impl<E: TableEntry + Clone> Map<E> {
|
|||
len
|
||||
],
|
||||
);
|
||||
self.map_bounds = self.table_lower()..self.table_upper();
|
||||
for Entry { entry, .. } in old {
|
||||
if entry.is_vacant() {
|
||||
if !entry.is_vacant() {
|
||||
self.set_table(entry)
|
||||
}
|
||||
}
|
||||
|
|
@ -142,7 +141,6 @@ impl<E: TableEntry + Clone> Map<E> {
|
|||
// too much free space
|
||||
self.exchange_table(self.map.len() / 2)
|
||||
}
|
||||
self.map_bounds = self.table_lower()..self.table_upper()
|
||||
}
|
||||
pub(crate) fn set_table(&mut self, entry: E) {
|
||||
#[cfg(feature = "assertions")]
|
||||
|
|
@ -234,7 +232,7 @@ impl<E: TableEntry + Clone> Map<E> {
|
|||
if self.map[home].displacement != Displacement::MAX {
|
||||
let mut largest = 0;
|
||||
for i in 1..self.map[home].displacement {
|
||||
let neighbour = &mut self.map[(home + i as usize) % range];
|
||||
let neighbour = &mut self.map[(home + i as usize) % range]; // todo: maybe mod slows this down
|
||||
if neighbour.entry.matches(&index) {
|
||||
neighbour.entry = E::new_vacant();
|
||||
self.map_count -= 1;
|
||||
|
|
@ -288,7 +286,7 @@ impl<E: TableEntry + Clone> Map<E> {
|
|||
Some(&home.entry)
|
||||
} else {
|
||||
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) {
|
||||
return Some(&neighbour.entry);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,2 +1,9 @@
|
|||
local table = { "hello", "friend" }
|
||||
print(table[1], table[2])
|
||||
local var = 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::table::{Equivalent, Hashed, Hashes, Map, Table, TableEntry};
|
||||
use std::any::Any;
|
||||
use std::fmt::Formatter;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::hash::{DefaultHasher, Hash, Hasher};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::rc::Rc;
|
||||
|
||||
// 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
|
||||
|
||||
#[derive(Clone, Debug, Copy, PartialEq)]
|
||||
|
|
@ -138,6 +139,12 @@ impl std::fmt::Debug for Callable {
|
|||
#[derive(Debug)]
|
||||
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 {
|
||||
fn meta_string(&mut self) -> Result<Value, RunError> {
|
||||
Ok(Value::Nil)
|
||||
|
|
@ -638,11 +645,11 @@ impl Machine {
|
|||
-> Result<Frame, RunError> {
|
||||
frame.counter += 1; // return to this frame at the next instruction
|
||||
let offset = frame.offset;
|
||||
match needle.stack[offset + index as usize].value.clone() {
|
||||
match needle.pop() {
|
||||
Value::Function(callable) => {
|
||||
match callable {
|
||||
Callable::Rust(function) => {
|
||||
function.0(machine, needle, offset + index as usize + 1)?;
|
||||
function.0(machine, needle, offset + index as usize)?;
|
||||
Ok(frame) // nothing changes
|
||||
}
|
||||
Callable::Mars(closure) => {
|
||||
|
|
@ -653,8 +660,8 @@ impl Machine {
|
|||
.resize(offset + index as usize + nargs as usize, Register::NIL);
|
||||
Ok(Frame {
|
||||
// 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
|
||||
offset: offset + index as usize + 1 + nargs as usize, // points to first argument (or last vararg if nargs = zero)
|
||||
vararg: offset + index as usize, // start of frame points to the register after the function
|
||||
offset: offset + index as usize + nargs as usize, // points to first argument (or last vararg if nargs = zero)
|
||||
counter: 0,
|
||||
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 {
|
||||
frame.counter -= -(offset) as usize
|
||||
frame.counter -= -(offset - 1) as usize
|
||||
} else {
|
||||
frame.counter += offset as usize
|
||||
}
|
||||
|
|
@ -685,7 +692,7 @@ impl Machine {
|
|||
.prototype
|
||||
.code
|
||||
.get(frame.counter)
|
||||
.cloned();
|
||||
.cloned(); // todo: this is probably slow for every instruction
|
||||
let code = match code {
|
||||
Some(code) => code,
|
||||
None => {
|
||||
|
|
@ -763,7 +770,7 @@ impl Machine {
|
|||
Code::Discard(index) => {
|
||||
needle
|
||||
.stack
|
||||
.resize(frame.offset + index as usize, Register::NIL);
|
||||
.resize(frame.offset + index as usize + 1, Register::NIL);
|
||||
}
|
||||
Code::VarArg(count) => {
|
||||
if count == 0 {
|
||||
|
|
@ -784,7 +791,6 @@ impl Machine {
|
|||
#[cfg(feature = "assertions")]
|
||||
assert_ne!(offset, 0);
|
||||
jump(&mut frame, offset);
|
||||
continue; // Skip the extra offset++
|
||||
}
|
||||
Code::Skip => {
|
||||
if needle.pop().bool() {
|
||||
|
|
@ -862,9 +868,9 @@ impl Machine {
|
|||
}
|
||||
Code::RangeBegin => {
|
||||
let len = needle.stack.len();
|
||||
let init = len - 2;
|
||||
let limit = len - 1;
|
||||
let step = len;
|
||||
let init = len - 3;
|
||||
let limit = len - 2;
|
||||
let step = len - 1;
|
||||
let init_value = self.try_number(&mut needle.stack[init].value)?;
|
||||
let limit_value = self.try_number(&mut needle.stack[limit].value)?;
|
||||
let step_value = self.try_number(&mut needle.stack[step].value)?;
|
||||
|
|
@ -877,12 +883,8 @@ impl Machine {
|
|||
_ => panic!(),
|
||||
};
|
||||
// All integers so we can carry this out nicely
|
||||
needle.stack[init].value = init_value.clone();
|
||||
// https://www.lua.org/source/5.5/lvm.c.html#floatforloop
|
||||
// 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[init].value = init_value;
|
||||
needle.stack[limit].value = limit_value; // todo: is this actually correct?
|
||||
needle.stack[step].value = step_value;
|
||||
} else {
|
||||
// Make them all as floats
|
||||
|
|
@ -898,10 +900,11 @@ impl Machine {
|
|||
needle.push(needle.stack[init].value.clone());
|
||||
}
|
||||
Code::RangeLoop(offset) => {
|
||||
needle.pop(); // get rid of temp
|
||||
let len = needle.stack.len();
|
||||
let init_index = len - 2;
|
||||
let limit_index = len - 1;
|
||||
let step_index = len;
|
||||
let init_index = len - 3;
|
||||
let limit_index = len - 2;
|
||||
let step_index = len - 1;
|
||||
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 step_value = self.try_number(&mut needle.stack[step_index].value)?;
|
||||
|
|
@ -915,12 +918,14 @@ impl Machine {
|
|||
Value::Integer(it) => it,
|
||||
_ => 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);
|
||||
} else {
|
||||
needle.stack[init_index].value = Value::Integer(init + step);
|
||||
needle.stack[limit_index].value = Value::Integer(limit - 1);
|
||||
needle.push(Value::Integer(init));
|
||||
needle.pop();
|
||||
needle.pop();
|
||||
needle.pop();
|
||||
}
|
||||
}
|
||||
Value::Number(init) => {
|
||||
|
|
@ -933,10 +938,13 @@ impl Machine {
|
|||
_ => panic!(),
|
||||
};
|
||||
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);
|
||||
} else {
|
||||
needle.stack[init_index].value = Value::Number(init + step);
|
||||
needle.push(Value::Number(init));
|
||||
needle.pop();
|
||||
needle.pop();
|
||||
needle.pop();
|
||||
}
|
||||
}
|
||||
_ => panic!(),
|
||||
|
|
@ -972,15 +980,21 @@ impl Machine {
|
|||
pub(crate) fn enter(&mut self, closure: Gc<Closure>) -> Result<(), RunError> {
|
||||
let mut needle = Needle {
|
||||
frames: vec![Frame {
|
||||
vararg: 1, // Both 1 since there is a single NIL on the stack
|
||||
offset: 1,
|
||||
vararg: 0,
|
||||
offset: 0,
|
||||
counter: 0,
|
||||
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)
|
||||
}
|
||||
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> {
|
||||
let mut allocator = Allocator::new();
|
||||
let mut machine = Machine {
|
||||
|
|
@ -988,10 +1002,9 @@ impl Machine {
|
|||
allocator,
|
||||
cache: Map::new(),
|
||||
};
|
||||
let string = machine.new_string("print").unwrap();
|
||||
machine.global.borrow_mut().set(
|
||||
string,
|
||||
Value::Function(Callable::Rust(Rc::from(Native(
|
||||
machine.register_global(
|
||||
"print",
|
||||
Native(
|
||||
|machine: &mut Machine,
|
||||
needle: &mut Needle,
|
||||
index: usize|
|
||||
|
|
@ -1002,7 +1015,22 @@ impl Machine {
|
|||
println!();
|
||||
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)
|
||||
}
|
||||
|
|
@ -1010,3 +1038,8 @@ impl Machine {
|
|||
|
||||
#[derive(Debug, Clone)]
|
||||
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