fork of https://github.com/tree-sitter/tree-sitter-graph

Compare changes

Choose any two refs to compare.

+10 -10
.github/workflows/ci.yml
··· 19 19 uses: hecrj/setup-rust-action@v1 20 20 with: 21 21 rust-version: ${{ matrix.rust }} 22 + - name: Install Cargo plugins 23 + run: | 24 + rustup toolchain install nightly 25 + cargo install cargo-hack cargo-minimal-versions 22 26 - name: Checkout code 23 27 uses: actions/checkout@v2 24 28 - name: Check formatting ··· 33 37 key: ${{ runner.OS }}-cargo-${{ hashFiles('**/Cargo.lock') }} 34 38 restore-keys: | 35 39 ${{ runner.OS }}-cargo- 36 - - name: Build library (default features) 37 - run: cargo build 38 - - name: Test library (default features) 39 - run: cargo test 40 - - name: Build library (all features) 41 - run: cargo build --all-features 42 - - name: Test library (all features) 43 - run: cargo test --all-features 44 - - name: Build program 45 - run: cargo build --bin tree-sitter-graph --features=cli 40 + - name: Build library (all feature combinations) 41 + run: cargo hack --feature-powerset --no-dev-deps build 42 + - name: Test library (all feature combinations) 43 + run: cargo hack --feature-powerset test 44 + - name: Build library (minimal versions) 45 + run: cargo minimal-versions build
+70
CHANGELOG.md
··· 5 5 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 6 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 7 8 + ## v0.12.0 -- 2024-12-12 9 + 10 + Upgraded the `tree-sitter` dependency to version 0.24. 11 + 12 + ## v0.11.3 -- 2024-05-29 13 + 14 + ### Library 15 + 16 + #### Fixed 17 + 18 + - Excerpts shown as part of errors now use formatting that works both in light and dark mode. 19 + 20 + ## v0.11.2 -- 2024-03-08 21 + 22 + ### DSL 23 + 24 + #### Added 25 + 26 + - Support adding an edge multiple times, or setting an attribute multiple times with the same value. Previously these would raise runtime errors. 27 + 28 + ## v0.11.1 -- 2024-03-06 29 + 30 + Updated the `tree-sitter` dependency to include the required minimal patch version. 31 + 32 + ## v0.11.0 -- 2023-07-17 33 + 34 + ### Library 35 + 36 + #### Added 37 + 38 + - Store a debug attribute for the matched syntax node, providing information about the syntactic category of the match that gave rise to the graph node. 39 + 40 + ## v0.10.5 -- 2023-06-26 41 + 42 + #### Fixed 43 + 44 + - A panic that sometimes occurred in lazy execution mode. 45 + 46 + ## v0.10.4 -- 2023-06-02 47 + 48 + ### Library 49 + 50 + #### Added 51 + 52 + - Several errors include more context in the error message: Duplicate errors report both statements using source snippets. Edge statements report which argument (the source or the sink) triggered an evluation error. 53 + 54 + #### Fixed 55 + 56 + - Ensure that edge attribute statements are executed after edges are created, to prevent non-deterministic ordering bugs in lazy execution mode. 57 + 58 + ## v0.10.3 -- 2023-06-01 59 + 60 + ### DSL 61 + 62 + #### Added 63 + 64 + - Scoped variables can be inherited by child nodes by specifying `inherit .var_name` as part of the source. Using `@node.var_name` will either use the value set on the node itself, or otherwise the one set on the closest enclosing node. 65 + 66 + ## v0.10.2 -- 2023-05-25 67 + 68 + ### Library 69 + 70 + #### Added 71 + 72 + - Edge debug information now contains the TSG location of the `edge` statement if `ExecutionConfig::location_attr` is set. 73 + 74 + #### Changed 75 + 76 + - The TSG location in the debug information is formatted more explicitly as `line XX column YY` instead of `(XX, YY)`. 77 + 8 78 ## v0.10.1 -- 2023-05-15 9 79 10 80 ### Library
+20 -44
Cargo.toml
··· 1 1 [package] 2 2 name = "tree-sitter-graph" 3 - version = "0.10.1" 3 + version = "0.12.0" 4 4 description = "Construct graphs from parsed source code" 5 5 homepage = "https://github.com/tree-sitter/tree-sitter-graph/" 6 6 repository = "https://github.com/tree-sitter/tree-sitter-graph/" ··· 15 15 # All of our tests are in the tests/it "integration" test executable. 16 16 test = false 17 17 18 - [dependencies] 19 - log = "0.4" 20 - regex = "1" 21 - serde = "1.0" 22 - serde_json = "1.0" 23 - smallvec = { version="1.6", features=["union"] } 24 - thiserror = "1.0" 25 - tree-sitter = "0.20" 26 - 27 - [dependencies.string-interner] 28 - version = "0.12" 29 - default-features = false 30 - features = ["std", "inline-more", "backends"] 31 - 32 - [dev-dependencies] 33 - env_logger = "0.9" 34 - indoc = "1.0" 35 - tree-sitter-python = "0.19.1" 36 - 37 - # cli-only dependencies below 38 - 39 18 [[bin]] 40 19 name = "tree-sitter-graph" 41 20 required-features = ["cli"] ··· 44 23 cli = ["anyhow", "clap", "env_logger", "term-colors", "tree-sitter-config", "tree-sitter-loader"] 45 24 term-colors = ["colored"] 46 25 47 - [dependencies.anyhow] 48 - optional = true 49 - version = "1.0" 50 - 51 - [dependencies.clap] 52 - optional = true 53 - version = "3.2" 54 - 55 - [dependencies.colored] 56 - optional = true 57 - version = "2" 58 - 59 - [dependencies.env_logger] 60 - optional = true 61 - version = "0.9" 62 - 63 - [dependencies.tree-sitter-config] 64 - optional = true 65 - version = "0.19" 26 + [dependencies] 27 + anyhow = { version = "1.0", optional = true } 28 + clap = { version = "3.2", optional = true } 29 + colored = { version = "2", optional = true } 30 + env_logger = { version = "0.9", optional = true } 31 + log = "0.4" 32 + regex = "1.3.2" 33 + serde = "1.0" 34 + serde_json = "1.0" 35 + smallvec = { version="1.6", features=["union"] } 36 + streaming-iterator = "0.1.9" 37 + thiserror = "1.0.7" 38 + tree-sitter = "0.25" 39 + tree-sitter-config = { version = "0.25", optional = true } 40 + tree-sitter-loader = { version = "0.25", optional = true } 66 41 67 - [dependencies.tree-sitter-loader] 68 - optional = true 69 - version = "0.19" 42 + [dev-dependencies] 43 + env_logger = "0.9" 44 + indoc = "1.0" 45 + tree-sitter-python = "=0.23.5"
+1 -1
README.md
··· 20 20 21 21 ``` toml 22 22 [dependencies] 23 - tree-sitter-graph = "0.10" 23 + tree-sitter-graph = "0.12" 24 24 ``` 25 25 26 26 To use it as a program, install it via `cargo install`:
+29
src/ast.rs
··· 9 9 10 10 use regex::Regex; 11 11 use std::collections::HashMap; 12 + use std::collections::HashSet; 12 13 use std::fmt; 13 14 use tree_sitter::CaptureQuantifier; 14 15 use tree_sitter::Language; ··· 24 25 pub language: Language, 25 26 /// The expected global variables used in this file 26 27 pub globals: Vec<Global>, 28 + /// The scoped variables that are inherited by child nodes 29 + pub inherited_variables: HashSet<Identifier>, 27 30 /// The combined query of all stanzas in the file 28 31 pub query: Option<Query>, 29 32 /// The list of stanzas in the file ··· 37 40 File { 38 41 language, 39 42 globals: Vec::new(), 43 + inherited_variables: HashSet::new(), 40 44 query: None, 41 45 stanzas: Vec::new(), 42 46 shorthands: AttributeShorthands::new(), ··· 77 81 DeclareImmutable(DeclareImmutable), 78 82 DeclareMutable(DeclareMutable), 79 83 Assign(Assign), 84 + Expr(ExpressionStatement), 80 85 // Graph nodes 81 86 CreateGraphNode(CreateGraphNode), 82 87 AddGraphNodeAttribute(AddGraphNodeAttribute), ··· 99 104 Self::DeclareImmutable(stmt) => stmt.fmt(f), 100 105 Self::DeclareMutable(stmt) => stmt.fmt(f), 101 106 Self::Assign(stmt) => stmt.fmt(f), 107 + Self::Expr(stmt) => stmt.fmt(f), 102 108 Self::CreateGraphNode(stmt) => stmt.fmt(f), 103 109 Self::AddGraphNodeAttribute(stmt) => stmt.fmt(f), 104 110 Self::CreateEdge(stmt) => stmt.fmt(f), ··· 157 163 write!(f, " {}", attr)?; 158 164 } 159 165 write!(f, " at {}", self.location) 166 + } 167 + } 168 + 169 + /// An `expression` statement whose output is ignored 170 + #[derive(Debug, Eq, PartialEq)] 171 + pub struct ExpressionStatement { 172 + pub value: Expression, 173 + pub location: Location, 174 + } 175 + 176 + impl From<ExpressionStatement> for Statement { 177 + fn from(statement: ExpressionStatement) -> Statement { 178 + Statement::Expr(statement) 179 + } 180 + } 181 + 182 + impl std::fmt::Display for ExpressionStatement { 183 + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 184 + write!( 185 + f, 186 + "{} at {}", 187 + self.value, self.location, 188 + ) 160 189 } 161 190 } 162 191
+3 -3
src/bin/tree-sitter-graph/main.rs
··· 89 89 )?; 90 90 } 91 91 92 - let config = Config::load()?; 92 + let config = Config::load(None)?; 93 93 let mut loader = Loader::new()?; 94 94 let loader_config = config.get()?; 95 95 loader.find_all_languages(&loader_config)?; ··· 98 98 let tsg = std::fs::read(tsg_path) 99 99 .with_context(|| format!("Cannot read TSG file {}", tsg_path.display()))?; 100 100 let tsg = String::from_utf8(tsg)?; 101 - let file = match File::from_str(language, &tsg) { 101 + let file = match File::from_str(language.clone(), &tsg) { 102 102 Ok(file) => file, 103 103 Err(err) => { 104 104 eprintln!("{}", err.display_pretty(tsg_path, &tsg)); ··· 110 110 .with_context(|| format!("Cannot read source file {}", source_path.display()))?; 111 111 let source = String::from_utf8(source)?; 112 112 let mut parser = Parser::new(); 113 - parser.set_language(language)?; 113 + parser.set_language(&language)?; 114 114 let tree = parser 115 115 .parse(&source, None) 116 116 .ok_or_else(|| anyhow!("Cannot parse {}", source_path.display()))?;
+11 -1
src/checker.rs
··· 188 188 .expect("capture should have index") 189 189 != self.full_match_stanza_capture_index as u32 190 190 }) 191 - .map(|cn| Identifier::from(cn.as_str())) 191 + .map(|cn| Identifier::from(*cn)) 192 192 .collect::<HashSet<_>>(); 193 193 let unused_captures = all_captures 194 194 .difference(&used_captures) ··· 220 220 Self::DeclareImmutable(stmt) => stmt.check(ctx), 221 221 Self::DeclareMutable(stmt) => stmt.check(ctx), 222 222 Self::Assign(stmt) => stmt.check(ctx), 223 + Self::Expr(stmt) => stmt.check(ctx), 223 224 Self::CreateGraphNode(stmt) => stmt.check(ctx), 224 225 Self::AddGraphNodeAttribute(stmt) => stmt.check(ctx), 225 226 Self::CreateEdge(stmt) => stmt.check(ctx), ··· 261 262 used_captures.extend(value.used_captures.iter().cloned()); 262 263 let var_result = self.variable.check_set(ctx, value.into())?; 263 264 used_captures.extend(var_result.used_captures); 265 + Ok(StatementResult { used_captures }) 266 + } 267 + } 268 + 269 + impl ast::ExpressionStatement { 270 + fn check(&mut self, ctx: &mut CheckContext) -> Result<StatementResult, CheckError> { 271 + let mut used_captures = HashSet::new(); 272 + let value = self.value.check(ctx)?; 273 + used_captures.extend(value.used_captures.iter().cloned()); 264 274 Ok(StatementResult { used_captures }) 265 275 } 266 276 }
+100 -40
src/execution/lazy/statements.rs
··· 14 14 15 15 use crate::execution::error::ExecutionError; 16 16 use crate::execution::error::ResultWithExecutionError; 17 + use crate::graph::Attributes; 17 18 use crate::Identifier; 18 19 19 20 use super::store::DebugInfo; 20 21 use super::values::*; 21 22 use super::EvaluationContext; 22 23 use super::GraphElementKey; 24 + 25 + /// Lazy graph 26 + #[derive(Debug)] 27 + pub(super) struct LazyGraph { 28 + edge_statements: Vec<LazyStatement>, 29 + attr_statements: Vec<LazyStatement>, 30 + print_statements: Vec<LazyStatement>, 31 + } 32 + 33 + impl LazyGraph { 34 + pub(super) fn new() -> Self { 35 + LazyGraph { 36 + edge_statements: Vec::new(), 37 + attr_statements: Vec::new(), 38 + print_statements: Vec::new(), 39 + } 40 + } 41 + 42 + pub(super) fn push(&mut self, stmt: LazyStatement) { 43 + match stmt { 44 + LazyStatement::AddGraphNodeAttribute(_) => self.attr_statements.push(stmt), 45 + LazyStatement::CreateEdge(_) => self.edge_statements.push(stmt), 46 + LazyStatement::AddEdgeAttribute(_) => self.attr_statements.push(stmt), 47 + LazyStatement::Print(_) => self.print_statements.push(stmt), 48 + } 49 + } 50 + 51 + pub(super) fn evaluate(&self, exec: &mut EvaluationContext) -> Result<(), ExecutionError> { 52 + for stmt in &self.edge_statements { 53 + stmt.evaluate(exec)?; 54 + } 55 + for stmt in &self.attr_statements { 56 + stmt.evaluate(exec)?; 57 + } 58 + for stmt in &self.print_statements { 59 + stmt.evaluate(exec)?; 60 + } 61 + Ok(()) 62 + } 63 + } 23 64 24 65 /// Lazy graph statements 25 66 #[derive(Debug)] ··· 111 152 } 112 153 113 154 pub(super) fn evaluate(&self, exec: &mut EvaluationContext) -> Result<(), ExecutionError> { 114 - let node = self.node.evaluate_as_graph_node(exec)?; 155 + let node = self 156 + .node 157 + .evaluate_as_graph_node(exec) 158 + .with_context(|| "Evaluating target node".to_string().into())?; 115 159 for attribute in &self.attributes { 116 160 let value = attribute.value.evaluate(exec)?; 117 161 let prev_debug_info = exec.prev_element_debug_info.insert( 118 162 GraphElementKey::NodeAttribute(node, attribute.name.clone()), 119 163 self.debug_info.clone(), 120 164 ); 121 - exec.graph[node] 165 + if let Err(_) = exec.graph[node] 122 166 .attributes 123 167 .add(attribute.name.clone(), value) 124 - .map_err(|_| { 125 - ExecutionError::DuplicateAttribute(format!( 126 - "{} on {} at {} and {}", 127 - attribute.name, 128 - node, 129 - prev_debug_info.unwrap(), 130 - self.debug_info, 131 - )) 132 - })?; 168 + { 169 + return Err(ExecutionError::DuplicateAttribute(format!( 170 + "{} on {}", 171 + attribute.name, node, 172 + ))) 173 + .with_context(|| { 174 + ( 175 + prev_debug_info.unwrap().into(), 176 + self.debug_info.clone().into(), 177 + ) 178 + .into() 179 + }); 180 + }; 133 181 } 134 182 Ok(()) 135 183 } ··· 150 198 pub(super) struct LazyCreateEdge { 151 199 source: LazyValue, 152 200 sink: LazyValue, 201 + attributes: Attributes, 153 202 debug_info: DebugInfo, 154 203 } 155 204 156 205 impl LazyCreateEdge { 157 - pub(super) fn new(source: LazyValue, sink: LazyValue, debug_info: DebugInfo) -> Self { 206 + pub(super) fn new( 207 + source: LazyValue, 208 + sink: LazyValue, 209 + attributes: Attributes, 210 + debug_info: DebugInfo, 211 + ) -> Self { 158 212 Self { 159 213 source, 160 214 sink, 215 + attributes, 161 216 debug_info, 162 217 } 163 218 } 164 219 165 220 pub(super) fn evaluate(&self, exec: &mut EvaluationContext) -> Result<(), ExecutionError> { 166 - let source = self.source.evaluate_as_graph_node(exec)?; 167 - let sink = self.sink.evaluate_as_graph_node(exec)?; 168 - let prev_debug_info = exec 169 - .prev_element_debug_info 170 - .insert(GraphElementKey::Edge(source, sink), self.debug_info.clone()); 171 - if let Err(_) = exec.graph[source].add_edge(sink) { 172 - Err(ExecutionError::DuplicateEdge(format!( 173 - "({} -> {}) at {} and {}", 174 - source, 175 - sink, 176 - prev_debug_info.unwrap(), 177 - self.debug_info, 178 - )))?; 179 - } 221 + let source = self 222 + .source 223 + .evaluate_as_graph_node(exec) 224 + .with_context(|| "Evaluating edge source".to_string().into())?; 225 + let sink = self 226 + .sink 227 + .evaluate_as_graph_node(exec) 228 + .with_context(|| "Evaluating edge sink".to_string().into())?; 229 + let edge = match exec.graph[source].add_edge(sink) { 230 + Ok(edge) | Err(edge) => edge, 231 + }; 232 + edge.attributes = self.attributes.clone(); 180 233 Ok(()) 181 234 } 182 235 } ··· 216 269 } 217 270 218 271 pub(super) fn evaluate(&self, exec: &mut EvaluationContext) -> Result<(), ExecutionError> { 219 - let source = self.source.evaluate_as_graph_node(exec)?; 220 - let sink = self.sink.evaluate_as_graph_node(exec)?; 272 + let source = self 273 + .source 274 + .evaluate_as_graph_node(exec) 275 + .with_context(|| "Evaluating edge source".to_string().into())?; 276 + let sink = self 277 + .sink 278 + .evaluate_as_graph_node(exec) 279 + .with_context(|| "Evaluating edge sink".to_string().into())?; 221 280 for attribute in &self.attributes { 222 281 let value = attribute.value.evaluate(exec)?; 223 282 let edge = match exec.graph[source].get_edge_mut(sink) { ··· 231 290 GraphElementKey::EdgeAttribute(source, sink, attribute.name.clone()), 232 291 self.debug_info.clone(), 233 292 ); 234 - edge.attributes 235 - .add(attribute.name.clone(), value) 236 - .map_err(|_| { 237 - ExecutionError::DuplicateAttribute(format!( 238 - "{} on edge ({} -> {}) at {} and {}", 239 - attribute.name, 240 - source, 241 - sink, 242 - prev_debug_info.unwrap(), 243 - self.debug_info, 244 - )) 245 - })?; 293 + if let Err(_) = edge.attributes.add(attribute.name.clone(), value) { 294 + return Err(ExecutionError::DuplicateAttribute(format!( 295 + "{} on edge ({} -> {})", 296 + attribute.name, source, sink, 297 + ))) 298 + .with_context(|| { 299 + ( 300 + prev_debug_info.unwrap().into(), 301 + self.debug_info.clone().into(), 302 + ) 303 + .into() 304 + }); 305 + } 246 306 } 247 307 Ok(()) 248 308 }
+38 -16
src/execution/lazy/store.rs
··· 20 20 use crate::execution::error::ResultWithExecutionError; 21 21 use crate::execution::error::StatementContext; 22 22 use crate::graph; 23 + use crate::graph::SyntaxNodeID; 23 24 use crate::graph::SyntaxNodeRef; 24 25 use crate::Identifier; 25 26 ··· 151 152 }; 152 153 let values = cell.replace(ScopedValues::Forcing); 153 154 let map = self.force(name, values, exec)?; 154 - let result = map 155 - .get(&scope) 156 - .ok_or(ExecutionError::UndefinedScopedVariable(format!( 157 - "{}.{}", 158 - scope, name, 159 - )))? 160 - .clone(); 155 + 156 + let mut result = None; 157 + 158 + if let Some(value) = map.get(&scope.index) { 159 + result = Some(value.clone()); 160 + } else if exec.inherited_variables.contains(name) { 161 + let mut parent = exec 162 + .graph 163 + .syntax_nodes 164 + .get(&scope.index) 165 + .and_then(|n| n.parent()); 166 + while let Some(scope) = parent { 167 + if let Some(value) = map.get(&(scope.id() as u32)) { 168 + result = Some(value.clone()); 169 + break; 170 + } 171 + parent = scope.parent(); 172 + } 173 + } 174 + 161 175 cell.replace(ScopedValues::Forced(map)); 162 - Ok(result) 176 + result.ok_or_else(|| ExecutionError::UndefinedScopedVariable(format!("{}.{}", scope, name))) 163 177 } 164 178 165 179 pub(super) fn evaluate_all(&self, exec: &mut EvaluationContext) -> Result<(), ExecutionError> { ··· 176 190 name: &Identifier, 177 191 values: ScopedValues, 178 192 exec: &mut EvaluationContext, 179 - ) -> Result<HashMap<SyntaxNodeRef, LazyValue>, ExecutionError> { 193 + ) -> Result<HashMap<SyntaxNodeID, LazyValue>, ExecutionError> { 180 194 match values { 181 195 ScopedValues::Unforced(pairs) => { 182 - let mut map = HashMap::new(); 196 + let mut values = HashMap::new(); 183 197 let mut debug_infos = HashMap::new(); 184 198 for (scope, value, debug_info) in pairs.into_iter() { 185 199 let node = scope 186 200 .evaluate_as_syntax_node(exec) 187 201 .with_context(|| format!("Evaluating scope of variable _.{}", name,).into()) 188 202 .with_context(|| debug_info.0.clone().into())?; 189 - let prev_debug_info = debug_infos.insert(node, debug_info.clone()); 190 - match map.insert(node, value.clone()) { 191 - Some(_) => { 203 + match ( 204 + values.insert(node.index, value.clone()), 205 + debug_infos.insert(node.index, debug_info.clone()), 206 + ) { 207 + (Some(_), Some(prev_debug_info)) => { 192 208 return Err(ExecutionError::DuplicateVariable(format!( 193 209 "{}.{}", 194 210 node, name, 195 211 ))) 196 - .with_context(|| (prev_debug_info.unwrap().0, debug_info.0).into()); 212 + .with_context(|| (prev_debug_info.0, debug_info.0).into()); 213 + } 214 + (Some(_), None) => { 215 + unreachable!( 216 + "previous value for syntax node {} without previous debug info", 217 + node 218 + ) 197 219 } 198 220 _ => {} 199 221 }; 200 222 } 201 - Ok(map) 223 + Ok(values) 202 224 } 203 225 ScopedValues::Forcing => Err(ExecutionError::RecursivelyDefinedScopedVariable( 204 226 format!("_.{}", name), ··· 211 233 enum ScopedValues { 212 234 Unforced(Vec<(LazyValue, LazyValue, DebugInfo)>), 213 235 Forcing, 214 - Forced(HashMap<SyntaxNodeRef, LazyValue>), 236 + Forced(HashMap<SyntaxNodeID, LazyValue>), 215 237 } 216 238 217 239 impl ScopedValues {
+6 -1
src/execution/lazy/values.rs
··· 13 13 use std::fmt; 14 14 15 15 use crate::execution::error::ExecutionError; 16 + use crate::execution::error::ResultWithExecutionError; 16 17 use crate::graph::GraphNodeRef; 17 18 use crate::graph::SyntaxNodeRef; 18 19 use crate::graph::Value; ··· 184 185 } 185 186 186 187 fn resolve<'a>(&self, exec: &'a mut EvaluationContext) -> Result<LazyValue, ExecutionError> { 187 - let scope = self.scope.as_ref().evaluate_as_syntax_node(exec)?; 188 + let scope = self 189 + .scope 190 + .as_ref() 191 + .evaluate_as_syntax_node(exec) 192 + .with_context(|| format!("Evaluating scope of variable _.{}", self.name).into())?; 188 193 let scoped_store = &exec.scoped_store; 189 194 scoped_store.evaluate(&scope, &self.name, exec) 190 195 }
+54 -11
src/execution/lazy.rs
··· 12 12 use log::{debug, trace}; 13 13 14 14 use std::collections::HashMap; 15 + use std::collections::HashSet; 15 16 16 17 use tree_sitter::QueryCursor; 17 18 use tree_sitter::QueryMatch; 18 19 use tree_sitter::Tree; 20 + 21 + use streaming_iterator::StreamingIterator; 19 22 20 23 use crate::ast; 21 24 use crate::execution::error::ExecutionError; ··· 24 27 use crate::execution::ExecutionConfig; 25 28 use crate::functions::Functions; 26 29 use crate::graph; 30 + use crate::graph::Attributes; 27 31 use crate::graph::Graph; 28 32 use crate::graph::Value; 29 33 use crate::variables::Globals; ··· 58 62 lazy: config.lazy, 59 63 location_attr: config.location_attr.clone(), 60 64 variable_name_attr: config.variable_name_attr.clone(), 65 + match_node_attr: config.match_node_attr.clone(), 61 66 }; 62 67 63 68 let mut locals = VariableMap::new(); 64 69 let mut store = LazyStore::new(); 65 70 let mut scoped_store = LazyScopedVariables::new(); 66 - let mut lazy_graph = Vec::new(); 71 + let mut lazy_graph = LazyGraph::new(); 67 72 let mut function_parameters = Vec::new(); 68 73 let mut prev_element_debug_info = HashMap::new(); 69 74 ··· 80 85 &mut lazy_graph, 81 86 &mut function_parameters, 82 87 &mut prev_element_debug_info, 88 + &self.inherited_variables, 83 89 &self.shorthands, 84 90 cancellation_flag, 85 91 ) ··· 91 97 functions: config.functions, 92 98 store: &store, 93 99 scoped_store: &scoped_store, 100 + inherited_variables: &self.inherited_variables, 94 101 function_parameters: &mut function_parameters, 95 102 prev_element_debug_info: &mut prev_element_debug_info, 96 103 cancellation_flag, 97 104 }; 98 - for graph_stmt in &lazy_graph { 99 - graph_stmt.evaluate(&mut exec)?; 100 - } 105 + lazy_graph.evaluate(&mut exec)?; 101 106 // make sure any unforced values are now forced, to surface any problems 102 107 // hidden by the fact that the values were unused 103 108 store.evaluate_all(&mut exec)?; ··· 113 118 mut visit: F, 114 119 ) -> Result<(), E> 115 120 where 116 - F: FnMut(&ast::Stanza, QueryMatch<'_, 'tree>) -> Result<(), E>, 121 + F: FnMut(&ast::Stanza, &QueryMatch<'_, 'tree>) -> Result<(), E>, 117 122 { 118 123 let mut cursor = QueryCursor::new(); 119 124 let query = self.query.as_ref().unwrap(); 120 - let matches = cursor.matches(query, tree.root_node(), source.as_bytes()); 121 - for mat in matches { 125 + let mut matches = cursor.matches(query, tree.root_node(), source.as_bytes()); 126 + while let Some(mat) = matches.next() { 122 127 let stanza = &self.stanzas[mat.pattern_index]; 123 128 visit(stanza, mat)?; 124 129 } ··· 134 139 locals: &'a mut dyn MutVariables<LazyValue>, 135 140 current_regex_captures: &'a Vec<String>, 136 141 mat: &'a QueryMatch<'a, 'tree>, 142 + full_match_file_capture_index: usize, 137 143 store: &'a mut LazyStore, 138 144 scoped_store: &'a mut LazyScopedVariables, 139 - lazy_graph: &'a mut Vec<LazyStatement>, 145 + lazy_graph: &'a mut LazyGraph, 140 146 function_parameters: &'a mut Vec<graph::Value>, // re-usable buffer to reduce memory allocations 141 147 prev_element_debug_info: &'a mut HashMap<GraphElementKey, DebugInfo>, 142 148 error_context: StatementContext, 149 + inherited_variables: &'a HashSet<Identifier>, 143 150 shorthands: &'a ast::AttributeShorthands, 144 151 cancellation_flag: &'a dyn CancellationFlag, 145 152 } ··· 151 158 pub functions: &'a Functions, 152 159 pub store: &'a LazyStore, 153 160 pub scoped_store: &'a LazyScopedVariables, 161 + pub inherited_variables: &'a HashSet<Identifier>, 154 162 pub function_parameters: &'a mut Vec<graph::Value>, // re-usable buffer to reduce memory allocations 155 163 pub prev_element_debug_info: &'a mut HashMap<GraphElementKey, DebugInfo>, 156 164 pub cancellation_flag: &'a dyn CancellationFlag, ··· 159 167 #[derive(Debug, Clone, Hash, PartialEq, Eq)] 160 168 pub(super) enum GraphElementKey { 161 169 NodeAttribute(graph::GraphNodeRef, Identifier), 162 - Edge(graph::GraphNodeRef, graph::GraphNodeRef), 163 170 EdgeAttribute(graph::GraphNodeRef, graph::GraphNodeRef, Identifier), 164 171 } 165 172 ··· 173 180 locals: &mut VariableMap<'l, LazyValue>, 174 181 store: &mut LazyStore, 175 182 scoped_store: &mut LazyScopedVariables, 176 - lazy_graph: &mut Vec<LazyStatement>, 183 + lazy_graph: &mut LazyGraph, 177 184 function_parameters: &mut Vec<graph::Value>, 178 185 prev_element_debug_info: &mut HashMap<GraphElementKey, DebugInfo>, 186 + inherited_variables: &HashSet<Identifier>, 179 187 shorthands: &ast::AttributeShorthands, 180 188 cancellation_flag: &dyn CancellationFlag, 181 189 ) -> Result<(), ExecutionError> { ··· 196 204 locals, 197 205 current_regex_captures: &current_regex_captures, 198 206 mat, 207 + full_match_file_capture_index: self.full_match_file_capture_index, 199 208 store, 200 209 scoped_store, 201 210 lazy_graph, 202 211 function_parameters, 203 212 prev_element_debug_info, 204 213 error_context, 214 + inherited_variables, 205 215 shorthands, 206 216 cancellation_flag, 207 217 }; ··· 221 231 Self::DeclareImmutable(statement) => statement.execute_lazy(exec), 222 232 Self::DeclareMutable(statement) => statement.execute_lazy(exec), 223 233 Self::Assign(statement) => statement.execute_lazy(exec), 234 + Self::Expr(statement) => statement.value.evaluate_lazy(exec).map(|_| ()), 224 235 Self::CreateGraphNode(statement) => statement.execute_lazy(exec), 225 236 Self::AddGraphNodeAttribute(statement) => statement.execute_lazy(exec), 226 237 Self::CreateEdge(statement) => statement.execute_lazy(exec), ··· 259 270 let graph_node = exec.graph.add_graph_node(); 260 271 self.node 261 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 + } 262 290 self.node.add_lazy(exec, graph_node.into(), false) 263 291 } 264 292 } ··· 282 310 fn execute_lazy(&self, exec: &mut ExecutionContext) -> Result<(), ExecutionError> { 283 311 let source = self.source.evaluate_lazy(exec)?; 284 312 let sink = self.sink.evaluate_lazy(exec)?; 285 - let stmt = LazyCreateEdge::new(source, sink, exec.error_context.clone().into()); 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()); 286 316 exec.lazy_graph.push(stmt.into()); 287 317 Ok(()) 288 318 } ··· 357 387 locals: &mut arm_locals, 358 388 current_regex_captures: &current_regex_captures, 359 389 mat: exec.mat, 390 + full_match_file_capture_index: exec.full_match_file_capture_index, 360 391 store: exec.store, 361 392 scoped_store: exec.scoped_store, 362 393 lazy_graph: exec.lazy_graph, 363 394 function_parameters: exec.function_parameters, 364 395 prev_element_debug_info: exec.prev_element_debug_info, 365 396 error_context: exec.error_context.clone(), 397 + inherited_variables: exec.inherited_variables, 366 398 shorthands: exec.shorthands, 367 399 cancellation_flag: exec.cancellation_flag, 368 400 }; ··· 422 454 locals: &mut arm_locals, 423 455 current_regex_captures: exec.current_regex_captures, 424 456 mat: exec.mat, 457 + full_match_file_capture_index: exec.full_match_file_capture_index, 425 458 store: exec.store, 426 459 scoped_store: exec.scoped_store, 427 460 lazy_graph: exec.lazy_graph, 428 461 function_parameters: exec.function_parameters, 429 462 prev_element_debug_info: exec.prev_element_debug_info, 430 463 error_context: exec.error_context.clone(), 464 + inherited_variables: exec.inherited_variables, 431 465 shorthands: exec.shorthands, 432 466 cancellation_flag: exec.cancellation_flag, 433 467 }; ··· 468 502 locals: &mut loop_locals, 469 503 current_regex_captures: exec.current_regex_captures, 470 504 mat: exec.mat, 505 + full_match_file_capture_index: exec.full_match_file_capture_index, 471 506 store: exec.store, 472 507 scoped_store: exec.scoped_store, 473 508 lazy_graph: exec.lazy_graph, 474 509 function_parameters: exec.function_parameters, 475 510 prev_element_debug_info: exec.prev_element_debug_info, 476 511 error_context: exec.error_context.clone(), 512 + inherited_variables: exec.inherited_variables, 477 513 shorthands: exec.shorthands, 478 514 cancellation_flag: exec.cancellation_flag, 479 515 }; ··· 517 553 functions: exec.config.functions, 518 554 store: exec.store, 519 555 scoped_store: exec.scoped_store, 556 + inherited_variables: exec.inherited_variables, 520 557 function_parameters: exec.function_parameters, 521 558 prev_element_debug_info: exec.prev_element_debug_info, 522 559 cancellation_flag: exec.cancellation_flag, ··· 560 597 locals: &mut loop_locals, 561 598 current_regex_captures: exec.current_regex_captures, 562 599 mat: exec.mat, 600 + full_match_file_capture_index: exec.full_match_file_capture_index, 563 601 store: exec.store, 564 602 scoped_store: exec.scoped_store, 565 603 lazy_graph: exec.lazy_graph, 566 604 function_parameters: exec.function_parameters, 567 605 prev_element_debug_info: exec.prev_element_debug_info, 568 606 error_context: exec.error_context.clone(), 607 + inherited_variables: exec.inherited_variables, 569 608 shorthands: exec.shorthands, 570 609 cancellation_flag: exec.cancellation_flag, 571 610 }; ··· 602 641 locals: &mut loop_locals, 603 642 current_regex_captures: exec.current_regex_captures, 604 643 mat: exec.mat, 644 + full_match_file_capture_index: exec.full_match_file_capture_index, 605 645 store: exec.store, 606 646 scoped_store: exec.scoped_store, 607 647 lazy_graph: exec.lazy_graph, 608 648 function_parameters: exec.function_parameters, 609 649 prev_element_debug_info: exec.prev_element_debug_info, 610 650 error_context: exec.error_context.clone(), 651 + inherited_variables: exec.inherited_variables, 611 652 shorthands: exec.shorthands, 612 653 cancellation_flag: exec.cancellation_flag, 613 654 }; ··· 816 857 locals: &mut shorthand_locals, 817 858 current_regex_captures: exec.current_regex_captures, 818 859 mat: exec.mat, 860 + full_match_file_capture_index: exec.full_match_file_capture_index, 819 861 store: exec.store, 820 862 scoped_store: exec.scoped_store, 821 863 lazy_graph: exec.lazy_graph, 822 864 function_parameters: exec.function_parameters, 823 865 prev_element_debug_info: exec.prev_element_debug_info, 824 866 error_context: exec.error_context.clone(), 867 + inherited_variables: exec.inherited_variables, 825 868 shorthands: exec.shorthands, 826 869 cancellation_flag: exec.cancellation_flag, 827 870 };
+97 -50
src/execution/strict.rs
··· 7 7 8 8 use std::collections::BTreeSet; 9 9 use std::collections::HashMap; 10 + use std::collections::HashSet; 11 + use streaming_iterator::StreamingIterator; 10 12 use tree_sitter::QueryCursor; 11 13 use tree_sitter::QueryMatch; 12 14 use tree_sitter::Tree; ··· 14 16 use crate::ast::AddEdgeAttribute; 15 17 use crate::ast::AddGraphNodeAttribute; 16 18 use crate::ast::Assign; 19 + use crate::ast::ExpressionStatement; 17 20 use crate::ast::Attribute; 18 21 use crate::ast::AttributeShorthand; 19 22 use crate::ast::AttributeShorthands; ··· 47 50 use crate::execution::error::StatementContext; 48 51 use crate::execution::CancellationFlag; 49 52 use crate::execution::ExecutionConfig; 50 - use crate::graph::Attributes; 51 53 use crate::graph::Graph; 54 + use crate::graph::SyntaxNodeID; 52 55 use crate::graph::SyntaxNodeRef; 53 56 use crate::graph::Value; 54 57 use crate::variables::Globals; ··· 80 83 lazy: config.lazy, 81 84 location_attr: config.location_attr.clone(), 82 85 variable_name_attr: config.variable_name_attr.clone(), 86 + match_node_attr: config.match_node_attr.clone(), 83 87 }; 84 88 85 89 let mut locals = VariableMap::new(); ··· 97 101 &mut scoped, 98 102 &current_regex_captures, 99 103 &mut function_parameters, 104 + &self.inherited_variables, 100 105 &self.shorthands, 101 106 cancellation_flag, 102 107 ) ··· 112 117 mut visit: F, 113 118 ) -> Result<(), E> 114 119 where 115 - F: FnMut(&Stanza, QueryMatch<'_, 'tree>) -> Result<(), E>, 120 + F: FnMut(&Stanza, &QueryMatch<'_, 'tree>) -> Result<(), E>, 116 121 { 117 122 for stanza in &self.stanzas { 118 123 stanza.try_visit_matches_strict(tree, source, |mat| visit(stanza, mat))?; ··· 131 136 current_regex_captures: &'a Vec<String>, 132 137 function_parameters: &'a mut Vec<Value>, 133 138 mat: &'a QueryMatch<'a, 'tree>, 139 + full_match_stanza_capture_index: usize, 134 140 error_context: StatementContext, 141 + inherited_variables: &'a HashSet<Identifier>, 135 142 shorthands: &'a AttributeShorthands, 136 143 cancellation_flag: &'a dyn CancellationFlag, 137 144 } 138 145 139 146 struct ScopedVariables<'a> { 140 - scopes: HashMap<SyntaxNodeRef, VariableMap<'a, Value>>, 147 + scopes: HashMap<SyntaxNodeID, VariableMap<'a, Value>>, 141 148 } 142 149 143 150 impl<'a> ScopedVariables<'a> { ··· 147 154 } 148 155 } 149 156 150 - fn get(&mut self, scope: SyntaxNodeRef) -> &mut VariableMap<'a, Value> { 151 - self.scopes.entry(scope).or_insert(VariableMap::new()) 157 + fn get_mut(&mut self, scope: SyntaxNodeRef) -> &mut VariableMap<'a, Value> { 158 + self.scopes.entry(scope.index).or_insert(VariableMap::new()) 159 + } 160 + 161 + fn try_get(&self, index: SyntaxNodeID) -> Option<&VariableMap<'a, Value>> { 162 + self.scopes.get(&index) 152 163 } 153 164 } 154 165 ··· 163 174 scoped: &mut ScopedVariables<'s>, 164 175 current_regex_captures: &Vec<String>, 165 176 function_parameters: &mut Vec<Value>, 177 + inherited_variables: &HashSet<Identifier>, 166 178 shorthands: &AttributeShorthands, 167 179 cancellation_flag: &dyn CancellationFlag, 168 180 ) -> Result<(), ExecutionError> { ··· 184 196 current_regex_captures, 185 197 function_parameters, 186 198 mat: &mat, 199 + full_match_stanza_capture_index: self.full_match_stanza_capture_index, 187 200 error_context, 201 + inherited_variables, 188 202 shorthands, 189 203 cancellation_flag, 190 204 }; ··· 202 216 mut visit: F, 203 217 ) -> Result<(), E> 204 218 where 205 - F: FnMut(QueryMatch<'_, 'tree>) -> Result<(), E>, 219 + F: FnMut(&QueryMatch<'_, 'tree>) -> Result<(), E>, 206 220 { 207 221 let mut cursor = QueryCursor::new(); 208 - let matches = cursor.matches(&self.query, tree.root_node(), source.as_bytes()); 209 - for mat in matches { 222 + let mut matches = cursor.matches(&self.query, tree.root_node(), source.as_bytes()); 223 + while let Some(mat) = matches.next() { 210 224 visit(mat)?; 211 225 } 212 226 Ok(()) ··· 219 233 Statement::DeclareImmutable(s) => s.location, 220 234 Statement::DeclareMutable(s) => s.location, 221 235 Statement::Assign(s) => s.location, 236 + Statement::Expr(s) => s.location, 222 237 Statement::CreateGraphNode(s) => s.location, 223 238 Statement::AddGraphNodeAttribute(s) => s.location, 224 239 Statement::CreateEdge(s) => s.location, ··· 236 251 Statement::DeclareImmutable(statement) => statement.execute(exec), 237 252 Statement::DeclareMutable(statement) => statement.execute(exec), 238 253 Statement::Assign(statement) => statement.execute(exec), 254 + Statement::Expr(statement) => statement.execute(exec), 239 255 Statement::CreateGraphNode(statement) => statement.execute(exec), 240 256 Statement::AddGraphNodeAttribute(statement) => statement.execute(exec), 241 257 Statement::CreateEdge(statement) => statement.execute(exec), ··· 269 285 } 270 286 } 271 287 288 + impl ExpressionStatement { 289 + fn execute(&self, exec: &mut ExecutionContext) -> Result<(), ExecutionError> { 290 + self.value.evaluate(exec).map(|_| ()) 291 + } 292 + } 293 + 272 294 impl CreateGraphNode { 273 295 fn execute(&self, exec: &mut ExecutionContext) -> Result<(), ExecutionError> { 274 296 let graph_node = exec.graph.add_graph_node(); 275 297 self.node 276 298 .add_debug_attrs(&mut exec.graph[graph_node].attributes, exec.config)?; 299 + if let Some(match_node_attr) = &exec.config.match_node_attr { 300 + let match_node = exec 301 + .mat 302 + .nodes_for_capture_index(exec.full_match_stanza_capture_index as u32) 303 + .next() 304 + .expect("missing capture for full match"); 305 + let syn_node = exec.graph.add_syntax_node(match_node); 306 + exec.graph[graph_node] 307 + .attributes 308 + .add(match_node_attr.clone(), syn_node) 309 + .map_err(|_| { 310 + ExecutionError::DuplicateAttribute(format!( 311 + " {} on graph node ({}) in {}", 312 + match_node_attr, graph_node, self, 313 + )) 314 + })?; 315 + } 277 316 let value = Value::GraphNode(graph_node); 278 317 self.node.add(exec, value, false) 279 318 } ··· 304 343 fn execute(&self, exec: &mut ExecutionContext) -> Result<(), ExecutionError> { 305 344 let source = self.source.evaluate(exec)?.into_graph_node_ref()?; 306 345 let sink = self.sink.evaluate(exec)?.into_graph_node_ref()?; 307 - if let Err(_) = exec.graph[source].add_edge(sink) { 308 - Err(ExecutionError::DuplicateEdge(format!( 309 - "({} -> {}) in {}", 310 - source, sink, self, 311 - )))?; 312 - } 346 + let edge = match exec.graph[source].add_edge(sink) { 347 + Ok(edge) | Err(edge) => edge, 348 + }; 349 + self.add_debug_attrs(&mut edge.attributes, exec.config)?; 313 350 Ok(()) 314 351 } 315 352 } ··· 395 432 current_regex_captures: &current_regex_captures, 396 433 function_parameters: exec.function_parameters, 397 434 mat: exec.mat, 435 + full_match_stanza_capture_index: exec.full_match_stanza_capture_index, 398 436 error_context: exec.error_context.clone(), 437 + inherited_variables: exec.inherited_variables, 399 438 shorthands: exec.shorthands, 400 439 cancellation_flag: exec.cancellation_flag, 401 440 }; ··· 454 493 current_regex_captures: exec.current_regex_captures, 455 494 function_parameters: exec.function_parameters, 456 495 mat: exec.mat, 496 + full_match_stanza_capture_index: exec.full_match_stanza_capture_index, 457 497 error_context: exec.error_context.clone(), 498 + inherited_variables: exec.inherited_variables, 458 499 shorthands: exec.shorthands, 459 500 cancellation_flag: exec.cancellation_flag, 460 501 }; ··· 495 536 current_regex_captures: exec.current_regex_captures, 496 537 function_parameters: exec.function_parameters, 497 538 mat: exec.mat, 539 + full_match_stanza_capture_index: exec.full_match_stanza_capture_index, 498 540 error_context: exec.error_context.clone(), 541 + inherited_variables: exec.inherited_variables, 499 542 shorthands: exec.shorthands, 500 543 cancellation_flag: exec.cancellation_flag, 501 544 }; ··· 569 612 current_regex_captures: exec.current_regex_captures, 570 613 function_parameters: exec.function_parameters, 571 614 mat: exec.mat, 615 + full_match_stanza_capture_index: exec.full_match_stanza_capture_index, 572 616 error_context: exec.error_context.clone(), 617 + inherited_variables: exec.inherited_variables, 573 618 shorthands: exec.shorthands, 574 619 cancellation_flag: exec.cancellation_flag, 575 620 }; ··· 608 653 current_regex_captures: exec.current_regex_captures, 609 654 function_parameters: exec.function_parameters, 610 655 mat: exec.mat, 656 + full_match_stanza_capture_index: exec.full_match_stanza_capture_index, 611 657 error_context: exec.error_context.clone(), 658 + inherited_variables: exec.inherited_variables, 612 659 shorthands: exec.shorthands, 613 660 cancellation_flag: exec.cancellation_flag, 614 661 }; ··· 706 753 ))) 707 754 } 708 755 }; 709 - let variables = exec.scoped.get(scope); 710 - if let Some(value) = variables.get(&self.name) { 711 - Ok(value) 712 - } else { 713 - Err(ExecutionError::UndefinedVariable(format!( 714 - "{} on node {}", 715 - self, scope 716 - ))) 756 + 757 + // search this node 758 + if let Some(value) = exec 759 + .scoped 760 + .try_get(scope.index) 761 + .and_then(|v| v.get(&self.name)) 762 + { 763 + return Ok(value); 717 764 } 765 + 766 + // search parent nodes 767 + if exec.inherited_variables.contains(&self.name) { 768 + let mut parent = exec 769 + .graph 770 + .syntax_nodes 771 + .get(&scope.index) 772 + .and_then(|n| n.parent()); 773 + while let Some(scope) = parent { 774 + if let Some(value) = exec 775 + .scoped 776 + .try_get(scope.id() as u32) 777 + .and_then(|v| v.get(&self.name)) 778 + { 779 + return Ok(value); 780 + } 781 + parent = scope.parent(); 782 + } 783 + } 784 + 785 + Err(ExecutionError::UndefinedVariable(format!( 786 + "{} on node {}", 787 + self, scope 788 + ))) 718 789 } 719 790 720 791 fn add( ··· 733 804 ))) 734 805 } 735 806 }; 736 - let variables = exec.scoped.get(scope); 807 + let variables = exec.scoped.get_mut(scope); 737 808 variables 738 809 .add(self.name.clone(), value, mutable) 739 810 .map_err(|_| ExecutionError::DuplicateVariable(format!("{}", self))) ··· 750 821 ))) 751 822 } 752 823 }; 753 - let variables = exec.scoped.get(scope); 824 + let variables = exec.scoped.get_mut(scope); 754 825 variables 755 826 .set(self.name.clone(), value) 756 827 .map_err(|_| ExecutionError::DuplicateVariable(format!("{}", self))) ··· 801 872 } 802 873 } 803 874 804 - impl Variable { 805 - pub(crate) fn add_debug_attrs( 806 - &self, 807 - attributes: &mut Attributes, 808 - config: &ExecutionConfig, 809 - ) -> Result<(), ExecutionError> { 810 - if let Some(variable_name_attr) = &config.variable_name_attr { 811 - attributes 812 - .add(variable_name_attr.clone(), format!("{}", self)) 813 - .map_err(|_| { 814 - ExecutionError::DuplicateAttribute(variable_name_attr.as_str().into()) 815 - })?; 816 - } 817 - if let Some(location_attr) = &config.location_attr { 818 - let location = match &self { 819 - Variable::Scoped(v) => v.location, 820 - Variable::Unscoped(v) => v.location, 821 - }; 822 - attributes 823 - .add(location_attr.clone(), format!("{}", location)) 824 - .map_err(|_| ExecutionError::DuplicateAttribute(location_attr.as_str().into()))?; 825 - } 826 - Ok(()) 827 - } 828 - } 829 - 830 875 impl Attribute { 831 876 fn execute<F>( 832 877 &self, ··· 866 911 current_regex_captures: exec.current_regex_captures, 867 912 function_parameters: exec.function_parameters, 868 913 mat: exec.mat, 914 + full_match_stanza_capture_index: exec.full_match_stanza_capture_index, 869 915 error_context: exec.error_context.clone(), 916 + inherited_variables: exec.inherited_variables, 870 917 shorthands: exec.shorthands, 871 918 cancellation_flag: exec.cancellation_flag, 872 919 };
+68 -10
src/execution.rs
··· 11 11 use tree_sitter::QueryMatch; 12 12 use tree_sitter::Tree; 13 13 14 + use crate::ast::CreateEdge; 14 15 use crate::ast::File; 15 16 use crate::ast::Stanza; 17 + use crate::ast::Variable; 16 18 use crate::execution::error::ExecutionError; 17 19 use crate::functions::Functions; 20 + use crate::graph::Attributes; 18 21 use crate::graph::Graph; 19 22 use crate::graph::Value; 20 23 use crate::variables::Globals; ··· 116 119 .iter() 117 120 .map(|name| { 118 121 let index = file_query 119 - .capture_index_for_name(name) 122 + .capture_index_for_name(*name) 120 123 .expect("missing index for capture"); 121 124 let quantifier = 122 125 file_query.capture_quantifiers(mat.pattern_index)[index as usize]; 123 - (name, quantifier, index) 126 + (*name, quantifier, index) 124 127 }) 125 128 .filter(|c| c.2 != stanza.full_match_file_capture_index as u32) 126 129 .collect(); ··· 140 143 .map(|name| { 141 144 let index = stanza 142 145 .query 143 - .capture_index_for_name(name) 146 + .capture_index_for_name(*name) 144 147 .expect("missing index for capture"); 145 148 let quantifier = stanza.query.capture_quantifiers(0)[index as usize]; 146 - (name, quantifier, index) 149 + (*name, quantifier, index) 147 150 }) 148 151 .filter(|c| c.2 != stanza.full_match_stanza_capture_index as u32) 149 152 .collect(); ··· 176 179 .map(|name| { 177 180 let index = self 178 181 .query 179 - .capture_index_for_name(name) 182 + .capture_index_for_name(*name) 180 183 .expect("missing index for capture"); 181 184 let quantifier = self.query.capture_quantifiers(0)[index as usize]; 182 - (name, quantifier, index) 185 + (*name, quantifier, index) 183 186 }) 184 187 .filter(|c| c.2 != self.full_match_stanza_capture_index as u32) 185 188 .collect(); ··· 194 197 } 195 198 196 199 pub struct Match<'a, 'tree> { 197 - mat: QueryMatch<'a, 'tree>, 200 + mat: &'a QueryMatch<'a, 'tree>, 198 201 full_capture_index: u32, 199 - named_captures: Vec<(&'a String, CaptureQuantifier, u32)>, 202 + named_captures: Vec<(&'a str, CaptureQuantifier, u32)>, 200 203 query_location: Location, 201 204 } 202 205 ··· 214 217 &'s self, 215 218 ) -> impl Iterator< 216 219 Item = ( 217 - &String, 220 + &'a str, 218 221 CaptureQuantifier, 219 222 impl Iterator<Item = Node<'tree>> + 's, 220 223 ), ··· 236 239 } 237 240 238 241 /// Return an iterator over all capture names. 239 - pub fn capture_names(&self) -> impl Iterator<Item = &String> { 242 + pub fn capture_names(&self) -> impl Iterator<Item = &str> { 240 243 self.named_captures.iter().map(|c| c.0) 241 244 } 242 245 ··· 253 256 pub(crate) lazy: bool, 254 257 pub(crate) location_attr: Option<Identifier>, 255 258 pub(crate) variable_name_attr: Option<Identifier>, 259 + pub(crate) match_node_attr: Option<Identifier>, 256 260 } 257 261 258 262 impl<'a, 'g> ExecutionConfig<'a, 'g> { ··· 263 267 lazy: false, 264 268 location_attr: None, 265 269 variable_name_attr: None, 270 + match_node_attr: None, 266 271 } 267 272 } 268 273 ··· 270 275 self, 271 276 location_attr: Identifier, 272 277 variable_name_attr: Identifier, 278 + match_node_attr: Identifier, 273 279 ) -> Self { 274 280 Self { 275 281 functions: self.functions, ··· 277 283 lazy: self.lazy, 278 284 location_attr: location_attr.into(), 279 285 variable_name_attr: variable_name_attr.into(), 286 + match_node_attr: match_node_attr.into(), 280 287 } 281 288 } 282 289 ··· 287 294 lazy, 288 295 location_attr: self.location_attr, 289 296 variable_name_attr: self.variable_name_attr, 297 + match_node_attr: self.match_node_attr, 290 298 } 291 299 } 292 300 } ··· 336 344 } 337 345 } 338 346 } 347 + 348 + impl 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 + } 369 + impl 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 + }
+1 -1
src/functions.rs
··· 575 575 let replacement = parameters.param()?.into_string()?; 576 576 parameters.finish()?; 577 577 Ok(Value::String( 578 - pattern.replace_all(&text, replacement).to_string(), 578 + pattern.replace_all(&text, replacement.as_str()).to_string(), 579 579 )) 580 580 } 581 581 }
+11 -6
src/graph.rs
··· 36 36 /// that they don't outlive the tree-sitter syntax tree that they are generated from. 37 37 #[derive(Default)] 38 38 pub struct Graph<'tree> { 39 - syntax_nodes: HashMap<SyntaxNodeID, Node<'tree>>, 39 + pub(crate) syntax_nodes: HashMap<SyntaxNodeID, Node<'tree>>, 40 40 graph_nodes: Vec<GraphNode>, 41 41 } 42 42 43 - type SyntaxNodeID = u32; 43 + pub(crate) type SyntaxNodeID = u32; 44 44 type GraphNodeID = u32; 45 45 46 46 impl<'tree> Graph<'tree> { ··· 260 260 } 261 261 262 262 /// A set of attributes associated with a graph node or edge 263 + #[derive(Clone, Debug)] 263 264 pub struct Attributes { 264 265 values: HashMap<Identifier, Value>, 265 266 } ··· 274 275 275 276 /// Adds an attribute to this attribute set. If there was already an attribute with the same 276 277 /// name, replaces its value and returns `Err`. 277 - pub fn add<V: Into<Value>>(&mut self, name: Identifier, value: V) -> Result<(), ()> { 278 + pub fn add<V: Into<Value>>(&mut self, name: Identifier, value: V) -> Result<(), Value> { 278 279 match self.values.entry(name) { 279 280 Entry::Occupied(mut o) => { 280 - o.insert(value.into()); 281 - Err(()) 281 + let value = value.into(); 282 + if o.get() != &value { 283 + Err(o.insert(value.into())) 284 + } else { 285 + Ok(()) 286 + } 282 287 } 283 288 Entry::Vacant(v) => { 284 289 v.insert(value.into()); ··· 634 639 /// A reference to a syntax node in a graph 635 640 #[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)] 636 641 pub struct SyntaxNodeRef { 637 - index: SyntaxNodeID, 642 + pub(crate) index: SyntaxNodeID, 638 643 kind: &'static str, 639 644 position: tree_sitter::Point, 640 645 }
+17 -17
src/parse_error.rs
··· 39 39 } 40 40 41 41 /// Return all parse errors in the given tree. 42 - pub fn all(tree: &'tree Tree) -> Vec<ParseError> { 42 + pub fn all(tree: &'tree Tree) -> Vec<ParseError<'tree>> { 43 43 let mut errors = Vec::new(); 44 44 find_errors(tree, &mut errors, false); 45 45 errors ··· 396 396 f, 397 397 "{}{}:{}:{}:", 398 398 " ".repeat(self.indent), 399 - white_bold(&self.path.to_string_lossy()), 400 - white_bold(&format!("{}", self.row + 1)), 401 - white_bold(&format!("{}", self.columns.start + 1)), 399 + header_style(&self.path.to_string_lossy()), 400 + header_style(&format!("{}", self.row + 1)), 401 + header_style(&format!("{}", self.columns.start + 1)), 402 402 )?; 403 403 if let Some(source) = self.source { 404 404 // first line: line number & source ··· 406 406 f, 407 407 "{}{}{}{}", 408 408 " ".repeat(self.indent), 409 - blue(&format!("{}", self.row + 1)), 410 - blue(" | "), 409 + prefix_style(&format!("{}", self.row + 1)), 410 + prefix_style(" | "), 411 411 source, 412 412 )?; 413 413 // second line: caret ··· 416 416 "{}{}{}{}{}", 417 417 " ".repeat(self.indent), 418 418 " ".repeat(self.gutter_width()), 419 - blue(" | "), 419 + prefix_style(" | "), 420 420 " ".repeat(self.columns.start), 421 - green_bold(&"^".repeat(self.columns.len())) 421 + underline_style(&"^".repeat(self.columns.len())), 422 422 )?; 423 423 } else { 424 424 writeln!(f, "{}{}", " ".repeat(self.indent), "<missing source>",)?; ··· 430 430 // coloring functions 431 431 432 432 #[cfg(feature = "term-colors")] 433 - fn blue(str: &str) -> impl std::fmt::Display { 434 - str.blue() 433 + fn header_style(str: &str) -> impl std::fmt::Display { 434 + str.bold() 435 435 } 436 436 #[cfg(not(feature = "term-colors"))] 437 - fn blue<'a>(str: &'a str) -> impl std::fmt::Display + 'a { 437 + fn header_style<'a>(str: &'a str) -> impl std::fmt::Display + 'a { 438 438 str 439 439 } 440 440 441 441 #[cfg(feature = "term-colors")] 442 - fn green_bold(str: &str) -> impl std::fmt::Display { 443 - str.green().bold() 442 + fn prefix_style(str: &str) -> impl std::fmt::Display { 443 + str.dimmed() 444 444 } 445 445 #[cfg(not(feature = "term-colors"))] 446 - fn green_bold<'a>(str: &'a str) -> impl std::fmt::Display + 'a { 446 + fn prefix_style<'a>(str: &'a str) -> impl std::fmt::Display + 'a { 447 447 str 448 448 } 449 449 450 450 #[cfg(feature = "term-colors")] 451 - fn white_bold(str: &str) -> impl std::fmt::Display { 452 - str.white().bold() 451 + fn underline_style(str: &str) -> impl std::fmt::Display { 452 + str.green() 453 453 } 454 454 #[cfg(not(feature = "term-colors"))] 455 - fn white_bold<'a>(str: &'a str) -> impl std::fmt::Display + 'a { 455 + fn underline_style<'a>(str: &'a str) -> impl std::fmt::Display + 'a { 456 456 str 457 457 }
+21 -8
src/parser.rs
··· 291 291 fn parse_into_file(&mut self, file: &mut ast::File) -> Result<(), ParseError> { 292 292 self.consume_whitespace(); 293 293 while self.try_peek().is_some() { 294 - if let Ok(_) = self.consume_token("global") { 294 + if let Ok(_) = self.consume_token("attribute") { 295 + self.consume_whitespace(); 296 + let shorthand = self.parse_shorthand()?; 297 + file.shorthands.add(shorthand); 298 + } else if let Ok(_) = self.consume_token("global") { 295 299 self.consume_whitespace(); 296 300 let global = self.parse_global()?; 297 301 file.globals.push(global); 298 - } else if let Ok(_) = self.consume_token("attribute") { 302 + } else if let Ok(_) = self.consume_token("inherit") { 299 303 self.consume_whitespace(); 300 - let shorthand = self.parse_shorthand()?; 301 - file.shorthands.add(shorthand); 304 + self.consume_token(".")?; 305 + let name = self.parse_identifier("inherit")?; 306 + file.inherited_variables.insert(name); 302 307 } else { 303 - let stanza = self.parse_stanza(file.language)?; 308 + let stanza = self.parse_stanza(&file.language)?; 304 309 file.stanzas.push(stanza); 305 310 } 306 311 self.consume_whitespace(); 307 312 } 308 313 // we can unwrap here because all queries have already been parsed before 309 - file.query = Some(Query::new(file.language, &self.query_source).unwrap()); 314 + file.query = Some(Query::new(&file.language, &self.query_source).unwrap()); 310 315 Ok(()) 311 316 } 312 317 ··· 364 369 Ok(quantifier) 365 370 } 366 371 367 - fn parse_stanza(&mut self, language: Language) -> Result<ast::Stanza, ParseError> { 372 + fn parse_stanza(&mut self, language: &Language) -> Result<ast::Stanza, ParseError> { 368 373 let start = self.location; 369 374 let (query, full_match_stanza_capture_index) = self.parse_query(language)?; 370 375 self.consume_whitespace(); ··· 380 385 }) 381 386 } 382 387 383 - fn parse_query(&mut self, language: Language) -> Result<(Query, usize), ParseError> { 388 + fn parse_query(&mut self, language: &Language) -> Result<(Query, usize), ParseError> { 384 389 let location = self.location; 385 390 let query_start = self.offset; 386 391 self.skip_query()?; ··· 477 482 } 478 483 479 484 fn parse_statement(&mut self) -> Result<ast::Statement, ParseError> { 485 + if matches!(self.peek(), Ok( '#' | '"' | '@' | '$' | '(' | '[' | '{')) { 486 + if let Ok(value) = self.parse_expression() { 487 + return Ok(ast::Statement::Expr(ast::ExpressionStatement { 488 + location: self.location, 489 + value, 490 + })); 491 + } 492 + } 480 493 let keyword_location = self.location; 481 494 let keyword = self.parse_name("keyword")?; 482 495 self.consume_whitespace();
+1 -1
src/variables.rs
··· 104 104 v.into_key().to_string(), 105 105 ))), 106 106 Occupied(mut o) => { 107 - let mut variable = o.get_mut(); 107 + let variable = o.get_mut(); 108 108 if variable.mutable { 109 109 variable.value = value; 110 110 Ok(())
+140 -2
tests/it/execution.rs
··· 27 27 fn execute(python_source: &str, dsl_source: &str) -> Result<String, ExecutionError> { 28 28 init_log(); 29 29 let mut parser = Parser::new(); 30 - parser.set_language(tree_sitter_python::language()).unwrap(); 30 + parser 31 + .set_language(&tree_sitter_python::LANGUAGE.into()) 32 + .unwrap(); 31 33 let tree = parser.parse(python_source, None).unwrap(); 32 34 let file = 33 - File::from_str(tree_sitter_python::language(), dsl_source).expect("Cannot parse file"); 35 + File::from_str(tree_sitter_python::LANGUAGE.into(), dsl_source).expect("Cannot parse file"); 34 36 let functions = Functions::stdlib(); 35 37 let mut globals = Variables::new(); 36 38 globals ··· 936 938 "#}, 937 939 ); 938 940 } 941 + 942 + #[test] 943 + fn can_access_inherited_attribute() { 944 + check_execution( 945 + indoc! { r#" 946 + def get_f(): 947 + pass 948 + "#}, 949 + indoc! {r#" 950 + inherit .test 951 + (function_definition)@def { 952 + node @def.test 953 + attr (@def.test) in_def 954 + } 955 + (pass_statement)@pass { 956 + attr (@pass.test) in_pass 957 + } 958 + "#}, 959 + indoc! {r#" 960 + node 0 961 + in_def: #true 962 + in_pass: #true 963 + "#}, 964 + ); 965 + } 966 + 967 + #[test] 968 + fn can_overwrite_inherited_attribute() { 969 + check_execution( 970 + indoc! { r#" 971 + def get_f(): 972 + pass 973 + "#}, 974 + indoc! {r#" 975 + inherit .test 976 + (function_definition)@def { 977 + node @def.test 978 + attr (@def.test) in_def 979 + } 980 + (pass_statement)@pass { 981 + node @pass.test 982 + } 983 + (pass_statement)@pass { 984 + attr (@pass.test) in_pass 985 + } 986 + "#}, 987 + indoc! {r#" 988 + node 0 989 + in_def: #true 990 + node 1 991 + in_pass: #true 992 + "#}, 993 + ); 994 + } 995 + 996 + #[test] 997 + fn cannot_access_non_inherited_variable() { 998 + fail_execution( 999 + indoc! { r#" 1000 + def get_f(): 1001 + pass 1002 + "#}, 1003 + indoc! {r#" 1004 + (function_definition)@def { 1005 + node @def.test 1006 + } 1007 + (pass_statement)@pass { 1008 + attr (@pass.test) in_pass 1009 + } 1010 + "#}, 1011 + ); 1012 + } 1013 + 1014 + #[test] 1015 + fn can_add_edge_twice() { 1016 + check_execution( 1017 + indoc! { r#" 1018 + pass 1019 + "#}, 1020 + indoc! {r#" 1021 + (module) { 1022 + node n1; 1023 + node n2; 1024 + edge n1 -> n2; 1025 + edge n1 -> n2; 1026 + } 1027 + "#}, 1028 + indoc! {r#" 1029 + node 0 1030 + edge 0 -> 1 1031 + node 1 1032 + "#}, 1033 + ); 1034 + } 1035 + 1036 + #[test] 1037 + fn can_set_node_attribute_value_twice() { 1038 + check_execution( 1039 + indoc! { r#" 1040 + pass 1041 + "#}, 1042 + indoc! {r#" 1043 + (module) { 1044 + node n; 1045 + attr (n) foo = #true; 1046 + } 1047 + "#}, 1048 + indoc! {r#" 1049 + node 0 1050 + foo: #true 1051 + "#}, 1052 + ); 1053 + } 1054 + 1055 + #[test] 1056 + fn cannot_change_attribute_value() { 1057 + check_execution( 1058 + indoc! { r#" 1059 + pass 1060 + "#}, 1061 + indoc! {r#" 1062 + (module) { 1063 + node n1; 1064 + node n2; 1065 + edge n1 -> n2; 1066 + attr (n1 -> n2) foo = #true; 1067 + } 1068 + "#}, 1069 + indoc! {r#" 1070 + node 0 1071 + edge 0 -> 1 1072 + foo: #true 1073 + node 1 1074 + "#}, 1075 + ); 1076 + }
+4 -2
tests/it/functions.rs
··· 27 27 fn execute(python_source: &str, dsl_source: &str) -> Result<String, ExecutionError> { 28 28 init_log(); 29 29 let mut parser = Parser::new(); 30 - parser.set_language(tree_sitter_python::language()).unwrap(); 30 + parser 31 + .set_language(&tree_sitter_python::LANGUAGE.into()) 32 + .unwrap(); 31 33 let tree = parser.parse(python_source, None).unwrap(); 32 34 let file = 33 - File::from_str(tree_sitter_python::language(), dsl_source).expect("Cannot parse file"); 35 + File::from_str(tree_sitter_python::LANGUAGE.into(), dsl_source).expect("Cannot parse file"); 34 36 let functions = Functions::stdlib(); 35 37 let mut globals = Variables::new(); 36 38 globals
+3 -1
tests/it/graph.rs
··· 51 51 fn can_display_graph() { 52 52 let python_source = "pass"; 53 53 let mut parser = Parser::new(); 54 - parser.set_language(tree_sitter_python::language()).unwrap(); 54 + parser 55 + .set_language(&tree_sitter_python::LANGUAGE.into()) 56 + .unwrap(); 55 57 let tree = parser.parse(python_source, None).unwrap(); 56 58 57 59 let mut graph = Graph::new();
+143 -5
tests/it/lazy_execution.rs
··· 26 26 fn execute(python_source: &str, dsl_source: &str) -> Result<String, ExecutionError> { 27 27 init_log(); 28 28 let mut parser = Parser::new(); 29 - parser.set_language(tree_sitter_python::language()).unwrap(); 29 + parser 30 + .set_language(&tree_sitter_python::LANGUAGE.into()) 31 + .unwrap(); 30 32 let tree = parser.parse(python_source, None).unwrap(); 31 33 let file = 32 - File::from_str(tree_sitter_python::language(), dsl_source).expect("Cannot parse file"); 34 + File::from_str(tree_sitter_python::LANGUAGE.into(), dsl_source).expect("Cannot parse file"); 33 35 let functions = Functions::stdlib(); 34 36 let mut globals = Variables::new(); 35 37 globals ··· 116 118 "#}, 117 119 indoc! {r#" 118 120 node 0 119 - name: "alpha" 120 - edge 0 -> 2 121 + edge 0 -> 1 121 122 node 1 122 - edge 1 -> 0 123 + name: "alpha" 124 + edge 1 -> 2 123 125 node 2 124 126 name: "beta" 125 127 edge 2 -> 3 ··· 1455 1457 "#}, 1456 1458 ); 1457 1459 } 1460 + 1461 + #[test] 1462 + fn can_access_inherited_attribute() { 1463 + check_execution( 1464 + indoc! { r#" 1465 + def get_f(): 1466 + pass 1467 + "#}, 1468 + indoc! {r#" 1469 + inherit .test 1470 + (function_definition)@def { 1471 + node @def.test 1472 + attr (@def.test) in_def 1473 + } 1474 + (pass_statement)@pass { 1475 + attr (@pass.test) in_pass 1476 + } 1477 + "#}, 1478 + indoc! {r#" 1479 + node 0 1480 + in_def: #true 1481 + in_pass: #true 1482 + "#}, 1483 + ); 1484 + } 1485 + 1486 + #[test] 1487 + fn can_overwrite_inherited_attribute() { 1488 + check_execution( 1489 + indoc! { r#" 1490 + def get_f(): 1491 + pass 1492 + "#}, 1493 + indoc! {r#" 1494 + inherit .test 1495 + (function_definition)@def { 1496 + node @def.test 1497 + attr (@def.test) in_def 1498 + } 1499 + (pass_statement)@pass { 1500 + node @pass.test 1501 + } 1502 + (pass_statement)@pass { 1503 + attr (@pass.test) in_pass 1504 + } 1505 + "#}, 1506 + indoc! {r#" 1507 + node 0 1508 + in_def: #true 1509 + node 1 1510 + in_pass: #true 1511 + "#}, 1512 + ); 1513 + } 1514 + 1515 + #[test] 1516 + fn cannot_access_non_inherited_variable() { 1517 + fail_execution( 1518 + indoc! { r#" 1519 + def get_f(): 1520 + pass 1521 + "#}, 1522 + indoc! {r#" 1523 + (function_definition)@def { 1524 + node @def.test 1525 + } 1526 + (pass_statement)@pass { 1527 + attr (@pass.test) in_pass 1528 + } 1529 + "#}, 1530 + ); 1531 + } 1532 + 1533 + #[test] 1534 + fn can_add_edge_twice() { 1535 + check_execution( 1536 + indoc! { r#" 1537 + pass 1538 + "#}, 1539 + indoc! {r#" 1540 + (module) { 1541 + node n1; 1542 + node n2; 1543 + edge n1 -> n2; 1544 + edge n1 -> n2; 1545 + } 1546 + "#}, 1547 + indoc! {r#" 1548 + node 0 1549 + edge 0 -> 1 1550 + node 1 1551 + "#}, 1552 + ); 1553 + } 1554 + 1555 + #[test] 1556 + fn can_set_node_attribute_value_twice() { 1557 + check_execution( 1558 + indoc! { r#" 1559 + pass 1560 + "#}, 1561 + indoc! {r#" 1562 + (module) { 1563 + node n; 1564 + attr (n) foo = #true; 1565 + } 1566 + "#}, 1567 + indoc! {r#" 1568 + node 0 1569 + foo: #true 1570 + "#}, 1571 + ); 1572 + } 1573 + 1574 + #[test] 1575 + fn cannot_change_attribute_value() { 1576 + check_execution( 1577 + indoc! { r#" 1578 + pass 1579 + "#}, 1580 + indoc! {r#" 1581 + (module) { 1582 + node n1; 1583 + node n2; 1584 + edge n1 -> n2; 1585 + attr (n1 -> n2) foo = #true; 1586 + } 1587 + "#}, 1588 + indoc! {r#" 1589 + node 0 1590 + edge 0 -> 1 1591 + foo: #true 1592 + node 1 1593 + "#}, 1594 + ); 1595 + }
+3 -1
tests/it/parse_errors.rs
··· 23 23 fn parse(python_source: &str) -> Tree { 24 24 init_log(); 25 25 let mut parser = Parser::new(); 26 - parser.set_language(tree_sitter_python::language()).unwrap(); 26 + parser 27 + .set_language(&tree_sitter_python::LANGUAGE.into()) 28 + .unwrap(); 27 29 parser.parse(python_source, None).unwrap() 28 30 } 29 31
+72 -38
tests/it/parser.rs
··· 27 27 set @cap2.var1 = loc1 28 28 } 29 29 "#; 30 - let file = File::from_str(tree_sitter_python::language(), source).expect("Cannot parse file"); 30 + let file = 31 + File::from_str(tree_sitter_python::LANGUAGE.into(), source).expect("Cannot parse file"); 31 32 32 33 let loc1 = Identifier::from("loc1"); 33 34 let precedence = Identifier::from("precedence"); ··· 228 229 let t = #true 229 230 } 230 231 "#; 231 - let file = File::from_str(tree_sitter_python::language(), source).expect("Cannot parse file"); 232 + let file = 233 + File::from_str(tree_sitter_python::LANGUAGE.into(), source).expect("Cannot parse file"); 232 234 233 235 let f = Identifier::from("f"); 234 236 let n = Identifier::from("n"); ··· 284 286 let loc1 = "\"abc,\ndef\\" 285 287 } 286 288 "#; 287 - let file = File::from_str(tree_sitter_python::language(), source).expect("Cannot parse file"); 289 + let file = 290 + File::from_str(tree_sitter_python::LANGUAGE.into(), source).expect("Cannot parse file"); 288 291 289 292 let loc1 = Identifier::from("loc1"); 290 293 ··· 318 321 let list3 = ["hello", "world",] 319 322 } 320 323 "#; 321 - let file = File::from_str(tree_sitter_python::language(), source).expect("Cannot parse file"); 324 + let file = 325 + File::from_str(tree_sitter_python::LANGUAGE.into(), source).expect("Cannot parse file"); 322 326 323 327 let list1 = Identifier::from("list1"); 324 328 let list2 = Identifier::from("list2"); ··· 395 399 let set3 = {"hello", "world",} 396 400 } 397 401 "#; 398 - let file = File::from_str(tree_sitter_python::language(), source).expect("Cannot parse file"); 402 + let file = 403 + File::from_str(tree_sitter_python::LANGUAGE.into(), source).expect("Cannot parse file"); 399 404 400 405 let set1 = Identifier::from("set1"); 401 406 let set2 = Identifier::from("set2"); ··· 470 475 print "x =", 5 471 476 } 472 477 "#; 473 - let file = File::from_str(tree_sitter_python::language(), source).expect("Cannot parse file"); 478 + let file = 479 + File::from_str(tree_sitter_python::LANGUAGE.into(), source).expect("Cannot parse file"); 474 480 475 481 let statements = file 476 482 .stanzas ··· 505 511 node n 506 512 } 507 513 "#; 508 - if let Ok(_) = File::from_str(tree_sitter_python::language(), source) { 514 + if let Ok(_) = File::from_str(tree_sitter_python::LANGUAGE.into(), source) { 509 515 panic!("Parse succeeded unexpectedly"); 510 516 } 511 517 } ··· 518 524 print @stmts 519 525 } 520 526 "#; 521 - let file = File::from_str(tree_sitter_python::language(), source).expect("Cannot parse file"); 527 + let file = 528 + File::from_str(tree_sitter_python::LANGUAGE.into(), source).expect("Cannot parse file"); 522 529 523 530 let stmts = Identifier::from("stmts"); 524 531 ··· 553 560 print @stmts 554 561 } 555 562 "#; 556 - let file = File::from_str(tree_sitter_python::language(), source).expect("Cannot parse file"); 563 + let file = 564 + File::from_str(tree_sitter_python::LANGUAGE.into(), source).expect("Cannot parse file"); 557 565 558 566 let stmt = Identifier::from("stmt"); 559 567 let stmts = Identifier::from("stmts"); ··· 602 610 print @stmts 603 611 } 604 612 "#; 605 - let file = File::from_str(tree_sitter_python::language(), source).expect("Cannot parse file"); 613 + let file = 614 + File::from_str(tree_sitter_python::LANGUAGE.into(), source).expect("Cannot parse file"); 606 615 607 616 let stmts = Identifier::from("stmts"); 608 617 ··· 636 645 print @stmt 637 646 } 638 647 "#; 639 - let file = File::from_str(tree_sitter_python::language(), source).expect("Cannot parse file"); 648 + let file = 649 + File::from_str(tree_sitter_python::LANGUAGE.into(), source).expect("Cannot parse file"); 640 650 641 651 let stmt = Identifier::from("stmt"); 642 652 ··· 670 680 print @stmt 671 681 } 672 682 "#; 673 - let file = File::from_str(tree_sitter_python::language(), source).expect("Cannot parse file"); 683 + let file = 684 + File::from_str(tree_sitter_python::LANGUAGE.into(), source).expect("Cannot parse file"); 674 685 675 686 let stmt = Identifier::from("stmt"); 676 687 ··· 704 715 print @stmt 705 716 } 706 717 "#; 707 - let file = File::from_str(tree_sitter_python::language(), source).expect("Cannot parse file"); 718 + let file = 719 + File::from_str(tree_sitter_python::LANGUAGE.into(), source).expect("Cannot parse file"); 708 720 709 721 let stmt = Identifier::from("stmt"); 710 722 ··· 738 750 print @stmt 739 751 } 740 752 "#; 741 - let file = File::from_str(tree_sitter_python::language(), source).expect("Cannot parse file"); 753 + let file = 754 + File::from_str(tree_sitter_python::LANGUAGE.into(), source).expect("Cannot parse file"); 742 755 743 756 let stmt = Identifier::from("stmt"); 744 757 ··· 774 787 } 775 788 } 776 789 "#; 777 - let file = File::from_str(tree_sitter_python::language(), source).expect("Cannot parse file"); 790 + let file = 791 + File::from_str(tree_sitter_python::LANGUAGE.into(), source).expect("Cannot parse file"); 778 792 779 793 let x = Identifier::from("x"); 780 794 ··· 826 840 } 827 841 } 828 842 "#; 829 - let file = File::from_str(tree_sitter_python::language(), source).expect("Cannot parse file"); 843 + let file = 844 + File::from_str(tree_sitter_python::LANGUAGE.into(), source).expect("Cannot parse file"); 830 845 831 846 let x = Identifier::from("x"); 832 847 ··· 903 918 } 904 919 } 905 920 "#; 906 - let file = File::from_str(tree_sitter_python::language(), source).expect("Cannot parse file"); 921 + let file = 922 + File::from_str(tree_sitter_python::LANGUAGE.into(), source).expect("Cannot parse file"); 907 923 908 924 let x = Identifier::from("x"); 909 925 ··· 967 983 } 968 984 } 969 985 "#; 970 - if let Ok(_) = File::from_str(tree_sitter_python::language(), source) { 986 + if let Ok(_) = File::from_str(tree_sitter_python::LANGUAGE.into(), source) { 971 987 panic!("Parse succeeded unexpectedly"); 972 988 } 973 989 } ··· 982 998 } 983 999 } 984 1000 "#; 985 - let file = File::from_str(tree_sitter_python::language(), source).expect("Cannot parse file"); 1001 + let file = 1002 + File::from_str(tree_sitter_python::LANGUAGE.into(), source).expect("Cannot parse file"); 986 1003 987 1004 let xs = Identifier::from("xs"); 988 1005 let x = Identifier::from("x"); ··· 1032 1049 } 1033 1050 } 1034 1051 "#; 1035 - if let Ok(_) = File::from_str(tree_sitter_python::language(), source) { 1052 + if let Ok(_) = File::from_str(tree_sitter_python::LANGUAGE.into(), source) { 1036 1053 panic!("Parse succeeded unexpectedly"); 1037 1054 } 1038 1055 } ··· 1051 1068 } 1052 1069 } 1053 1070 "#; 1054 - if let Ok(_) = File::from_str(tree_sitter_python::language(), source) { 1071 + if let Ok(_) = File::from_str(tree_sitter_python::LANGUAGE.into(), source) { 1055 1072 panic!("Parse succeeded unexpectedly"); 1056 1073 } 1057 1074 } ··· 1071 1088 } 1072 1089 } 1073 1090 "#; 1074 - if let Ok(_) = File::from_str(tree_sitter_python::language(), source) { 1091 + if let Ok(_) = File::from_str(tree_sitter_python::LANGUAGE.into(), source) { 1075 1092 panic!("Parse succeeded unexpectedly"); 1076 1093 } 1077 1094 } ··· 1084 1101 print [ (named-child-index x) for x in @xs ] 1085 1102 } 1086 1103 "#; 1087 - let file = File::from_str(tree_sitter_python::language(), source).expect("Cannot parse file"); 1104 + let file = 1105 + File::from_str(tree_sitter_python::LANGUAGE.into(), source).expect("Cannot parse file"); 1088 1106 1089 1107 let statements = file 1090 1108 .stanzas ··· 1137 1155 print { (named-child-index x) for x in @xs } 1138 1156 } 1139 1157 "#; 1140 - let file = File::from_str(tree_sitter_python::language(), source).expect("Cannot parse file"); 1158 + let file = 1159 + File::from_str(tree_sitter_python::LANGUAGE.into(), source).expect("Cannot parse file"); 1141 1160 1142 1161 let statements = file 1143 1162 .stanzas ··· 1192 1211 edge n -> root 1193 1212 } 1194 1213 "#; 1195 - let file = File::from_str(tree_sitter_python::language(), source).expect("Cannot parse file"); 1214 + let file = 1215 + File::from_str(tree_sitter_python::LANGUAGE.into(), source).expect("Cannot parse file"); 1196 1216 1197 1217 assert_eq!( 1198 1218 file.globals, ··· 1248 1268 print PKG_NAME 1249 1269 } 1250 1270 "#; 1251 - let file = File::from_str(tree_sitter_python::language(), source).expect("Cannot parse file"); 1271 + let file = 1272 + File::from_str(tree_sitter_python::LANGUAGE.into(), source).expect("Cannot parse file"); 1252 1273 1253 1274 assert_eq!( 1254 1275 file.globals, ··· 1287 1308 edge n -> root 1288 1309 } 1289 1310 "#; 1290 - if let Ok(_) = File::from_str(tree_sitter_python::language(), source) { 1311 + if let Ok(_) = File::from_str(tree_sitter_python::LANGUAGE.into(), source) { 1291 1312 panic!("Parse succeeded unexpectedly"); 1292 1313 } 1293 1314 } ··· 1304 1325 } 1305 1326 } 1306 1327 "#; 1307 - let file = File::from_str(tree_sitter_python::language(), source).expect("Cannot parse file"); 1328 + let file = 1329 + File::from_str(tree_sitter_python::LANGUAGE.into(), source).expect("Cannot parse file"); 1308 1330 1309 1331 assert_eq!( 1310 1332 file.globals, ··· 1377 1399 } 1378 1400 } 1379 1401 "#; 1380 - let file = File::from_str(tree_sitter_python::language(), source).expect("Cannot parse file"); 1402 + let file = 1403 + File::from_str(tree_sitter_python::LANGUAGE.into(), source).expect("Cannot parse file"); 1381 1404 1382 1405 assert_eq!( 1383 1406 file.globals, ··· 1448 1471 node root 1449 1472 } 1450 1473 "#; 1451 - if let Ok(_) = File::from_str(tree_sitter_python::language(), source) { 1474 + if let Ok(_) = File::from_str(tree_sitter_python::LANGUAGE.into(), source) { 1452 1475 panic!("Parse succeeded unexpectedly"); 1453 1476 } 1454 1477 } ··· 1462 1485 node root 1463 1486 } 1464 1487 "#; 1465 - if let Ok(_) = File::from_str(tree_sitter_python::language(), source) { 1488 + if let Ok(_) = File::from_str(tree_sitter_python::LANGUAGE.into(), source) { 1466 1489 panic!("Parse succeeded unexpectedly"); 1467 1490 } 1468 1491 } ··· 1476 1499 set root = #null 1477 1500 } 1478 1501 "#; 1479 - if let Ok(_) = File::from_str(tree_sitter_python::language(), source) { 1502 + if let Ok(_) = File::from_str(tree_sitter_python::LANGUAGE.into(), source) { 1480 1503 panic!("Parse succeeded unexpectedly"); 1481 1504 } 1482 1505 } ··· 1490 1513 attr (n) sh = @name 1491 1514 } 1492 1515 "#; 1493 - let file = File::from_str(tree_sitter_python::language(), source).expect("Cannot parse file"); 1516 + let file = 1517 + File::from_str(tree_sitter_python::LANGUAGE.into(), source).expect("Cannot parse file"); 1494 1518 1495 1519 let shorthands = file.shorthands.into_iter().collect::<Vec<_>>(); 1496 1520 assert_eq!( ··· 1536 1560 { 1537 1561 } 1538 1562 "#; 1539 - if let Ok(_) = File::from_str(tree_sitter_python::language(), source) { 1563 + if let Ok(_) = File::from_str(tree_sitter_python::LANGUAGE.into(), source) { 1540 1564 panic!("Parse succeeded unexpectedly"); 1541 1565 } 1542 1566 } ··· 1548 1572 (module (non_existing_node)) 1549 1573 {} 1550 1574 "#; 1551 - let err = match File::from_str(tree_sitter_python::language(), source) { 1575 + let err = match File::from_str(tree_sitter_python::LANGUAGE.into(), source) { 1552 1576 Ok(_) => panic!("Parse succeeded unexpectedly"), 1553 1577 Err(ParseError::QueryError(e)) => e, 1554 1578 Err(e) => panic!("Unexpected error: {}", e), ··· 1570 1594 ] 1571 1595 {} 1572 1596 "#; 1573 - let err = match File::from_str(tree_sitter_python::language(), source) { 1597 + let err = match File::from_str(tree_sitter_python::LANGUAGE.into(), source) { 1574 1598 Ok(_) => panic!("Parse succeeded unexpectedly"), 1575 1599 Err(ParseError::QueryError(e)) => e, 1576 1600 Err(e) => panic!("Unexpected error: {}", e), ··· 1586 1610 (function_definition name: (identifier) @name) { 1587 1611 } 1588 1612 "#; 1589 - if let Ok(_) = File::from_str(tree_sitter_python::language(), source) { 1613 + if let Ok(_) = File::from_str(tree_sitter_python::LANGUAGE.into(), source) { 1590 1614 panic!("Parse succeeded unexpectedly"); 1591 1615 } 1592 1616 } ··· 1597 1621 (function_definition name: (identifier) @_name) { 1598 1622 } 1599 1623 "#; 1600 - File::from_str(tree_sitter_python::language(), source).expect("parse to succeed"); 1624 + File::from_str(tree_sitter_python::LANGUAGE.into(), source).expect("parse to succeed"); 1625 + } 1626 + 1627 + #[test] 1628 + fn can_parse_inherit_directives() { 1629 + let source = r#" 1630 + inherit .scope 1631 + "#; 1632 + let file = 1633 + File::from_str(tree_sitter_python::LANGUAGE.into(), source).expect("parse to succeed"); 1634 + assert!(file.inherited_variables.contains("scope".into())); 1601 1635 }
+12
vscode/CHANGELOG.md
··· 5 5 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 6 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 7 8 + ## 0.1.2 -- 2023-12-12 9 + 10 + ### Added 11 + 12 + - Add missing `else` keyword 13 + 14 + ## 0.1.1 -- 2023-06-01 15 + 16 + ### Added 17 + 18 + - Add new `inherit` keyword 19 + 8 20 ## 0.1.0 -- 2022-05-11 9 21 10 22 ### Added
+1 -1
vscode/package.json
··· 1 1 { 2 2 "name": "tree-sitter-graph", 3 - "version": "0.1.0", 3 + "version": "0.1.2", 4 4 "publisher": "tree-sitter", 5 5 "engines": { 6 6 "vscode": "^1.60.0"
+1 -1
vscode/syntaxes/tree-sitter-graph.tmLanguage.json
··· 16 16 "keywords": { 17 17 "patterns": [{ 18 18 "name": "keyword.control.tsg", 19 - "match": "^\\s*(attr|attribute|edge|for|global|if|let|node|none|print|scan|set|some|var)\\b" 19 + "match": "^\\s*(attr|attribute|edge|else|for|global|if|inherit|let|node|none|print|scan|set|some|var)\\b" 20 20 }] 21 21 }, 22 22 "functions": {