use super::{variables::VariableAllocation, CodeGenerator, Expression, ExpressionResult};
use crate::ll::{
ast::{Ast, NodeId, NodeKind},
bytecode::{Opcode, Opr24},
error::{LanguageError, LanguageErrorKind},
};
#[derive(Debug, Default)]
pub(super) struct BreakableBlock {
pub(super) breaks: Vec<usize>,
start: usize,
}
impl<'e> CodeGenerator<'e> {
pub(super) fn push_breakable_block(&mut self) {
let start = self.chunk.emit(Opcode::Nop);
self.breakable_blocks.push(BreakableBlock { breaks: Vec::new(), start });
}
pub(super) fn pop_breakable_block(&mut self) {
let block = self.breakable_blocks.pop().unwrap();
if !block.breaks.is_empty() {
self.chunk.patch(block.start, Opcode::EnterBreakableBlock);
for jump in block.breaks {
self.chunk.patch(jump, Opcode::jump_forward(jump, self.chunk.len()).unwrap());
}
self.chunk.emit((Opcode::ExitBreakableBlock, 1));
}
}
}
impl<'e> CodeGenerator<'e> {
pub(super) fn generate_do(
&mut self,
ast: &Ast,
node: NodeId,
) -> Result<ExpressionResult, LanguageError> {
let children = ast.children(node).unwrap();
self.push_scope();
self.generate_node_list(ast, children)?;
self.pop_scope();
Ok(ExpressionResult::Present)
}
pub(super) fn generate_if(
&mut self,
ast: &Ast,
node: NodeId,
) -> Result<ExpressionResult, LanguageError> {
let branches = ast.children(node).unwrap();
let mut jumps_to_end = Vec::new();
for (i, &branch) in branches.iter().enumerate() {
if i > 0 {
self.chunk.emit(Opcode::Discard);
}
let then = ast.children(branch).unwrap();
match ast.kind(branch) {
NodeKind::IfBranch => {
let (condition, _) = ast.node_pair(branch);
self.push_scope();
self.generate_node(ast, condition, Expression::Used)?;
let jump = self.chunk.emit(Opcode::Nop);
self.chunk.emit(Opcode::Discard); self.generate_node_list(ast, then)?;
self.pop_scope();
let jump_to_end = self.chunk.emit(Opcode::Nop);
jumps_to_end.push(jump_to_end);
self.chunk.patch(
jump,
Opcode::jump_forward_if_falsy(jump, self.chunk.len())
.map_err(|_| ast.error(branch, LanguageErrorKind::IfBranchTooLarge))?,
);
}
NodeKind::ElseBranch => {
self.push_scope();
self.generate_node_list(ast, then)?;
self.pop_scope();
}
_ => unreachable!(),
}
}
if ast.kind(*branches.last().unwrap()) != NodeKind::ElseBranch {
self.chunk.emit(Opcode::Discard);
self.chunk.emit(Opcode::PushNil);
}
for jump in jumps_to_end {
self.chunk.patch(
jump,
Opcode::jump_forward(jump, self.chunk.len())
.map_err(|_| ast.error(node, LanguageErrorKind::IfExpressionTooLarge))?,
);
}
Ok(ExpressionResult::Present)
}
pub(super) fn generate_and(
&mut self,
ast: &Ast,
node: NodeId,
) -> Result<ExpressionResult, LanguageError> {
let (left, right) = ast.node_pair(node);
self.push_scope();
self.generate_node(ast, left, Expression::Used)?;
let jump_past_right = self.chunk.emit(Opcode::Nop);
self.chunk.emit(Opcode::Discard);
self.generate_node(ast, right, Expression::Used)?;
self.chunk.patch(
jump_past_right,
Opcode::jump_forward_if_falsy(jump_past_right, self.chunk.len())
.map_err(|_| ast.error(node, LanguageErrorKind::OperatorRhsTooLarge))?,
);
self.pop_scope();
Ok(ExpressionResult::Present)
}
pub(super) fn generate_or(
&mut self,
ast: &Ast,
node: NodeId,
) -> Result<ExpressionResult, LanguageError> {
let (left, right) = ast.node_pair(node);
self.push_scope();
self.generate_node(ast, left, Expression::Used)?;
let jump_past_right = self.chunk.emit(Opcode::Nop);
self.chunk.emit(Opcode::Discard);
self.generate_node(ast, right, Expression::Used)?;
self.chunk.patch(
jump_past_right,
Opcode::jump_forward_if_truthy(jump_past_right, self.chunk.len())
.map_err(|_| ast.error(node, LanguageErrorKind::OperatorRhsTooLarge))?,
);
self.pop_scope();
Ok(ExpressionResult::Present)
}
pub(super) fn generate_conditional_loop(
&mut self,
ast: &Ast,
node: NodeId,
generate_condition: &dyn Fn(&mut CodeGenerator<'_>) -> Result<(), LanguageError>,
generate_body: &dyn Fn(&mut CodeGenerator<'_>) -> Result<(), LanguageError>,
) -> Result<(), LanguageError> {
self.push_scope();
self.push_breakable_block();
let start = self.chunk.len();
generate_condition(self)?;
let jump_to_end = self.chunk.emit(Opcode::Nop);
self.chunk.emit(Opcode::Discard);
generate_body(self)?;
self.chunk.emit(Opcode::Discard);
self.chunk.emit(
Opcode::jump_backward(self.chunk.len(), start)
.map_err(|_| ast.error(node, LanguageErrorKind::LoopTooLarge))?,
);
self.chunk.patch(
jump_to_end,
Opcode::jump_forward_if_falsy(jump_to_end, self.chunk.len())
.map_err(|_| ast.error(node, LanguageErrorKind::LoopTooLarge))?,
);
self.chunk.emit(Opcode::Discard);
self.chunk.emit(Opcode::PushNil);
self.pop_breakable_block();
self.pop_scope();
Ok(())
}
pub(super) fn generate_while(
&mut self,
ast: &Ast,
node: NodeId,
) -> Result<ExpressionResult, LanguageError> {
let (condition, _) = ast.node_pair(node);
let body = ast.children(node).unwrap();
self.generate_conditional_loop(
ast,
node,
&|generator| generator.generate_node(ast, condition, Expression::Used),
&|generator| generator.generate_node_list(ast, body),
)?;
Ok(ExpressionResult::Present)
}
pub(super) fn generate_for(
&mut self,
ast: &Ast,
node: NodeId,
) -> Result<ExpressionResult, LanguageError> {
let (binding, iterator) = ast.node_pair(node);
let body = ast.children(node).unwrap();
self.push_scope();
let iterator_var = self
.create_variable("<iterator>", VariableAllocation::Allocate)
.map_err(|kind| ast.error(iterator, kind))?;
self.generate_node(ast, iterator, Expression::Used)?;
self.generate_variable_sink(iterator_var);
self.generate_conditional_loop(
ast,
node,
&|generator| {
generator.generate_variable_load(iterator_var);
generator.chunk.emit((
Opcode::CallMethod,
Opr24::pack((generator.builtin_traits.iterator_has_next.to_u16(), 1)),
));
Ok(())
},
&|generator| {
generator.generate_variable_load(iterator_var);
generator.chunk.emit((
Opcode::CallMethod,
Opr24::pack((generator.builtin_traits.iterator_next.to_u16(), 1)),
));
generator.generate_pattern_destructuring(ast, binding)?;
generator.generate_node_list(ast, body)?;
Ok(())
},
)?;
self.pop_scope();
Ok(ExpressionResult::Present)
}
pub(super) fn generate_break(
&mut self,
ast: &Ast,
node: NodeId,
) -> Result<ExpressionResult, LanguageError> {
let (right, _) = ast.node_pair(node);
if right != NodeId::EMPTY {
self.generate_node(ast, right, Expression::Used)?;
} else {
let _ = self.generate_nil();
}
let jump = self.chunk.emit(Opcode::Nop);
if let Some(block) = self.breakable_blocks.last_mut() {
block.breaks.push(jump);
} else {
return Err(ast.error(node, LanguageErrorKind::BreakOutsideOfLoop));
}
Ok(ExpressionResult::NoReturn)
}
pub(super) fn generate_return(
&mut self,
ast: &Ast,
node: NodeId,
) -> Result<ExpressionResult, LanguageError> {
let (value, _) = ast.node_pair(node);
if value == NodeId::EMPTY {
let _ = self.generate_nil();
} else {
self.generate_node(ast, value, Expression::Used)?;
}
self.chunk.emit(Opcode::Return);
Ok(ExpressionResult::NoReturn)
}
}