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