use super::parser::Item;
use crate::engine::error::PrefixError;
use crate::engine::grammar::SPG;
use crate::engine::parse::TypedParser;
use crate::engine::parse::arena::{
ArenaNode, ChildRef, EffectId, EvidenceId, NodeStatus, Span, TOP,
};
use crate::semantics::SemanticSummary;
enum Elaboration {
Bare,
Transparent,
Structural,
Opaque,
}
impl Elaboration {
fn classify(grammar: &SPG, item: &Item, status: NodeStatus) -> Self {
if status == NodeStatus::Prefix && item.start == item.pos {
return Self::Bare;
}
if grammar.is_transparent(item.prod) {
return Self::Transparent;
}
if grammar.rule_of_prod(item.prod).is_none() {
return Self::Structural;
}
Self::Opaque
}
}
impl TypedParser {
pub(super) fn elaborate(
&self,
item: &Item,
status: NodeStatus,
summary: Option<&SemanticSummary>,
) -> Result<(EvidenceId, Option<EffectId>), PrefixError> {
let exact = status == NodeStatus::Exact;
match Elaboration::classify(&self.grammar, item, status) {
Elaboration::Bare => Ok((TOP, None)),
Elaboration::Structural => {
let nt = self.grammar.nt(item.prod.0).unwrap_or("?");
let kids = self.child_node_evidence(item);
let span = Span {
start: item.start as u32,
end: item.pos as u32,
};
Ok((self.typing.structural_evidence(nt, &kids, span), None))
}
Elaboration::Opaque => Ok(summary
.map_or((TOP, None), |s| (s.evidence, if exact { s.effect } else { None }))),
Elaboration::Transparent => {
let evidence = self
.first_child_node(item)
.map_or(TOP, |child| child.evidence);
let effect = if exact {
let mut effects = self.child_effects(item);
if let Some(local) = summary.and_then(|s| s.effect) {
effects.push(local);
}
self.typing.compose_effects(effects).map_err(|e| {
PrefixError::rejected(
item.pos,
format!("semantic effect composition failed: {e:?}"),
)
})?
} else {
None
};
Ok((evidence, effect))
}
}
}
fn first_child_node(&self, item: &Item) -> Option<ArenaNode> {
item.children.iter().find_map(|child| match child {
ChildRef::Node(id) => self.arena.node(*id).map(|n| n.clone()),
ChildRef::Terminal(_) => None,
})
}
fn child_node_evidence(&self, item: &Item) -> Vec<EvidenceId> {
item.children
.iter()
.filter_map(|child| match child {
ChildRef::Node(id) => self.arena.node(*id).map(|n| n.evidence),
ChildRef::Terminal(_) => None,
})
.collect()
}
fn child_effects(&self, item: &Item) -> Vec<EffectId> {
item.children
.iter()
.filter_map(|child| match child {
ChildRef::Node(id) => self.arena.node(*id).and_then(|n| n.effect),
ChildRef::Terminal(_) => None,
})
.collect()
}
}