Fixed loop instructions, local registers in other instructions, upvalues and tables. Builds and passes test.

This commit is contained in:
paladin 2026-08-29 13:33:48 +01:00
parent 110e035efd
commit 60c097518a
9 changed files with 229 additions and 151 deletions

1
.gitignore vendored
View file

@ -1,2 +1,3 @@
.idea .idea
target target
Cargo.lock

25
Cargo.lock generated
View file

@ -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",
]

View file

@ -11,3 +11,4 @@ assertions = []
[dependencies] [dependencies]
glam = { version = "0.33.3", optional = true } glam = { version = "0.33.3", optional = true }
colored = { version = "3.1.1" }

View file

@ -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( _ => {
compile_declaration(
traversal, traversal,
expression, expression,
names_iter.next().unwrap(), names_iter.next().unwrap(),
)?, )?;
depth += 1;
} }
} }
_ => compile_declaration( }
_ => {
compile_declaration(
traversal, traversal,
expression, expression,
names_iter.next().unwrap(), names_iter.next().unwrap(),
)?, )?;
depth += 1;
}
} }
break; break;
} }
(Some(expression), false) => compile_declaration( (Some(expression), false) => {
compile_declaration(
traversal, traversal,
expression, expression,
names_iter.next().unwrap(), names_iter.next().unwrap(),
)?, // expression is not last, just use it )?;
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;

View file

@ -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),
_ => {} _ => {}
}; };
} }

View file

@ -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];
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() { for line in reader.lines() {
// todo: improve this index = line.as_ptr().addr() - reader.as_ptr().addr();
count += 1; if index < begin {
if line.len() >= begin { line_num += 1;
begin = 0;
roll = roll.add(format!("\n{}\t", count).as_str());
roll = roll.add(line);
} else {
begin -= line.len();
} }
if line.len() > end { if (index..index + line.len()).contains(&begin) {
break; col_num = begin - index;
} else {
end -= line.len()
} }
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 { 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>()?;

View file

@ -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);
} }

View file

@ -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
View file

@ -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)
}
}