use std::collections::HashSet;
use crate::analyze::type_check::{TypeContext, TypeId, TypeShape};
use crate::bytecode::EffectIR;
use crate::parser::ast::{self, Expr};
use plotnik_bytecode::EffectOpcode;
use super::Compiler;
use super::navigation::{inner_creates_scope, is_star_or_plus_quantifier, is_truly_empty_scope};
#[derive(Clone, Default)]
pub struct CaptureEffects {
pub pre: Vec<EffectIR>,
pub post: Vec<EffectIR>,
}
impl CaptureEffects {
pub fn new(pre: Vec<EffectIR>, post: Vec<EffectIR>) -> Self {
Self { pre, post }
}
pub fn new_pre(pre: Vec<EffectIR>) -> Self {
Self { pre, post: vec![] }
}
pub fn new_post(post: Vec<EffectIR>) -> Self {
Self { pre: vec![], post }
}
pub fn nest_scope(mut self, open: EffectIR, close: EffectIR) -> Self {
assert!(
matches!(
open.opcode,
EffectOpcode::Obj
| EffectOpcode::Enum
| EffectOpcode::Arr
| EffectOpcode::SuppressBegin
),
"nest_scope expects scope-opening effect, got {:?}",
open.opcode
);
assert!(
matches!(
close.opcode,
EffectOpcode::EndObj
| EffectOpcode::EndEnum
| EffectOpcode::EndArr
| EffectOpcode::SuppressEnd
),
"nest_scope expects scope-closing effect, got {:?}",
close.opcode
);
self.pre.push(open);
self.post.insert(0, close);
self
}
pub fn with_pre_values(mut self, effects: Vec<EffectIR>) -> Self {
self.pre.extend(effects);
self
}
pub fn with_post_values(mut self, effects: Vec<EffectIR>) -> Self {
self.post.splice(0..0, effects);
self
}
}
impl Compiler<'_> {
pub(super) fn build_capture_effects(
&self,
cap: &ast::CapturedExpr,
inner: Option<&Expr>,
) -> Vec<EffectIR> {
let mut effects = Vec::with_capacity(2);
let is_structured_ref = inner.is_some_and(|i| self.is_ref_returning_structured(i));
let is_array = is_star_or_plus_quantifier(inner);
let creates_structured_scope = inner.and_then(unwrap_field_value).is_some_and(|ei| {
if is_truly_empty_scope(&ei) {
return true;
}
if !inner_creates_scope(&ei) {
return false;
}
let Some(info) = self.ctx.type_ctx.get_term_info(&ei) else {
return false;
};
info.flow
.type_id()
.and_then(|id| self.ctx.type_ctx.get_type(id))
.is_some_and(|shape| matches!(shape, TypeShape::Struct(_) | TypeShape::Enum(_)))
});
if !is_structured_ref && !creates_structured_scope && !is_array {
let effect = if cap.has_string_annotation() {
EffectIR::text()
} else {
EffectIR::node()
};
effects.push(effect);
}
if let Some(name_token) = cap.name() {
let capture_name = &name_token.text()[1..]; let member_ref = self.lookup_member_in_scope(capture_name);
if let Some(member_ref) = member_ref {
effects.push(EffectIR::with_member(EffectOpcode::Set, member_ref));
}
}
effects
}
pub(super) fn quantifier_needs_node_for_push(&self, expr: &Expr) -> bool {
let Expr::QuantifiedExpr(quant) = expr else {
return true;
};
let Some(inner) = quant.inner() else {
return true;
};
if self.is_ref_returning_structured(&inner) {
return false;
}
let Some(info) = self.ctx.type_ctx.get_term_info(&inner) else {
return true;
};
!info
.flow
.type_id()
.and_then(|id| self.ctx.type_ctx.get_type(id))
.is_some_and(|shape| matches!(shape, TypeShape::Struct(_) | TypeShape::Enum(_)))
}
pub(super) fn is_ref_returning_structured(&self, expr: &Expr) -> bool {
match expr {
Expr::Ref(r) => self.ref_returns_structured(r),
Expr::QuantifiedExpr(q) => q
.inner()
.is_some_and(|i| self.is_ref_returning_structured(&i)),
Expr::CapturedExpr(c) => c
.inner()
.is_some_and(|i| self.is_ref_returning_structured(&i)),
Expr::FieldExpr(f) => f
.value()
.is_some_and(|v| self.is_ref_returning_structured(&v)),
_ => false,
}
}
fn ref_returns_structured(&self, r: &ast::Ref) -> bool {
r.name()
.and_then(|name| self.ctx.type_ctx.get_def_id(self.ctx.interner, name.text()))
.and_then(|def_id| self.ctx.type_ctx.get_def_type(def_id))
.and_then(|def_type| self.ctx.type_ctx.get_type(def_type))
.is_some_and(|shape| {
matches!(
shape,
TypeShape::Struct(_) | TypeShape::Enum(_) | TypeShape::Array { .. }
)
})
}
pub(super) fn collect_captures(expr: &Expr) -> HashSet<String> {
fn collect(expr: &Expr, names: &mut HashSet<String>) {
if let Expr::CapturedExpr(cap) = expr
&& let Some(name) = cap.name()
{
names.insert(name.text()[1..].to_string()); }
for child in expr.children() {
collect(&child, names);
}
}
let mut names = HashSet::new();
collect(expr, &mut names);
names
}
}
fn unwrap_field_value(expr: &Expr) -> Option<Expr> {
match expr {
Expr::FieldExpr(f) => f.value(),
other => Some(other.clone()),
}
}
pub fn check_needs_struct_wrapper(inner: &Expr, type_ctx: &TypeContext) -> bool {
let Some(info) = type_ctx.get_term_info(inner) else {
return false;
};
if !info.flow.is_bubble() {
return false;
}
info.flow
.type_id()
.and_then(|id| type_ctx.get_type(id))
.is_some_and(|shape| matches!(shape, TypeShape::Struct(_)))
}
pub fn get_row_type_id(inner: &Expr, type_ctx: &TypeContext) -> Option<TypeId> {
type_ctx
.get_term_info(inner)
.and_then(|info| info.flow.type_id())
}