use std::collections::btree_map::Entry;
use std::collections::{BTreeMap, BTreeSet};
use crate::algebra::signature::{FuncId, PredId, Signature, TypeId, TypeKind};
use crate::algebra::structure::{
ConcreteType, ElId, ElMap, FuncApp, PredApp, Structure, StructureCat, StructureId,
};
use crate::ast::*;
use crate::error::CompileError;
use crate::scopes::{Scope, ScopeId, Scopes, Symbol};
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum MorphismKind {
If,
SurjThen,
NonSurjThen,
Noop,
}
#[derive(Clone, Debug, Default)]
pub struct RuleStructures {
pub cat: StructureCat,
pub semantic_els: Vec<BTreeMap<TermId, ElId>>,
pub stmt_before: BTreeMap<StmtId, StructureId>,
pub stmt_after: BTreeMap<StmtId, StructureId>,
pub morphism_kinds: BTreeMap<(StructureId, StructureId), MorphismKind>,
pub branch_block_starts: BTreeMap<(BranchStmtId, usize), StructureId>,
pub match_after_scrutinee: BTreeMap<MatchStmtId, StructureId>,
pub match_case_starts: BTreeMap<MatchCaseId, StructureId>,
}
impl RuleStructures {
fn push_blank(&mut self) -> StructureId {
let id = self.cat.push(Structure::default());
self.semantic_els.push(BTreeMap::new());
id
}
fn clone_structure(&mut self, id: StructureId) -> StructureId {
let clone = self.cat.structures[id.0].clone();
let identity = identity_elmap(&clone);
let new_id = self.cat.push(clone);
self.semantic_els.push(BTreeMap::new());
self.cat.add_morphism(id, new_id, identity);
new_id
}
}
fn identity_elmap(s: &Structure) -> ElMap {
s.els.keys().map(|&el| (el, el)).collect()
}
fn register_under_prod_projections(
rule: &mut RuleStructures,
top: StructureId,
meet: StructureId,
ends: &[StructureId],
) {
for &end in ends {
let map = identity_elmap(&rule.cat.structures[meet.0]);
rule.cat.add_morphism(meet, end, map);
}
rule.cat.add_under_prod(top, meet, ends.to_vec());
}
pub fn walk_rule(
rule: &mut RuleStructures,
rid: RuleDeclId,
enclosing_models: &[TypeId],
ast: &Ast,
scopes: &Scopes,
signature: &Signature,
errors: &mut Vec<CompileError>,
) -> bool {
let mut changed = false;
let initial = if rule.cat.structures.is_empty() {
changed = true;
rule.push_blank()
} else {
StructureId(0)
};
changed |= ensure_ambient_els(&mut rule.cat.structures[initial.0], enclosing_models);
let body = ast.rule_decl(rid).body.clone();
let (_after, walk_changed) =
walk_stmt_block(&body, initial, rule, ast, scopes, signature, errors);
changed | walk_changed
}
fn walk_stmt_block(
stmts: &[StmtId],
mut current: StructureId,
rule: &mut RuleStructures,
ast: &Ast,
scopes: &Scopes,
signature: &Signature,
errors: &mut Vec<CompileError>,
) -> (StructureId, bool) {
let mut changed = false;
for stmt in stmts {
let before = match rule.stmt_before.entry(*stmt) {
Entry::Vacant(v) => {
v.insert(current);
changed = true;
current
}
Entry::Occupied(o) => *o.get(),
};
debug_assert_eq!(
before, current,
"stmt_before for {stmt:?} drifted between populate calls"
);
let (after, stmt_changed) = walk_stmt(*stmt, before, rule, ast, scopes, signature, errors);
changed |= stmt_changed;
current = after;
}
(current, changed)
}
fn walk_stmt(
stmt: StmtId,
current: StructureId,
rule: &mut RuleStructures,
ast: &Ast,
scopes: &Scopes,
signature: &Signature,
errors: &mut Vec<CompileError>,
) -> (StructureId, bool) {
match *ast.stmt(stmt) {
Stmt::If(id) => {
let (next, mut changed) = ensure_stmt_after(rule, stmt, current, MorphismKind::If);
let atom = ast.if_stmt(id).atom;
changed |= walk_if_atom(atom, next, rule, ast, scopes, signature, errors);
(next, changed)
}
Stmt::Then(id) => {
let atom = ast.then_stmt(id).atom;
let kind = match *ast.then_atom(atom) {
ThenAtom::Equal(_) | ThenAtom::Pred(_) => MorphismKind::SurjThen,
ThenAtom::Defined(_) => MorphismKind::NonSurjThen,
};
let (next, mut changed) = ensure_stmt_after(rule, stmt, current, kind);
changed |= walk_then_atom(atom, next, rule, ast, scopes, signature, errors);
(next, changed)
}
Stmt::Branch(id) => {
let blocks = ast.branch_stmt(id).blocks.clone();
let (after, after_was_new) = ensure_stmt_after(rule, stmt, current, MorphismKind::Noop);
let mut changed = after_was_new;
let mut block_ends: Vec<StructureId> = Vec::with_capacity(blocks.len());
for (idx, block) in blocks.iter().enumerate() {
let (block_start, c1) = ensure_branch_block_start(rule, id, idx, current);
changed |= c1;
let (block_end, c2) =
walk_stmt_block(block, block_start, rule, ast, scopes, signature, errors);
changed |= c2;
block_ends.push(block_end);
}
if after_was_new {
register_under_prod_projections(rule, current, after, &block_ends);
}
(after, changed)
}
Stmt::Match(id) => {
let MatchStmt { term, cases } = ast.match_stmt(id);
let term = *term;
let cases = cases.clone();
let (after_scrutinee, mut changed) = ensure_match_after_scrutinee(rule, id, current);
let (term_el, c1) =
walk_term(term, after_scrutinee, rule, ast, scopes, signature, errors);
changed |= c1;
let (after, after_was_new) =
ensure_stmt_after(rule, stmt, after_scrutinee, MorphismKind::Noop);
changed |= after_was_new;
let mut case_ends: Vec<StructureId> = Vec::with_capacity(cases.len());
for case in &cases {
let MatchCase { pattern, body } = ast.match_case(*case).clone();
let (case_start, c2) = ensure_match_case_start(rule, *case, after_scrutinee);
changed |= c2;
let (pattern_el, c3) =
walk_term(pattern, case_start, rule, ast, scopes, signature, errors);
changed |= c3;
changed |= rule.cat.structures[case_start.0].equate(term_el, pattern_el);
let (case_end, c4) =
walk_stmt_block(&body, case_start, rule, ast, scopes, signature, errors);
changed |= c4;
case_ends.push(case_end);
}
if after_was_new {
register_under_prod_projections(rule, after_scrutinee, after, &case_ends);
}
(after, changed)
}
}
}
fn ensure_stmt_after(
rule: &mut RuleStructures,
stmt: StmtId,
src: StructureId,
kind: MorphismKind,
) -> (StructureId, bool) {
if let Some(&id) = rule.stmt_after.get(&stmt) {
return (id, false);
}
let id = rule.clone_structure(src);
rule.stmt_after.insert(stmt, id);
rule.morphism_kinds.insert((src, id), kind);
(id, true)
}
fn ensure_branch_block_start(
rule: &mut RuleStructures,
branch: BranchStmtId,
idx: usize,
src: StructureId,
) -> (StructureId, bool) {
if let Some(&id) = rule.branch_block_starts.get(&(branch, idx)) {
return (id, false);
}
let id = rule.clone_structure(src);
rule.branch_block_starts.insert((branch, idx), id);
(id, true)
}
fn ensure_match_after_scrutinee(
rule: &mut RuleStructures,
match_id: MatchStmtId,
src: StructureId,
) -> (StructureId, bool) {
if let Some(&id) = rule.match_after_scrutinee.get(&match_id) {
return (id, false);
}
let id = rule.clone_structure(src);
rule.match_after_scrutinee.insert(match_id, id);
(id, true)
}
fn ensure_match_case_start(
rule: &mut RuleStructures,
case: MatchCaseId,
src: StructureId,
) -> (StructureId, bool) {
if let Some(&id) = rule.match_case_starts.get(&case) {
return (id, false);
}
let id = rule.clone_structure(src);
rule.match_case_starts.insert(case, id);
(id, true)
}
fn walk_if_atom(
atom: IfAtomId,
current: StructureId,
rule: &mut RuleStructures,
ast: &Ast,
scopes: &Scopes,
signature: &Signature,
errors: &mut Vec<CompileError>,
) -> bool {
match *ast.if_atom(atom) {
IfAtom::Equal(id) => {
let EqualAtom { lhs, rhs } = *ast.equal_atom(id);
let (lhs_el, c1) = walk_term(lhs, current, rule, ast, scopes, signature, errors);
let (rhs_el, c2) = walk_term(rhs, current, rule, ast, scopes, signature, errors);
let eq = rule.cat.structures[current.0].equate(lhs_el, rhs_el);
c1 || c2 || eq
}
IfAtom::Defined(id) => {
let DefinedIfAtom { term } = *ast.defined_if_atom(id);
let (_el, c) = walk_term(term, current, rule, ast, scopes, signature, errors);
c
}
IfAtom::Pred(id) => walk_pred_atom(id, current, rule, ast, scopes, signature, errors),
IfAtom::Var(id) => {
let VarIfAtom { term, typ } = *ast.var_if_atom(id);
let (cts, mut changed) =
walk_var_type_expr(typ, current, rule, ast, scopes, signature, errors);
let el_id = match rule.semantic_els[current.0].get(&term).copied() {
Some(el) => el,
None => {
let structure = &mut rule.cat.structures[current.0];
let el_id = structure.push_el();
rule.semantic_els[current.0].insert(term, el_id);
match *ast.term(term) {
Term::Var(vid) => {
let name = ast.var_term(vid).name.clone();
rule.cat.structures[current.0].var_els.insert(name, el_id);
}
Term::Wildcard => {}
Term::App(_) | Term::Dom(_) | Term::Cod(_) | Term::MorApp(_) => {
unreachable!(
"VarIfAtom lhs must be a variable or wildcard; \
enforced by syntactic.rs"
)
}
}
changed = true;
el_id
}
};
for ct in cts {
changed |= rule.cat.structures[current.0].impose_type(el_id, ct);
}
changed
}
}
}
fn walk_then_atom(
atom: ThenAtomId,
current: StructureId,
rule: &mut RuleStructures,
ast: &Ast,
scopes: &Scopes,
signature: &Signature,
errors: &mut Vec<CompileError>,
) -> bool {
match *ast.then_atom(atom) {
ThenAtom::Equal(id) => {
let EqualAtom { lhs, rhs } = *ast.equal_atom(id);
let (lhs_el, c1) = walk_term(lhs, current, rule, ast, scopes, signature, errors);
let (rhs_el, c2) = walk_term(rhs, current, rule, ast, scopes, signature, errors);
let eq = rule.cat.structures[current.0].equate(lhs_el, rhs_el);
c1 || c2 || eq
}
ThenAtom::Defined(id) => {
let DefinedThenAtom { var, term } = *ast.defined_then_atom(id);
let mut changed = false;
let var_el = if let Some(v) = var {
let (e, c) = walk_term(v, current, rule, ast, scopes, signature, errors);
changed |= c;
Some(e)
} else {
None
};
let (term_el, c) = walk_term(term, current, rule, ast, scopes, signature, errors);
changed |= c;
if let Some(var_el) = var_el {
changed |= rule.cat.structures[current.0].equate(var_el, term_el);
}
changed
}
ThenAtom::Pred(id) => walk_pred_atom(id, current, rule, ast, scopes, signature, errors),
}
}
fn walk_pred_atom(
id: PredAtomId,
current: StructureId,
rule: &mut RuleStructures,
ast: &Ast,
scopes: &Scopes,
signature: &Signature,
errors: &mut Vec<CompileError>,
) -> bool {
let PredAtom { pred, args } = *ast.pred_atom(id);
let arg_terms = ast.term_list(args).terms.clone();
let mut changed = false;
let arg_els: Vec<ElId> = arg_terms
.iter()
.map(|t| {
let (e, c) = walk_term(*t, current, rule, ast, scopes, signature, errors);
changed |= c;
e
})
.collect();
let (resolved, c) = resolve_pred_expr(pred, current, rule, ast, scopes, signature, errors);
changed |= c;
if resolved.is_empty() {
return changed;
}
for (pred_id, parents) in resolved {
let pred_data = signature.pred(pred_id);
if arg_els.len() != pred_data.arity.len() {
errors.push(CompileError::PredicateArgumentNumber {
expected: pred_data.arity.len(),
got: arg_els.len(),
location: ast.loc(id),
});
continue;
}
let structure = &mut rule.cat.structures[current.0];
let parents: Vec<ElId> = parents
.into_iter()
.map(|e| structure.unification.root(e))
.collect();
let canonical_args: Vec<ElId> = arg_els
.iter()
.map(|e| structure.unification.root(*e))
.collect();
changed |= structure.pred_apps.insert(PredApp {
pred: pred_id,
parents,
args: canonical_args,
});
}
changed
}
fn resolve_pred_expr(
pred: PredExprId,
current: StructureId,
rule: &mut RuleStructures,
ast: &Ast,
scopes: &Scopes,
signature: &Signature,
errors: &mut Vec<CompileError>,
) -> (Vec<(PredId, Vec<ElId>)>, bool) {
match *ast.pred_expr(pred) {
PredExpr::Ambient(aid) => {
let scope = scopes.entry(aid);
let name = &ast.ambient_pred_expr(aid).name;
let pred_id = match scopes.lookup(scope, name) {
Some(Symbol::Pred(pd)) => signature.pred_for_pred_decl(pd),
_ => None,
};
let resolved = pred_id
.map(|pid| {
let parents = rule.cat.structures[current.0]
.ambient_parents(&signature.pred(pid).parents);
vec![(pid, parents)]
})
.unwrap_or_default();
(resolved, false)
}
PredExpr::Member(mid) => {
let MemberPredExpr {
term: parent_term,
name,
} = ast.member_pred_expr(mid).clone();
let (parent_el, changed) =
walk_term(parent_term, current, rule, ast, scopes, signature, errors);
let resolved = member_scopes_and_parents(rule, current, parent_el, scopes, signature)
.into_iter()
.filter_map(|(body, parents)| {
let pd = match body.symbols.get(&name).copied()? {
Symbol::Pred(pd) => pd,
_ => return None,
};
let pid = signature.pred_for_pred_decl(pd)?;
Some((pid, parents))
})
.collect();
(resolved, changed)
}
}
}
fn walk_term(
term: TermId,
current: StructureId,
rule: &mut RuleStructures,
ast: &Ast,
scopes: &Scopes,
signature: &Signature,
errors: &mut Vec<CompileError>,
) -> (ElId, bool) {
let prior_el = rule.semantic_els[current.0].get(&term).copied();
let mut changed = false;
let el = match *ast.term(term) {
Term::Var(vid) => {
if let Some(el) = prior_el {
el
} else {
let name = ast.var_term(vid).name.clone();
let structure = &mut rule.cat.structures[current.0];
if let Some(&el_id) = structure.var_els.get(&name) {
el_id
} else {
let el_id = structure.push_el();
structure.var_els.insert(name, el_id);
changed = true;
el_id
}
}
}
Term::Wildcard => {
if let Some(el) = prior_el {
el
} else {
changed = true;
rule.cat.structures[current.0].push_el()
}
}
Term::App(aid) => {
let AppTerm { func, args } = *ast.app_term(aid);
let arg_terms = ast.term_list(args).terms.clone();
let arg_els: Vec<ElId> = arg_terms
.iter()
.map(|t| {
let (e, c) = walk_term(*t, current, rule, ast, scopes, signature, errors);
changed |= c;
e
})
.collect();
let (e, c) = emit_app(
aid, func, arg_els, prior_el, current, rule, ast, scopes, signature, errors,
);
changed |= c;
e
}
Term::Dom(did) => {
let DomTerm { arg } = *ast.dom_term(did);
let (arg_el, c) = walk_term(arg, current, rule, ast, scopes, signature, errors);
changed |= c;
let candidates = resolve_mor_projection_apps(
MorProjection::Dom,
arg_el,
prior_el,
current,
rule,
signature,
);
match emit_known_apps(candidates, vec![arg_el], prior_el, current, rule) {
Some((e, c)) => {
changed |= c;
e
}
None => {
let (e, c) = expected_or_fresh(prior_el, current, rule);
changed |= c;
e
}
}
}
Term::Cod(cid) => {
let CodTerm { arg } = *ast.cod_term(cid);
let (arg_el, c) = walk_term(arg, current, rule, ast, scopes, signature, errors);
changed |= c;
let candidates = resolve_mor_projection_apps(
MorProjection::Cod,
arg_el,
prior_el,
current,
rule,
signature,
);
match emit_known_apps(candidates, vec![arg_el], prior_el, current, rule) {
Some((e, c)) => {
changed |= c;
e
}
None => {
let (e, c) = expected_or_fresh(prior_el, current, rule);
changed |= c;
e
}
}
}
Term::MorApp(mid) => {
let MorAppTerm { mor, arg } = *ast.mor_app_term(mid);
let (mor_el, c1) = walk_term(mor, current, rule, ast, scopes, signature, errors);
let (arg_el, c2) = walk_term(arg, current, rule, ast, scopes, signature, errors);
changed |= c1 || c2;
let candidates =
resolve_mor_app_apps(mor_el, arg_el, prior_el, current, rule, signature);
match emit_known_apps(candidates, vec![mor_el, arg_el], prior_el, current, rule) {
Some((e, c)) => {
changed |= c;
e
}
None => {
let (e, c) = expected_or_fresh(prior_el, current, rule);
changed |= c;
e
}
}
}
};
if prior_el.is_none() {
rule.semantic_els[current.0].insert(term, el);
changed = true;
}
(el, changed)
}
#[derive(Copy, Clone)]
enum MorProjection {
Dom,
Cod,
}
fn resolve_mor_projection_apps(
projection: MorProjection,
arg_el: ElId,
result_el: Option<ElId>,
current: StructureId,
rule: &RuleStructures,
signature: &Signature,
) -> Vec<(FuncId, Vec<ElId>)> {
let mut apps = BTreeSet::new();
for ct in concrete_types_of_el(rule, current, arg_el) {
if let TypeKind::Mor(model_tid) = signature.type_(ct.typ).kind {
let Some(ids) = signature.ids_for_model_type(model_tid) else {
continue;
};
let func = match projection {
MorProjection::Dom => ids.dom,
MorProjection::Cod => ids.cod,
};
apps.insert((func, ct.parents));
}
}
if let Some(result_el) = result_el {
for ct in concrete_types_of_el(rule, current, result_el) {
let Some(ids) = signature.ids_for_model_type(ct.typ) else {
continue;
};
let func = match projection {
MorProjection::Dom => ids.dom,
MorProjection::Cod => ids.cod,
};
apps.insert((func, ct.parents));
}
}
apps.into_iter().collect()
}
fn resolve_mor_app_apps(
mor_el: ElId,
arg_el: ElId,
result_el: Option<ElId>,
current: StructureId,
rule: &RuleStructures,
signature: &Signature,
) -> Vec<(FuncId, Vec<ElId>)> {
let mor_types = concrete_types_of_el(rule, current, mor_el);
let mor_models: BTreeSet<TypeId> = mor_types
.iter()
.filter_map(|ct| match signature.type_(ct.typ).kind {
TypeKind::Mor(model_tid) => Some(model_tid),
_ => None,
})
.collect();
if !mor_types.is_empty() && mor_models.is_empty() {
return Vec::new();
}
let mut apps = BTreeSet::new();
for ct in concrete_types_of_el(rule, current, arg_el) {
insert_mor_app_candidate(signature, &mor_models, &mut apps, ct);
}
if let Some(result_el) = result_el {
for ct in concrete_types_of_el(rule, current, result_el) {
insert_mor_app_candidate(signature, &mor_models, &mut apps, ct);
}
}
apps.into_iter().collect()
}
fn member_model_type(signature: &Signature, ct: &ConcreteType) -> Option<TypeId> {
signature.type_(ct.typ).parents.last().copied()
}
fn insert_mor_app_candidate(
signature: &Signature,
mor_models: &BTreeSet<TypeId>,
apps: &mut BTreeSet<(FuncId, Vec<ElId>)>,
ct: ConcreteType,
) {
let Some(fid) = signature.mor_app_func_for_type(ct.typ) else {
return;
};
if !mor_models.is_empty()
&& !member_model_type(signature, &ct).is_some_and(|m| mor_models.contains(&m))
{
return;
}
let Some((_model_parent, outer_parents)) = ct.parents.split_last() else {
return;
};
apps.insert((fid, outer_parents.to_vec()));
}
fn concrete_types_of_el(
rule: &RuleStructures,
current: StructureId,
el: ElId,
) -> Vec<ConcreteType> {
rule.cat.structures[current.0].concrete_types_of(el)
}
fn expected_or_fresh(
expected: Option<ElId>,
current: StructureId,
rule: &mut RuleStructures,
) -> (ElId, bool) {
match expected {
Some(el) => (el, false),
None => (rule.cat.structures[current.0].push_el(), true),
}
}
fn emit_known_app(
func_id: FuncId,
parents: Vec<ElId>,
arg_els: Vec<ElId>,
expected: Option<ElId>,
current: StructureId,
rule: &mut RuleStructures,
) -> (ElId, bool) {
let structure = &mut rule.cat.structures[current.0];
let parents: Vec<ElId> = parents
.into_iter()
.map(|e| structure.unification.root(e))
.collect();
let canonical_args: Vec<ElId> = arg_els
.iter()
.map(|e| structure.unification.root(*e))
.collect();
let app_key = FuncApp {
func: func_id,
parents,
args: canonical_args,
};
match structure.func_apps.get(&app_key).copied() {
Some(existing) => match expected {
Some(exp) => {
let changed = exp != existing && structure.equate(exp, existing);
(exp, changed)
}
None => (existing, false),
},
None => {
let (result, _allocated) = expected_or_fresh(expected, current, rule);
rule.cat.structures[current.0]
.func_apps
.insert(app_key, result);
(result, true)
}
}
}
fn emit_known_apps(
candidates: Vec<(FuncId, Vec<ElId>)>,
arg_els: Vec<ElId>,
expected: Option<ElId>,
current: StructureId,
rule: &mut RuleStructures,
) -> Option<(ElId, bool)> {
if candidates.is_empty() {
return None;
}
let mut result = expected;
let mut changed = false;
for (func_id, parents) in candidates {
let (el, c) = emit_known_app(func_id, parents, arg_els.clone(), result, current, rule);
changed |= c;
if result.is_none() {
result = Some(el);
}
}
result.map(|el| (el, changed))
}
fn emit_app(
app: AppTermId,
func: FuncExprId,
arg_els: Vec<ElId>,
expected: Option<ElId>,
current: StructureId,
rule: &mut RuleStructures,
ast: &Ast,
scopes: &Scopes,
signature: &Signature,
errors: &mut Vec<CompileError>,
) -> (ElId, bool) {
let (resolved, mut changed) =
resolve_func_expr(func, current, rule, ast, scopes, signature, errors);
if resolved.is_empty() {
let structure = &mut rule.cat.structures[current.0];
return match expected {
Some(el) => (el, changed),
None => (structure.push_el(), true),
};
}
let mut result = expected;
for (func_id, parents) in resolved {
let func_data = signature.func(func_id);
if arg_els.len() != func_data.domain.len() {
errors.push(CompileError::FunctionArgumentNumber {
expected: func_data.domain.len(),
got: arg_els.len(),
location: ast.loc(app),
});
continue;
}
let (el, c) = emit_known_app(func_id, parents, arg_els.clone(), result, current, rule);
changed |= c;
if result.is_none() {
result = Some(el);
}
}
match result {
Some(el) => (el, changed),
None => {
let structure = &mut rule.cat.structures[current.0];
(structure.push_el(), true)
}
}
}
fn resolve_func_expr(
func: FuncExprId,
current: StructureId,
rule: &mut RuleStructures,
ast: &Ast,
scopes: &Scopes,
signature: &Signature,
errors: &mut Vec<CompileError>,
) -> (Vec<(FuncId, Vec<ElId>)>, bool) {
match *ast.func_expr(func) {
FuncExpr::Ambient(aid) => {
let scope = scopes.entry(aid);
let name = &ast.ambient_func_expr(aid).name;
let func_id = match scopes.lookup(scope, name) {
Some(Symbol::Func(fd)) => signature.func_for_func_decl(fd),
Some(Symbol::Ctor(cd)) => signature.func_for_ctor_decl(cd),
_ => None,
};
let resolved = func_id
.map(|fid| {
let parents = rule.cat.structures[current.0]
.ambient_parents(&signature.func(fid).parents);
vec![(fid, parents)]
})
.unwrap_or_default();
(resolved, false)
}
FuncExpr::Member(mid) => {
let MemberFuncExpr {
term: parent_term,
name,
} = ast.member_func_expr(mid).clone();
let (parent_el, changed) =
walk_term(parent_term, current, rule, ast, scopes, signature, errors);
let resolved = member_scopes_and_parents(rule, current, parent_el, scopes, signature)
.into_iter()
.filter_map(|(body, parents)| {
let fid = match body.symbols.get(&name).copied()? {
Symbol::Func(fd) => signature.func_for_func_decl(fd)?,
Symbol::Ctor(cd) => signature.func_for_ctor_decl(cd)?,
_ => return None,
};
Some((fid, parents))
})
.collect();
(resolved, changed)
}
}
}
fn member_scopes_and_parents<'a>(
rule: &RuleStructures,
current: StructureId,
parent_el: ElId,
scopes: &'a Scopes,
signature: &Signature,
) -> Vec<(&'a Scope, Vec<ElId>)> {
let structure = &rule.cat.structures[current.0];
structure
.concrete_types_of(parent_el)
.into_iter()
.filter_map(|ct| {
let model_decl = signature.model_decl_for_type(ct.typ)?;
let body_scope = scopes.unordered(model_decl);
let mut parents = ct.parents;
parents.push(parent_el);
Some((scopes.scope(body_scope), parents))
})
.collect()
}
fn walk_var_type_expr(
type_expr: TypeExprId,
current: StructureId,
rule: &mut RuleStructures,
ast: &Ast,
scopes: &Scopes,
signature: &Signature,
errors: &mut Vec<CompileError>,
) -> (Vec<ConcreteType>, bool) {
let scope: ScopeId = scopes.entry(type_expr);
match *ast.type_expr(type_expr) {
TypeExpr::Ambient(id) => {
let name = &ast.ambient_type_expr(id).name;
let tid = match scopes.lookup(scope, name) {
Some(Symbol::Type(td)) => Some(signature.type_for_type_decl(td)),
Some(Symbol::Enum(ed)) => Some(signature.type_for_enum_decl(ed)),
Some(Symbol::Model(md)) => Some(signature.ids_for_model_decl(md).type_),
_ => None,
};
let structure = &rule.cat.structures[current.0];
(
concrete_type_for(signature, tid, structure)
.into_iter()
.collect(),
false,
)
}
TypeExpr::Mor(id) => {
let name = &ast.mor_type_expr(id).name;
let tid = match scopes.lookup(scope, name) {
Some(Symbol::Model(md)) => Some(signature.ids_for_model_decl(md).mor),
_ => None,
};
let structure = &rule.cat.structures[current.0];
(
concrete_type_for(signature, tid, structure)
.into_iter()
.collect(),
false,
)
}
TypeExpr::Member(mid) => {
let MemberTypeExpr {
term: parent_term,
name,
} = ast.member_type_expr(mid).clone();
let (parent_el, changed) =
walk_term(parent_term, current, rule, ast, scopes, signature, errors);
let resolved = member_scopes_and_parents(rule, current, parent_el, scopes, signature)
.into_iter()
.filter_map(|(body, parents)| {
let tid = match body.symbols.get(&name).copied()? {
Symbol::Type(td) => signature.type_for_type_decl(td),
Symbol::Enum(ed) => signature.type_for_enum_decl(ed),
Symbol::Model(md) => signature.ids_for_model_decl(md).type_,
_ => return None,
};
Some(ConcreteType { typ: tid, parents })
})
.collect();
(resolved, changed)
}
}
}
fn concrete_type_for(
signature: &Signature,
typ_id: Option<TypeId>,
structure: &Structure,
) -> Option<ConcreteType> {
let tid = typ_id?;
Some(ConcreteType {
typ: tid,
parents: structure.ambient_parents(&signature.type_(tid).parents),
})
}
fn ensure_ambient_els(init: &mut Structure, enclosing_models: &[TypeId]) -> bool {
if init.ambient_model_els.len() == enclosing_models.len() {
return false;
}
let mut parents: Vec<ElId> = Vec::new();
for &model_tid in enclosing_models {
let el_id = init.push_el();
init.insert_concrete_type_fact(
el_id,
ConcreteType {
typ: model_tid,
parents: parents.clone(),
},
);
init.ambient_model_els.insert(model_tid, el_id);
parents.push(el_id);
}
true
}