1125 lines
49 KiB
Rust
1125 lines
49 KiB
Rust
// COMPILER
|
|
|
|
use crate::parser::{
|
|
Args, Block, Call, Chunk, Constructor, Expression, Function, LoadError, Name, Piece, Prefix,
|
|
Statement, VarArg, Variable,
|
|
};
|
|
use crate::vm::{Code, Index, Machine, Offset, Prototype, Reference, Value};
|
|
use deimos::map::Map;
|
|
use std::cmp::PartialEq;
|
|
use std::ops::Deref;
|
|
use std::rc::Rc;
|
|
|
|
pub(crate) struct Traversal<'a, S> {
|
|
// A 'cursor' of where we are in assumed execution
|
|
machine: &'a mut Machine<S>,
|
|
contexts: Vec<Context>, // Stack of function prototypes being compiled
|
|
}
|
|
|
|
pub(crate) struct Context {
|
|
// Everything known about a closure at a time
|
|
scopes: Vec<Scope>,
|
|
vararg: Option<Piece<Option<String>>>,
|
|
constants: Map<Value, Index>, // map some constant value to an index for not repeating constants
|
|
prototype: Prototype,
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
enum BlindJump {
|
|
Break(usize),
|
|
Continue(usize),
|
|
Goto(String, usize),
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub(crate) struct Scope {
|
|
index: Index, // Current index on stack
|
|
jumps: Vec<BlindJump>, // todo: probably shouldn't have two vectors on each scope, they are used for temporaries too
|
|
labels: Vec<(String, usize)>,
|
|
symbol: Option<String>, // If that index has a symbol
|
|
upvalue: bool,
|
|
}
|
|
|
|
impl<S> Traversal<'_, S> {
|
|
fn compile<T>(&mut self, it: &T) -> Result<(), LoadError>
|
|
where
|
|
T: Compile,
|
|
{
|
|
it.compile(self)
|
|
}
|
|
fn push_code(&mut self, it: Piece<Code>) -> usize {
|
|
match it.it {
|
|
Code::TableNew => {
|
|
self.temp_scope();
|
|
}
|
|
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) => {
|
|
let diff = self.index().unwrap() - index;
|
|
self.consume_scope(diff + 1);
|
|
//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(_)
|
|
| Code::Message(_) => {}
|
|
}
|
|
let prototype = &mut self.contexts.last_mut().unwrap().prototype;
|
|
prototype.code.push(it.it);
|
|
prototype.pieces.push(it.unit());
|
|
assert_eq!(prototype.code.len(), prototype.pieces.len());
|
|
prototype.code.len() - 1
|
|
}
|
|
fn insert_code(&mut self, location: usize, it: Code) {
|
|
// slightly dangerous since it doesn't consider scopes
|
|
self.last_context().prototype.code[location] = it;
|
|
}
|
|
fn push_constant(&mut self, it: Value) -> Index {
|
|
// todo: constants need pieces too!!!
|
|
// don't use constants that already exist
|
|
if let Some(entry) = self.contexts.last_mut().unwrap().constants.get(it.clone()) {
|
|
*entry
|
|
} else {
|
|
let index = {
|
|
let constants = &mut self.contexts.last_mut().unwrap().prototype.constants;
|
|
constants.push(it.clone());
|
|
constants.len() - 1
|
|
}
|
|
.try_into()
|
|
.unwrap();
|
|
self.contexts.last_mut().unwrap().constants.set(it, index);
|
|
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 push_string(&mut self, string: String, piece: Piece<()>) -> Result<(), LoadError> {
|
|
match self.machine.new_string(string.clone().as_str()) {
|
|
Err(error) => Err(LoadError(error.0, piece)),
|
|
Ok(string) => Ok(self.push_literal(string, piece)),
|
|
}
|
|
}
|
|
fn new_scope(&mut self) {
|
|
// Make some new scope
|
|
let index = self.last_scope().index;
|
|
self.contexts.last_mut().unwrap().scopes.push(Scope {
|
|
index,
|
|
jumps: vec![],
|
|
labels: vec![],
|
|
symbol: None,
|
|
upvalue: false,
|
|
});
|
|
}
|
|
fn temp_scope(&mut self) -> Index {
|
|
// Make a new scope with index++
|
|
self.new_scope();
|
|
if self.last_context().scopes.len() > 2 {
|
|
// For the dummy scope
|
|
self.last_scope().index += 1;
|
|
}
|
|
self.last_scope().index
|
|
}
|
|
fn last_scope(&mut self) -> &mut Scope {
|
|
self.last_context().scopes.last_mut().unwrap()
|
|
}
|
|
fn index(&mut self) -> Option<Index> {
|
|
if self.last_context().scopes.len() == 1 {
|
|
None
|
|
} else {
|
|
Some(self.last_context().scopes.last_mut().unwrap().index)
|
|
}
|
|
}
|
|
fn code_length(&mut self) -> usize {
|
|
self.last_context().prototype.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(Code::Close(_)) = self.last_context().prototype.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()
|
|
}
|
|
pub(crate) fn close_context(&mut self) -> Result<Prototype, LoadError> {
|
|
let mut context = self.contexts.pop().unwrap();
|
|
for jump in context.scopes.pop().unwrap().jumps {
|
|
// Get back the dummy one and error any jumps
|
|
return Err(LoadError::from_str(
|
|
format!("{:?}", jump).as_str(),
|
|
&Piece::<()>::null(),
|
|
)); // todo: we need Vec or some append in LoadError and also BlindJumps need pieces!
|
|
}
|
|
Ok(context.prototype)
|
|
}
|
|
pub(crate) fn new(machine: &mut Machine<S>) -> Traversal<S> {
|
|
Traversal {
|
|
machine,
|
|
contexts: vec![Context {
|
|
scopes: vec![Scope {
|
|
// Dummy scope
|
|
index: 0,
|
|
jumps: vec![],
|
|
labels: vec![],
|
|
symbol: None,
|
|
upvalue: false,
|
|
}],
|
|
vararg: Some(Piece::<Name>::null().swap(None)),
|
|
constants: Map::new(),
|
|
prototype: Prototype {
|
|
args: 0,
|
|
vararg: true,
|
|
prototypes: vec![],
|
|
constants: vec![],
|
|
upvalues: vec![],
|
|
pieces: vec![],
|
|
code: vec![],
|
|
},
|
|
}],
|
|
}
|
|
}
|
|
}
|
|
|
|
// todo: not all impl Compile for Piece<T> needs to be &T
|
|
trait Compile {
|
|
fn compile<S>(&self, traversal: &mut Traversal<S>) -> Result<(), LoadError>;
|
|
}
|
|
|
|
fn compile_function<S>(
|
|
this: &Piece<&Function>,
|
|
traversal: &mut Traversal<S>,
|
|
method: bool,
|
|
) -> Result<(), LoadError> {
|
|
let mut self_arguments = Vec::new();
|
|
if method {
|
|
self_arguments.push("self".to_string());
|
|
}
|
|
self_arguments.append(&mut this.it.args.iter().map(|it| it.it.0.clone()).collect());
|
|
let mut argument_scopes = vec![Scope {
|
|
// Dummy initial scope so the rest can start from index = 0 (since there can be zero arguments!!)
|
|
index: 0,
|
|
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
|
|
.vararg
|
|
.as_ref()
|
|
.map(|it| it.swap(it.clone().it.0.map(|string| string.0.clone()))),
|
|
scopes: argument_scopes,
|
|
prototype: Prototype {
|
|
args: this.it.args.len().try_into().unwrap(),
|
|
vararg: this.it.vararg.is_some(),
|
|
prototypes: vec![],
|
|
constants: vec![],
|
|
upvalues: vec![Reference::Upvalue(0)], // _ENV
|
|
pieces: vec![],
|
|
code: vec![],
|
|
},
|
|
constants: Map::new(),
|
|
});
|
|
traversal.compile(&this.it.block.inner_ref())?; // compile the block
|
|
let prototype = traversal.close_context()?;
|
|
let prototypes = &mut traversal.last_context().prototype.prototypes; // add this to the upper function's prototypes list
|
|
let index = prototypes.len().try_into().unwrap();
|
|
prototypes.push(Rc::new(prototype));
|
|
traversal.push_code(this.swap(Code::Closure(index))); // reference this closure
|
|
Ok(())
|
|
}
|
|
|
|
enum Access {
|
|
Set,
|
|
Get,
|
|
}
|
|
|
|
fn compile_identifier<S>(
|
|
name: Piece<String>,
|
|
traversal: &mut Traversal<S>,
|
|
access: Access,
|
|
) -> Result<(), LoadError> {
|
|
let contexts_len = traversal.contexts.len();
|
|
for context_index in 0..contexts_len {
|
|
// For each context
|
|
let context_index = contexts_len - context_index - 1; // From top to bottom
|
|
if context_index == contexts_len - 1 {
|
|
// If this is our context
|
|
for scope in traversal.contexts[context_index].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),
|
|
}));
|
|
return Ok(()); // Then break out
|
|
}
|
|
}
|
|
} else {
|
|
// Otherwise it's another function's 'scope'(s)
|
|
let scopes_len = traversal.contexts[context_index].scopes.len();
|
|
for scope_index in 0..scopes_len {
|
|
// For each scope in that foreign context
|
|
let scope_index = scopes_len - scope_index - 1; // From top to bottom
|
|
if traversal.contexts[context_index].scopes[scope_index]
|
|
.symbol
|
|
.as_ref()
|
|
.is_some_and(|it| it.eq(&name.it))
|
|
{
|
|
let register = traversal.contexts[context_index].scopes[scope_index].index;
|
|
traversal.contexts[context_index].scopes[scope_index].upvalue = true;
|
|
let mut reference = Reference::Closing(register);
|
|
// its important that a few conditions are held here e.g. length > 2, j < length + 1...
|
|
for down_context_index in context_index + 1..contexts_len {
|
|
// Ok now going from every context to the one wanting the upvalue
|
|
// set reference to what we found for the next context
|
|
reference = match traversal.contexts[down_context_index]
|
|
.prototype
|
|
.upvalues
|
|
.iter()
|
|
.position(|it| *it == reference)
|
|
{
|
|
Some(index) => Reference::Upvalue(index.try_into().unwrap()),
|
|
None => {
|
|
// we don't even have a reference to this in our upvalues so add it
|
|
let length = traversal.contexts[down_context_index]
|
|
.prototype
|
|
.upvalues
|
|
.len();
|
|
traversal.contexts[down_context_index]
|
|
.prototype
|
|
.upvalues
|
|
.push(reference);
|
|
Reference::Upvalue(length.try_into().unwrap()) // Reference (child) to this reference to that reference (parent) for next context
|
|
}
|
|
}
|
|
}
|
|
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),
|
|
}));
|
|
return Ok(());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// At this point there were no returns so we must use _G table
|
|
traversal.push_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<S>(
|
|
variable: Piece<&Variable>,
|
|
traversal: &mut Traversal<S>,
|
|
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<S>(&self, traversal: &mut Traversal<S>) -> Result<(), LoadError> {
|
|
compile_function(self, traversal, false)
|
|
}
|
|
}
|
|
|
|
impl Compile for Piece<&Args> {
|
|
fn compile<S>(&self, traversal: &mut Traversal<S>) -> 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<S>(&self, traversal: &mut Traversal<S>) -> Result<(), LoadError> {
|
|
let index = traversal.index().map(|it| it + 1).unwrap_or(0); // there will always be at least the callable
|
|
traversal.compile(&self.it.args.inner_ref())?; // arguments
|
|
traversal.compile(&self.it.prefix.as_ref().inner_ref())?; // call prefix
|
|
match &self.it.method {
|
|
Some(name) => {
|
|
traversal.push_string(name.it.0.clone(), name.unit())?;
|
|
traversal.push_code(name.swap(Code::TableGet));
|
|
}
|
|
None => {}
|
|
}
|
|
traversal.push_code(self.it.prefix.unit().end().swap(Code::Call(index))); // Our responsibility to discard here
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl Compile for Piece<&Prefix> {
|
|
fn compile<'a, 'b, S>(&self, traversal: &mut Traversal<S>) -> Result<(), LoadError> {
|
|
match &self.it {
|
|
Prefix::Call(call) => {
|
|
let index = traversal.index().unwrap();
|
|
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, S>(&self, traversal: &mut Traversal<S>) -> 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, S>(&self, traversal: &mut Traversal<S>) -> Result<(), LoadError> {
|
|
traversal.push_code(self.swap(Code::TableNew));
|
|
let index_table = traversal.index().unwrap();
|
|
for field in self.it.0.iter() {
|
|
match &field.it.index {
|
|
Some(index) => {
|
|
traversal.compile(&index.inner_ref())?;
|
|
traversal.push_code(
|
|
field
|
|
.it
|
|
.expression
|
|
.swap(Code::RegisterGet(index_table))
|
|
.start(),
|
|
);
|
|
traversal.push_code(field.it.expression.swap(Code::TableSet));
|
|
}
|
|
None => {
|
|
match &field.it.expression.it {
|
|
Expression::VarArg(vararg) => {
|
|
traversal.compile(&field.it.expression.swap(vararg))?;
|
|
traversal.push_code(field.it.expression.swap(Code::VarArg(0)));
|
|
}
|
|
it => {
|
|
traversal.compile(&field.it.expression.swap(it))?;
|
|
}
|
|
}
|
|
let index = traversal
|
|
.contexts
|
|
.last()
|
|
.unwrap()
|
|
.scopes
|
|
.last()
|
|
.unwrap()
|
|
.index;
|
|
traversal.push_code(
|
|
field
|
|
.it
|
|
.expression
|
|
.swap(Code::RegisterGet(index_table))
|
|
.start(),
|
|
);
|
|
traversal.push_code(field.it.expression.swap(Code::TableInsert(index)));
|
|
}
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl Compile for Piece<&Expression> {
|
|
fn compile<'a, S>(&self, traversal: &mut Traversal<S>) -> 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::<u64>()
|
|
.map(|it| Value::Integer(it))
|
|
.unwrap_or(Value::Number(
|
|
number
|
|
.0
|
|
.parse::<f64>()
|
|
.map_err(|it| LoadError(it.to_string(), self.unit()))?,
|
|
));
|
|
traversal.push_literal(number, self.unit());
|
|
}
|
|
Expression::String(string) => {
|
|
traversal.push_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<Offset, LoadError> {
|
|
Ok((there as i64 - here as i64) as Offset)
|
|
}
|
|
|
|
fn resolve_break<S>(
|
|
traversal: &mut Traversal<S>,
|
|
begin: usize,
|
|
end: usize,
|
|
) -> Result<(), LoadError> {
|
|
for jump in std::mem::replace(&mut traversal.last_scope().jumps, Vec::new()) {
|
|
// ?
|
|
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)?));
|
|
}
|
|
_ => traversal.last_scope().jumps.push(jump),
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn compile_assignment<S>(
|
|
traversal: &mut Traversal<S>,
|
|
variables: &Vec<Piece<Variable>>,
|
|
expressions: &Vec<Piece<Expression>>,
|
|
) -> 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<S>,
|
|
variable: Option<&Piece<Variable>>,
|
|
expression: &Piece<&Expression>|
|
|
-> Result<(), LoadError> {
|
|
let index = traversal.index().unwrap_or(0);
|
|
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(ref call) => {
|
|
// multiple
|
|
let mut index = traversal.index().unwrap();
|
|
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.try_into().unwrap()),
|
|
);
|
|
}
|
|
ref 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.try_into().unwrap()));
|
|
}
|
|
}
|
|
_ => {
|
|
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<S>(&self, traversal: &mut Traversal<S>) -> Result<(), LoadError> {
|
|
let last_scope_index = traversal.index().unwrap_or(0);
|
|
let mut depth = 0; // Counter for 'dangling' scopes produced by any declarations to be closed.
|
|
// todo: for some reason, register indices are called "<thing>_index" and code indices "index_<thing>"? fix it
|
|
// todo: maybe refactor this so that there are less indents
|
|
for statement in self.it.statements.iter() {
|
|
let piece = statement.unit();
|
|
match &statement.it {
|
|
Statement::Semicolon => {}
|
|
Statement::Assignment(variables, expressions) => {
|
|
compile_assignment(traversal, variables, expressions)?;
|
|
}
|
|
Statement::Call(call) => {
|
|
let index = traversal.index().unwrap_or(0);
|
|
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().prototype.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().peekable();
|
|
let mut blocks = blocks.iter();
|
|
assert_eq!(conditions.len(), blocks.len());
|
|
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)); // jump to the next branch
|
|
traversal.compile(&block.inner_ref())?;
|
|
if conditions.peek().is_some() || otherwise.is_some() {
|
|
// don't jump ahead by 1 if it's at the end already and there is no else block
|
|
jumps.push(traversal.push_code(condition.swap(Code::UNSURE))); // jump all the way to the end
|
|
}
|
|
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) => {
|
|
traversal.compile(&init.inner_ref())?;
|
|
traversal.compile(&limit.inner_ref())?;
|
|
match step {
|
|
Some(expression) => traversal.compile(&expression.inner_ref())?,
|
|
None => traversal.push_literal(Value::Integer(1), limit.unit().end()),
|
|
};
|
|
let index_loop =
|
|
traversal.push_code(block.unit().start().swap(Code::RangeBegin)) + 1;
|
|
traversal.temp_scope();
|
|
traversal.last_scope().symbol = Some(var.it.0.clone());
|
|
traversal.compile(&block.inner_ref())?;
|
|
traversal.close_scope(); // temporary for v
|
|
let index_jump = traversal.push_code(block.unit().start().swap(Code::UNSURE)); // Dummy NOP
|
|
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.consume_scope(3); // 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.index().unwrap()))
|
|
.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.index().unwrap();
|
|
traversal.push_code(
|
|
block
|
|
.swap(Code::Discard(
|
|
(index as usize + names.len()).try_into().unwrap(),
|
|
))
|
|
.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_string(name.it.0.clone(), name.unit())?;
|
|
traversal.push_code(name.unit().end().swap(Code::TableSet));
|
|
} else {
|
|
traversal.push_string(name.it.0.clone(), name.unit())?;
|
|
traversal.push_code(name.unit().end().swap(Code::TableGet));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Statement::LocalFunction(name, function) => {
|
|
traversal.compile(&self.swap(function))?;
|
|
traversal.last_scope().symbol = Some(name.it.0.clone());
|
|
depth += 1;
|
|
}
|
|
Statement::Declaration(names, expressions) => {
|
|
let mut expressions_iter = expressions.iter().peekable();
|
|
let mut names_iter = names.iter().peekable();
|
|
let compile_declaration = |traversal: &mut Traversal<S>,
|
|
expression: &Piece<Expression>,
|
|
name: &Piece<Name>|
|
|
-> Result<(), LoadError> {
|
|
traversal.compile(&expression.inner_ref())?;
|
|
traversal.last_scope().symbol = Some(name.it.0.clone());
|
|
Ok(())
|
|
};
|
|
while names_iter.peek().is_some() {
|
|
match (expressions_iter.next(), expressions_iter.peek().is_none()) {
|
|
(Some(expression), true) => {
|
|
// expression is last, has multiplicity
|
|
match expression.it {
|
|
Expression::VarArg(ref vararg) => {
|
|
let names: Vec<&Piece<Name>> = names_iter.collect();
|
|
traversal.compile(&expression.swap(vararg))?; // for error checking only
|
|
traversal.push_code(
|
|
expression.swap(Code::VarArg(
|
|
names.len().try_into().unwrap(),
|
|
)),
|
|
);
|
|
let mut scopes =
|
|
traversal.last_context().scopes.iter_mut().rev();
|
|
for name in names.iter().rev() {
|
|
scopes.next().unwrap().symbol = Some(name.it.0.clone());
|
|
depth += 1;
|
|
}
|
|
}
|
|
Expression::Prefix(ref prefix) => {
|
|
match prefix.deref() {
|
|
Prefix::Call(call) => {
|
|
let names: Vec<&Piece<Name>> = names_iter.collect();
|
|
traversal.compile(&expression.swap(call))?;
|
|
let index = (traversal.index().unwrap_or(0)
|
|
as usize
|
|
+ names.len())
|
|
.try_into()
|
|
.unwrap();
|
|
traversal.push_code(
|
|
expression.swap(Code::Discard(index)).end(),
|
|
); // discard and extend to names
|
|
let mut scopes = traversal
|
|
.last_context()
|
|
.scopes
|
|
.iter_mut()
|
|
.rev();
|
|
for name in names.iter().rev() {
|
|
scopes.next().unwrap().symbol =
|
|
Some(name.it.0.clone());
|
|
depth += 1;
|
|
}
|
|
}
|
|
_ => {
|
|
compile_declaration(
|
|
traversal,
|
|
expression,
|
|
names_iter.next().unwrap(),
|
|
)?;
|
|
depth += 1;
|
|
}
|
|
}
|
|
}
|
|
_ => {
|
|
compile_declaration(
|
|
traversal,
|
|
expression,
|
|
names_iter.next().unwrap(),
|
|
)?;
|
|
depth += 1;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
(Some(expression), false) => {
|
|
compile_declaration(
|
|
traversal,
|
|
expression,
|
|
names_iter.next().unwrap(),
|
|
)?;
|
|
depth += 1;
|
|
} // expression is not last, just use it
|
|
(None, ..) => {
|
|
// no more expressions
|
|
let index = traversal.index().unwrap_or(0) as usize;
|
|
traversal.push_code(
|
|
names_iter
|
|
.next()
|
|
.unwrap()
|
|
.swap(Code::Discard(
|
|
(index + names_iter.count()).try_into().unwrap(),
|
|
))
|
|
.end(),
|
|
);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
let index = traversal.index().unwrap_or(0);
|
|
// clear up the remaining expressions and discard if they existed
|
|
if expressions_iter.peek().is_some() {
|
|
while let Some(expression) = expressions_iter.next() {
|
|
traversal.compile(&expression.inner_ref())?
|
|
}
|
|
traversal.push_code(Piece::<()>::null().swap(Code::Discard(index)));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Any 'dangling' scopes that weren't closed (declarations) will be at the end of this block
|
|
let return_index = traversal.index().unwrap_or(0);
|
|
if let Some(return_expressions) = &self.it.return_exp {
|
|
// arguments are backwards
|
|
for return_expression in return_expressions.iter().rev() {
|
|
traversal.compile(&return_expression.inner_ref())?;
|
|
}
|
|
if !return_expressions.is_empty() {
|
|
traversal.push_code(
|
|
return_expressions
|
|
.first()
|
|
.unwrap()
|
|
.swap(Code::Return(return_index)),
|
|
);
|
|
}
|
|
}
|
|
for _ in 0..depth {
|
|
traversal.close_scope();
|
|
}
|
|
assert_eq!(traversal.index().unwrap_or(0), last_scope_index);
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl Compile for Chunk {
|
|
fn compile<S>(&self, traversal: &mut Traversal<S>) -> Result<(), LoadError> {
|
|
traversal.compile(&self.0.inner_ref())
|
|
}
|
|
}
|
|
|
|
pub(crate) fn compile<S>(chunk: &Chunk, machine: &mut Machine<S>) -> Result<Prototype, LoadError> {
|
|
let mut traversal = Traversal::new(machine);
|
|
traversal.compile(chunk)?;
|
|
traversal.close_context()
|
|
}
|