use brink_format::CountingFlags;
use crate::hir;
use super::context::LowerCtx;
use super::expr::lower_expr;
use super::lir;
use super::recognize::build_source_location;
pub fn lower_content(content: &hir::Content, ctx: &mut LowerCtx<'_>) -> lir::Content {
let source_location = build_source_location(content, ctx);
lir::Content {
parts: lower_content_parts(&content.parts, ctx),
tags: content
.tags
.iter()
.map(|t| lower_content_parts(&t.parts, ctx))
.collect(),
source_location,
}
}
pub fn lower_content_parts_pub(
parts: &[hir::ContentPart],
ctx: &mut LowerCtx<'_>,
) -> Vec<lir::ContentPart> {
lower_content_parts(parts, ctx)
}
fn lower_content_parts(
parts: &[hir::ContentPart],
ctx: &mut LowerCtx<'_>,
) -> Vec<lir::ContentPart> {
let mut out = Vec::with_capacity(parts.len());
for part in parts {
lower_content_part_into(part, ctx, &mut out);
}
out
}
fn lower_content_part_into(
part: &hir::ContentPart,
ctx: &mut LowerCtx<'_>,
out: &mut Vec<lir::ContentPart>,
) {
if let hir::ContentPart::Span(span) = part {
for child in &span.children {
lower_content_part_into(child, ctx, out);
}
return;
}
out.push(lower_content_part(part, ctx));
}
fn lower_content_part(part: &hir::ContentPart, ctx: &mut LowerCtx<'_>) -> lir::ContentPart {
match part {
hir::ContentPart::Text(t) => lir::ContentPart::Text(t.clone()),
hir::ContentPart::Glue => lir::ContentPart::Glue,
hir::ContentPart::Spring => lir::ContentPart::Spring,
hir::ContentPart::Interpolation(expr) => {
lir::ContentPart::Interpolation(lower_expr(expr, ctx))
}
hir::ContentPart::InlineConditional(cond) => {
let branches = cond
.branches
.iter()
.map(|b| {
ctx.push_block_scope();
let condition = match (b.condition.as_ref(), b.binding.as_ref()) {
(Some(e), Some(binding)) => {
Some(super::blocks::lower_bound_condition(e, binding, ctx))
}
(Some(e), None) => Some(lower_expr(e, ctx)),
(None, _) => None,
};
let body = lower_inline_block(&b.body, ctx);
ctx.pop_block_scope();
lir::CondBranch { condition, body }
})
.collect();
lir::ContentPart::InlineConditional(lir::Conditional {
kind: lir::CondKind::InitialCondition,
branches,
})
}
hir::ContentPart::InlineSequence(seq) => lower_inline_sequence(seq, ctx),
hir::ContentPart::Span(_) => unreachable!("Span is intercepted by lower_content_part_into"),
}
}
pub(super) fn sequence_counting_flags(seq: &hir::Sequence) -> CountingFlags {
if seq.counter_id.is_some() {
CountingFlags::empty()
} else {
CountingFlags::VISITS | CountingFlags::COUNT_START_ONLY
}
}
fn lower_inline_sequence(seq: &hir::Sequence, ctx: &mut LowerCtx<'_>) -> lir::ContentPart {
let seq_idx = ctx
.pending_children
.iter()
.filter(|c| c.kind == lir::ContainerKind::Sequence)
.count();
let wrapper_id = seq
.container_id
.unwrap_or_else(|| ctx.alloc_sequence_id(seq_idx));
if seq.container_id.is_some() && seq.counter_id.is_none() {
ctx.ids.mark_bodied_emitted(wrapper_id);
}
let branches = seq
.branches
.iter()
.map(|b| lower_inline_block(&b.body, ctx))
.collect();
let provenance = ctx.enter_stmt(seq.ptr);
let display_name = format!("s-{seq_idx}");
let wrapper = lir::Container {
id: wrapper_id,
provenance,
name: Some(display_name),
kind: lir::ContainerKind::Sequence,
params: Vec::new(),
body: vec![lir::Stmt::new(
lir::StmtKind::Sequence(lir::Sequence {
kind: seq.kind,
branches,
counter: seq.counter_id,
}),
provenance,
)],
children: Vec::new(),
counting_flags: sequence_counting_flags(seq),
temp_slot_count: 0,
labeled: false,
inline: false,
is_function: false,
local: false,
};
ctx.pending_children.push(wrapper);
lir::ContentPart::EnterSequence(wrapper_id)
}
fn lower_inline_block(block: &hir::Block, ctx: &mut LowerCtx<'_>) -> Vec<lir::Stmt> {
let mut stmts = Vec::new();
for stmt in &block.stmts {
if let hir::Stmt::LogicBlock(lb) = stmt {
stmts.extend(super::blocks::lower_logic_block(lb, ctx));
} else if let hir::Stmt::Assignment(a) = stmt
&& super::blocks::try_lower_field_assignment(a, ctx, &mut stmts)
{
} else if let hir::Stmt::Assignment(a) = stmt
&& super::blocks::try_lower_indexed_assignment(a, ctx, &mut stmts)
{
} else if let hir::Stmt::ExprStmt(e) = stmt
&& super::blocks::try_lower_postfix_stmt(e, ctx, &mut stmts)
{
} else if let hir::Stmt::ExprStmt(e) = stmt
&& super::blocks::try_lower_mutator_stmt(e, ctx, &mut stmts)
{
} else if let Some(s) = super::stmts::lower_stmt(stmt, ctx) {
stmts.push(s);
}
}
stmts
}