// 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, contexts: Vec, // Stack of function prototypes being compiled } pub(crate) struct Context { // Everything known about a closure at a time scopes: Vec, vararg: Option>>, constants: Map, // 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, // 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<'_, S> { 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::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 { 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 { 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) -> Traversal { Traversal { machine, contexts: vec![Context { scopes: vec![Scope { // Dummy scope index: 0, jumps: vec![], labels: vec![], symbol: None, upvalue: false, }], vararg: Some(Piece::::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 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.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( name: Piece, traversal: &mut Traversal, 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( 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> { 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) -> 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) -> 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) -> 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) -> 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(|it| Value::Integer(it)) .unwrap_or(Value::Number( number .0 .parse::() .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 { Ok((there as i64 - here as i64) as Offset) } fn resolve_break( traversal: &mut Traversal, 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( 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.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(&self, traversal: &mut Traversal) -> Result<(), LoadError> { let last_scope_index = traversal.index().unwrap_or(0); let mut depth = 0; // Counter for 'dangling' scopes produced by any declarations to be closed. // todo: for some reason, register indices are called "_index" and code indices "index_"? 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, expression: &Piece, name: &Piece| -> 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> = 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> = 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(&self, traversal: &mut Traversal) -> Result<(), LoadError> { traversal.compile(&self.0.inner_ref()) } } pub(crate) fn compile(chunk: &Chunk, machine: &mut Machine) -> Result { let mut traversal = Traversal::new(machine); traversal.compile(chunk)?; traversal.close_context() }