fork of https://github.com/tree-sitter/tree-sitter-graph
1// -*- coding: utf-8 -*-
2// ------------------------------------------------------------------------------------------------
3// Copyright © 2022, tree-sitter authors.
4// Licensed under either of Apache License, Version 2.0, or MIT license, at your option.
5// Please see the LICENSE-APACHE or LICENSE-MIT files in this distribution for license details.
6// ------------------------------------------------------------------------------------------------
7
8mod statements;
9mod store;
10mod values;
11
12use log::{debug, trace};
13
14use std::collections::HashMap;
15use std::collections::HashSet;
16
17use tree_sitter::QueryCursor;
18use tree_sitter::QueryMatch;
19use tree_sitter::Tree;
20
21use streaming_iterator::StreamingIterator;
22
23use crate::ast;
24use crate::execution::error::ExecutionError;
25use crate::execution::error::ResultWithExecutionError;
26use crate::execution::error::StatementContext;
27use crate::execution::ExecutionConfig;
28use crate::functions::Functions;
29use crate::graph;
30use crate::graph::Attributes;
31use crate::graph::Graph;
32use crate::graph::Value;
33use crate::variables::Globals;
34use crate::variables::MutVariables;
35use crate::variables::VariableMap;
36use crate::CancellationFlag;
37use crate::Identifier;
38
39use statements::*;
40use store::*;
41use values::*;
42
43impl ast::File {
44 /// Executes this graph DSL file against a source file, saving the results into an existing
45 /// `Graph` instance. You must provide the parsed syntax tree (`tree`) as well as the source
46 /// text that it was parsed from (`source`). You also provide the set of functions and global
47 /// variables that are available during execution. This variant is useful when you need to
48 /// “pre-seed” the graph with some predefined nodes and/or edges before executing the DSL file.
49 pub(super) fn execute_lazy_into<'a, 'tree>(
50 &self,
51 graph: &mut Graph<'tree>,
52 tree: &'tree Tree,
53 source: &'tree str,
54 config: &ExecutionConfig,
55 cancellation_flag: &dyn CancellationFlag,
56 ) -> Result<(), ExecutionError> {
57 let mut globals = Globals::nested(config.globals);
58 self.check_globals(&mut globals)?;
59 let mut config = ExecutionConfig {
60 functions: config.functions,
61 globals: &globals,
62 lazy: config.lazy,
63 location_attr: config.location_attr.clone(),
64 variable_name_attr: config.variable_name_attr.clone(),
65 match_node_attr: config.match_node_attr.clone(),
66 };
67
68 let mut locals = VariableMap::new();
69 let mut store = LazyStore::new();
70 let mut scoped_store = LazyScopedVariables::new();
71 let mut lazy_graph = LazyGraph::new();
72 let mut function_parameters = Vec::new();
73 let mut prev_element_debug_info = HashMap::new();
74
75 self.try_visit_matches_lazy(tree, source, |stanza, mat| {
76 cancellation_flag.check("processing matches")?;
77 stanza.execute_lazy(
78 source,
79 &mat,
80 graph,
81 &mut config,
82 &mut locals,
83 &mut store,
84 &mut scoped_store,
85 &mut lazy_graph,
86 &mut function_parameters,
87 &mut prev_element_debug_info,
88 &self.inherited_variables,
89 &self.shorthands,
90 cancellation_flag,
91 )
92 })?;
93
94 let mut exec = EvaluationContext {
95 source,
96 graph,
97 functions: config.functions,
98 store: &store,
99 scoped_store: &scoped_store,
100 inherited_variables: &self.inherited_variables,
101 function_parameters: &mut function_parameters,
102 prev_element_debug_info: &mut prev_element_debug_info,
103 cancellation_flag,
104 };
105 lazy_graph.evaluate(&mut exec)?;
106 // make sure any unforced values are now forced, to surface any problems
107 // hidden by the fact that the values were unused
108 store.evaluate_all(&mut exec)?;
109 scoped_store.evaluate_all(&mut exec)?;
110
111 Ok(())
112 }
113
114 pub(super) fn try_visit_matches_lazy<'tree, E, F>(
115 &self,
116 tree: &'tree Tree,
117 source: &'tree str,
118 mut visit: F,
119 ) -> Result<(), E>
120 where
121 F: FnMut(&ast::Stanza, &QueryMatch<'_, 'tree>) -> Result<(), E>,
122 {
123 let mut cursor = QueryCursor::new();
124 let query = self.query.as_ref().unwrap();
125 let mut matches = cursor.matches(query, tree.root_node(), source.as_bytes());
126 while let Some(mat) = matches.next() {
127 let stanza = &self.stanzas[mat.pattern_index];
128 visit(stanza, mat)?;
129 }
130 Ok(())
131 }
132}
133
134/// Context for execution, which executes stanzas to build the lazy graph
135struct ExecutionContext<'a, 'c, 'g, 'tree> {
136 source: &'tree str,
137 graph: &'a mut Graph<'tree>,
138 config: &'a ExecutionConfig<'c, 'g>,
139 locals: &'a mut dyn MutVariables<LazyValue>,
140 current_regex_captures: &'a Vec<String>,
141 mat: &'a QueryMatch<'a, 'tree>,
142 full_match_file_capture_index: usize,
143 store: &'a mut LazyStore,
144 scoped_store: &'a mut LazyScopedVariables,
145 lazy_graph: &'a mut LazyGraph,
146 function_parameters: &'a mut Vec<graph::Value>, // re-usable buffer to reduce memory allocations
147 prev_element_debug_info: &'a mut HashMap<GraphElementKey, DebugInfo>,
148 error_context: StatementContext,
149 inherited_variables: &'a HashSet<Identifier>,
150 shorthands: &'a ast::AttributeShorthands,
151 cancellation_flag: &'a dyn CancellationFlag,
152}
153
154/// Context for evaluation, which evalautes the lazy graph to build the actual graph
155pub(self) struct EvaluationContext<'a, 'tree> {
156 pub source: &'tree str,
157 pub graph: &'a mut Graph<'tree>,
158 pub functions: &'a Functions,
159 pub store: &'a LazyStore,
160 pub scoped_store: &'a LazyScopedVariables,
161 pub inherited_variables: &'a HashSet<Identifier>,
162 pub function_parameters: &'a mut Vec<graph::Value>, // re-usable buffer to reduce memory allocations
163 pub prev_element_debug_info: &'a mut HashMap<GraphElementKey, DebugInfo>,
164 pub cancellation_flag: &'a dyn CancellationFlag,
165}
166
167#[derive(Debug, Clone, Hash, PartialEq, Eq)]
168pub(super) enum GraphElementKey {
169 NodeAttribute(graph::GraphNodeRef, Identifier),
170 EdgeAttribute(graph::GraphNodeRef, graph::GraphNodeRef, Identifier),
171}
172
173impl ast::Stanza {
174 fn execute_lazy<'a, 'l, 'g, 'q, 'tree>(
175 &self,
176 source: &'tree str,
177 mat: &QueryMatch<'_, 'tree>,
178 graph: &mut Graph<'tree>,
179 config: &ExecutionConfig,
180 locals: &mut VariableMap<'l, LazyValue>,
181 store: &mut LazyStore,
182 scoped_store: &mut LazyScopedVariables,
183 lazy_graph: &mut LazyGraph,
184 function_parameters: &mut Vec<graph::Value>,
185 prev_element_debug_info: &mut HashMap<GraphElementKey, DebugInfo>,
186 inherited_variables: &HashSet<Identifier>,
187 shorthands: &ast::AttributeShorthands,
188 cancellation_flag: &dyn CancellationFlag,
189 ) -> Result<(), ExecutionError> {
190 let current_regex_captures = vec![];
191 locals.clear();
192 let node = mat
193 .nodes_for_capture_index(self.full_match_file_capture_index as u32)
194 .next()
195 .expect("missing capture for full match");
196 debug!("match {:?} at {}", node, self.range.start);
197 trace!("{{");
198 for statement in &self.statements {
199 let error_context = { StatementContext::new(&statement, &self, &node) };
200 let mut exec = ExecutionContext {
201 source,
202 graph,
203 config,
204 locals,
205 current_regex_captures: ¤t_regex_captures,
206 mat,
207 full_match_file_capture_index: self.full_match_file_capture_index,
208 store,
209 scoped_store,
210 lazy_graph,
211 function_parameters,
212 prev_element_debug_info,
213 error_context,
214 inherited_variables,
215 shorthands,
216 cancellation_flag,
217 };
218 statement
219 .execute_lazy(&mut exec)
220 .with_context(|| exec.error_context.into())?;
221 }
222 trace!("}}");
223 Ok(())
224 }
225}
226
227impl ast::Statement {
228 fn execute_lazy(&self, exec: &mut ExecutionContext) -> Result<(), ExecutionError> {
229 exec.cancellation_flag.check("executing statement")?;
230 match self {
231 Self::DeclareImmutable(statement) => statement.execute_lazy(exec),
232 Self::DeclareMutable(statement) => statement.execute_lazy(exec),
233 Self::Assign(statement) => statement.execute_lazy(exec),
234 Self::Expr(statement) => statement.value.evaluate_lazy(exec).map(|_| ()),
235 Self::CreateGraphNode(statement) => statement.execute_lazy(exec),
236 Self::AddGraphNodeAttribute(statement) => statement.execute_lazy(exec),
237 Self::CreateEdge(statement) => statement.execute_lazy(exec),
238 Self::AddEdgeAttribute(statement) => statement.execute_lazy(exec),
239 Self::Scan(statement) => statement.execute_lazy(exec),
240 Self::Print(statement) => statement.execute_lazy(exec),
241 Self::If(statement) => statement.execute_lazy(exec),
242 Self::ForIn(statement) => statement.execute_lazy(exec),
243 }
244 }
245}
246
247impl ast::DeclareImmutable {
248 fn execute_lazy(&self, exec: &mut ExecutionContext) -> Result<(), ExecutionError> {
249 let value = self.value.evaluate_lazy(exec)?;
250 self.variable.add_lazy(exec, value, false)
251 }
252}
253
254impl ast::DeclareMutable {
255 fn execute_lazy(&self, exec: &mut ExecutionContext) -> Result<(), ExecutionError> {
256 let value = self.value.evaluate_lazy(exec)?;
257 self.variable.add_lazy(exec, value, true)
258 }
259}
260
261impl ast::Assign {
262 fn execute_lazy(&self, exec: &mut ExecutionContext) -> Result<(), ExecutionError> {
263 let value = self.value.evaluate_lazy(exec)?;
264 self.variable.set_lazy(exec, value)
265 }
266}
267
268impl ast::CreateGraphNode {
269 fn execute_lazy(&self, exec: &mut ExecutionContext) -> Result<(), ExecutionError> {
270 let graph_node = exec.graph.add_graph_node();
271 self.node
272 .add_debug_attrs(&mut exec.graph[graph_node].attributes, exec.config)?;
273 if let Some(match_node_attr) = &exec.config.match_node_attr {
274 let match_node = exec
275 .mat
276 .nodes_for_capture_index(exec.full_match_file_capture_index as u32)
277 .next()
278 .expect("missing capture for full match");
279 let syn_node = exec.graph.add_syntax_node(match_node);
280 exec.graph[graph_node]
281 .attributes
282 .add(match_node_attr.clone(), syn_node)
283 .map_err(|_| {
284 ExecutionError::DuplicateAttribute(format!(
285 " {} on graph node ({}) in {}",
286 match_node_attr, graph_node, self,
287 ))
288 })?;
289 }
290 self.node.add_lazy(exec, graph_node.into(), false)
291 }
292}
293
294impl ast::AddGraphNodeAttribute {
295 fn execute_lazy(&self, exec: &mut ExecutionContext) -> Result<(), ExecutionError> {
296 let node = self.node.evaluate_lazy(exec)?;
297 let mut attributes = Vec::new();
298 let mut add_attribute = |a| attributes.push(a);
299 for attribute in &self.attributes {
300 attribute.execute_lazy(exec, &mut add_attribute)?;
301 }
302 let stmt =
303 LazyAddGraphNodeAttribute::new(node, attributes, exec.error_context.clone().into());
304 exec.lazy_graph.push(stmt.into());
305 Ok(())
306 }
307}
308
309impl ast::CreateEdge {
310 fn execute_lazy(&self, exec: &mut ExecutionContext) -> Result<(), ExecutionError> {
311 let source = self.source.evaluate_lazy(exec)?;
312 let sink = self.sink.evaluate_lazy(exec)?;
313 let mut attributes = Attributes::new();
314 self.add_debug_attrs(&mut attributes, exec.config)?;
315 let stmt = LazyCreateEdge::new(source, sink, attributes, exec.error_context.clone().into());
316 exec.lazy_graph.push(stmt.into());
317 Ok(())
318 }
319}
320
321impl ast::AddEdgeAttribute {
322 fn execute_lazy(&self, exec: &mut ExecutionContext) -> Result<(), ExecutionError> {
323 let source = self.source.evaluate_lazy(exec)?;
324 let sink = self.sink.evaluate_lazy(exec)?;
325 let mut attributes = Vec::new();
326 let mut add_attribute = |a| attributes.push(a);
327 for attribute in &self.attributes {
328 attribute.execute_lazy(exec, &mut add_attribute)?;
329 }
330 let stmt =
331 LazyAddEdgeAttribute::new(source, sink, attributes, exec.error_context.clone().into());
332 exec.lazy_graph.push(stmt.into());
333 Ok(())
334 }
335}
336
337impl ast::Scan {
338 fn execute_lazy(&self, exec: &mut ExecutionContext) -> Result<(), ExecutionError> {
339 let match_string = self.value.evaluate_eager(exec)?.into_string()?;
340
341 let mut i = 0;
342 let mut matches = Vec::new();
343 while i < match_string.len() {
344 matches.clear();
345 for (index, arm) in self.arms.iter().enumerate() {
346 exec.cancellation_flag.check("processing scan matches")?;
347 let captures = arm.regex.captures(&match_string[i..]);
348 if let Some(captures) = captures {
349 if captures
350 .get(0)
351 .expect("missing regex capture")
352 .range()
353 .is_empty()
354 {
355 return Err(ExecutionError::EmptyRegexCapture(format!(
356 "for regular expression /{}/",
357 arm.regex
358 )));
359 }
360 matches.push((captures, index));
361 }
362 }
363
364 if matches.is_empty() {
365 return Ok(());
366 }
367
368 matches.sort_by_key(|(captures, index)| {
369 let range = captures.get(0).expect("missing regex capture").range();
370 (range.start, *index)
371 });
372
373 let (regex_captures, block_index) = &matches[0];
374 let arm = &self.arms[*block_index];
375
376 let mut current_regex_captures = Vec::new();
377 for regex_capture in regex_captures.iter() {
378 current_regex_captures
379 .push(regex_capture.map(|m| m.as_str()).unwrap_or("").to_string());
380 }
381
382 let mut arm_locals = VariableMap::nested(exec.locals);
383 let mut arm_exec = ExecutionContext {
384 source: exec.source,
385 graph: exec.graph,
386 config: exec.config,
387 locals: &mut arm_locals,
388 current_regex_captures: ¤t_regex_captures,
389 mat: exec.mat,
390 full_match_file_capture_index: exec.full_match_file_capture_index,
391 store: exec.store,
392 scoped_store: exec.scoped_store,
393 lazy_graph: exec.lazy_graph,
394 function_parameters: exec.function_parameters,
395 prev_element_debug_info: exec.prev_element_debug_info,
396 error_context: exec.error_context.clone(),
397 inherited_variables: exec.inherited_variables,
398 shorthands: exec.shorthands,
399 cancellation_flag: exec.cancellation_flag,
400 };
401
402 for statement in &arm.statements {
403 arm_exec.error_context.statement = format!("{}", statement);
404 arm_exec.error_context.statement_location = statement.location();
405 statement
406 .execute_lazy(&mut arm_exec)
407 .with_context(|| {
408 format!("matching {} with arm \"{}\"", match_string, arm.regex,).into()
409 })
410 .with_context(|| arm_exec.error_context.clone().into())?;
411 }
412
413 i += regex_captures
414 .get(0)
415 .expect("missing regex capture")
416 .range()
417 .end;
418 }
419
420 Ok(())
421 }
422}
423
424impl ast::Print {
425 fn execute_lazy(&self, exec: &mut ExecutionContext) -> Result<(), ExecutionError> {
426 let mut arguments = Vec::new();
427 for value in &self.values {
428 let argument = if let ast::Expression::StringConstant(expr) = value {
429 LazyPrintArgument::Text(expr.value.clone())
430 } else {
431 LazyPrintArgument::Value(value.evaluate_lazy(exec)?)
432 };
433 arguments.push(argument);
434 }
435 let stmt = LazyPrint::new(arguments, exec.error_context.clone().into());
436 exec.lazy_graph.push(stmt.into());
437 Ok(())
438 }
439}
440
441impl ast::If {
442 fn execute_lazy(&self, exec: &mut ExecutionContext) -> Result<(), ExecutionError> {
443 for arm in &self.arms {
444 let mut result = true;
445 for condition in &arm.conditions {
446 result &= condition.test_eager(exec)?;
447 }
448 if result {
449 let mut arm_locals = VariableMap::nested(exec.locals);
450 let mut arm_exec = ExecutionContext {
451 source: exec.source,
452 graph: exec.graph,
453 config: exec.config,
454 locals: &mut arm_locals,
455 current_regex_captures: exec.current_regex_captures,
456 mat: exec.mat,
457 full_match_file_capture_index: exec.full_match_file_capture_index,
458 store: exec.store,
459 scoped_store: exec.scoped_store,
460 lazy_graph: exec.lazy_graph,
461 function_parameters: exec.function_parameters,
462 prev_element_debug_info: exec.prev_element_debug_info,
463 error_context: exec.error_context.clone(),
464 inherited_variables: exec.inherited_variables,
465 shorthands: exec.shorthands,
466 cancellation_flag: exec.cancellation_flag,
467 };
468 for stmt in &arm.statements {
469 arm_exec.error_context.statement = format!("{}", stmt);
470 arm_exec.error_context.statement_location = stmt.location();
471 stmt.execute_lazy(&mut arm_exec)?;
472 }
473 break;
474 }
475 }
476 Ok(())
477 }
478}
479
480impl ast::Condition {
481 // Eagerly evaluate the condition to a boolean. It assumes the argument expressions
482 // are local (i.e., `is_local = true` in the checker).
483 fn test_eager(&self, exec: &mut ExecutionContext) -> Result<bool, ExecutionError> {
484 match self {
485 Self::Some { value, .. } => Ok(!value.evaluate_eager(exec)?.is_null()),
486 Self::None { value, .. } => Ok(value.evaluate_eager(exec)?.is_null()),
487 Self::Bool { value, .. } => Ok(value.evaluate_eager(exec)?.into_boolean()?),
488 }
489 }
490}
491
492impl ast::ForIn {
493 fn execute_lazy(&self, exec: &mut ExecutionContext) -> Result<(), ExecutionError> {
494 let values = self.value.evaluate_eager(exec)?.into_list()?;
495 let mut loop_locals = VariableMap::nested(exec.locals);
496 for value in values {
497 loop_locals.clear();
498 let mut loop_exec = ExecutionContext {
499 source: exec.source,
500 graph: exec.graph,
501 config: exec.config,
502 locals: &mut loop_locals,
503 current_regex_captures: exec.current_regex_captures,
504 mat: exec.mat,
505 full_match_file_capture_index: exec.full_match_file_capture_index,
506 store: exec.store,
507 scoped_store: exec.scoped_store,
508 lazy_graph: exec.lazy_graph,
509 function_parameters: exec.function_parameters,
510 prev_element_debug_info: exec.prev_element_debug_info,
511 error_context: exec.error_context.clone(),
512 inherited_variables: exec.inherited_variables,
513 shorthands: exec.shorthands,
514 cancellation_flag: exec.cancellation_flag,
515 };
516 self.variable
517 .add_lazy(&mut loop_exec, value.into(), false)?;
518 for stmt in &self.statements {
519 loop_exec.error_context.statement = format!("{}", stmt);
520 loop_exec.error_context.statement_location = stmt.location();
521 stmt.execute_lazy(&mut loop_exec)?;
522 }
523 }
524 Ok(())
525 }
526}
527
528impl ast::Expression {
529 fn evaluate_lazy(&self, exec: &mut ExecutionContext) -> Result<LazyValue, ExecutionError> {
530 match self {
531 Self::FalseLiteral => Ok(false.into()),
532 Self::NullLiteral => Ok(graph::Value::Null.into()),
533 Self::TrueLiteral => Ok(true.into()),
534 Self::IntegerConstant(expr) => expr.evaluate_lazy(exec),
535 Self::StringConstant(expr) => expr.evaluate_lazy(exec),
536 Self::ListLiteral(expr) => expr.evaluate_lazy(exec),
537 Self::SetLiteral(expr) => expr.evaluate_lazy(exec),
538 Self::ListComprehension(expr) => expr.evaluate_lazy(exec),
539 Self::SetComprehension(expr) => expr.evaluate_lazy(exec),
540 Self::Capture(expr) => expr.evaluate_lazy(exec),
541 Self::Variable(expr) => expr.evaluate_lazy(exec),
542 Self::Call(expr) => expr.evaluate_lazy(exec),
543 Self::RegexCapture(expr) => expr.evaluate_lazy(exec),
544 }
545 }
546
547 // Eagerly evaluate the expression to a `Value`, instead of a `LazyValue`. This method should
548 // only be called on expressions that are local (i.e., `is_local = true` in the checker).
549 fn evaluate_eager(&self, exec: &mut ExecutionContext) -> Result<graph::Value, ExecutionError> {
550 self.evaluate_lazy(exec)?.evaluate(&mut EvaluationContext {
551 source: exec.source,
552 graph: exec.graph,
553 functions: exec.config.functions,
554 store: exec.store,
555 scoped_store: exec.scoped_store,
556 inherited_variables: exec.inherited_variables,
557 function_parameters: exec.function_parameters,
558 prev_element_debug_info: exec.prev_element_debug_info,
559 cancellation_flag: exec.cancellation_flag,
560 })
561 }
562}
563
564impl ast::IntegerConstant {
565 fn evaluate_lazy(&self, _exec: &mut ExecutionContext) -> Result<LazyValue, ExecutionError> {
566 Ok(self.value.into())
567 }
568}
569
570impl ast::StringConstant {
571 fn evaluate_lazy(&self, _exec: &mut ExecutionContext) -> Result<LazyValue, ExecutionError> {
572 Ok(self.value.clone().into())
573 }
574}
575
576impl ast::ListLiteral {
577 fn evaluate_lazy(&self, exec: &mut ExecutionContext) -> Result<LazyValue, ExecutionError> {
578 let mut elements = Vec::new();
579 for element in &self.elements {
580 elements.push(element.evaluate_lazy(exec)?);
581 }
582 Ok(elements.into())
583 }
584}
585
586impl ast::ListComprehension {
587 fn evaluate_lazy(&self, exec: &mut ExecutionContext) -> Result<LazyValue, ExecutionError> {
588 let values = self.value.evaluate_eager(exec)?.into_list()?;
589 let mut elements = Vec::new();
590 let mut loop_locals = VariableMap::nested(exec.locals);
591 for value in values {
592 loop_locals.clear();
593 let mut loop_exec = ExecutionContext {
594 source: exec.source,
595 graph: exec.graph,
596 config: exec.config,
597 locals: &mut loop_locals,
598 current_regex_captures: exec.current_regex_captures,
599 mat: exec.mat,
600 full_match_file_capture_index: exec.full_match_file_capture_index,
601 store: exec.store,
602 scoped_store: exec.scoped_store,
603 lazy_graph: exec.lazy_graph,
604 function_parameters: exec.function_parameters,
605 prev_element_debug_info: exec.prev_element_debug_info,
606 error_context: exec.error_context.clone(),
607 inherited_variables: exec.inherited_variables,
608 shorthands: exec.shorthands,
609 cancellation_flag: exec.cancellation_flag,
610 };
611 self.variable
612 .add_lazy(&mut loop_exec, value.into(), false)?;
613 let element = self.element.evaluate_lazy(&mut loop_exec)?;
614 elements.push(element);
615 }
616 Ok(elements.into())
617 }
618}
619
620impl ast::SetLiteral {
621 fn evaluate_lazy(&self, exec: &mut ExecutionContext) -> Result<LazyValue, ExecutionError> {
622 let mut elements = Vec::new();
623 for element in &self.elements {
624 elements.push(element.evaluate_lazy(exec)?);
625 }
626 Ok(LazySet::new(elements).into())
627 }
628}
629
630impl ast::SetComprehension {
631 fn evaluate_lazy(&self, exec: &mut ExecutionContext) -> Result<LazyValue, ExecutionError> {
632 let values = self.value.evaluate_eager(exec)?.into_list()?;
633 let mut elements = Vec::new();
634 let mut loop_locals = VariableMap::nested(exec.locals);
635 for value in values {
636 loop_locals.clear();
637 let mut loop_exec = ExecutionContext {
638 source: exec.source,
639 graph: exec.graph,
640 config: exec.config,
641 locals: &mut loop_locals,
642 current_regex_captures: exec.current_regex_captures,
643 mat: exec.mat,
644 full_match_file_capture_index: exec.full_match_file_capture_index,
645 store: exec.store,
646 scoped_store: exec.scoped_store,
647 lazy_graph: exec.lazy_graph,
648 function_parameters: exec.function_parameters,
649 prev_element_debug_info: exec.prev_element_debug_info,
650 error_context: exec.error_context.clone(),
651 inherited_variables: exec.inherited_variables,
652 shorthands: exec.shorthands,
653 cancellation_flag: exec.cancellation_flag,
654 };
655 self.variable
656 .add_lazy(&mut loop_exec, value.into(), false)?;
657 let element = self.element.evaluate_lazy(&mut loop_exec)?;
658 elements.push(element);
659 }
660 Ok(LazySet::new(elements).into())
661 }
662}
663
664impl ast::Capture {
665 fn evaluate_lazy(&self, exec: &mut ExecutionContext) -> Result<LazyValue, ExecutionError> {
666 Ok(Value::from_nodes(
667 exec.graph,
668 exec.mat
669 .nodes_for_capture_index(self.file_capture_index as u32),
670 self.quantifier,
671 )
672 .into())
673 }
674}
675
676impl ast::Call {
677 fn evaluate_lazy(&self, exec: &mut ExecutionContext) -> Result<LazyValue, ExecutionError> {
678 let mut parameters = Vec::new();
679 for parameter in &self.parameters {
680 parameters.push(parameter.evaluate_lazy(exec)?);
681 }
682 Ok(LazyCall::new(self.function.clone(), parameters).into())
683 }
684}
685
686impl ast::RegexCapture {
687 fn evaluate_lazy(&self, exec: &mut ExecutionContext) -> Result<LazyValue, ExecutionError> {
688 let value = exec.current_regex_captures[self.match_index].clone();
689 Ok(value.into())
690 }
691}
692
693impl ast::Variable {
694 fn evaluate_lazy(&self, exec: &mut ExecutionContext) -> Result<LazyValue, ExecutionError> {
695 match self {
696 Self::Scoped(variable) => variable.evaluate_lazy(exec),
697 Self::Unscoped(variable) => variable.evaluate_lazy(exec),
698 }
699 }
700}
701
702impl ast::Variable {
703 fn add_lazy(
704 &self,
705 exec: &mut ExecutionContext,
706 value: LazyValue,
707 mutable: bool,
708 ) -> Result<(), ExecutionError> {
709 match self {
710 Self::Scoped(variable) => variable.add_lazy(exec, value, mutable),
711 Self::Unscoped(variable) => variable.add_lazy(exec, value, mutable),
712 }
713 }
714
715 fn set_lazy(
716 &self,
717 exec: &mut ExecutionContext,
718 value: LazyValue,
719 ) -> Result<(), ExecutionError> {
720 match self {
721 Self::Scoped(variable) => variable.set_lazy(exec, value),
722 Self::Unscoped(variable) => variable.set_lazy(exec, value),
723 }
724 }
725}
726
727impl ast::ScopedVariable {
728 fn evaluate_lazy(&self, exec: &mut ExecutionContext) -> Result<LazyValue, ExecutionError> {
729 let scope = self.scope.evaluate_lazy(exec)?;
730 let value = LazyScopedVariable::new(scope, self.name.clone());
731 Ok(value.into())
732 }
733
734 fn add_lazy(
735 &self,
736 exec: &mut ExecutionContext,
737 value: LazyValue,
738 mutable: bool,
739 ) -> Result<(), ExecutionError> {
740 if mutable {
741 return Err(ExecutionError::CannotDefineMutableScopedVariable(format!(
742 "{}",
743 self
744 )));
745 }
746 let scope = self.scope.evaluate_lazy(exec)?;
747 let variable = exec.store.add(value, exec.error_context.clone().into());
748 exec.scoped_store.add(
749 scope,
750 self.name.clone(),
751 variable.into(),
752 exec.error_context.clone().into(),
753 )
754 }
755
756 fn set_lazy(
757 &self,
758 _exec: &mut ExecutionContext,
759 _value: LazyValue,
760 ) -> Result<(), ExecutionError> {
761 Err(ExecutionError::CannotAssignScopedVariable(format!(
762 "{}",
763 self
764 )))
765 }
766}
767
768impl ast::UnscopedVariable {
769 fn evaluate_lazy(&self, exec: &mut ExecutionContext) -> Result<LazyValue, ExecutionError> {
770 if let Some(value) = exec.config.globals.get(&self.name) {
771 Some(value.clone().into())
772 } else {
773 exec.locals.get(&self.name).map(|value| value.clone())
774 }
775 .ok_or_else(|| ExecutionError::UndefinedVariable(format!("{}", self)))
776 }
777}
778
779impl ast::UnscopedVariable {
780 fn add_lazy(
781 &self,
782 exec: &mut ExecutionContext,
783 value: LazyValue,
784 mutable: bool,
785 ) -> Result<(), ExecutionError> {
786 if exec.config.globals.get(&self.name).is_some() {
787 return Err(ExecutionError::DuplicateVariable(format!(
788 " global {}",
789 self
790 )));
791 }
792 let value = exec.store.add(value, exec.error_context.clone().into());
793 exec.locals
794 .add(self.name.clone(), value.into(), mutable)
795 .map_err(|_| ExecutionError::DuplicateVariable(format!(" local {}", self)))
796 }
797
798 fn set_lazy(
799 &self,
800 exec: &mut ExecutionContext,
801 value: LazyValue,
802 ) -> Result<(), ExecutionError> {
803 if exec.config.globals.get(&self.name).is_some() {
804 return Err(ExecutionError::CannotAssignImmutableVariable(format!(
805 " global {}",
806 self
807 )));
808 }
809 let value = exec.store.add(value, exec.error_context.clone().into());
810 exec.locals
811 .set(self.name.clone(), value.into())
812 .map_err(|_| {
813 if exec.locals.get(&self.name).is_some() {
814 ExecutionError::CannotAssignImmutableVariable(format!("{}", self))
815 } else {
816 ExecutionError::UndefinedVariable(format!("{}", self))
817 }
818 })
819 }
820}
821
822impl ast::Attribute {
823 fn execute_lazy<F>(
824 &self,
825 exec: &mut ExecutionContext,
826 add_attribute: &mut F,
827 ) -> Result<(), ExecutionError>
828 where
829 F: FnMut(LazyAttribute) -> (),
830 {
831 exec.cancellation_flag.check("executing attribute")?;
832 let value = self.value.evaluate_lazy(exec)?;
833 if let Some(shorthand) = exec.shorthands.get(&self.name) {
834 shorthand.execute_lazy(exec, add_attribute, value)
835 } else {
836 add_attribute(LazyAttribute::new(self.name.clone(), value));
837 Ok(())
838 }
839 }
840}
841
842impl ast::AttributeShorthand {
843 fn execute_lazy<F>(
844 &self,
845 exec: &mut ExecutionContext,
846 add_attribute: &mut F,
847 value: LazyValue,
848 ) -> Result<(), ExecutionError>
849 where
850 F: FnMut(LazyAttribute) -> (),
851 {
852 let mut shorthand_locals = VariableMap::new();
853 let mut shorthand_exec = ExecutionContext {
854 source: exec.source,
855 graph: exec.graph,
856 config: exec.config,
857 locals: &mut shorthand_locals,
858 current_regex_captures: exec.current_regex_captures,
859 mat: exec.mat,
860 full_match_file_capture_index: exec.full_match_file_capture_index,
861 store: exec.store,
862 scoped_store: exec.scoped_store,
863 lazy_graph: exec.lazy_graph,
864 function_parameters: exec.function_parameters,
865 prev_element_debug_info: exec.prev_element_debug_info,
866 error_context: exec.error_context.clone(),
867 inherited_variables: exec.inherited_variables,
868 shorthands: exec.shorthands,
869 cancellation_flag: exec.cancellation_flag,
870 };
871 self.variable.add_lazy(&mut shorthand_exec, value, false)?;
872 for attr in &self.attributes {
873 attr.execute_lazy(&mut shorthand_exec, add_attribute)?;
874 }
875 Ok(())
876 }
877}