use crate::bytecode::{Instruction, OpCode, Operand};
use shape_ast::ast::Expr;
use shape_ast::error::{Result, ShapeError};
use super::super::BytecodeCompiler;
impl BytecodeCompiler {
pub(super) fn compile_expr_break(&mut self, value_expr: &Option<Box<Expr>>) -> Result<()> {
if self.loop_stack.is_empty() {
return Err(ShapeError::RuntimeError {
message: "break expression outside of loop".to_string(),
location: None,
});
}
let break_value_local = self.loop_stack.last().and_then(|ctx| ctx.break_value_local);
if let Some(value) = value_expr {
self.compile_expr(value)?;
} else if break_value_local.is_some() {
self.emit(Instruction::simple(OpCode::PushNull));
}
if let Some(local_idx) = break_value_local {
self.emit(Instruction::new(
OpCode::StoreLocal,
Some(Operand::Local(local_idx)),
));
} else if value_expr.is_some() {
self.emit(Instruction::simple(OpCode::Pop));
}
let scopes_to_exit = self
.loop_stack
.last()
.map(|ctx| self.drop_locals.len().saturating_sub(ctx.drop_scope_depth))
.unwrap_or(0);
if scopes_to_exit > 0 {
self.emit_drops_for_early_exit(scopes_to_exit)?;
}
if self
.loop_stack
.last()
.map_or(false, |ctx| ctx.iterator_on_stack)
{
self.emit(Instruction::simple(OpCode::Pop));
}
let jump_idx = self.emit_jump(OpCode::Jump, 0);
if let Some(loop_ctx) = self.loop_stack.last_mut() {
loop_ctx.break_jumps.push(jump_idx);
}
Ok(())
}
pub(super) fn compile_expr_continue(&mut self) -> Result<()> {
if let Some(loop_ctx) = self.loop_stack.last() {
let scopes_to_exit = self
.drop_locals
.len()
.saturating_sub(loop_ctx.drop_scope_depth);
let continue_target = loop_ctx.continue_target;
if scopes_to_exit > 0 {
self.emit_drops_for_early_exit(scopes_to_exit)?;
}
if continue_target == usize::MAX {
let jump_idx = self.emit_jump(OpCode::Jump, 0);
if let Some(loop_ctx) = self.loop_stack.last_mut() {
loop_ctx.continue_jumps.push(jump_idx);
}
} else {
let offset = continue_target as i32 - self.program.current_offset() as i32 - 1;
self.emit(Instruction::new(
OpCode::Jump,
Some(Operand::Offset(offset)),
));
}
} else {
return Err(ShapeError::RuntimeError {
message: "continue expression outside of loop".to_string(),
location: None,
});
}
Ok(())
}
pub(super) fn compile_expr_return(&mut self, value_expr: &Option<Box<Expr>>) -> Result<()> {
if let Some(expr) = value_expr {
self.plan_flexible_binding_escape_from_expr(expr);
if self.current_function_return_reference_summary.is_some() {
self.compile_expr_preserving_refs(expr)?;
} else {
self.compile_expr(expr)?;
}
} else {
self.emit_unit();
}
let total_scopes = self.drop_locals.len();
if total_scopes > 0 {
self.emit_drops_for_early_exit(total_scopes)?;
}
self.emit(Instruction::simple(OpCode::ReturnValue));
Ok(())
}
}