use crate::compiling::v1::assemble::prelude::*;
impl AssembleClosure for ast::Block {
fn assemble_closure(
&self,
c: &mut Compiler<'_>,
captures: &[CompileMetaCapture],
) -> CompileResult<()> {
let span = self.span();
log::trace!("ExprBlock (procedure) => {:?}", c.source.source(span));
let guard = c.scopes.push_child(span)?;
for capture in captures {
c.scopes.new_var(&capture.ident, span)?;
}
self.assemble(c, Needs::Value)?.apply(c)?;
c.clean_last_scope(span, guard, Needs::Value)?;
c.asm.push(Inst::Return, span);
Ok(())
}
}
impl Assemble for ast::Block {
fn assemble(&self, c: &mut Compiler<'_>, needs: Needs) -> CompileResult<Asm> {
let span = self.span();
log::trace!("Block => {:?}", c.source.source(span));
c.contexts.push(span);
let scopes_count = c.scopes.push_child(span)?;
let mut last = None::<(&ast::Expr, bool)>;
for stmt in &self.statements {
let (expr, term) = match stmt {
ast::Stmt::Local(local) => {
if let Some((stmt, _)) = std::mem::take(&mut last) {
stmt.assemble(c, Needs::None)?.apply(c)?;
}
local.assemble(c, Needs::None)?.apply(c)?;
continue;
}
ast::Stmt::Expr(expr, semi) => (expr, semi.is_some()),
ast::Stmt::Item(..) => continue,
};
if let Some((stmt, _)) = std::mem::replace(&mut last, Some((expr, term))) {
stmt.assemble(c, Needs::None)?.apply(c)?;
}
}
let produced = if let Some((expr, term)) = last {
if term {
expr.assemble(c, Needs::None)?.apply(c)?;
false
} else {
expr.assemble(c, needs)?.apply(c)?;
true
}
} else {
false
};
let scope = c.scopes.pop(scopes_count, span)?;
if needs.value() {
if produced {
c.locals_clean(scope.local_var_count, span);
} else {
c.locals_pop(scope.local_var_count, span);
c.asm.push(Inst::unit(), span);
}
} else {
c.locals_pop(scope.local_var_count, span);
}
c.contexts
.pop()
.ok_or_else(|| CompileError::msg(&span, "missing parent context"))?;
Ok(Asm::top(span))
}
}