conjure_core/rule_engine/
submodel_zipper.rs

1#![allow(dead_code)]
2use std::sync::Arc;
3
4use uniplate::zipper::Zipper;
5
6use crate::ast::{Expression, SubModel};
7
8/// Traverses expressions in this sub-model, but not into inner sub-models.
9///
10/// Same types and usage as `Biplate::contexts_bi`.
11pub(super) fn submodel_ctx(
12    m: SubModel,
13) -> impl Iterator<Item = (Expression, Arc<dyn Fn(Expression) -> SubModel>)> {
14    SubmodelCtx {
15        zipper: SubmodelZipper {
16            inner: Zipper::new(m.root().clone()),
17        },
18        submodel: m.clone(),
19        done: false,
20    }
21}
22
23/// A zipper that traverses over the current submodel only, and does not traverse into nested
24/// scopes.
25#[derive(Clone)]
26struct SubmodelZipper {
27    inner: Zipper<Expression>,
28}
29
30impl SubmodelZipper {
31    fn go_left(&mut self) -> Option<()> {
32        self.inner.go_left()
33    }
34
35    fn go_right(&mut self) -> Option<()> {
36        self.inner.go_right()
37    }
38
39    fn go_up(&mut self) -> Option<()> {
40        self.inner.go_up()
41    }
42
43    fn rebuild_root(self) -> Expression {
44        self.inner.rebuild_root()
45    }
46
47    fn go_down(&mut self) -> Option<()> {
48        // do not enter things that create new submodels
49        if matches!(
50            self.inner.focus(),
51            Expression::Scope(_, _) | Expression::Comprehension(_, _)
52        ) {
53            None
54        } else {
55            self.inner.go_down()
56        }
57    }
58
59    fn focus(&self) -> &Expression {
60        self.inner.focus()
61    }
62
63    fn focus_mut(&mut self) -> &mut Expression {
64        self.inner.focus_mut()
65    }
66}
67
68pub struct SubmodelCtx {
69    zipper: SubmodelZipper,
70    submodel: SubModel,
71    done: bool,
72}
73
74impl Iterator for SubmodelCtx {
75    type Item = (Expression, Arc<dyn Fn(Expression) -> SubModel>);
76
77    fn next(&mut self) -> Option<Self::Item> {
78        if self.done {
79            return None;
80        }
81        let node = self.zipper.focus().clone();
82        let submodel = self.submodel.clone();
83        let zipper = self.zipper.clone();
84
85        #[allow(clippy::arc_with_non_send_sync)]
86        let ctx = Arc::new(move |x| {
87            let mut zipper2 = zipper.clone();
88            *zipper2.focus_mut() = x;
89            let root = zipper2.rebuild_root();
90            let mut submodel2 = submodel.clone();
91            submodel2.replace_root(root);
92            submodel2
93        });
94
95        // prepare iterator for next element.
96        // try moving down or right. if we can't move up the tree until we can move right.
97        if self.zipper.go_down().is_none() {
98            while self.zipper.go_right().is_none() {
99                if self.zipper.go_up().is_none() {
100                    // at the top again, so this will be the last time we return a node
101                    self.done = true;
102                    break;
103                };
104            }
105        }
106
107        Some((node, ctx))
108    }
109}