1use thiserror::Error;
9use tree_sitter::CaptureQuantifier;
10use tree_sitter::Node;
11use tree_sitter::QueryMatch;
12use tree_sitter::Tree;
13
14use crate::ast::CreateEdge;
15use crate::ast::File;
16use crate::ast::Stanza;
17use crate::ast::Variable;
18use crate::execution::error::ExecutionError;
19use crate::functions::Functions;
20use crate::graph::Attributes;
21use crate::graph::Graph;
22use crate::graph::Value;
23use crate::variables::Globals;
24use crate::Identifier;
25use crate::Location;
26
27pub(crate) mod error;
28mod lazy;
29mod strict;
30
31impl File {
32 pub fn execute<'a, 'tree>(
36 &self,
37 tree: &'tree Tree,
38 source: &'tree str,
39 config: &ExecutionConfig,
40 cancellation_flag: &dyn CancellationFlag,
41 ) -> Result<Graph<'tree>, ExecutionError> {
42 let mut graph = Graph::new();
43 self.execute_into(&mut graph, tree, source, config, cancellation_flag)?;
44 Ok(graph)
45 }
46
47 pub fn execute_into<'a, 'tree>(
53 &self,
54 graph: &mut Graph<'tree>,
55 tree: &'tree Tree,
56 source: &'tree str,
57 config: &ExecutionConfig,
58 cancellation_flag: &dyn CancellationFlag,
59 ) -> Result<(), ExecutionError> {
60 if config.lazy {
61 self.execute_lazy_into(graph, tree, source, config, cancellation_flag)
62 } else {
63 self.execute_strict_into(graph, tree, source, config, cancellation_flag)
64 }
65 }
66
67 pub(self) fn check_globals(&self, globals: &mut Globals) -> Result<(), ExecutionError> {
68 for global in &self.globals {
69 match globals.get(&global.name) {
70 None => {
71 if let Some(default) = &global.default {
72 globals
73 .add(global.name.clone(), default.to_string().into())
74 .map_err(|_| {
75 ExecutionError::DuplicateVariable(format!(
76 "global variable {} already defined",
77 global.name
78 ))
79 })?;
80 } else {
81 return Err(ExecutionError::MissingGlobalVariable(
82 global.name.as_str().to_string(),
83 ));
84 }
85 }
86 Some(value) => {
87 if global.quantifier == CaptureQuantifier::ZeroOrMore
88 || global.quantifier == CaptureQuantifier::OneOrMore
89 {
90 if value.as_list().is_err() {
91 return Err(ExecutionError::ExpectedList(
92 global.name.as_str().to_string(),
93 ));
94 }
95 }
96 }
97 }
98 }
99
100 Ok(())
101 }
102
103 pub fn try_visit_matches<'tree, E, F>(
104 &self,
105 tree: &'tree Tree,
106 source: &'tree str,
107 lazy: bool,
108 mut visit: F,
109 ) -> Result<(), E>
110 where
111 F: FnMut(Match<'_, 'tree>) -> Result<(), E>,
112 {
113 if lazy {
114 let file_query = self.query.as_ref().expect("missing file query");
115 self.try_visit_matches_lazy(tree, source, |stanza, mat| {
116 let named_captures = stanza
117 .query
118 .capture_names()
119 .iter()
120 .map(|name| {
121 let index = file_query
122 .capture_index_for_name(*name)
123 .expect("missing index for capture");
124 let quantifier =
125 file_query.capture_quantifiers(mat.pattern_index)[index as usize];
126 (*name, quantifier, index)
127 })
128 .filter(|c| c.2 != stanza.full_match_file_capture_index as u32)
129 .collect();
130 visit(Match {
131 mat,
132 full_capture_index: stanza.full_match_file_capture_index as u32,
133 named_captures,
134 query_location: stanza.range.start,
135 })
136 })
137 } else {
138 self.try_visit_matches_strict(tree, source, |stanza, mat| {
139 let named_captures = stanza
140 .query
141 .capture_names()
142 .iter()
143 .map(|name| {
144 let index = stanza
145 .query
146 .capture_index_for_name(*name)
147 .expect("missing index for capture");
148 let quantifier = stanza.query.capture_quantifiers(0)[index as usize];
149 (*name, quantifier, index)
150 })
151 .filter(|c| c.2 != stanza.full_match_stanza_capture_index as u32)
152 .collect();
153 visit(Match {
154 mat,
155 full_capture_index: stanza.full_match_stanza_capture_index as u32,
156 named_captures,
157 query_location: stanza.range.start,
158 })
159 })
160 }
161 }
162}
163
164impl Stanza {
165 pub fn try_visit_matches<'tree, E, F>(
166 &self,
167 tree: &'tree Tree,
168 source: &'tree str,
169 mut visit: F,
170 ) -> Result<(), E>
171 where
172 F: FnMut(Match<'_, 'tree>) -> Result<(), E>,
173 {
174 self.try_visit_matches_strict(tree, source, |mat| {
175 let named_captures = self
176 .query
177 .capture_names()
178 .iter()
179 .map(|name| {
180 let index = self
181 .query
182 .capture_index_for_name(*name)
183 .expect("missing index for capture");
184 let quantifier = self.query.capture_quantifiers(0)[index as usize];
185 (*name, quantifier, index)
186 })
187 .filter(|c| c.2 != self.full_match_stanza_capture_index as u32)
188 .collect();
189 visit(Match {
190 mat,
191 full_capture_index: self.full_match_stanza_capture_index as u32,
192 named_captures,
193 query_location: self.range.start,
194 })
195 })
196 }
197}
198
199pub struct Match<'a, 'tree> {
200 mat: &'a QueryMatch<'a, 'tree>,
201 full_capture_index: u32,
202 named_captures: Vec<(&'a str, CaptureQuantifier, u32)>,
203 query_location: Location,
204}
205
206impl<'a, 'tree> Match<'a, 'tree> {
207 pub fn full_capture(&self) -> Node<'tree> {
209 self.mat
210 .nodes_for_capture_index(self.full_capture_index)
211 .next()
212 .expect("missing full capture")
213 }
214
215 pub fn named_captures<'s: 'a + 'tree>(
217 &'s self,
218 ) -> impl Iterator<
219 Item = (
220 &'a str,
221 CaptureQuantifier,
222 impl Iterator<Item = Node<'tree>> + 's,
223 ),
224 > {
225 self.named_captures
226 .iter()
227 .map(move |c| (c.0, c.1, self.mat.nodes_for_capture_index(c.2)))
228 }
229
230 pub fn named_capture<'s: 'a + 'tree>(
232 &'s self,
233 name: &str,
234 ) -> Option<(CaptureQuantifier, impl Iterator<Item = Node<'tree>> + 's)> {
235 self.named_captures
236 .iter()
237 .find(|c| c.0 == name)
238 .map(|c| (c.1, self.mat.nodes_for_capture_index(c.2)))
239 }
240
241 pub fn capture_names(&self) -> impl Iterator<Item = &str> {
243 self.named_captures.iter().map(|c| c.0)
244 }
245
246 pub fn query_location(&self) -> &Location {
248 &self.query_location
249 }
250}
251
252pub struct ExecutionConfig<'a, 'g> {
254 pub(crate) functions: &'a Functions,
255 pub(crate) globals: &'a Globals<'g>,
256 pub(crate) lazy: bool,
257 pub(crate) location_attr: Option<Identifier>,
258 pub(crate) variable_name_attr: Option<Identifier>,
259 pub(crate) match_node_attr: Option<Identifier>,
260}
261
262impl<'a, 'g> ExecutionConfig<'a, 'g> {
263 pub fn new(functions: &'a Functions, globals: &'a Globals<'g>) -> Self {
264 Self {
265 functions,
266 globals,
267 lazy: false,
268 location_attr: None,
269 variable_name_attr: None,
270 match_node_attr: None,
271 }
272 }
273
274 pub fn debug_attributes(
275 self,
276 location_attr: Identifier,
277 variable_name_attr: Identifier,
278 match_node_attr: Identifier,
279 ) -> Self {
280 Self {
281 functions: self.functions,
282 globals: self.globals,
283 lazy: self.lazy,
284 location_attr: location_attr.into(),
285 variable_name_attr: variable_name_attr.into(),
286 match_node_attr: match_node_attr.into(),
287 }
288 }
289
290 pub fn lazy(self, lazy: bool) -> Self {
291 Self {
292 functions: self.functions,
293 globals: self.globals,
294 lazy,
295 location_attr: self.location_attr,
296 variable_name_attr: self.variable_name_attr,
297 match_node_attr: self.match_node_attr,
298 }
299 }
300}
301
302pub trait CancellationFlag {
304 fn check(&self, at: &'static str) -> Result<(), CancellationError>;
305}
306
307pub struct NoCancellation;
308impl CancellationFlag for NoCancellation {
309 fn check(&self, _at: &'static str) -> Result<(), CancellationError> {
310 Ok(())
311 }
312}
313
314#[derive(Debug, Error)]
315#[error("Cancelled at \"{0}\"")]
316pub struct CancellationError(pub &'static str);
317
318impl Value {
319 pub fn from_nodes<'tree, NI: IntoIterator<Item = Node<'tree>>>(
320 graph: &mut Graph<'tree>,
321 nodes: NI,
322 quantifier: CaptureQuantifier,
323 ) -> Value {
324 let mut nodes = nodes.into_iter();
325 match quantifier {
326 CaptureQuantifier::Zero => unreachable!(),
327 CaptureQuantifier::One => {
328 let syntax_node = graph.add_syntax_node(nodes.next().expect("missing capture"));
329 syntax_node.into()
330 }
331 CaptureQuantifier::ZeroOrMore | CaptureQuantifier::OneOrMore => {
332 let syntax_nodes = nodes
333 .map(|n| graph.add_syntax_node(n.clone()).into())
334 .collect::<Vec<Value>>();
335 syntax_nodes.into()
336 }
337 CaptureQuantifier::ZeroOrOne => match nodes.next() {
338 None => Value::Null.into(),
339 Some(node) => {
340 let syntax_node = graph.add_syntax_node(node);
341 syntax_node.into()
342 }
343 },
344 }
345 }
346}
347
348impl CreateEdge {
349 pub(crate) fn add_debug_attrs(
350 &self,
351 attributes: &mut Attributes,
352 config: &ExecutionConfig,
353 ) -> Result<(), ExecutionError> {
354 if let Some(location_attr) = &config.location_attr {
355 attributes
356 .add(
357 location_attr.clone(),
358 format!(
359 "line {} column {}",
360 self.location.row + 1,
361 self.location.column + 1
362 ),
363 )
364 .map_err(|_| ExecutionError::DuplicateAttribute(location_attr.as_str().into()))?;
365 }
366 Ok(())
367 }
368}
369impl Variable {
370 pub(crate) fn add_debug_attrs(
371 &self,
372 attributes: &mut Attributes,
373 config: &ExecutionConfig,
374 ) -> Result<(), ExecutionError> {
375 if let Some(variable_name_attr) = &config.variable_name_attr {
376 attributes
377 .add(variable_name_attr.clone(), format!("{}", self))
378 .map_err(|_| {
379 ExecutionError::DuplicateAttribute(variable_name_attr.as_str().into())
380 })?;
381 }
382 if let Some(location_attr) = &config.location_attr {
383 let location = match &self {
384 Variable::Scoped(v) => v.location,
385 Variable::Unscoped(v) => v.location,
386 };
387 attributes
388 .add(
389 location_attr.clone(),
390 format!("line {} column {}", location.row + 1, location.column + 1),
391 )
392 .map_err(|_| ExecutionError::DuplicateAttribute(location_attr.as_str().into()))?;
393 }
394 Ok(())
395 }
396}