use crate::adapter::ast::ModelGraph;
use seesaw_tgg::engine::Engine;
use seesaw_tgg::ident::GhostId;
use seesaw_tgg::rules::format::RuleFile;
use seesaw_tgg::rules::{lower::lower_all, validate::validate};
use std::collections::BTreeMap;
use zerodds_ast_core::NodeId;
const MAX_STEPS: usize = 100_000;
pub struct Applied {
pub graph: seesaw_tgg::graph::Graph,
pub origin: BTreeMap<GhostId, NodeId>,
pub steps: usize,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ApplyError {
Lowering {
rule: String,
detail: String,
},
DidNotSettle {
steps: usize,
},
}
impl core::fmt::Display for ApplyError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::Lowering { rule, detail } => write!(f, "rule `{rule}` did not lower: {detail}"),
Self::DidNotSettle { steps } => {
write!(f, "the catalogue did not settle within {steps} steps")
}
}
}
}
impl core::error::Error for ApplyError {}
pub fn apply(model: &ModelGraph, rules: &RuleFile) -> Result<Applied, ApplyError> {
let mut graph = model.graph.clone();
let values = model.values.clone();
let resolved = validate(rules).map_err(|error| ApplyError::Lowering {
rule: rules.name.clone(),
detail: format!("{error:?}"),
})?;
let all = lower_all(&resolved, &mut graph).map_err(|error| ApplyError::Lowering {
rule: rules.name.clone(),
detail: format!("{error:?}"),
})?;
let directed: Vec<_> = all
.into_iter()
.filter(|rule| !rule.name.ends_with("__backward"))
.collect();
let mut engine = Engine::new(&directed);
engine.seed(&graph, &values);
let mut steps = 0usize;
while engine.step(&mut graph, &values).is_some() {
steps += 1;
if steps >= MAX_STEPS {
return Err(ApplyError::DidNotSettle { steps });
}
}
Ok(Applied {
graph,
origin: origins(model),
steps,
})
}
fn origins(model: &ModelGraph) -> BTreeMap<GhostId, NodeId> {
let mut out = BTreeMap::new();
for (ghost, node) in model.pairs() {
let _ = out.insert(ghost, node);
}
out
}