grit_pattern_matcher/pattern/
contains.rs

1use super::{
2    patterns::{Matcher, Pattern, PatternName},
3    resolved_pattern::{LazyBuiltIn, ResolvedPattern, ResolvedSnippet},
4    State,
5};
6use crate::{
7    binding::Binding, constants::PROGRAM_INDEX, context::QueryContext,
8    pattern::resolved_pattern::File,
9};
10use crate::{constants::GLOBAL_VARS_SCOPE_INDEX, context::ExecContext};
11use core::fmt::Debug;
12use grit_util::{error::GritResult, AnalysisLogs};
13use grit_util::{AstCursor, AstNode};
14
15#[derive(Debug, Clone)]
16pub struct Contains<Q: QueryContext> {
17    pub contains: Pattern<Q>,
18    pub until: Option<Pattern<Q>>,
19}
20
21impl<Q: QueryContext> Contains<Q> {
22    pub fn new(contains: Pattern<Q>, until: Option<Pattern<Q>>) -> Self {
23        Self { contains, until }
24    }
25
26    pub fn new_pattern(contains: Pattern<Q>, until: Option<Pattern<Q>>) -> Pattern<Q> {
27        Pattern::Contains(Box::new(Contains::new(contains, until)))
28    }
29}
30
31impl<Q: QueryContext> PatternName for Contains<Q> {
32    fn name(&self) -> &'static str {
33        "CONTAINS"
34    }
35}
36
37fn execute_until<'a, Q: QueryContext>(
38    init_state: &mut State<'a, Q>,
39    node: &Q::Node<'a>,
40    context: &'a Q::ExecContext<'a>,
41    logs: &mut AnalysisLogs,
42    the_contained: &'a Pattern<Q>,
43    until: &'a Option<Pattern<Q>>,
44) -> GritResult<bool> {
45    let mut did_match = false;
46    let mut cur_state = init_state.clone();
47    let mut cursor = node.walk();
48    let mut still_computing = true;
49    while still_computing {
50        let node = cursor.node();
51        let node_lhs = ResolvedPattern::from_node_binding(node);
52
53        let state = cur_state.clone();
54
55        if the_contained.execute(&node_lhs, &mut cur_state, context, logs)? {
56            did_match = true;
57        } else {
58            cur_state = state;
59        }
60
61        let mut state = cur_state.clone();
62        let skip_children = if let Some(until) = until {
63            until.execute(&node_lhs, &mut state, context, logs)?
64        } else {
65            false
66        };
67
68        if (!skip_children && cursor.goto_first_child()) || cursor.goto_next_sibling() {
69            // all good
70            continue;
71        }
72        // go up the parent chain until we find a sibling
73        loop {
74            if !cursor.goto_parent() {
75                still_computing = false;
76                break;
77            }
78
79            if cursor.goto_next_sibling() {
80                break;
81            }
82        }
83    }
84    if did_match {
85        *init_state = cur_state;
86    }
87    Ok(did_match)
88}
89
90// Contains and within should call the same function taking an iterator as an argument
91// even better two arguments an accumulator and an iterator.
92impl<Q: QueryContext> Matcher<Q> for Contains<Q> {
93    fn execute<'a>(
94        &'a self,
95        resolved_pattern: &Q::ResolvedPattern<'a>,
96        init_state: &mut State<'a, Q>,
97        context: &'a Q::ExecContext<'a>,
98        logs: &mut AnalysisLogs,
99    ) -> GritResult<bool> {
100        if let Some(binding) = resolved_pattern.get_last_binding() {
101            if let Some(node) = binding.as_node() {
102                execute_until(
103                    init_state,
104                    &node,
105                    context,
106                    logs,
107                    &self.contains,
108                    &self.until,
109                )
110            } else if let Some(items) = binding.list_items() {
111                let mut cur_state = init_state.clone();
112                let mut did_match = false;
113                for item in items {
114                    let state = cur_state.clone();
115                    if self.execute(
116                        &ResolvedPattern::from_node_binding(item),
117                        &mut cur_state,
118                        context,
119                        logs,
120                    )? {
121                        did_match = true;
122                    } else {
123                        cur_state = state;
124                    }
125                }
126                if !did_match {
127                    return Ok(false);
128                }
129                *init_state = cur_state;
130                Ok(true)
131            } else if let Some(_c) = binding.as_constant() {
132                // this seems like an infinite loop, todo return false?
133                self.contains
134                    .execute(resolved_pattern, init_state, context, logs)
135            } else {
136                Ok(false)
137            }
138        } else if let Some(items) = resolved_pattern.get_list_items() {
139            let mut cur_state = init_state.clone();
140            let mut did_match = false;
141            for item in items {
142                let state = cur_state.clone();
143                if self.execute(item, &mut cur_state, context, logs)? {
144                    did_match = true;
145                } else {
146                    cur_state = state;
147                }
148            }
149            if !did_match {
150                return Ok(false);
151            }
152            *init_state = cur_state;
153            Ok(true)
154        } else if let Some(file) = resolved_pattern.get_file() {
155            // Load the file in, if it wasn't already
156            if !context.load_file(file, init_state, logs)? {
157                return Ok(false);
158            }
159
160            init_state.bindings[GLOBAL_VARS_SCOPE_INDEX as usize]
161                .last_mut()
162                .unwrap()[PROGRAM_INDEX]
163                .value = Some(file.binding(&init_state.files));
164
165            let mut cur_state = init_state.clone();
166            let mut did_match = false;
167            let prev_state = cur_state.clone();
168
169            if self
170                .contains
171                .execute(resolved_pattern, &mut cur_state, context, logs)?
172            {
173                did_match = true;
174            } else {
175                cur_state = prev_state;
176            }
177
178            let prev_state = cur_state.clone();
179            if self
180                .contains
181                .execute(&file.name(&cur_state.files), &mut cur_state, context, logs)?
182            {
183                did_match = true;
184            } else {
185                cur_state = prev_state;
186            }
187
188            let prev_state = cur_state.clone();
189            if self.execute(
190                &file.binding(&cur_state.files),
191                &mut cur_state,
192                context,
193                logs,
194            )? {
195                did_match = true;
196            } else {
197                cur_state = prev_state;
198            }
199            if !did_match {
200                return Ok(false);
201            }
202            *init_state = cur_state;
203            Ok(true)
204        } else if let Some(files) = resolved_pattern.get_files() {
205            let mut cur_state = init_state.clone();
206            let mut did_match = false;
207            let prev_state = cur_state.clone();
208            if self
209                .contains
210                .execute(resolved_pattern, &mut cur_state, context, logs)?
211            {
212                did_match = true;
213            } else {
214                cur_state = prev_state;
215            }
216            let prev_state = cur_state.clone();
217            if self.execute(files, &mut cur_state, context, logs)? {
218                did_match = true;
219            } else {
220                cur_state = prev_state;
221            }
222            if !did_match {
223                return Ok(false);
224            }
225            *init_state = cur_state;
226            Ok(true)
227        } else if let Some(snippets) = resolved_pattern.get_snippets() {
228            let mut cur_state = init_state.clone();
229            let mut did_match = false;
230            for snippet in snippets {
231                let state = cur_state.clone();
232                let resolved = match snippet {
233                    ResolvedSnippet::Text(_) => {
234                        ResolvedPattern::from_resolved_snippet(snippet.to_owned())
235                    }
236                    ResolvedSnippet::Binding(b) => ResolvedPattern::from_binding(b.to_owned()),
237                    ResolvedSnippet::LazyFn(l) => match &*l {
238                        LazyBuiltIn::Join(j) => {
239                            ResolvedPattern::from_list_parts(j.list.iter().cloned())
240                        }
241                    },
242                };
243                if self.execute(&resolved, &mut cur_state, context, logs)? {
244                    did_match = true;
245                } else {
246                    cur_state = state;
247                }
248            }
249            if !did_match {
250                return Ok(false);
251            }
252            *init_state = cur_state;
253            Ok(true)
254        } else {
255            return Ok(false);
256        }
257    }
258}