fork of https://github.com/tree-sitter/tree-sitter-graph
1// -*- coding: utf-8 -*- 2// ------------------------------------------------------------------------------------------------ 3// Copyright © 2021, 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 8use thiserror::Error; 9use tree_sitter::CaptureQuantifier; 10use tree_sitter::QueryMatch; 11use tree_sitter::Tree; 12 13use crate::ast::File; 14use crate::execution::error::ExecutionError; 15use crate::functions::Functions; 16use crate::graph::Graph; 17use crate::graph::Value; 18use crate::variables::Globals; 19use crate::Identifier; 20 21pub(crate) mod error; 22mod lazy; 23mod strict; 24 25impl File { 26 /// Executes this graph DSL file against a source file. You must provide the parsed syntax 27 /// tree (`tree`) as well as the source text that it was parsed from (`source`). You also 28 /// provide the set of functions and global variables that are available during execution. 29 pub fn execute<'a, 'tree>( 30 &self, 31 tree: &'tree Tree, 32 source: &'tree str, 33 config: &ExecutionConfig, 34 cancellation_flag: &dyn CancellationFlag, 35 ) -> Result<Graph<'tree>, ExecutionError> { 36 let mut graph = Graph::new(); 37 self.execute_into(&mut graph, tree, source, config, cancellation_flag)?; 38 Ok(graph) 39 } 40 41 /// Executes this graph DSL file against a source file, saving the results into an existing 42 /// `Graph` instance. You must provide the parsed syntax tree (`tree`) as well as the source 43 /// text that it was parsed from (`source`). You also provide the set of functions and global 44 /// variables that are available during execution. This variant is useful when you need to 45 /// “pre-seed” the graph with some predefined nodes and/or edges before executing the DSL file. 46 pub fn execute_into<'a, 'tree>( 47 &self, 48 graph: &mut Graph<'tree>, 49 tree: &'tree Tree, 50 source: &'tree str, 51 config: &ExecutionConfig, 52 cancellation_flag: &dyn CancellationFlag, 53 ) -> Result<(), ExecutionError> { 54 if config.lazy { 55 self.execute_lazy_into(graph, tree, source, config, cancellation_flag) 56 } else { 57 self.execute_strict_into(graph, tree, source, config, cancellation_flag) 58 } 59 } 60 61 pub(self) fn check_globals(&self, globals: &mut Globals) -> Result<(), ExecutionError> { 62 for global in &self.globals { 63 match globals.get(&global.name) { 64 None => { 65 if let Some(default) = &global.default { 66 globals 67 .add(global.name.clone(), default.to_string().into()) 68 .map_err(|_| { 69 ExecutionError::DuplicateVariable(format!( 70 "global variable {} already defined", 71 global.name 72 )) 73 })?; 74 } else { 75 return Err(ExecutionError::MissingGlobalVariable( 76 global.name.as_str().to_string(), 77 )); 78 } 79 } 80 Some(value) => { 81 if global.quantifier == CaptureQuantifier::ZeroOrMore 82 || global.quantifier == CaptureQuantifier::OneOrMore 83 { 84 if value.as_list().is_err() { 85 return Err(ExecutionError::ExpectedList( 86 global.name.as_str().to_string(), 87 )); 88 } 89 } 90 } 91 } 92 } 93 94 Ok(()) 95 } 96} 97 98/// Configuration for the execution of a File 99pub struct ExecutionConfig<'a, 'g> { 100 pub(crate) functions: &'a Functions, 101 pub(crate) globals: &'a Globals<'g>, 102 pub(crate) lazy: bool, 103 pub(crate) location_attr: Option<Identifier>, 104 pub(crate) variable_name_attr: Option<Identifier>, 105} 106 107impl<'a, 'g> ExecutionConfig<'a, 'g> { 108 pub fn new(functions: &'a Functions, globals: &'a Globals<'g>) -> Self { 109 Self { 110 functions, 111 globals, 112 lazy: false, 113 location_attr: None, 114 variable_name_attr: None, 115 } 116 } 117 118 pub fn debug_attributes( 119 self, 120 location_attr: Identifier, 121 variable_name_attr: Identifier, 122 ) -> Self { 123 Self { 124 functions: self.functions, 125 globals: self.globals, 126 lazy: self.lazy, 127 location_attr: location_attr.into(), 128 variable_name_attr: variable_name_attr.into(), 129 } 130 } 131 132 pub fn lazy(self, lazy: bool) -> Self { 133 Self { 134 functions: self.functions, 135 globals: self.globals, 136 lazy, 137 location_attr: self.location_attr, 138 variable_name_attr: self.variable_name_attr, 139 } 140 } 141} 142 143/// Trait to signal that the execution is cancelled 144pub trait CancellationFlag { 145 fn check(&self, at: &'static str) -> Result<(), CancellationError>; 146} 147 148pub struct NoCancellation; 149impl CancellationFlag for NoCancellation { 150 fn check(&self, _at: &'static str) -> Result<(), CancellationError> { 151 Ok(()) 152 } 153} 154 155#[derive(Debug, Error)] 156#[error("Cancelled at \"{0}\"")] 157pub struct CancellationError(pub &'static str); 158 159/// Get the value for the given capture, considering the suffix 160pub(self) fn query_capture_value<'tree>( 161 index: usize, 162 quantifier: CaptureQuantifier, 163 mat: &QueryMatch<'_, 'tree>, 164 graph: &mut Graph<'tree>, 165) -> Value { 166 let mut nodes = mat 167 .captures 168 .iter() 169 .filter(|c| c.index as usize == index) 170 .map(|c| c.node); 171 match quantifier { 172 CaptureQuantifier::Zero => unreachable!(), 173 CaptureQuantifier::One => { 174 let syntax_node = graph.add_syntax_node(nodes.next().expect("missing capture")); 175 syntax_node.into() 176 } 177 CaptureQuantifier::ZeroOrMore | CaptureQuantifier::OneOrMore => { 178 let syntax_nodes = nodes 179 .map(|n| graph.add_syntax_node(n.clone()).into()) 180 .collect::<Vec<Value>>(); 181 syntax_nodes.into() 182 } 183 CaptureQuantifier::ZeroOrOne => match nodes.next() { 184 None => Value::Null.into(), 185 Some(node) => { 186 let syntax_node = graph.add_syntax_node(node); 187 syntax_node.into() 188 } 189 }, 190 } 191}