commit 5d4fac0a9165700c75e2524dd3a0f528a4048ebb Author: paladin Date: Wed Aug 19 23:28:35 2026 +0100 Added parser, compiler, virtual machine, garbage collector, tables and values. Builds but may have errors. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ec376bb --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.idea +target \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..c2dca4f --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,16 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "glam" +version = "0.33.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7360bd2cd76e0cd9032d42cf2922155cecea2685b0cfa4630c3246df030bcfd6" + +[[package]] +name = "mars" +version = "0.1.0" +dependencies = [ + "glam", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..a734020 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "mars" +version = "0.1.0" +edition = "2024" + +[features] +vector3 = ["dep:glam"] +messages = [] +assertions = [] + +[dependencies] +glam = { version = "0.33.3", optional = true } \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/src/gc.rs b/src/gc.rs new file mode 100644 index 0000000..9ef4159 --- /dev/null +++ b/src/gc.rs @@ -0,0 +1,139 @@ +use std::alloc::{alloc, dealloc, Layout}; +use std::any::Any; +use std::cell::{Ref, RefCell, RefMut}; +use std::fmt::Debug; +use std::ops::{Deref, DerefMut}; +use std::ptr::NonNull; +// A simple 'stop the world' 'mark and sweep' garbage collector +// This approach is used since game engine frames provide a nice 'resting' interval for the interpreter to clean up faster +// todo! generational and naive reference counted collection +/* naive reference counting could be done as: +keep a traditional reference count => + - field count: u8 (>256 references -> a cycle will prevent premature deallocation anyway) +if the count reaches zero, prematurely deallocate by using an optional soft 'drop' method in the VM => + - field location: usize (location of this pointer in the big 'objects' array of the allocator) + - drop(&self,allocator) { + if count == 0 { + allocator[self.pointer->location] = nullptr; // make allocator forget this + dealloc(self.pointer); // deallocate now + } + } +in the allocator ensure location is consistent + + */ + +#[derive(Debug)] +pub struct Gc { + it: NonNull>> +} + +pub struct Agc {} + +impl Clone for Gc { + fn clone(&self) -> Self { + Gc { it: self.it } + } +} + +impl Traverse for Gc { + fn traverse(&self) { + self.borrow().traverse() + } +} + +impl PartialEq for Gc { + fn eq(&self, other: &Gc) -> bool { + self.it == other.it + } +} + +impl Gc { + pub fn borrow(&self) -> impl Deref { + Ref::map(unsafe { // ??? + (*self.it.as_ptr()).borrow() + },|it| &it.data) + } + pub fn borrow_mut(&mut self) -> impl DerefMut { + RefMut::map(unsafe { + (*self.it.as_mut()).borrow_mut() + },|it| &mut it.data) + } + + pub fn replace(&mut self, new: T) where T: Sized { + unsafe { + (*self.it.as_mut()).borrow_mut().data = new; + } + } + + pub fn addr(&self) -> usize { + self.it.as_ptr().addr() + } +} + +#[derive(Clone, Debug, Copy)] +enum Color { + White, + Black, +} + +struct Header { + layout: Layout, + color: Color, // NOT for incremental tri-color right now; white == mark-free, black == mark-keep + data: T +} + +pub struct Allocator { + // An array of pointers is used here since I did not want to use a compactor and larger types + // held on the GC (game objects) will most likely be massive anyway. + objects: Vec>>>, +} + +impl Allocator { + pub fn new() -> Allocator { + Allocator { + objects: Vec::new(), + } + } + pub fn alloc(&mut self, it: T) -> Gc { + let layout = Layout::new::>>(); + let pointer = unsafe { alloc(layout) as *mut RefCell> }; // todo: maybe use a compacting GC + assert!(!pointer.is_null()); // lol idk how to fix this rn + let mut header = unsafe { pointer.as_mut() }.unwrap().borrow_mut(); + header.layout = layout; + header.color = Color::Black; + header.data = it; + self.objects.push(Some(pointer as *mut RefCell>)); + Gc { + it: NonNull::new(pointer).unwrap(), + } + } + fn mark_white(&mut self) { + for i in 0..self.objects.len() { + if let Some(object) = self.objects[i] { + unsafe { (*object).borrow_mut().color = Color::White }; + } + } + } + fn collect(&mut self) { + self.objects.retain(|object| { + if let Some(object) = object { + let header = unsafe { (**object).borrow_mut() }; + match header.color { + Color::White => { + unsafe { dealloc(*object as *mut u8,header.layout); } + true + } + Color::Black => false + } + } else { + false + } + }); + } +} + +pub trait Traverse: Any + Debug { + fn traverse(&self) { // mark or grey + () + } +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..6c2bbd8 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,2675 @@ +#![warn(clippy::cast_lossless)] + +// PARSER + +pub mod gc; +mod table; + +use std::fmt::{Formatter}; +use std::ops::{Add, Deref, DerefMut}; +// TODO: Vec> 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)] +struct Reader<'a> { // 'cursor' states that follow through parsed source + slice: &'a str, + full: &'a str, + white: bool, // Is the previous 'thing' whitespace? +} + +pub struct LoadError(String, Piece<()>); // Any error returned by a machine loading code + +impl LoadError { + fn new(message: &str, reader: &Reader) -> Result { + Err(LoadError(message.to_string(), reader.marker())) + } + fn from(message: &str, piece: &Piece) -> LoadError { + LoadError(message.to_string(), piece.unit()) + } + fn msg(&self, reader: &Reader) -> String { + self.0.clone().add(reader.slice) + } +} + +impl std::fmt::Debug for LoadError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.write_fmt(format_args!("Error: {} {}", self.0, self.1.index)) + } +} + + +type Comment = String; + +#[derive(Clone)] +struct Piece { // A wrapper of T providing a reference to source code, used in errors + comment: Comment, + index: usize, + width: usize, + it: T, +} + +impl std::fmt::Debug for Piece where T: std::fmt::Debug { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + self.it.fmt(f) + } +} + + +impl Piece { + fn of(it: T, reader: &Reader, width: usize) -> Piece { + Piece { + it, + index: reader.slice.as_ptr() as usize - reader.full.as_ptr() as usize, + width, + comment: String::new() + } + } + fn from(it: T, begin: &Piece, end: &Piece) -> Piece { + Piece { + it, + index: begin.index, + width: end.index + end.width - begin.index, + comment: begin.comment.clone() + } + } + fn swap(&self, it: A) -> Piece { + Piece { + it, + index: self.index, + width: self.width, + comment: self.comment.clone() + } + } + fn unit(&self) -> Piece<()> { + Piece { + it: (), + index: self.index, + width: self.width, + comment: self.comment.clone() + } + } + fn null() -> Piece<()> { + Piece { + it: (), + index: 0, + width: 0, + comment: String::new() + } + } + fn map(self, f: F) -> Piece where F: FnOnce(T) -> U { + let it = f(self.it); + Piece { + it, + index: self.index, + width: self.width, + comment: self.comment // this is stupid + } + } + fn new(it: T) -> Piece { + Piece { + it, + index: 0, + width: 0, + comment: String::new() + } + } + fn end(self) -> Piece { + Piece { + it: self.it, + index: self.index + self.width, + width: 0, + comment: self.comment, + } + } + fn start(self) -> Piece { + Piece { + it: self.it, + index: self.index, + width: 0, + comment: self.comment, + } + } + fn inner_ref(&self) -> Piece<&T> { + self.unit().swap(&self.it) + } // TODO: .as_ref() +} + +impl<'a> From<&'a str> for Reader<'a> { + fn from(str: &'a str) -> Self { + Reader { + full: str, + slice: str, + white: true, + } + } +} + +impl<'a> Reader<'a> { + fn trim(&mut self) -> Result { + let start = self.slice.as_ptr() as usize; + self.slice = self.slice.trim_start(); + if self.slice.starts_with("--") { + let comment = if self.slice[2..].starts_with("[[") { + match self.slice.find("]]") { + Some(i) => { + let comment = &self.slice[4..i + 2]; + self.slice = &self.slice[i + 2..].trim(); + self.white = true; + Ok(comment) + } + _ => self.error("Unterminated multi-line comment"), + } + } else { + let comment; + if let Some(i) = self.slice.find("\n") { + self.slice = &self.slice[i + 1..].trim(); + comment = &self.slice[2..i+1]; + self.white = true; + } else { + self.slice = &self.slice[..self.slice.len() - 1]; + comment = &self.slice[2..]; + self.white = false; + } + Ok(comment) + }?; + self.trim().map(|it| { // Try another comment and append them to each other, just in case + it + "\n" + comment + }) + } else { + if start < self.slice.as_ptr() as usize { + self.white = true; + } + Ok(String::new()) + } + } + fn white(&mut self) -> Result<&mut Self, LoadError> { + if self.white { + Ok(self) + } else { + self.error("Expected whitespace") + } + } + fn consume(&mut self, start: &str) -> Result<&mut Self, LoadError> { + self.trim()?; + if self.slice.starts_with(start) { + self.slice = &self.slice[start.len()..]; + Ok(self) + } else { + self.error(format!("Expected '{}'", start).as_str()) + } + } + // Consume but require whitespace before + fn white_consume(&mut self, start: &str) -> Result<&mut Self, LoadError> { + if self.white { + self.consume(start) + } else { + self.error(format!("Expected whitespace before parsing {}",start).as_str()) + } + } + // Consume but set whitespace after + fn consume_white(&mut self, start: &str) -> Result<&mut Self, LoadError> { + let result = self.consume(start)?; + result.white = true; + Ok(result) + } + fn take(&mut self) -> Result, LoadError> + where + T: Parse, + { + T::consume(self) + } + fn finished(&self) -> bool { + self.slice.is_empty() + } + fn error(&self, message: &str) -> Result { + LoadError::new(message, self) + } + fn marker(&self) -> Piece<()> { + Piece::of((), self, 0) + } +} + +#[derive(Debug)] +struct Number(String); +#[derive(Debug, Clone)] +struct Name(String); +// Requirements for Parse: +// - The reader.slice must be pushed to the end of the consumed text (or restored in an error) +// - ??? +// - The reader may have not been trimmed +trait Parse { + fn consume(reader: &mut Reader) -> Result, LoadError> + where + Self: Sized; +} + +static KEYWORDS: &[&str] = &["and","break","do","else","elseif","end","false","for","function","func","global","goto","if","in","local","nil","not","or","repeat","return","then","true","until","while"]; +impl Parse for Name { + fn consume(reader: &mut Reader) -> Result, LoadError> + where + Self: Sized, + { + reader.trim()?; + reader.white()?; + let mut chars = reader.slice.chars(); + let mut end = 1; + match chars.next() { + None => { + return reader.error("Expected identifier"); + } + Some(c) => { + if !(c.is_alphabetic() || c == '_') { + return reader.error("Identifier must begin with alphabetic character"); + } + } + }; + end += chars + .take_while(|c| c.is_alphanumeric() || *c == '_') + .count(); + let name = reader.slice[..end].to_string(); + if KEYWORDS.contains(&name.as_str()) { + return reader.error(format!("Expected name, got keyword {}",name).as_str()) + } + let result = Ok(Piece::of( + Name(name), + reader, + end, + )); + reader.slice = &reader.slice[end..]; + result + } +} +impl Parse for Number { + fn consume(reader: &mut Reader) -> Result, LoadError> + where + Self: Sized, + { + reader.trim()?; + reader.white()?; + if !reader.white { + return reader.error("Expected whitespace before parsing number"); + } + let mut end; + if reader.slice.starts_with("0x") { + end = reader.slice[2..] + .chars() + .take_while(|c| c.is_digit(16)) + .count() + + 2; + if end == 2 { + end = 0 + } + } else { + end = reader + .slice + .chars() + .take_while(|c| c.is_digit(10) || *c == '.') + .count(); + } + if end == 0 { + reader.error("Could not parse number".into()) + } else { + reader.slice = &reader.slice[end..]; + Ok(Piece::of( + Number(reader.slice[..end].to_string()), + reader, + end, + )) + } + } +} + +impl Parse for String { + fn consume(reader: &mut Reader) -> Result, LoadError> + where + Self: Sized, + { + reader.trim()?; + let chars: &mut std::str::Chars = &mut reader.slice.chars(); + let mut string = String::new(); + let term1: char; + let term2: Option; + let extra; + match chars.next() { + Some('"') => { + term1 = '"'; + term2 = None; + extra = 2; + } + Some('\'') => { + term1 = '\''; + term2 = None; + extra = 2; + } + Some('[') if chars.next().is_some_and(|it| it == '[') => { + term1 = ']'; + term2 = Some(']'); + extra = 4; + } + _ => { + return reader.error("Could not parse string"); + } + } + loop { + match chars.next() { + Some(c) => { + match c { + '\\' => match chars.next() { + Some(c) => match c { + 'n' => string.push('\n'), + 'r' => string.push('\r'), + 't' => string.push('\t'), + '"' => string.push('"'), + '\'' => string.push('\''), + '\\' => string.push('\\'), + it => { + string.push(it); + } + }, + None => {} + }, + '\r' => {} // todo: ? + '\n' if term2.is_none() => { + return reader.error("Found newline while parsing string"); + } + term if term == term1 => { + // If the second terminator exists and is the next char, or there is no second terminator + if !term2.is_some() + || chars.next().is_some_and(|it| it == term2.unwrap()) + { + let length = string.len(); + reader.slice = &reader.slice[length + extra..]; + return Ok(Piece::of(string, reader, length)); + // There is a second terminator and it wasn't the next char + } else { + string.push(term) + } + } + _ => { + string.push(c); + } + } + } + None => { + return reader.error("Found EOF while parsing string"); + } + } + } + } +} + +// AST +#[derive(Debug)] +struct Block(Vec>, Option>>); // +#[derive(Debug)] +struct Chunk(Piece); +#[derive(Debug)] +enum Statement { + Semicolon, + Assignment(Vec>, Vec>), + Call(Call), + Label(Piece), + Break, + Continue, + Goto(Piece), + Do(Block), + While(Piece, Block), + Repeat(Block, Piece), + If(Vec>, Vec>, Option>), + Range( + Piece, + Piece, + Piece, + Option>, + Piece, + ), + Iterator(Vec>, Vec>, Piece), + // TODO: Maybe make this work on just Piece,Option> maybe + Function(Vec>, Option>, Function), + LocalFunction(Piece, Function), + Declaration(Vec>, Vec>), +} +#[derive(Debug)] +enum Variable { + Name(Name), + Index(Box>, Piece), + Register(Index), // todo: this is kind of bad because the parser and the compiler should be separate. a wrapper could be used instead! +} +#[derive(Debug)] +enum Expression { + Nil, + True, + False, + Number(Number), + String(String), + VarArg(VarArg), + Function(Function), + Prefix(Box), + Table(Constructor), + Binary(Box>, Piece, Box>), + Unary(Piece, Box>), +} +#[derive(Debug)] +struct Constructor(Vec>); +#[derive(Debug)] +enum Prefix { + Variable(Variable), + Call(Call), + Expression(Expression), +} +#[derive(Debug,Clone)] +struct VarArg(Option); +#[derive(Debug)] +struct Call(Box>, Option>, Piece); +#[derive(Debug)] +struct Args(Vec>); +#[derive(Debug)] +struct Function(Vec>, Option>, Piece); +#[derive(Debug)] +struct Field(Option>, Piece); +#[derive(Debug)] +enum FieldSep { + Comma, + Semicolon, +} +#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Clone, Copy)] +enum BinaryOp { + Or, + And, + Less, + Greater, + LessEqual, + GreatEqual, + Equivalent, + NotEqual, + Pipe, + Tilde, + Ampersand, + ShiftLeft, + ShiftRight, + Concat, + Plus, + Minus, + Mul, + Divide, + DivFloor, + Modulo, + Caret, +} +#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Clone, Copy)] +enum UnaryOp { + Negate, + Not, + Ampersand, + Tilde +} + +impl Parse for Vec> { + fn consume(reader: &mut Reader) -> Result>>, LoadError> + where + Self: Sized, + { + let mut result: Vec> = Vec::new(); + reader.trim()?; + result.push(reader.take::()?); + while reader.consume_white(",").is_ok() { + result.push(reader.take::()?); + } + let first = result.first().unwrap().unit(); + let last = result.last().unwrap().unit(); + Ok(Piece::from(result, &first, &last)) + } +} + +impl Parse for BinaryOp { + fn consume(reader: &mut Reader) -> Result, LoadError> { + reader.trim()?; + let mut chars = reader.slice.chars(); + if let Some(char) = chars.next() { + let mut count = 1; + let op = match char { + '+' => BinaryOp::Plus, + '-' => BinaryOp::Minus, + '*' => BinaryOp::Mul, + '/' => { + if let Some('/') = chars.next() { + count += 1; + BinaryOp::DivFloor + } else { + BinaryOp::Divide + } + }, + '^' => BinaryOp::Caret, + '%' => BinaryOp::Modulo, + '&' => BinaryOp::Ampersand, + '~' => { + match chars.next() { + Some('=') => { + count += 1; + BinaryOp::NotEqual + }, + _ => { + BinaryOp::Tilde + } + } + }, + '|' => BinaryOp::Pipe, + '>' => { + match chars.next() { + Some('>') => { + count += 1; + BinaryOp::ShiftRight + }, + Some('=') => { + count += 1; + BinaryOp::GreatEqual + } + _ => { + BinaryOp::Greater + } + } + }, + '<' => { + match chars.next() { + Some('<') => { + count += 1; + BinaryOp::ShiftLeft + }, + Some('=') => { + count += 1; + BinaryOp::LessEqual + } + _ => { + BinaryOp::Less + } + } + }, + '.' => { + if let Some('.') = chars.next() { + count += 1; + BinaryOp::Concat + } else { + return reader.error("Expected '.' for concatenation") + } + }, + '=' => { + if let Some('=') = chars.next() { + count += 1; + BinaryOp::Equivalent + } else { + return reader.error("Expected '=' for equality") + } + }, + _ => { + if reader.white_consume("and").is_ok() { + count+=2; + BinaryOp::And + } else if reader.white_consume("or").is_ok() { + count+=1; + BinaryOp::Or + } else { + return reader.error("Expected binary operation") + } + } + }; + if !matches!(op,BinaryOp::And | BinaryOp::Or) { + reader.white = true; + } + let result = Ok(Piece::of(op,reader,count)); + reader.slice = &reader.slice[count..]; + result + } else { + reader.error("Expected binary operand, got EOF") + } + } +} + +impl Parse for UnaryOp { + fn consume(reader: &mut Reader) -> Result, LoadError> { + let start = &reader.marker(); + reader.trim()?; + let op = if reader.consume("-").is_ok() { + UnaryOp::Negate + } else if reader.white_consume("not").is_ok() { + UnaryOp::Not + } else if reader.consume("#").is_ok() { + UnaryOp::Ampersand + } else if reader.consume("~").is_ok() { + UnaryOp::Tilde + } else { + return reader.error("Expected unary operand") + }; + if !matches!(op,UnaryOp::Not) { + reader.white = true; + } + Ok(Piece::from(op, &start, &reader.marker())) + } +} + +impl Parse for Function { + fn consume(reader: &mut Reader) -> Result, LoadError> { + reader.trim()?; + let start = reader.marker(); + reader.consume_white("(")?; + let mut pre_parameters: Vec> = Vec::new(); + let mut vararg = None; + loop { + if let Ok(name) = reader.take::() { + if vararg.is_some() { + return reader.error("Unexpected extra argument") + } else { + pre_parameters.push(name); + } + } else if let Ok(new_vararg) = reader.take::() { + if vararg.is_some() { + return reader.error("Unexpected extra vararg") + } else { + vararg = Some(new_vararg); + } + } else { + break + } + if !reader.consume_white(",").is_ok() { // rust can we has `repeat` pls + break; + } + } + reader.consume_white(")")?; + let end = reader.marker(); + let block = reader.take::()?; + reader.white_consume("end")?; + Ok(Piece::from( + Function(pre_parameters, vararg, block), + &start, + &end, + )) + } +} + +impl Parse for VarArg { + fn consume(reader: &mut Reader) -> Result, LoadError> { + reader.trim()?; + let start = reader.marker(); + reader.white()?.consume_white("...")?; + let name = reader.take::().ok(); + let end; + if let Some(name) = name { + let end = name.unit(); + Ok(Piece::from(VarArg(Some(name.it)), &start, &end)) + } else { + end = reader.marker(); + Ok(Piece::from( + VarArg(name.map(|piece| piece.it)), + &start, + &end, + )) + } + } +} + +impl BinaryOp { + fn compare(&self, other: &BinaryOp) -> bool { + self < other + } + fn compare_unary(&self) -> bool { + match self { + BinaryOp::Caret => true, + _ => false, + } + } +} + +fn inject(current: Piece, op: Piece, next: Piece) -> Piece { + let start = current.unit(); + if let Expression::Binary(left,op2,right) = current.it { + if op.it.compare(&op2.it) { + let start = left.unit(); + let new_right = Box::new(inject(*right, op, next)); + let end = new_right.unit(); + Piece::from(Expression::Binary(left,op2,new_right),&start,&end) + } else { + let start2 = start.unit(); + let end = next.unit(); + Piece::from(Expression::Binary(Box::new(start.map(|_| Expression::Binary(left,op2,right))),op,Box::new(next)),&start2,&end) + } + } else if let Expression::Unary(op2,inner) = current.it { + if op.it.compare_unary() { + let start = op2.unit(); + let inner = Box::new(inject(*inner, op, next)); + let end = inner.unit(); + Piece::from(Expression::Unary(op2,inner),&start,&end) + } else { + let start2 = start.unit(); + let end = next.unit(); + Piece::from(Expression::Binary(Box::new(start.map(|_| Expression::Unary(op2,inner))),op,Box::new(next)),&start2,&end) + } + } else { + let start = current.unit(); + let end = next.unit(); + Piece::from(Expression::Binary(Box::new(current),op,Box::new(next)),&start,&end) + } +} + +impl Parse for Expression { + fn consume(reader: &mut Reader) -> Result, LoadError> { + let taker = |reader: &mut Reader| { + let start = reader.marker(); + reader.trim()?; + let result = if reader.white_consume("nil").is_ok() { + Expression::Nil + } else if reader.white_consume("true").is_ok() { + Expression::True + } else if reader.white_consume("false").is_ok() { + Expression::False + } else if let Ok(vararg) = reader.take::() { + Expression::VarArg(vararg.it) + } else if let Ok(number) = reader.take::() { + Expression::Number(number.it) + } else if let Ok(string) = reader.take::() { + Expression::String(string.it) + } else if reader.white_consume("function").is_ok() || reader.white_consume("func").is_ok() { + Expression::Function(reader.take::()?.it) + } else if let Ok(table) = reader.take::() { + Expression::Table(table.it) + } else if let Ok(unary) = reader.take::() { + Expression::Unary(unary,Box::new(reader.take::()?)) + } else { + Expression::Prefix(Box::new(reader.take::()?.it)) + }; + Ok(Piece::from(result,&start,&reader.marker())) + }; + let mut current = taker(reader)?; + while let Ok(binary) = reader.take::() { + let next = taker(reader)?; + current = inject(current,binary,next); + } + Ok(current) + } +} + +impl Parse for Call { + fn consume(reader: &mut Reader) -> Result, LoadError> { + reader.trim()?; + let prefix = reader.take::()?; + let piece = prefix.unit(); + if let Prefix::Call(call) = prefix.it { + Ok(piece.map(|_| call)) + } else { + reader.error("Expected variable, found expression.") + } + } +} + +impl Parse for Field { + fn consume(reader: &mut Reader) -> Result, LoadError> { + let start = reader.marker(); + reader.trim()?; + let result = if reader.consume_white("[").is_ok() { + let index = reader.take::()?; + reader.consume_white("=")?; + let value = reader.take::()?; + reader.consume_white("]")?; + Field(Some(index),value) + } else if let Ok(name) = reader.take::() { + reader.consume_white("=")?; + let index = name.map(|it| Expression::String(it.0)); + let value = reader.take::()?; + Field(Some(index),value) + } else { + Field(None,reader.take::()?) + }; + Ok(Piece::from(result, &start, &reader.marker())) + } +} + +impl Parse for Constructor { + fn consume(reader: &mut Reader) -> Result, LoadError> { + reader.trim()?; + reader.consume_white("{")?; + let fields: Piece>>; + if reader.clone().take::().is_ok() { + fields = reader.take::>>()?; + } else { + fields = Piece::from(Vec::new(),&reader.marker(),&reader.marker()); + } + reader.consume_white("}")?; + Ok(fields.map(|it| Constructor(it))) + } +} + +impl Parse for Args { + fn consume(reader: &mut Reader) -> Result, LoadError> { + reader.trim()?; + let start = reader.marker(); + let result = if reader.consume_white("(").is_ok() { + let expressions = reader.take::>>()?; + reader.consume_white(")")?; + Args(expressions.it) + } else if let Ok(table) = reader.take::() { + Args(vec!(table.map(|it| Expression::Table(it)))) + } else if let Ok(string) = reader.take::() { + Args(vec!(string.map(|it| Expression::String(it)))) + } else { + return reader.error("Expected arguments") + }; + Ok(Piece::from(result, &start, &reader.marker())) + } +} + +impl Parse for Prefix { + fn consume(reader: &mut Reader) -> Result, LoadError> { + reader.trim()?; + let start = reader.marker(); + let mut current: Box> = if reader.consume_white("(").is_ok() { + let exp = reader.take::()?; + reader.consume_white(")")?; + Box::new(Piece::from(Prefix::Expression(exp.it),&start,&reader.marker())) + } else if let Ok(name) = reader.take::() { + Box::new(Piece::from(Prefix::Variable(Variable::Name(name.it)), &start, &reader.marker())) + } else { + return reader.error("Expected expression") + }; + loop { + let start = reader.marker(); + let result: Prefix = if reader.consume_white("[").is_ok() { + let exp = reader.take::()?; + reader.consume_white("]")?; + Prefix::Variable(Variable::Index(current, exp)) + } else if reader.consume_white(".").is_ok() { + let field = reader.take::()?; + Prefix::Variable(Variable::Index(current, field.map(|it| Expression::String(it.0)))) + } else if reader.consume_white(":").is_ok() { + let field = reader.take::()?; + let args = reader.take::()?; + Prefix::Call(Call(current,Some(field),args)) + } else if let Ok(args) = reader.take::() { + Prefix::Call(Call(current,None,args)) + } else { + break + }; + current = Box::new(Piece::from(*Box::new(result), &start, &reader.marker())); + } + Ok(*current) + } +} + +impl Parse for Variable { + fn consume(reader: &mut Reader) -> Result, LoadError> { + reader.trim()?; + let prefix = reader.take::()?; + let piece = prefix.unit(); + if let Prefix::Variable(var) = prefix.it { + Ok(piece.map(|_| var)) + } else { + reader.error("Expected variable, found expression.") + } + } +} + +impl Parse for Statement { + fn consume(reader: &mut Reader) -> Result, LoadError> { + reader.trim()?; + let start = reader.marker(); + // todo: it is safer to pass back the new reader in the Ok, and then map it out (in fact, something else changed too) + if reader.white_consume("break").is_ok() { + Ok(Piece::from(Statement::Break, &start, &reader.marker())) + } else if reader.white_consume("continue").is_ok() { + Ok(Piece::from(Statement::Continue, &start, &reader.marker())) + } else if reader.consume_white(";").is_ok() { + Ok(Piece::from(Statement::Semicolon, &start, &reader.marker())) + } else if reader.white_consume("local").is_ok() { + if reader.white_consume("function").is_ok() || + reader.white_consume("func").is_ok() { + let name = reader.take::()?; + let function = reader.take::()?; + *reader = *reader; + Ok(Piece::from( + Statement::LocalFunction(name, function.it), + &start, + &reader.marker(), + )) + } else { + let names = reader.take::>>()?.it; + reader.consume_white("=")?; + let expressions = reader.take::>>()?.it; + *reader = *reader; + Ok(Piece::from( + Statement::Declaration(names, expressions), + &start, + &reader.marker(), + )) + } + } else if reader.white_consume("function").is_ok() || + reader.white_consume("func").is_ok() { + let mut names = Vec::new(); + loop { + if let Ok(name) = reader.take::() { + names.push(name); + if !reader.consume_white(".").is_ok() { + break; + } + } + } + let mut member = None; + if reader.consume_white(":").is_ok() { + member = Some(reader.take::()?); + } + let function = reader.take::()?; + *reader = *reader; + Ok(Piece::from( + Statement::Function(names, member, function.it), + &start, + &reader.marker(), + )) + } else if reader.white_consume("for").is_ok() { + let names = reader.take::>>()?.it; + let result = if names.len() < 1 { + let name = names.first().unwrap(); + reader.consume_white("=")?; + let begin = reader.take::()?; + reader.consume_white(",")?; + let finish = reader.take::()?; + let delta = if reader.consume(",").is_ok() { + Some(reader.take::()?) + } else { + None + }; + reader.white_consume("do")?; + let block = reader.take::()?; + reader.white_consume("end")?; + Ok(Piece::from( + Statement::Range(Piece { + it: name.it.clone(), + width: name.width, + index: name.index, + comment: name.comment.clone(), + }, begin, finish, delta, block), + &start, + &reader.marker(), + )) + } else { + reader.white_consume("in")?; + let expressions = reader.take::>>()?.it; + reader.white_consume("do")?; + let block = reader.take::()?; + reader.white_consume("end")?; + Ok(Piece::from( + Statement::Iterator(names, expressions, block), + &start, + &reader.marker(), + )) + }; + *reader = *reader; + result + } else if reader.white_consume("while").is_ok() { + let condition = reader.take::()?; + reader.white_consume("do")?; + let block = reader.take::()?; + reader.white_consume("end")?; + *reader = *reader; + Ok(Piece::from( + Statement::While(condition, block.it), + &start, + &reader.marker(), + )) + } else if reader.white_consume("if").is_ok() { + let mut conditions = Vec::new(); + let mut blocks = Vec::new(); + let mut branch = |reader: &mut Reader| -> Result<(), LoadError> { + conditions.push(reader.take::()?); + reader.white_consume("then")?; + blocks.push(reader.take::()?); + Ok(()) + }; + branch(reader)?; + let mut block = None; + while reader.white_consume("else").is_ok() { + if reader.consume("if").is_ok() { + branch(reader)?; + } else { + block = Some(reader.take::()?); + break + } + } + reader.white_consume("end")?; + *reader = *reader; + Ok(Piece::from( + Statement::If(conditions, blocks, block), + &start, + &reader.marker(), + )) + } else if reader.white_consume("goto").is_ok() { + let name = reader.take::()?; + *reader = *reader; + Ok(Piece::from(Statement::Goto(name), &start, &reader.marker())) + } else if reader.white_consume("do").is_ok() { + let block = reader.take::()?; + *reader = *reader; + Ok(Piece::from( + Statement::Do(block.it), + &start, + &reader.marker(), + )) + } else if reader.white_consume("repeat").is_ok() { + reader.white_consume("repeat")?; + let block = reader.take::()?; + reader.white_consume("until")?; + let condition = reader.take::()?; + *reader = *reader; + Ok(Piece::from( + Statement::Repeat(block.it, condition), + &start, + &reader.marker(), + )) + } else if reader.consume_white("::").is_ok() { + let name = reader.take::()?; + reader.consume_white("::")?; + *reader = *reader; + Ok(Piece::from( + Statement::Label(name), + &start, + &reader.marker(), + )) + } else { + let mut clone = reader.clone(); + if let Ok(call) = clone.take::() { + *reader = clone; + Ok(call.map(|it| Statement::Call(it))) + } else { + let mut clone = reader.clone(); + let vars = clone.take::>>()?; + clone.consume("=")?; + let expressions = clone.take::>>()?; + *reader = clone; + Ok(Piece::from(Statement::Assignment(vars.it,expressions.it),&start,&reader.marker())) + } + } + } +} + +impl Parse for Block { + fn consume(reader: &mut Reader) -> Result, LoadError> + where + Self: Sized, + { + let mut statements = Vec::new(); + let start = reader.marker(); + while let Ok(statement) = reader.take::() { + statements.push(statement); + } + let expressions = if reader.consume("return").is_ok() { + if reader.clone().take::().is_ok() { // TODO: make this dirty check less silly + let result = reader.take::>>()?; + reader.consume_white(";").ok(); + Some(result.it) + } else { + Some(Vec::new()) + } + } else { + None + }; + Ok(Piece::from( + Block(statements, expressions), + &start, + &reader.marker(), + )) + } +} + +impl Parse for Chunk { + fn consume(reader: &mut Reader) -> Result, LoadError> + where + Self: Sized, + { + let mut statements = Vec::new(); + let start = reader.marker(); + while !reader.finished() { + let statement = reader.take::()?; + statements.push(statement); + reader.trim()?; + } + let expressions = if reader.white_consume("return").is_ok() { + let result = reader.take::>>()?; + reader.consume_white(";").ok(); + Some(result.it) + } else { + None + }; + Ok(Piece::from( + Chunk(Piece::from( + Block(statements, expressions), + &start, + &reader.marker(), + )), + &start, + &reader.marker(), + )) + } +} + +pub fn parse(str: &str) -> Result { + let mut reader = Reader::from(str); + reader.take::().map(|it| it.it) +} + +// COMPILER + +use std::any::Any; +use std::cmp::PartialEq; +use std::rc::Rc; +use crate::gc::*; + +struct Traversal { // A 'cursor' of where we are in assumed execution + contexts: Vec, // Stack of function prototypes being compiled +} + +struct Context { // Everything known about a closure at a time + code: Vec>, + scopes: Vec, + constants: Vec, + vararg: Option>>, + prototype: Prototype, +} + +#[derive(Clone)] +enum BlindJump { + Break(usize), + Continue(usize), + Goto(String,usize), +} + +struct Scope { + index: Index, // Current index on stack + jumps: Vec, // todo: probably shouldn't have two vectors on each scope, they are used for temporaries too + labels: Vec<(String,usize)>, + symbol: Option, // If that index has a symbol + upvalue: bool, +} + +impl Traversal { + fn compile(&mut self, it: &T) -> Result<(), LoadError> where T: Compile { + it.compile(self) + } + fn push_code(&mut self, it: Piece) -> usize { + match it.it { + Code::TableSet => { + self.consume_scope(3) // Value, Index, Table + } + Code::TableGet | Code::Binary(_) => { + self.consume_scope(2); // Index, Table + self.temp_scope(); // Result + } + Code::UpvalueGet(_) | Code::RegisterGet(_) | Code::Constant(_) | Code::Closure(_) => { + self.temp_scope(); + } + Code::Unary(_) => { + self.consume_scope(1); + self.temp_scope(); + } + Code::TableInsert(_) => { + self.consume_scope(2); // Value, Table + } + Code::Return(index) | Code::Call(index) => { + while self.last_scope().index > index { + self.consume_scope(1); // Pass Value + } + assert_eq!(self.last_scope().index, index); + /* So note that Code::Call here doesn't do any temp_scope() magic, this is an edge-case for + Code::Discard (if it's not used then Code::Call(index) will eat everything up to index + and this consumption loop here won't do anything anyway */ + } + Code::Discard(_index) => { + // Unfortunately can't do any checks on the temporary scopes here, we don't know anything + /* while self.last_scope().index > index { // Shouldn't this not even be here? + self.consume_scope(1); + } + while self.last_scope().index < index { + self.temp_scope(); + } */ + } + Code::VarArg(count) => { + for _ in 0..count { + self.temp_scope(); + } + /* This is also weird like Code::Call and Code::Discard in that for count == 0 any number + of vararg values are being pushed to the stack, but this is an edge-case that the compiler + promises it will only use in Code::Return and Code::Call where they won't loop anyway */ + } + Code::Skip | Code::UpvalueSet(_) | Code::RegisterSet(_) => { + self.consume_scope(1); + } + Code::Close(_) | Code::Jump(_) | Code::RangeLoop(_) | Code::RangeBegin | Code::IteratorLoop(_) => {} + } + self.contexts.last_mut().unwrap().code.push(it); + self.contexts.len() - 1 + } + fn insert_code(&mut self, location: usize, it: Code) { + let code = &mut self.last_context().code; + let piece = code[location].unit(); + code[location] = piece.swap(it); + } + fn push_constant(&mut self, it: Value) -> Index { + let it = match it { + Value::String(string) => { // todo! not great to construct a string then immediately destruct it + string.into() + }, + other => other, + }; + let constants = &mut self.contexts.last_mut().unwrap().constants; + let index; + if let Some(position) = constants.iter().position(|found| it.eq(found)) { + index = position; + } else { + index = constants.len(); + } + constants.push(it); + index as Index + } + fn push_literal(&mut self, it: Value, piece: Piece<()>) { + let constant = self.push_constant(it); + self.push_code(piece.swap(Code::Constant(constant))); + } + fn new_scope(&mut self) { + let context = &mut self.contexts.last_mut().unwrap(); + let scopes = &mut context.scopes; + let index = scopes.last().unwrap().index; + scopes.push(Scope { + index, + jumps: vec![], + labels: vec![], + symbol: None, + upvalue: false, + }); + } + fn temp_scope(&mut self) -> Index { + self.new_scope(); + let index = self.last_scope().index + 1; + self.last_scope().index = index; + index + } + fn last_scope(&mut self) -> &mut Scope { + self.last_context().scopes.last_mut().unwrap() + } + fn code_length(&mut self) -> usize { + self.last_context().code.len() + } + fn close_scope(&mut self) { // Handle exiting a variable register's scope + let mut last = self.last_context().scopes.pop().unwrap(); // Remove it + self.last_scope().jumps.append(&mut last.jumps); // Bubble up the orphaned blind jumps + // If this scope's declaration was an upvalue then the VM needs to close it at runtime + assert!(last.symbol.is_some()); + // Always have to pop off variable registers + if let Some(Piece {it: Code::Close(_), ..}) = self.last_context().code.last() { // Did we just close a scope? + let len = self.code_length(); // Ok, what's the last instruction index? + self.insert_code(len - 1,Code::Close(last.index)); // Push that Code::Close even further back to this register. + } + } + fn consume_scope(&mut self, times: u16) { // Handle consuming a temporary on the stack + for _ in 0..times { + assert!(self.last_context().scopes.pop().unwrap().symbol.is_none()); // Remove it + } + } + fn last_context(&mut self) -> &mut Context { + self.contexts.last_mut().unwrap() + } + fn close_context(&mut self) -> Result { + let context = self.contexts.pop().unwrap(); + Ok(context.prototype) + } +} + +// todo: not all impl Compile for Piece needs to be &T +trait Compile { + fn compile(&self, traversal: &mut Traversal) -> Result<(), LoadError>; +} + +fn compile_function(this: &Piece<&Function>, traversal: &mut Traversal, method: bool) -> Result<(), LoadError> { + let mut self_arguments = Vec::new(); + if method { + self_arguments.push("self".to_string()); + } + self_arguments.append(&mut this.it.0.iter().map(|it| it.it.0.clone()).collect()); + let mut argument_scopes = vec![Scope { // Dummy initial scope so the rest can start from index = 0 (since there can be zero arguments!!) + index: 0, + jumps: vec![], + labels: vec![], + symbol: None, + upvalue: false, + }]; + let mut index = 0; + for argument in self_arguments.iter().rev() { + argument_scopes.push(Scope { + index, + jumps: vec![], + labels: vec![], + symbol: Some(argument.clone()), + upvalue: false, + }); + index += 1; + } + traversal.contexts.push(Context { + vararg: this.it.1.as_ref().map(|it| it.swap(it.clone().it.0.map(|string| string.0.clone()))), + constants: vec![], + code: vec![], + scopes: argument_scopes, + prototype: Prototype { + args: this.it.0.len() as Index, + vararg: this.it.1.is_some(), + prototypes: vec![], + constants: vec![], + upvalues: vec![Reference::Upvalue(0)], // _ENV + pieces: vec![], + code: vec![], + }, + }); + traversal.compile(&this.it.2.inner_ref())?; + let prototype = traversal.close_context()?; + let prototypes = &mut traversal.last_context().prototype.prototypes; + let index = prototypes.len() as Index; + prototypes.push(Rc::new(prototype)); + traversal.push_code(this.swap(Code::Closure(index))); + Ok(()) +} + +enum Access { + Set, + Get, +} + +fn compile_identifier(name: Piece, traversal: &mut Traversal, access: Access) -> Result<(), LoadError> { + let length = traversal.contexts.len(); + 'search: for i in 0..length { // For each context + let i = length - i - 1; // From top to bottom + if i == length - 1 { // If this is our context + for scope in traversal.contexts[i].scopes.iter().rev() { // Go through the scopes + if scope.symbol.as_ref().is_some_and(|it| it.eq(&name.it)) { // Find it + let index = scope.index; + traversal.push_code(name.swap(match access { + Access::Set => Code::RegisterSet(index), + Access::Get => Code::RegisterGet(index), + })); + break 'search; // Then break out + } + } + } else { // Otherwise it's another function's 'scope'(s) + let length = traversal.contexts[i].scopes.len(); + for j in 0..length { // For each scope in that foreign context + let j = length - j - 1; // From top to bottom + if traversal.contexts[i].scopes[j].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 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 { // 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].prototype.upvalues.iter().position(|it| *it == reference) { + Some(index) => Reference::Upvalue(index as Index), + 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); + Reference::Upvalue(length as Index) // Reference (child) to this reference to that reference (parent) for next context + } + } + } + let index = match reference { + Reference::Closing(_) => panic!(), + Reference::Upvalue(index) => index, + }; + traversal.push_code(name.swap(match access { + Access::Set => Code::UpvalueSet(index), + Access::Get => Code::UpvalueGet(index), + })); + break 'search + } + } + // break 'search avoids this use of the _G table + traversal.push_literal(Value::String(name.it.clone()), name.unit()); + traversal.push_code(name.swap(Code::UpvalueGet(0))); + traversal.push_code(name.swap(match access { + Access::Set => Code::TableSet, + Access::Get => Code::TableGet, + })); + } + } + Ok(()) +} + +fn compile_variable(variable: Piece<&Variable>, traversal: &mut Traversal, access: Access) -> Result<(), LoadError> { + match &variable.it { + Variable::Name(Name(name)) => { + compile_identifier(variable.swap(name.clone()), traversal, access)?; + } + Variable::Index(prefix,index) => { // Compile the prefix expression, the index, and set + traversal.compile(&index.inner_ref())?; + traversal.compile(&prefix.as_ref().inner_ref())?; + traversal.push_code(prefix.unit().end().swap(match access { + Access::Set => Code::TableSet, + Access::Get => Code::TableGet, + })); + } + Variable::Register(index) => { + traversal.push_code(variable.swap(match access { + Access::Set => Code::RegisterSet(*index), + Access::Get => Code::RegisterGet(*index), + })); + } + } + Ok(()) +} + +impl Compile for Piece<&Function> { +fn compile(&self, traversal: &mut Traversal) -> Result<(), LoadError> { + compile_function(self,traversal,false) + } +} + +impl Compile for Piece<&Args> { + fn compile(&self, traversal: &mut Traversal) -> Result<(), LoadError> { + let mut expressions_iter = self.it.0.iter().rev(); + if let Some(expression) = expressions_iter.next() { + let piece = expression.unit(); + match &expression.it { + Expression::Prefix(prefix) => { + match &**prefix { + Prefix::Call(call) => { + traversal.compile(&piece.swap(call))?; + } + prefix => { + traversal.compile(&piece.swap(prefix))?; + } + } + }, + Expression::VarArg(vararg) => { + traversal.compile(&piece.swap(vararg))?; + traversal.push_code(piece.swap(Code::VarArg(0))); // Dump all the varargs + } + _ => { + traversal.compile(&expression.inner_ref())?; + } + } + } + while let Some(expression) = expressions_iter.next() { + traversal.compile(&expression.inner_ref())?; + }; + Ok(()) + } +} + +impl Compile for Piece<&Call> { + fn compile(&self, traversal: &mut Traversal) -> Result<(), LoadError> { + traversal.compile(&self.it.0.as_ref().inner_ref())?; + match &self.it.1 { + Some(name) => { + traversal.push_literal(Value::String(name.it.0.clone()), name.unit()); + traversal.push_code(name.swap(Code::TableGet)); + }, + None => {} + } + let index = traversal.last_scope().index; + traversal.compile(&self.it.2.inner_ref())?; // arguments + traversal.push_code(self.it.0.unit().end().swap(Code::Call(index))); // Our responsibility to discard here + Ok(()) + } +} + +impl Compile for Piece<&Prefix> { + fn compile<'a,'b>(&self, traversal: &mut Traversal) -> Result<(), LoadError> { + match &self.it { + Prefix::Call(call) => { + let index = traversal.last_scope().index; + traversal.compile(&self.swap(call))?; + traversal.push_code(self.swap(Code::Discard(index + 1)).end()); // produce 1 return value, or pad nil + }, + Prefix::Variable(variable) => { + compile_variable(self.swap(variable),traversal,Access::Get)?; + }, + Prefix::Expression(expression) => { + traversal.compile(&self.swap(expression))?; + } + } + Ok(()) + } +} + +// for the sake of erroring alone +impl Compile for Piece<&VarArg> { + fn compile<'a,'b>(&self, traversal: &mut Traversal) -> Result<(), LoadError> { + match &traversal.contexts.last().unwrap().vararg { + Some(piece) => { + match &piece.it { + Some(that_name) => { + match &self.it.0 { + Some(Name(this_name)) => { + if that_name.eq(this_name) { + Ok(()) + } else { + Err(LoadError(format!( + "This context's only visible vararg parameter '{:?}' is not named '{:?}'", + that_name,this_name + ), self.unit())) + } + } + None => { + Err(LoadError(format!( + "This context's only visible vararg parameter must be referred to by its name '{:?}'", + that_name + ), self.unit())) + } + } + } + None => { + match &self.it.0 { + Some(name) => { + Err(LoadError(format!( + "This context's only visible vararg parameter has no name '{:?}'", + name + ), self.unit())) + } + None => { + Ok(()) + } + } + }, + } + + }, + None => { + Err(LoadError(format!("This context has no accessible vararg parameter '{:?}'", self.it), self.unit())) + } + } + } +} + +impl Compile for Piece<&Constructor> { + fn compile<'a,'b>(&self, traversal: &mut Traversal) -> Result<(), LoadError> { + for field in self.it.0.iter() { + match &field.it.0 { + Some(index) => { + traversal.compile(&index.inner_ref())?; + traversal.push_code(field.it.1.swap(Code::TableSet)); + }, + None => { + match &field.it.1.it { + Expression::VarArg(vararg) => { + traversal.compile(&field.it.1.swap(vararg))?; + traversal.push_code(field.it.1.swap(Code::VarArg(0))); + }, + it => { + traversal.compile(&field.it.1.swap(it))?; + } + } + let index = traversal.contexts.last().unwrap().scopes.last().unwrap().index; + traversal.push_code(field.it.1.swap(Code::TableInsert(index))); + } + } + } + Ok(()) + } +} + +impl Compile for Piece<&Expression> { + fn compile<'a>(&self, traversal: &mut Traversal) -> Result<(), LoadError> { + match self.it { + Expression::Nil => { + traversal.push_literal(Value::Nil, self.unit()); + }, + Expression::True => { + traversal.push_literal(Value::Bool(true), self.unit()); + }, + Expression::False => { + traversal.push_literal(Value::Bool(false), self.unit()); + }, + Expression::Number(number) => { + let number = number.0.parse::().map_err(|it| LoadError(it.to_string(), self.unit()))?; + traversal.push_literal(Value::Number(number), self.unit()); + }, + Expression::String(string) => { + traversal.push_literal(Value::String(string.clone()), self.unit()); + }, + Expression::VarArg(vararg) => { + traversal.compile(&self.swap(vararg))?; + traversal.push_code(self.swap(Code::VarArg(1))); + }, + Expression::Function(function) => { + traversal.compile(&self.swap(function))?; + }, + Expression::Prefix(prefix) => { + traversal.compile(&self.swap(prefix.as_ref()))?; + }, + Expression::Table(constructor) => { + traversal.compile(&self.swap(constructor))?; + }, + Expression::Binary(left,op,right) => { + traversal.compile(&left.as_ref().inner_ref())?; + traversal.compile(&right.as_ref().inner_ref())?; + traversal.push_code(op.swap(Code::Binary(op.it))); + }, + Expression::Unary(op,expression) => { + traversal.compile(&expression.as_ref().inner_ref())?; + traversal.push_code(op.swap(Code::Unary(op.it))); + } + }; + Ok(()) + } +} + +fn jump_from(here: usize, there: usize) -> Result { + Ok(((there as i64 - here as i64) - 1) as Offset) +} + +fn resolve_break(traversal: &mut Traversal, begin: usize, end: usize) -> Result<(), LoadError> { + let mut jumps = Vec::new(); + for jump in traversal.last_scope().jumps.clone() { // ? + match jump { + BlindJump::Continue(index) => { + traversal.insert_code(index, Code::Jump(jump_from(index, begin)?)); + }, + BlindJump::Break(index) => { + traversal.insert_code(index, Code::Jump(jump_from(index, end)?)); + }, + _ => jumps.push(jump) + } + } + traversal.last_scope().jumps = jumps; + Ok(()) +} + +fn compile_assignment( + traversal: &mut Traversal, + variables: &Vec>, + expressions: &Vec> +) -> Result<(), LoadError> { + let mut variables_iter = variables.iter().peekable(); + let mut expressions_iter = expressions.iter().peekable(); + while let Some(expression) = expressions_iter.next() { + let compile_variable_expression = | // Compile a variable normally with one value from an expression + traversal: &mut Traversal, + variable: Option<&Piece>, + expression: &Piece<&Expression>, + | -> Result<(), LoadError> { + let index = traversal.last_scope().index; + traversal.compile(expression)?; + if let Some(variable) = variable { + compile_variable(variable.inner_ref(),traversal,Access::Set)?; + } else { + traversal.push_code(expression.swap(Code::Discard(index))); + } + Ok(()) + }; + if expressions_iter.peek().is_none() { // If this is the last expression it CAN have multiplicity + match &expression.it { + Expression::Prefix(prefix) => { + match &**prefix { + Prefix::Call(call) => { // multiple + let mut index = traversal.last_scope().index; + traversal.compile(&expression.swap(call))?; + let index_discard = traversal.push_code(expression.swap(Code::UNSURE)); + while let Some(variable) = variables_iter.next() { + index += 1; // The index we would need to pad nil to or restrict to + compile_variable(variable.inner_ref(),traversal,Access::Set)?; // Pops each return value off the stack + } + traversal.insert_code(index_discard,Code::Discard(index as Index)); + }, + prefix => { + if let Some(variable) = variables_iter.next() { + traversal.compile(&expression.swap(prefix))?; + compile_variable(variable.inner_ref(),traversal,Access::Set)? + } + } + } + }, + Expression::VarArg(vararg) => { + traversal.compile(&expression.swap(vararg))?; // Does nothing, error check + if variables_iter.peek().is_some() { + let mut count = 0; + let index_vararg = traversal.push_code(expression.swap(Code::VarArg(0))); + for variable in variables_iter.clone() { + count += 1; + compile_variable(variable.inner_ref(),traversal,Access::Set)?; + } + traversal.insert_code(index_vararg,Code::VarArg(count as Index)); + } + } + _ => { + compile_variable_expression(traversal,variables_iter.next(),&expression.inner_ref())?; + } + } + } else { // otherwise treat it normally and consume one variable + compile_variable_expression(traversal,variables_iter.next(),&expression.inner_ref())?; + } + } + while let Some(variable) = variables_iter.next() { // Set the rest of the variables to nil + traversal.push_literal(Value::Nil, variable.unit()); + compile_variable(variable.inner_ref(),traversal,Access::Set)?; + } + Ok(()) +} + +impl Compile for Piece<&Block> { + fn compile(&self, traversal: &mut Traversal) -> Result<(), LoadError> { + let last_scope_index = traversal.last_scope().index; + 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 "_index" and code indices "index_"? fix it + for statement in self.it.0.iter() { + let piece = statement.unit(); + match &statement.it { + Statement::Semicolon => {}, + Statement::Assignment(variables,expressions) => { + compile_assignment(traversal,variables,expressions)?; + }, + Statement::Call(call) => { + let index = traversal.last_scope().index; + traversal.compile(&self.swap(call))?; + traversal.push_code(self.swap(Code::Discard(index))); + }, + Statement::Label(label) => { + let index_label = traversal.code_length() - 1; + traversal.last_scope().labels.push((label.it.0.clone(),index_label)); + let mut jumps = Vec::new(); + for jump in traversal.last_scope().jumps.clone().iter().rev() { + match jump { + BlindJump::Goto(name,index) => { + if name.eq(&label.it.0) { + traversal.insert_code(*index,Code::Jump(jump_from(*index,index_label)?)) + } else { + jumps.push(jump); + } + } + _ => jumps.push(jump) + } + } + }, + Statement::Break => { + let index = traversal.push_code(piece.swap(Code::UNSURE)); + traversal.last_scope().jumps.push(BlindJump::Break(index)); + }, + Statement::Continue => { + let index = traversal.push_code(piece.swap(Code::UNSURE)); + traversal.last_scope().jumps.push(BlindJump::Continue(index)); + }, + Statement::Goto(name) => { + let mut index = None; + for scope in traversal.contexts.last().unwrap().scopes.iter().rev() { + for (label_name,label_index) in scope.labels.iter().rev() { + if name.it.0 == *label_name { + index = Some(label_index.clone()); + break; + } + } + } + if let Some(index) = index { + let current = (traversal.last_context().code.len() - 1) as Offset; + traversal.push_code(piece.swap(Code::Jump(index as Offset - current))); + } else { + let index = traversal.push_code(piece.swap(Code::UNSURE)); + traversal.last_scope().jumps.push(BlindJump::Goto(name.it.0.clone(),index)); + } + }, + Statement::Do(block) => { + traversal.compile(&self.swap(block))?; + }, + Statement::While(condition,block) => { + let piece = condition.unit().end(); + let index_exp = traversal.code_length(); + traversal.compile(&condition.inner_ref())?; + traversal.push_code(piece.swap(Code::Skip)); + let index_exit = traversal.push_code(piece.swap(Code::UNSURE)); + traversal.compile(&self.swap(block))?; + let index_jump = traversal.code_length(); + resolve_break(traversal,index_exp,index_jump + 1)?; + traversal.push_code(piece.swap(Code::Jump(jump_from(index_jump, index_exp)?))); + traversal.insert_code(index_exit,Code::Jump(jump_from(index_exit, index_jump + 1)?)); + }, + Statement::Repeat(block,condition) => { + let piece = condition.unit().end(); + let index_block = traversal.code_length(); + traversal.compile(&self.swap(block))?; + let index_exp = traversal.code_length(); + traversal.compile(&condition.inner_ref())?; + traversal.push_code(piece.swap(Code::Skip)); + let index_jump = traversal.code_length(); + resolve_break(traversal,index_exp,index_jump + 1)?; + traversal.push_code(piece.swap(Code::Jump(jump_from(index_jump, index_block)?))); + }, + Statement::If(conditions,blocks,otherwise) => { + let mut conditions = conditions.iter(); + let mut blocks = blocks.iter(); + let mut jumps = Vec::new(); + while let Some(condition) = conditions.next() { + let block = blocks.next().unwrap(); + traversal.compile(&condition.inner_ref())?; + traversal.push_code(condition.swap(Code::Skip)); + let index_next = traversal.push_code(condition.swap(Code::UNSURE)); + traversal.compile(&block.inner_ref())?; + jumps.push(traversal.push_code(condition.swap(Code::UNSURE))); + let length = traversal.code_length(); + traversal.insert_code(index_next,Code::Jump(jump_from(index_next, length)?)); + } + assert!(blocks.next().is_none()); + if let Some(otherwise) = otherwise { + traversal.compile(&otherwise.inner_ref())?; + } + for jump in jumps { + let length = traversal.code_length(); + traversal.insert_code(jump,Code::Jump(jump_from(jump,length)?)) + } + }, + 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.compile(&block.inner_ref())?; + traversal.close_scope(); // temporary for v + let index_jump = traversal.push_code(block.unit().start().swap(Code::UNSURE)); // Dummy NOP + traversal.insert_code(index_jump,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 + }, + Statement::Iterator(names,expressions,block) => { + let mut iterator_variables = Vec::new(); + for _ in 0..2 { // f, s, var + traversal.new_scope(); + iterator_variables.push(block.swap(Variable::Register(traversal.last_scope().index)).start()); + } + // So we put 'explist' into f, s, var as in https://www.lua.org/manual/5.3/manual.html#3.3.5 + compile_assignment(traversal,&iterator_variables,expressions)?; + let index_loop = traversal.push_code(block.swap(Code::UNSURE)); + // Discard to fill all names + let index = traversal.last_scope().index; + traversal.push_code(block.swap(Code::Discard(index + names.len() as Index)).start()); + for name in names.iter() { + traversal.new_scope(); + traversal.last_scope().symbol = Some(name.it.0.clone()); + } + traversal.compile(&block.inner_ref())?; + for _name in names.iter() { + traversal.close_scope(); + } + let index_jump = traversal.push_code(block.unit().end().swap(Code::UNSURE)); + traversal.insert_code(index_jump,Code::Jump(jump_from(index_jump,index_loop)?)); + traversal.insert_code(index_loop,Code::IteratorLoop(jump_from(index_loop,index_jump + 1)?)); + resolve_break(traversal,index_loop,index_jump + 1)?; + traversal.close_scope(); traversal.close_scope(); traversal.close_scope(); // f, s, var + }, + Statement::Function(names,method_name,function) => { // todo: ideally make names an expression, change the parser for that too + let mut names_method = names.clone(); + let is_method = if let Some(method) = method_name { // for use in compiling the function with 'self' as an argument + names_method.push(method.clone()); + true + } else { + false + }; + let mut names = names_method.iter().peekable(); + let first = names.next().unwrap(); + let var = first.swap(Variable::Name(first.it.clone())); + if !names.peek().is_some() { + compile_function(&self.swap(function),traversal,is_method)?; + compile_variable(var.inner_ref(),traversal,Access::Set)?; + } else { + compile_variable(var.inner_ref(),traversal,Access::Get)?; + while let Some(name) = names.next() { + if !names.peek().is_some() { + compile_function(&self.swap(function),traversal,is_method)?; + traversal.push_literal(Value::String(name.it.0.clone()), name.unit()); + traversal.push_code(name.unit().end().swap(Code::TableSet)); + } else { + traversal.push_literal(Value::String(name.it.0.clone()), name.unit()); + traversal.push_code(name.unit().end().swap(Code::TableGet)); + } + } + } + }, + Statement::LocalFunction(name,function) => { + var_scope(traversal, name.it.0.clone()); + let index = traversal.last_scope().index; + traversal.compile(&self.swap(function))?; + traversal.push_code(name.swap(Code::RegisterSet(index)).end()); + }, + Statement::Declaration(names,expressions) => { + let variables = &names.iter().map(|name| { + var_scope(traversal, name.it.0.clone()); + name.swap(Variable::Name(name.it.clone())) + }).collect(); + compile_assignment(traversal,variables,expressions)?; + } + } + } + // Any 'dangling' scopes that weren't closed (declarations) will be at the end of this block + if let Some(return_expressions) = &self.it.1 { // arguments are backwards + for return_expression in return_expressions.iter().rev() { + traversal.compile(&return_expression.inner_ref())?; + } + } + for _ in 0..depth { + traversal.close_scope(); + } + assert_eq!(traversal.last_scope().index, last_scope_index); + Ok(()) + } +} + +impl Compile for Chunk { + fn compile(&self, traversal: &mut Traversal) -> Result<(), LoadError> { + traversal.compile(&self.0.inner_ref()) + } +} + +// MACHINE + +use crate::table::Table; + +// Rust function to call +struct Native(dyn Fn(&mut Machine, &mut Needle) -> Result<(), RunError>); // A native function in Rust + +#[derive(Clone, Debug, Copy, PartialEq)] +enum Reference { + Closing(Index), + Upvalue(Index), +} + +#[derive(Clone, Debug)] +pub struct Prototype { // A function prototype + args: Index, + vararg: bool, // Does this function receive varargs? + prototypes: Vec>, // List of other prototypes this Prototype can make a Closure from + constants: Vec, // List of objects statically attached to this Prototype + upvalues: Vec, // Location of upvalues in (parent closure's upvalue or VM's closing) list + pieces: Vec>, // List of all pieces corresponding to it's equivalently indexed code + code: Vec, +} + +#[derive(Clone, Debug)] +enum Upvalue { // Do we REALLY have to store each Index in the Rc> too? + Stack(usize), + Heap(Value), +} + +#[derive(Clone)] +pub struct Closure { // An actual callable function + prototype: Rc, + upvalues: Vec>, // List of upvalues associated with this function +} + +impl std::fmt::Debug for Closure { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + for upvalue in self.upvalues.iter() { + write!(f,"{:?}",upvalue.borrow().deref())? + } + write!(f,"{:?}",self.prototype) + } +} + +impl Traverse for Upvalue { + fn traverse(&self) { + if let Upvalue::Heap(object) = self { + object.traverse() + } + } +} + +#[derive(Clone)] +pub enum Callable { + Rust(Rc), + Mars(Gc), +} + +impl Traverse for Callable { + fn traverse(&self) { + match self { + Callable::Rust(_) => {}, + Callable::Mars(closure) => { + closure.traverse(); + } + } + } +} + +impl Traverse for Closure { + fn traverse(&self) { + for upvalue in self.upvalues.iter() { + upvalue.traverse() + } + } +} + +impl PartialEq for Callable { + fn eq(&self, other: &Self) -> bool { + match self { + Callable::Rust(a) => { + match other { + Callable::Rust(b) => Rc::ptr_eq(a, b), + Callable::Mars(_) => false + } + }, + Callable::Mars(a) => { + match other { + Callable::Mars(b) => a == b, + Callable::Rust(_) => false, + } + }, + } + } +} + +impl std::fmt::Debug for Callable { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + Callable::Rust(_) => f.write_str("Native"), + Callable::Mars(closure) => write!(f,"{:?}",closure.borrow().deref()), + } + } +} + +struct RunError(String); + +pub trait Object: std::fmt::Debug { + fn meta_string(&mut self) -> Result { + Ok(Value::Nil) + } + fn meta_number(&mut self) -> Result { + Ok(Value::Nil) + } + fn meta_new_index(&mut self, index: Value, item: Value) -> Result<(), RunError> { + Err(RunError(format!("Cannot set index in {:?}", &self))) + } + fn meta_index(&mut self, index: Value) -> Result { + Ok(Value::Nil) + } +} + +impl From for Value { + fn from(string: String) -> Self { + let bytes = string.as_bytes(); + if bytes.len() < PHRASE_LEN - 1 { + let array: [u8; PHRASE_LEN] = bytes.try_into().unwrap(); + let hash = table::hash(&Value::String(string)); + Value::Phrase((array,hash)) + } else { + Value::String(string) + } + } +} + +impl Value { + fn number(&mut self) -> Result { + match self { + integer@Value::Integer(_) => Ok(integer.clone()), + number@Value::Number(_) => Ok(number.clone()), + Value::Object(object) => object.borrow_mut().0.meta_number(), + Value::Table(table) => { + let mut table = table.borrow_mut(); + if let Some(_metatable) = &table.meta { + Err(RunError("Metatables unimplemented".to_string())) + } else { + Ok(Value::Nil) + } + } + _ => Ok(Value::Nil), + } + } + fn string(&mut self) -> Result { + Ok(match self { + Value::Nil => "nil".to_string(), + Value::Bool(bool) => format!("{}", bool), + Value::Integer(integer) => format!("{}", integer), + Value::Number(number) => format!("{}", number), + Value::Phrase(phrase) => format!("{}", String::from_utf8_lossy(&phrase.0)), + Value::String(string) => format!("{}", string), + #[cfg(feature = "vector3")] + Value::Vector(vector) => format!("{}", vector), + Value::Table(table) => { + let mut table = table.borrow_mut(); + if let Some(_metatable) = &table.meta { + return Err(RunError("Metatables unimplemented".to_string())) + } else { + "{...}".to_string() + } + }, + Value::Object(object) => match object.borrow_mut().0.meta_string()? { + Value::String(string) => string, + it => return Err(RunError(format!("Expected string but got {:?}",it))) + }, + Value::Function(callable) => format!("{:?}",callable), + }.into()) + } + fn set(&mut self, index: Value, item: Value) -> Result<(),RunError> { + match self { + Value::Nil => Err(RunError("Cannot index nil".to_string())), + Value::Bool(bool) => Err(RunError(format!("Cannot index {:?}",bool))), + Value::Integer(_integer) => Err(RunError("Integer index unimplemented".to_string())), + Value::Number(_number) => Err(RunError("Number index unimplemented".to_string())), + Value::Phrase(_phrase) => Err(RunError("Phrase index unimplemented".to_string())), + Value::String(_string) => Err(RunError("String index unimplemented".to_string())), + #[cfg(feature = "vector3")] + Value::Vector(_vector) => Err(RunError("Vector index unimplemented".to_string())), + Value::Table(table) => { + let mut table = table.borrow_mut(); + if let Some(_metatable) = &table.meta { + Err(RunError("Metatables unimplemented".to_string())) + } else { + table.set(index,item) + } + }, + Value::Object(object) => object.borrow_mut().0.meta_new_index(index,item), + Value::Function(_callable) => Err(RunError("Function index unimplemented".to_string())), + } + } + fn get(&mut self, index: Value) -> Result { + match self { + Value::Nil => Err(RunError("Cannot index nil".to_string())), + Value::Bool(bool) => Err(RunError(format!("Cannot index {:?}",bool))), + Value::Integer(_integer) => Err(RunError("Integer index unimplemented".to_string())), + Value::Number(_number) => Err(RunError("Number index unimplemented".to_string())), + Value::Phrase(_phrase) => Err(RunError("Phrase index unimplemented".to_string())), + Value::String(_string) => Err(RunError("String index unimplemented".to_string())), + #[cfg(feature = "vector3")] + Value::Vector(_vector) => Err(RunError("Vector index unimplemented".to_string())), + Value::Table(table) => { + let table = table.borrow_mut(); + if let Some(_metatable) = &table.meta { + Err(RunError("Metatables unimplemented".to_string())) + } else { + table.get(index) + } + }, + Value::Object(object) => object.borrow_mut().0.meta_index(index), + Value::Function(_callable) => Err(RunError("Function index unimplemented".to_string())), + } + } +} + +trait Userdata: Traverse + std::fmt::Debug + Any + Object {} + +struct Anything(dyn Userdata); // can't impl PartialEq for Rc + +impl std::fmt::Debug for Anything { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + self.0.fmt(f) + } +} + +impl Traverse for Anything { + fn traverse(&self) { + self.0.traverse() + } +} + +#[cfg(feature = "vector3")] +use glam::Vec3; + +// Any value in the language +const PHRASE_LEN: usize = 16; +#[derive(Debug, Clone, PartialEq)] +pub enum Value { + Nil, // Empty value + Bool(bool), + Number(f64), + Integer(u64), // maybe we should use signed integers... + Phrase(([u8; 16],usize)), // "VeryShortString" + hash + String(String), + Table(Gc), + Object(Gc), + Function(Callable), + #[cfg(feature = "vector3")] + Vector(Vec3), +} + +impl Traverse for Value { + fn traverse(&self) { + match self { + Value::Table(table) => { + table.traverse(); + } + Value::Object(anything) => { + anything.traverse(); + } + Value::Function(callable) => { + callable.traverse() + } + _ => {} + } + } +} + +// Frame for calling a function +pub struct Frame { + vararg: usize, // Where varargs start + offset: usize, // Where arguments start + counter: usize, // Which instruction we are at + closure: Gc, // The function that was called on this frame +} + +// Machine for executing functions +pub struct Machine { + global: Gc
, + allocator: Allocator, +} + +/* +Note: I was debating using some compiler magic for closing values in a separate resizing vector. +I decided to sacrifice another usize instead on the stack specifically for potential upvalues. +I think this is the best method since the stack isn't really using too much memory anyway. +Also, I had a premonition I may need this for multithreading later on! +*/ +#[derive(Clone)] +pub struct Register { + value: Value, + upvalue: Option>, +} + +impl Register { + const NIL: Register = Register { + value: Value::Nil, + upvalue: None, + }; +} + +pub struct Needle { + frames: Vec, // Delineation of the stack and execution state for individual functions + stack: Vec, // Temporary storage for programs +} + +impl Needle { + fn pop(&mut self) -> Value { + self.stack.pop().unwrap().value + } + fn push(&mut self, value: Value) { + self.stack.push(Register { + value, + upvalue: None, + }) + } + fn index(&self, index: Index, frame: &Frame) -> &Register { + &self.stack[frame.offset + index as usize] + } +} + +type Index = u16; // Vector index +type Count = u16; // Number of arguments +type Offset = i16; // Jump to offset + +// Bytecode for execution in a procedure +#[derive(Debug, Clone, Copy)] +#[repr(u32)] +enum Code { + TableSet, // consume: value, index, table, + TableGet, // consume: index, table; push: result + TableInsert(Index), // consume: table, value + UpvalueGet(Index), // push: U[I] + UpvalueSet(Index), // consume: value + RegisterGet(Index), // push: R[I] + RegisterSet(Index), // consume: value + Constant(Index), // push: C[I] + Call(Index), // consume: R[I...] push: ... (all results) + Discard(Index), // consume: R[I...] (used with above to cull results or pad nil) + VarArg(Count), // push: VA[1 -> count] OR VA[all] + Jump(Offset), // PC += offset + Close(Index), // Raise R[I...].upvalue from Upvalue::Stack to Upvalue::Heap, 'closing' the register, same effect as Discard for None upvalues + Skip, // consume: condition; if condition then PC++ (and naturally ++ again) + Return(Index), // consume: R[I...] (move arguments around and finish) + Binary(BinaryOp), // consume: fst, snd; push: result TODO: just use direct instructions? + Unary(UnaryOp), // consume: value; push: result + Closure(Index), // push: closure constructed from prototypes[I] + IteratorLoop(Offset), + RangeBegin, // + RangeLoop(Offset), +} + +impl Code { + const NO_OP: Code = Code::Jump(1); + const UNSURE: Code = Code::Jump(0); +} + +impl Machine { + fn load(&mut self, source: &str) -> Result { + let mut traversal = Traversal{contexts: vec![Context { + scopes: vec![Scope { // Dummy initial scope so the rest can start from index = 0 + index: 0, + jumps: vec![], + labels: vec![], + symbol: None, + upvalue: false, + }], + code: vec![], + vararg: Some(Piece::::null().swap(None)), + constants: vec![], + prototype: Prototype { + args: 0, + vararg: true, + prototypes: vec![], + constants: vec![], + upvalues: vec![], + pieces: vec![], + code: vec![], + }, + }]}; + parse(source)?.compile(&mut traversal)?; + + let global = self.global.clone(); + let upvalues = vec![self.allocator.alloc(Upvalue::Heap(Value::Table(global)))]; + Ok(Callable::Mars(self.allocator.alloc(Closure { + prototype: Rc::new(traversal.close_context()?), + upvalues, + }))) + } + fn dispatch(&mut self, needle: &mut Needle) -> Result<(), RunError> { + let mut frame: Frame = needle.frames.pop().ok_or(RunError("No frames in needle!".to_string()))?; + let mut call = |machine: &mut Machine, needle: &mut Needle, mut frame: Frame, index: Index| -> Result { + frame.counter += 1; // return to this frame at the next instruction + let offset = frame.offset; + match needle.stack[offset + index as usize].value.clone() { + Value::Function(callable) => { + match callable { + Callable::Rust(function) => { + function.0(machine,needle)?; + Ok(frame) // nothing changes + }, + Callable::Mars(closure) => { + needle.frames.push(frame); + let nargs = closure.borrow().prototype.args; + needle.stack.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) + counter: 0, + closure, + }) + } + } + } + _ => { + return Err(RunError("Calling non-function types are not yet implemented".to_string())); + } + } + }; + let mut jump = |frame: &mut Frame, offset: Offset| { + if offset < 0 { + frame.counter -= (-offset) as usize + } else { + frame.counter += offset as usize + } + }; + loop { + if needle.stack.len() > 65536 { + return Err(RunError("Soft stack limit of 65536 reached".to_string())); + } + let code = frame.closure.borrow_mut().prototype.code[frame.counter]; + match code { + Code::TableSet => { + let value = needle.pop(); + let index = needle.pop(); + let mut table = needle.pop(); + table.set(index,value)?; + } + Code::TableGet => { + let index = needle.pop(); + let mut table = needle.pop(); + needle.push(table.get(index)?); + } + Code::TableInsert(index) => { + match needle.pop() { + Value::Table(mut table) => { + for i in frame.offset + index as usize..needle.stack.len() { + table.borrow_mut().append(needle.stack[i].value.clone()) + } + }, + _ => { + return Err(RunError("Inserting into non-table types is not implemented".to_string())); // todo! + } + } + } + Code::UpvalueGet(index) => { + match frame.closure.borrow_mut().upvalues[index as usize].borrow_mut().deref() { + Upvalue::Stack(stack) => needle.push(needle.stack[*stack].value.clone()), + Upvalue::Heap(object) => needle.push(object.clone()) + } + } + Code::UpvalueSet(index) => { + match frame.closure.borrow_mut().upvalues[index as usize].borrow_mut().deref_mut() { + Upvalue::Stack(register) => needle.stack[*register].value = needle.pop(), + Upvalue::Heap(it) => *it = needle.pop(), + } + } + Code::RegisterGet(index) => { + needle.push(needle.stack[frame.vararg + index as usize].value.clone()); + } + Code::RegisterSet(index) => { + needle.stack[frame.vararg + index as usize].value = needle.pop(); + } + Code::Constant(constant) => { + needle.push(frame.closure.borrow_mut().prototype.constants[constant as usize].clone()); + } + Code::Call(index) => { + frame = call(self, needle, frame, index)?; + continue; + }, + Code::Discard(index) => { + needle.stack.resize(frame.offset + index as usize, Register::NIL); + } + Code::VarArg(count) => { + if count == 0 { + for i in frame.vararg..frame.offset { + needle.push(needle.stack[i].value.clone()); + } + } else { + for i in 0..count as usize { + if frame.offset - i - 1 < frame.vararg { + needle.push(Value::Nil); + } else { + needle.push(needle.stack[frame.offset - i - 1].value.clone()); + } + } + } + } + Code::Jump(offset) => { + #[cfg(feature="assertions")] + assert_ne!(offset,0); + jump(&mut frame,offset); + } + Code::Skip => { + if matches!(needle.pop(),Value::Nil | Value::Bool(false)) { + frame.counter += 1; + } + } + Code::Return(index) => { + // Settle the return arguments to where this frame was started originally + for i in frame.offset + index as usize..needle.stack.len() { + needle.stack[frame.vararg + i - 1] = needle.stack[i].clone(); + } + if let Some(upper) = needle.frames.pop() { + frame = upper; + } else { + break; + } + } + Code::Binary(op) => { + return Err(RunError("Binary operations are not implemented".to_string())) + } + Code::Unary(op) => { + return Err(RunError("Unary operations are not implemented".to_string())) + } + Code::Close(index) => { + for i in frame.offset + index as usize..needle.stack.len() { + match &mut needle.stack[i] { + Register { value, upvalue: Some(upvalue) } => { + if matches!(upvalue.borrow().deref(),Upvalue::Stack(_)) { + upvalue.replace(Upvalue::Heap(value.clone())) + } else { + panic!() // Because it's on the stack since it's right here! + } + }, + Register { upvalue: None, .. } => {} + } + } + needle.stack.resize(frame.offset + index as usize, Register::NIL); // As with Code::Discard + } + Code::Closure(index) => { + let prototype = frame.closure.borrow().prototype.prototypes[index as usize].clone(); + let mut upvalues = Vec::new(); + for reference in prototype.upvalues.iter() { + upvalues.push(match reference { + Reference::Closing(index) => { + let index = frame.offset + *index as usize; + match &mut needle.stack[index].upvalue { + Some(upvalue) => upvalue.clone(), + it => { + let upvalue = self.allocator.alloc(Upvalue::Stack(index)); + *it = Some(upvalue.clone()); + upvalue + } + } + }, + Reference::Upvalue(index) => frame.closure.borrow_mut().upvalues[*index as usize].clone() + }); + } + needle.push(Value::Function(Callable::Mars(self.allocator.alloc(Closure { prototype, upvalues, })))); + } + Code::RangeBegin => { + let len = needle.stack.len(); + let init = len - 2; + let limit = len - 1; + let step = len; + let init_value = needle.stack[init].value.number()?; + let limit_value = needle.stack[limit].value.number()?; + let step_value = needle.stack[step].value.number()?; + if matches!(init_value,Value::Integer(_)) && matches!(limit_value,Value::Integer(_)) && matches!(step_value,Value::Integer(_)) { + let get = |value: Value| match value { + Value::Integer(integer) => integer.clone(), + _ => 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[step].value = step_value; + } else { + // Make them all as floats + let cast = |value: Value| match value { + Value::Integer(integer) => Value::Number(integer as f64), + number@Value::Number(_) => number, + _ => panic!() + }; + needle.stack[init].value = cast(init_value); + needle.stack[limit].value = cast(limit_value); + needle.stack[step].value = cast(step_value); + } + needle.push(needle.stack[init].value.clone()); + } + Code::RangeLoop(offset) => { + let len = needle.stack.len(); + let init_index = len - 2; + let limit_index = len - 1; + let step_index = len; + let init_value = needle.stack[init_index].value.number()?; + let limit_value = needle.stack[limit_index].value.number()?; + let step_value = needle.stack[step_index].value.number()?; + match init_value { + Value::Integer(init) => { + let step = match step_value { Value::Integer(it) => it, _ => panic!() }; + let limit = match limit_value { Value::Integer(it) => it, _ => panic!() }; + if limit == 0 { + 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)); + } + }, + Value::Number(init) => { + let step = match step_value { Value::Number(it) => it, _ => panic!() }; + let limit = match limit_value { Value::Number(it) => it, _ => panic!() }; + if (step >= 0.0 && init > limit) || (step < 0.0 && init < limit) { + jump(&mut frame,offset); + } else { + needle.stack[init_index].value = Value::Number(init + step); + needle.push(Value::Number(init)); + } + } + _ => panic!() + } + } + Code::IteratorLoop(offset) => { + let len = needle.stack.len(); + let function_index = len - 2; + let state_index = len - 1; + let var_index = len; + let function = needle.stack[function_index].value.clone(); + let state = needle.stack[state_index].value.clone(); + let var = needle.stack[var_index].value.clone(); + needle.push(function); + needle.push(var); + needle.push(state); + let offset = frame.offset; + frame = call(self, needle,frame,Index::try_from(needle.stack.len() - offset - 2).unwrap())?; + needle.stack.get(len + 2).unwrap_or(&Register::NIL); + } + } + frame.counter += 1; + } + Ok(()) + } + fn enter(&mut self, closure: Gc) -> Result<(), RunError> { + let mut needle = Needle { + frames: vec![Frame { + 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 + }; + self.dispatch(&mut needle) + } + fn new() -> Machine { + let mut allocator = Allocator::new(); + Machine { + global: allocator.alloc(Table::new()), + allocator, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn simple() { + let mut machine = Machine::new(); + let result = machine.load(include_str!("tests/script.lua")); + println!("{:#?}", result); + } +} \ No newline at end of file diff --git a/src/table.rs b/src/table.rs new file mode 100644 index 0000000..e795d75 --- /dev/null +++ b/src/table.rs @@ -0,0 +1,340 @@ +use std::hash::{DefaultHasher, Hash, Hasher}; +use std::rc::Rc; +use crate::gc::{Gc, Traverse}; +use crate::{Callable, RunError, Value}; + +// I've already spent 2 months on this interpreter, and I'm tired, so I've cut a few corners... +/* See some of Lua's notes on this table: https://www.lua.org/source/5.5/ltable.c.html +This table uses an 'open-addressed' hashmap https://en.wikipedia.org/wiki/Open_addressing +where collisions are handled by 'linear-probing' and something I call 'displacement'. + +Motivation: +Afaik, without having some mechanism that records or ensures known proximity between a displaced element +that collided with other(s) in the hashmap and it's original home (at element's hash's index), +the entire array may have to be checked just to see if an element is present. How can we fix this? + +Solution: +Lua uses Brent's method, which I couldn't find a concrete explanation of, here I'm just recording +the maximum displacement that will need to be linearly-probed from the home entry to be certain of +any 'home' element's presence in the greater array. + +Notes: +- If this value becomes large, the load factor is probably high and there would be a resize. +- Values would only need to be shuffled around closer to their 'home' during a resize. +- It is unlikely the displacement would large value without a resize amending that problem. + */ + +type Displacement = u8; + +#[derive(Debug, Clone)] +struct Entry { + index: Value, + item: Value, + home: usize, + displacement: Displacement, +} + +impl Default for Entry { + fn default() -> Self { + Entry { + index: Value::Nil, + item: Value::Nil, + home: 0, + displacement: 0, + } + } +} + +pub fn hash(value: &Value) -> usize { + match value { + Value::Nil => { 0 } + Value::Bool(boolean) => { if *boolean { 1 } else { 0 } } + Value::String(string) => { + let mut hasher = DefaultHasher::new(); + string.hash(&mut hasher); + hasher.finish() as usize + } + Value::Phrase(phrase) => { + phrase.1 + } + Value::Function(callable) => { + match callable { + Callable::Rust(native) => { + Rc::as_ptr(native).addr() + } + Callable::Mars(closure) => { + closure.addr() + } + } + } + Value::Integer(integer) => { + *integer as usize + } + Value::Number(number) => { + if number.is_nan() { + 0 + } else { + number.to_bits() as usize + } + } + Value::Table(table) => { + table.addr() + } + Value::Object(object) => { + object.addr() + } + #[cfg(feature = "vector3")] + Value::Vector(vec) => { + let mut hasher = DefaultHasher::new(); + vec.as_u64vec3().hash(&mut hasher); + hasher.finish() as usize + } + } +} + +// todo: displaced items don't ever get shuffled closer to their homes unless the table is resized +#[derive(Debug, Clone)] +pub struct Table { + table: Vec, + array: Vec, + table_bounds: std::ops::Range, + table_count: usize, // number of elements in table + pub meta: Option> +} + +impl Traverse for Table { + fn traverse(&self) { + for Entry { index, item, .. } in self.table.iter() { + if !matches!(index,Value::Nil) { + index.traverse(); + item.traverse(); + } + } + for item in self.array.iter() { + item.traverse() + } + } +} + +impl Table { + fn exchange_table(&mut self, len: usize) { + let old = std::mem::replace(&mut self.table, vec![Entry::default(); len]); + for Entry { index, item, .. } in old { + if index != Value::Nil { + self.set_table(index,item) + } + } + } + pub fn resize_table(&mut self, len: usize) { + self.exchange_table(len.max(self.table_count + (self.table_count / 3))); + self.table_bounds = 0..self.table_upper(); + } + pub fn resize_array(&mut self, len: usize) { + self.array.resize(len,Value::Nil) + } + fn table_upper(&self) -> usize { + (self.table.len() / 4) * 3 + } + fn table_lower(&self) -> usize { + self.table.len() / 3 + } + fn ensure_table(&mut self) { + if self.table_count > self.table_upper() { // free space is short + self.exchange_table((self.table.len() + 4) * 2); + } else if self.table_count < self.table_lower() { // too much free space + self.exchange_table(self.table.len() / 2) + } + self.table_bounds = self.table_lower()..self.table_upper() + } + fn set_table(&mut self, index: Value, item: Value) { + #[cfg(feature = "assertions")] + assert_ne!(index,Value::Nil); + #[cfg(feature = "assertions")] + assert_ne!(item,Value::Nil); + if !self.table_bounds.contains(&self.table_count) { // make sure the table is appropriately sized + self.ensure_table(); + } + let range = self.table.len(); + let home = hash(&index) % range; + if self.table[home].index == Value::Nil { // attempt to place it directly in an empty space + // INSERT + self.table[home].home = home; + self.table[home].item = item; // home.item.soft_drop() + self.table[home].index = index; + self.table[home].displacement = self.table[home].displacement.max(0); + self.table_count += 1; + } else if self.table[home].index == index { // attempt to replace it directly + // REPLACE + self.table[home].item = item; // home.item.soft_drop() + } else { + // attempt to replace it in a collided neighbour location + for i in 1..self.table[home].displacement + 1 { + let neighbour = &mut self.table[(home + i as usize) % range]; // todo: is mod expensive? + if neighbour.index == index { // found where it was displaced to + // REPLACE + neighbour.item = item; + return; + } + } + // at this point it must be added, probe to place in an empty space + for j in self.table[home].displacement + 1..Displacement::MAX - 1 { + let neighbour = &mut self.table[(home + j as usize) % range]; + if neighbour.index == Value::Nil { // new empty slot hooray! + // INSERT + neighbour.home = home; + neighbour.item = item; + neighbour.index = index; + self.table[home].displacement = j; + self.table_count += 1; + return; + } + } + // impossible but this still needs to be complete + #[cfg(feature = "messages")] + eprintln!("Large table collision, are the hashes ok?\n\tBrute-force probing..."); + let mut free: Option = None; + for k in 0..self.table.len() { + let neighbour = &mut self.table[k]; + if neighbour.index == Value::Nil && free.is_none() { + free = Some(k); + } else if neighbour.index == index { + // REPLACE + neighbour.item = item; + return; + } + } + if let Some(k) = free { + // INSERT + self.table[k].home = home; + self.table[k].index = index; + self.table[k].item = item; + self.table[home].displacement = Displacement::MAX; + self.table_count += 1; + return; + } + // must resize + #[cfg(feature = "messages")] + eprintln!("\n\tResizing..."); + self.resize_table((self.table.len() + 4) * 2); + self.set_table(index,item); + } + } + fn rem_table(&mut self, index: Value) { + let range = self.table.len(); + let home = hash(&index) % range; + if self.table[home].index == index { + self.table[home].index = Value::Nil; + self.table[home].item = Value::Nil; // is this necessary? + self.table_count -= 1; + } else { + if self.table[home].displacement != Displacement::MAX { + let mut largest = 0; + for i in 1..self.table[home].displacement { + let neighbour = &mut self.table[(home + i as usize) % range]; + if neighbour.index == index { + neighbour.index = Value::Nil; + neighbour.item = Value::Nil; + self.table_count -= 1; + if i == self.table[home].displacement { + self.table[home].displacement = largest + } + return; + } else if neighbour.index != Value::Nil && neighbour.home == home { + largest = i; + } + } + } else { + let mut largest = 0; + let mut finished = false; // what + for k in 0..self.table.len() { + let neighbour = &mut self.table[k]; + if neighbour.index == index { + neighbour.index = Value::Nil; + neighbour.item = Value::Nil; + self.table_count -= 1; + #[cfg(feature = "assertions")] + assert!(!finished); + finished = true; + } + if neighbour.home == home && neighbour.index != Value::Nil { + largest = largest.max(if k < home { + k + self.table.len() - home - 1 // ? + } else { + k - home + }) + } + } + self.table[home].displacement = if largest > Displacement::MAX as usize { + Displacement::MAX + } else { + largest as Displacement + } + } + } + } + pub fn set(&mut self, index: Value, item: Value) -> Result<(),RunError> { + match index { + Value::Integer(index) => { // This is an integer index, try the array first + match item { + Value::Nil => { + if (0..self.array.len() + 1).contains(&(index as usize)) { + if self.array.len() == index as usize { + self.array.pop(); + } else { + self.array[index as usize + 1] = Value::Nil + } + } + self.rem_table(Value::Integer(index)); + }, + item => { + if (0..self.array.len() + 1).contains(&(index as usize)) { + if self.array.len() == index as usize { + self.array.push(item) + } else { + self.array[index as usize + 1] = item; + } + } else { + self.set_table(Value::Integer(index),item) + } + } + } + Ok(()) + }, + Value::Nil => Err(RunError("Attempt to set new index of tabel with key: nil".to_string())), + index => Ok(self.set_table(index,item)) + } + } + pub fn get(&self, index: Value) -> Result { + match index { + Value::Integer(index) => Ok(self.array.get(index as usize).unwrap_or(&Value::Nil).clone()), + Value::Nil => Err(RunError("Attempt to index table with key: nil".to_string())), + index => { + let location = hash(&index) % self.table.len(); + let home = &self.table[location]; + if home.index == index { + Ok(home.item.clone()) + } else { + for i in 1..home.displacement as usize { + let neighbour = &self.table[location + i]; + if neighbour.index == index { + return Ok(neighbour.item.clone()) + } + } + Ok(Value::Nil) + } + } + } + } + pub fn append(&mut self, item: Value) { + self.array.push(item) + } + pub fn new() -> Self { + Table { + table: Vec::new(), + array: Vec::new(), + table_count: 0, + table_bounds: (0..0).into(), + meta: None + } + } +} \ No newline at end of file diff --git a/src/tests/script.lua b/src/tests/script.lua new file mode 100644 index 0000000..092709f --- /dev/null +++ b/src/tests/script.lua @@ -0,0 +1,15 @@ +local two +do + local one + if something then + local three + print(something) + end + local four + if something2 then + + end +end +local function bricked(hello, there) + +end \ No newline at end of file