conjure_cp_core/rule_engine/
rule.rs1use std::collections::BTreeSet;
2use std::fmt::{self, Display, Formatter};
3use std::hash::Hash;
4
5use thiserror::Error;
6
7use crate::ast::{DeclarationPtr, Expression, Name, SubModel, SymbolTable};
8use tree_morph::prelude::Commands;
9use tree_morph::prelude::Rule as MorphRule;
10
11#[derive(Debug, Error)]
12pub enum ApplicationError {
13 #[error("Rule is not applicable")]
14 RuleNotApplicable,
15
16 #[error("Could not calculate the expression domain")]
17 DomainError,
18}
19
20#[non_exhaustive]
55#[derive(Clone, Debug)]
56pub struct Reduction {
57 pub new_expression: Expression,
58 pub new_top: Vec<Expression>,
59 pub symbols: SymbolTable,
60}
61
62pub type ApplicationResult = Result<Reduction, ApplicationError>;
65
66impl Reduction {
67 pub fn new(new_expression: Expression, new_top: Vec<Expression>, symbols: SymbolTable) -> Self {
68 Self {
69 new_expression,
70 new_top,
71 symbols,
72 }
73 }
74
75 pub fn pure(new_expression: Expression) -> Self {
77 Self {
78 new_expression,
79 new_top: Vec::new(),
80 symbols: SymbolTable::new(),
81 }
82 }
83
84 pub fn with_symbols(new_expression: Expression, symbols: SymbolTable) -> Self {
86 Self {
87 new_expression,
88 new_top: Vec::new(),
89 symbols,
90 }
91 }
92
93 pub fn with_top(new_expression: Expression, new_top: Vec<Expression>) -> Self {
95 Self {
96 new_expression,
97 new_top,
98 symbols: SymbolTable::new(),
99 }
100 }
101
102 pub fn apply(self, model: &mut SubModel) {
104 model.symbols_mut().extend(self.symbols); model.add_constraints(self.new_top);
106 }
107
108 pub fn added_symbols(&self, initial_symbols: &SymbolTable) -> BTreeSet<Name> {
110 let initial_symbols_set: BTreeSet<Name> = initial_symbols
111 .clone()
112 .into_iter_local()
113 .map(|x| x.0)
114 .collect();
115 let new_symbols_set: BTreeSet<Name> = self
116 .symbols
117 .clone()
118 .into_iter_local()
119 .map(|x| x.0)
120 .collect();
121
122 new_symbols_set
123 .difference(&initial_symbols_set)
124 .cloned()
125 .collect()
126 }
127
128 pub fn changed_symbols(
132 &self,
133 initial_symbols: &SymbolTable,
134 ) -> Vec<(Name, DeclarationPtr, DeclarationPtr)> {
135 let mut changes: Vec<(Name, DeclarationPtr, DeclarationPtr)> = vec![];
136
137 for (var_name, initial_value) in initial_symbols.clone().into_iter_local() {
138 let Some(new_value) = self.symbols.lookup(&var_name) else {
139 continue;
140 };
141
142 if new_value != initial_value {
143 changes.push((var_name.clone(), initial_value.clone(), new_value.clone()));
144 }
145 }
146 changes
147 }
148}
149
150pub type RuleFn = fn(&Expression, &SymbolTable) -> ApplicationResult;
152
153#[derive(Clone, Debug)]
162pub struct Rule<'a> {
163 pub name: &'a str,
164 pub application: RuleFn,
165 pub rule_sets: &'a [(&'a str, u16)], }
167
168impl<'a> Rule<'a> {
169 pub const fn new(
170 name: &'a str,
171 application: RuleFn,
172 rule_sets: &'a [(&'static str, u16)],
173 ) -> Self {
174 Self {
175 name,
176 application,
177 rule_sets,
178 }
179 }
180
181 pub fn apply(&self, expr: &Expression, symbols: &SymbolTable) -> ApplicationResult {
182 (self.application)(expr, symbols)
183 }
184}
185
186impl Display for Rule<'_> {
187 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
188 write!(f, "{}", self.name)
189 }
190}
191
192impl PartialEq for Rule<'_> {
193 fn eq(&self, other: &Self) -> bool {
194 self.name == other.name
195 }
196}
197
198impl Eq for Rule<'_> {}
199
200impl Hash for Rule<'_> {
201 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
202 self.name.hash(state);
203 }
204}
205
206impl MorphRule<Expression, SymbolTable> for Rule<'_> {
207 fn apply(
208 &self,
209 commands: &mut Commands<Expression, SymbolTable>,
210 subtree: &Expression,
211 meta: &SymbolTable,
212 ) -> Option<Expression> {
213 let reduction = self.apply(subtree, meta).ok()?;
214 commands.mut_meta(Box::new(|m: &mut SymbolTable| m.extend(reduction.symbols)));
215 if !reduction.new_top.is_empty() {
216 commands.transform(Box::new(|m| m.extend_root(reduction.new_top)));
217 }
218 Some(reduction.new_expression)
219 }
220}
221
222impl MorphRule<Expression, SymbolTable> for &Rule<'_> {
223 fn apply(
224 &self,
225 commands: &mut Commands<Expression, SymbolTable>,
226 subtree: &Expression,
227 meta: &SymbolTable,
228 ) -> Option<Expression> {
229 let reduction = Rule::apply(self, subtree, meta).ok()?;
230 commands.mut_meta(Box::new(|m: &mut SymbolTable| m.extend(reduction.symbols)));
231 if !reduction.new_top.is_empty() {
232 commands.transform(Box::new(|m| m.extend_root(reduction.new_top)));
233 }
234 Some(reduction.new_expression)
235 }
236}