grit_pattern_matcher/pattern/
rewrite.rs

1use super::{
2    dynamic_snippet::DynamicPattern,
3    functions::{Evaluator, FuncEvaluation},
4    patterns::{Matcher, Pattern, PatternName},
5    resolved_pattern::ResolvedPattern,
6    variable_content::VariableContent,
7    State,
8};
9use crate::{context::QueryContext, effects::Effect};
10use core::fmt::Debug;
11use grit_util::{
12    error::{GritPatternError, GritResult},
13    AnalysisLogs, EffectKind,
14};
15use std::borrow::Cow;
16
17#[derive(Debug, Clone)]
18pub struct Rewrite<Q: QueryContext> {
19    pub left: Pattern<Q>,
20    pub right: DynamicPattern<Q>,
21    pub annotation: Option<String>,
22}
23
24impl<Q: QueryContext> Rewrite<Q> {
25    pub fn new(left: Pattern<Q>, right: DynamicPattern<Q>, annotation: Option<String>) -> Self {
26        Self {
27            left,
28            right,
29            annotation,
30        }
31    }
32
33    pub fn new_pattern(
34        left: Pattern<Q>,
35        right: DynamicPattern<Q>,
36        annotation: Option<String>,
37    ) -> Pattern<Q> {
38        Pattern::Rewrite(Box::new(Rewrite {
39            left,
40            right,
41            annotation,
42        }))
43    }
44
45    /**
46     * Execute a rewrite rule, returning the new binding.
47     *
48     * If called from a rewrite side-condition, the binding should be None.
49     * In this case, the left-hand side must be a variable, and the binding
50     * will be taken from the current state.
51     *
52     * If called from a rewrite pattern, the binding should be Some(the current node).
53     */
54    pub(crate) fn execute_generalized<'a>(
55        &'a self,
56        resolved: Option<&Q::ResolvedPattern<'a>>,
57        state: &mut State<'a, Q>,
58        context: &'a Q::ExecContext<'a>,
59        logs: &mut AnalysisLogs,
60    ) -> GritResult<bool> {
61        let resolved = match resolved {
62            Some(b) => {
63                if !self.left.execute(b, state, context, logs)? {
64                    return Ok(false);
65                } else {
66                    Cow::Borrowed(b)
67                }
68            }
69            None => {
70                if let Pattern::Variable(v) = &self.left {
71                    let var = state.trace_var_mut(v);
72                    if let Some(VariableContent {
73                        value: Some(content),
74                        ..
75                    }) = state
76                        .bindings
77                        .get(var.try_scope().unwrap() as usize)
78                        .and_then(|scope| {
79                            scope.last().unwrap().get(var.try_index().unwrap() as usize)
80                        })
81                        .cloned()
82                        .map(|b| *b)
83                    {
84                        Cow::Owned(content)
85                    } else {
86                        return Err(GritPatternError::new(format!("Variable {:?} not bound", v)));
87                    }
88                } else {
89                    return Err(GritPatternError::new(
90                        "Rewrite side-conditions must have variable on left-hand side",
91                    ));
92                }
93            }
94        };
95        let Some(bindings) = resolved.get_bindings() else {
96            return Err(
97                GritPatternError::new(
98
99            "variable on left hand side of rewrite side-conditions can only be bound to bindings"
100                )
101            );
102        };
103        let replacement: Q::ResolvedPattern<'_> =
104            ResolvedPattern::from_dynamic_pattern(&self.right, state, context, logs)?;
105        let effects = bindings.map(|b| Effect {
106            binding: b.clone(),
107            pattern: replacement.clone(),
108            kind: EffectKind::Rewrite,
109        });
110        state.effects.extend(effects);
111        Ok(true)
112    }
113}
114
115impl<Q: QueryContext> PatternName for Rewrite<Q> {
116    fn name(&self) -> &'static str {
117        "REWRITE"
118    }
119}
120
121impl<Q: QueryContext> Matcher<Q> for Rewrite<Q> {
122    fn execute<'a>(
123        &'a self,
124        binding: &Q::ResolvedPattern<'a>,
125        state: &mut State<'a, Q>,
126        context: &'a Q::ExecContext<'a>,
127        logs: &mut AnalysisLogs,
128    ) -> GritResult<bool> {
129        self.execute_generalized(Some(binding), state, context, logs)
130    }
131}
132
133impl<Q: QueryContext> Evaluator<Q> for Rewrite<Q> {
134    fn execute_func<'a>(
135        &'a self,
136        state: &mut State<'a, Q>,
137        context: &'a Q::ExecContext<'a>,
138        logs: &mut AnalysisLogs,
139    ) -> GritResult<FuncEvaluation<Q>> {
140        let predicator = self.execute_generalized(None, state, context, logs)?;
141        Ok(FuncEvaluation {
142            predicator,
143            ret_val: None,
144        })
145    }
146}