fork of https://github.com/tree-sitter/tree-sitter-graph
at main 33 kB view raw
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: &current_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: &current_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}