grit_pattern_matcher/pattern/
contains.rs1use 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 continue;
71 }
72 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
90impl<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 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 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}