1use std::collections::BTreeSet;
2use std::fmt::{self, Debug, Display, Formatter};
3use std::hash::Hash;
4use std::sync::Arc;
5
6use thiserror::Error;
7
8use crate::Model;
9use crate::ast::{
10 DeclarationKind, DeclarationPtr, Expression, Metadata, Name, SatEncodingDecision, SymbolTable,
11};
12
13#[derive(Debug, Error)]
14pub enum ApplicationError {
15 #[error("Rule is not applicable")]
16 RuleNotApplicable,
17
18 #[error("Could not calculate the expression domain")]
19 DomainError,
20}
21
22#[non_exhaustive]
58#[derive(Clone)]
59pub struct RuleEffect {
60 pub new_expression: Expression,
61 pub new_top: Vec<Expression>,
62 pub symbols: SymbolTable,
63 pub new_sat_decisions: Vec<SatEncodingDecision>,
64 pub(crate) declaration_updates: Vec<DeclarationUpdate>,
66 materialise: Option<DeferredRuleEffect>,
67 pub(crate) uniform_choices: crate::representation::uniform::Choices,
68}
69
70#[derive(Clone, Debug)]
72pub(crate) struct DeclarationUpdate {
73 target: DeclarationPtr,
74 replacement_kind: DeclarationKind,
75}
76
77impl DeclarationUpdate {
78 fn new(target: DeclarationPtr, replacement_kind: DeclarationKind) -> Self {
80 Self {
81 target,
82 replacement_kind,
83 }
84 }
85
86 pub(crate) fn name(&self) -> Name {
88 self.target.name().clone()
89 }
90
91 pub(crate) fn apply(mut self) {
93 let _ = self.target.replace_kind(self.replacement_kind);
94 }
95
96 fn preview(&self) -> DeclarationPtr {
98 DeclarationPtr::new(self.name(), self.replacement_kind.clone())
99 }
100}
101
102type DeferredRuleEffect = Arc<dyn Fn(&SymbolTable) -> RuleEffect + Send + Sync>;
104
105pub type ApplicationResult = Result<RuleEffect, ApplicationError>;
108
109impl Debug for RuleEffect {
110 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
111 f.debug_struct("RuleEffect")
112 .field("new_expression", &self.new_expression)
113 .field("new_top", &self.new_top)
114 .field("symbols", &self.symbols)
115 .field("new_sat_decisions", &self.new_sat_decisions)
116 .field("declaration_updates", &self.declaration_updates)
117 .field("is_deferred", &self.materialise.is_some())
118 .finish()
119 }
120}
121
122impl RuleEffect {
123 pub fn new(new_expression: Expression, new_top: Vec<Expression>, symbols: SymbolTable) -> Self {
124 Self {
125 new_expression,
126 new_top,
127 symbols,
128 new_sat_decisions: Vec::new(),
129 declaration_updates: Vec::new(),
130 materialise: None,
131 uniform_choices: Default::default(),
132 }
133 }
134
135 pub fn pure(new_expression: Expression) -> Self {
137 Self {
138 new_expression,
139 new_top: Vec::new(),
140 symbols: SymbolTable::new(),
141 new_sat_decisions: Vec::new(),
142 declaration_updates: Vec::new(),
143 materialise: None,
144 uniform_choices: Default::default(),
145 }
146 }
147
148 pub fn with_symbols(new_expression: Expression, symbols: SymbolTable) -> Self {
150 Self {
151 new_expression,
152 new_top: Vec::new(),
153 symbols,
154 new_sat_decisions: Vec::new(),
155 declaration_updates: Vec::new(),
156 materialise: None,
157 uniform_choices: Default::default(),
158 }
159 }
160
161 pub fn with_top(new_expression: Expression, new_top: Vec<Expression>) -> Self {
163 Self {
164 new_expression,
165 new_top,
166 symbols: SymbolTable::new(),
167 new_sat_decisions: Vec::new(),
168 declaration_updates: Vec::new(),
169 materialise: None,
170 uniform_choices: Default::default(),
171 }
172 }
173
174 pub fn sat(
176 new_expression: Expression,
177 new_sat_decisions: Vec<SatEncodingDecision>,
178 symbols: SymbolTable,
179 ) -> Self {
180 Self {
181 new_expression,
182 new_top: Vec::new(),
183 symbols,
184 new_sat_decisions,
185 declaration_updates: Vec::new(),
186 materialise: None,
187 uniform_choices: Default::default(),
188 }
189 }
190
191 pub fn deferred(
197 materialise: impl Fn(&SymbolTable) -> RuleEffect + Send + Sync + 'static,
198 ) -> Self {
199 Self {
200 new_expression: Expression::Root(Metadata::new(), Vec::new()),
201 new_top: Vec::new(),
202 symbols: SymbolTable::new(),
203 new_sat_decisions: Vec::new(),
204 declaration_updates: Vec::new(),
205 materialise: Some(Arc::new(materialise)),
206 uniform_choices: Default::default(),
207 }
208 }
209
210 pub fn materialise(mut self, symbols: &SymbolTable) -> Self {
216 crate::representation::uniform::commit(std::mem::take(&mut self.uniform_choices));
217 if let Some(materialise) = self.materialise.take() {
218 return materialise(symbols).materialise(symbols);
219 }
220
221 self.symbols.retain_local_changes_from(symbols);
222 self
223 }
224
225 pub(crate) fn is_deferred(&self) -> bool {
226 self.materialise.is_some()
227 }
228
229 pub fn with_declaration_updates(
231 mut self,
232 updates: impl IntoIterator<Item = (DeclarationPtr, DeclarationKind)>,
233 ) -> Self {
234 self.declaration_updates.extend(
235 updates
236 .into_iter()
237 .map(|(target, kind)| DeclarationUpdate::new(target, kind)),
238 );
239 self
240 }
241
242 pub fn updated_declaration_names(&self) -> impl Iterator<Item = Name> + '_ {
244 self.declaration_updates.iter().map(DeclarationUpdate::name)
245 }
246
247 pub(crate) fn preview_declaration_updates(&self, symbols: &mut SymbolTable) {
249 for update in &self.declaration_updates {
250 symbols.update_insert(update.preview());
251 }
252 }
253
254 pub fn apply(mut self, model: &mut Model) {
256 crate::representation::uniform::commit(std::mem::take(&mut self.uniform_choices));
257 debug_assert!(
258 self.materialise.is_none(),
259 "deferred rule effects must be materialised before being applied"
260 );
261 for update in self.declaration_updates {
262 update.apply();
263 }
264 model.symbols_mut().extend(self.symbols); model.add_constraints(self.new_top);
266 model.add_sat_decisions(self.new_sat_decisions);
267 }
268
269 pub fn added_symbols(&self, initial_symbols: &SymbolTable) -> BTreeSet<Name> {
275 self.symbols
276 .iter_local()
277 .filter(|(name, _)| initial_symbols.lookup_local(name).is_none())
278 .map(|(name, _)| name.clone())
279 .collect()
280 }
281
282 pub fn changed_symbols(
290 &self,
291 initial_symbols: &SymbolTable,
292 ) -> Vec<(Name, DeclarationPtr, DeclarationPtr)> {
293 let mut changes: Vec<(Name, DeclarationPtr, DeclarationPtr)> = self
294 .symbols
295 .iter_local()
296 .filter_map(|(name, new_value)| {
297 let initial_value = initial_symbols.lookup_local(name)?;
298 (!new_value.content_eq(&initial_value))
299 .then(|| (name.clone(), initial_value.clone(), new_value.clone()))
300 })
301 .collect();
302 changes.sort_by(|(lhs, ..), (rhs, ..)| lhs.cmp(rhs));
303 changes
304 }
305}
306
307pub type RuleFn = fn(&Expression, &SymbolTable) -> ApplicationResult;
309
310#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
312pub enum AtomKind {
313 Literal,
315 Reference,
317}
318
319#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
325pub enum RulePrefilter {
326 Variant(usize),
328 Child { child: usize },
330 VariantChild { variant: usize, child: usize },
332 Atom(AtomKind),
334 ChildAtom(AtomKind),
336}
337
338#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
346pub enum RuleFailureInvalidation {
347 #[default]
349 ExpressionOrSymbols,
350 SymbolsOnly,
352}
353
354#[derive(Clone, Debug)]
363pub struct Rule<'a> {
364 pub name: &'a str,
365 pub application: RuleFn,
366 pub rule_sets: &'a [(&'a str, u16)], pub prefilters: Option<&'static [RulePrefilter]>,
369 pub failure_invalidation: RuleFailureInvalidation,
371}
372
373impl<'a> Rule<'a> {
374 pub const fn new(
375 name: &'a str,
376 application: RuleFn,
377 rule_sets: &'a [(&'static str, u16)],
378 ) -> Self {
379 Self {
380 name,
381 application,
382 rule_sets,
383 prefilters: None,
384 failure_invalidation: RuleFailureInvalidation::ExpressionOrSymbols,
385 }
386 }
387
388 pub fn apply(&self, expr: &Expression, symbols: &SymbolTable) -> ApplicationResult {
389 crate::representation::uniform::attempt(|| (self.application)(expr, symbols))
390 }
391}
392
393impl Display for Rule<'_> {
394 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
395 write!(f, "{}", self.name)
396 }
397}
398
399impl PartialEq for Rule<'_> {
400 fn eq(&self, other: &Self) -> bool {
401 self.name == other.name
402 }
403}
404
405impl Eq for Rule<'_> {}
406
407impl Hash for Rule<'_> {
408 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
409 self.name.hash(state);
410 }
411}
412
413#[cfg(test)]
414mod tests {
415 use super::*;
416 use crate::ast::{DecisionVariable, Domain, Range, Reference};
417 use crate::rule_engine::rewriter_common::{
418 snapshot_symbols_after_effect, snapshot_variable_declarations,
419 };
420
421 #[test]
422 fn declaration_updates_are_applied_only_when_effect_is_committed() {
423 let old_domain = Domain::int(vec![Range::Bounded(1, 3)]);
424 let new_domain = Domain::int(vec![Range::Bounded(1, 2)]);
425 let mut model = Model::new(Default::default());
426 let declaration = DeclarationPtr::new_find(Name::user("x"), old_domain.clone());
427 let reference = Reference::new(declaration.clone());
428 model.symbols_mut().insert(declaration.clone()).unwrap();
429
430 let effect = RuleEffect::pure(Expression::from(1)).with_declaration_updates([(
431 declaration,
432 DeclarationKind::Find(DecisionVariable::new(new_domain.clone())),
433 )]);
434
435 let before = snapshot_variable_declarations(&model.symbols());
436 let after = snapshot_symbols_after_effect(&model.symbols(), &effect);
437 assert_eq!(before.get(&Name::user("x")).unwrap(), "find x: int(1..3)");
438 assert_eq!(after.get(&Name::user("x")).unwrap(), "find x: int(1..2)");
439 assert_eq!(reference.domain(), Some(old_domain));
440 effect.apply(&mut model);
441 assert_eq!(reference.domain(), Some(new_domain));
442 }
443}