grit_pattern_matcher/pattern/
accumulate.rs

1use super::{
2    dynamic_snippet::DynamicPattern,
3    functions::{Evaluator, FuncEvaluation},
4    patterns::{Matcher, Pattern, PatternName},
5    resolved_pattern::ResolvedPattern,
6    PatternOrResolved, State,
7};
8use crate::{
9    context::{ExecContext, QueryContext},
10    effects::insert_effect,
11};
12use grit_util::{
13    error::{GritPatternError, GritResult},
14    AnalysisLogs,
15};
16
17#[derive(Debug, Clone)]
18pub struct Accumulate<Q: QueryContext> {
19    pub(crate) left: Pattern<Q>,
20    pub(crate) right: Pattern<Q>,
21    dynamic_right: Option<DynamicPattern<Q>>,
22}
23
24impl<Q: QueryContext> Accumulate<Q> {
25    pub fn new(
26        left: Pattern<Q>,
27        right: Pattern<Q>,
28        dynamic_right: Option<DynamicPattern<Q>>,
29    ) -> Self {
30        Self {
31            left,
32            right,
33            dynamic_right,
34        }
35    }
36}
37
38impl<Q: QueryContext> PatternName for Accumulate<Q> {
39    fn name(&self) -> &'static str {
40        "ACCUMULATE"
41    }
42}
43
44impl<Q: QueryContext> Matcher<Q> for Accumulate<Q> {
45    fn execute<'a>(
46        &'a self,
47        context_node: &Q::ResolvedPattern<'a>,
48        state: &mut State<'a, Q>,
49        context: &'a Q::ExecContext<'a>,
50        logs: &mut AnalysisLogs,
51    ) -> GritResult<bool> {
52        if let Pattern::Variable(_) = &self.left {
53            let left = PatternOrResolved::Pattern(&self.left);
54            let right = ResolvedPattern::from_pattern(&self.right, state, context, logs)?;
55            insert_effect(&left, right, state, context)
56        } else {
57            if !self.left.execute(context_node, state, context, logs)? {
58                return Ok(false);
59            };
60            let resolved = context_node;
61            let left = PatternOrResolved::Resolved(resolved);
62
63            let right = if let Some(dynamic_right) = &self.dynamic_right {
64                ResolvedPattern::from_dynamic_pattern(dynamic_right, state, context, logs)?
65            } else {
66                ResolvedPattern::from_pattern(&self.right, state, context, logs)?
67            };
68
69            insert_effect(&left, right, state, context)
70        }
71    }
72}
73
74impl<Q: QueryContext> Evaluator<Q> for Accumulate<Q> {
75    fn execute_func<'a>(
76        &'a self,
77        state: &mut State<'a, Q>,
78        context: &'a Q::ExecContext<'a>,
79        logs: &mut AnalysisLogs,
80    ) -> GritResult<FuncEvaluation<Q>> {
81        if let Pattern::Variable(var) = &self.left {
82            let var = state.trace_var_mut(var);
83            let append = ResolvedPattern::from_pattern(&self.right, state, context, logs)?;
84            let scope = var.get_scope(state)?;
85            let index = var.get_index(state)?;
86            if let Some(base) = state.bindings[scope as usize].last_mut().unwrap()[index as usize]
87                .value
88                .as_mut()
89            {
90                base.extend(append, &mut state.effects, context.language())?;
91                Ok(FuncEvaluation {
92                    predicator: true,
93                    ret_val: None,
94                })
95            } else {
96                Err(GritPatternError::new(format!(
97                    "Variable {} is not bound",
98                    state.bindings[var.try_scope().unwrap() as usize]
99                        .last()
100                        .unwrap()[var.try_index().unwrap() as usize]
101                        .name
102                )))
103            }
104        } else {
105            Err(GritPatternError::new(
106                "Insert side-conditions must have variable on left-hand side",
107            ))
108        }
109    }
110}