+10
-10
.github/workflows/ci.yml
+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
+50
CHANGELOG.md
+50
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
+
8
58
## v0.10.3 -- 2023-06-01
9
59
10
60
### DSL
+20
-44
Cargo.toml
+20
-44
Cargo.toml
···
1
1
[package]
2
2
name = "tree-sitter-graph"
3
-
version = "0.10.3"
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
+1
-1
README.md
+25
src/ast.rs
+25
src/ast.rs
···
81
81
DeclareImmutable(DeclareImmutable),
82
82
DeclareMutable(DeclareMutable),
83
83
Assign(Assign),
84
+
Expr(ExpressionStatement),
84
85
// Graph nodes
85
86
CreateGraphNode(CreateGraphNode),
86
87
AddGraphNodeAttribute(AddGraphNodeAttribute),
···
103
104
Self::DeclareImmutable(stmt) => stmt.fmt(f),
104
105
Self::DeclareMutable(stmt) => stmt.fmt(f),
105
106
Self::Assign(stmt) => stmt.fmt(f),
107
+
Self::Expr(stmt) => stmt.fmt(f),
106
108
Self::CreateGraphNode(stmt) => stmt.fmt(f),
107
109
Self::AddGraphNodeAttribute(stmt) => stmt.fmt(f),
108
110
Self::CreateEdge(stmt) => stmt.fmt(f),
···
161
163
write!(f, " {}", attr)?;
162
164
}
163
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
+
)
164
189
}
165
190
}
166
191
+3
-3
src/bin/tree-sitter-graph/main.rs
+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
+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
}
+41
-17
src/execution/lazy/statements.rs
+41
-17
src/execution/lazy/statements.rs
···
22
22
use super::EvaluationContext;
23
23
use super::GraphElementKey;
24
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
+
}
64
+
25
65
/// Lazy graph statements
26
66
#[derive(Debug)]
27
67
pub(super) enum LazyStatement {
···
186
226
.sink
187
227
.evaluate_as_graph_node(exec)
188
228
.with_context(|| "Evaluating edge sink".to_string().into())?;
189
-
let prev_debug_info = exec
190
-
.prev_element_debug_info
191
-
.insert(GraphElementKey::Edge(source, sink), self.debug_info.clone());
192
229
let edge = match exec.graph[source].add_edge(sink) {
193
-
Ok(edge) => edge,
194
-
Err(_) => {
195
-
return Err(ExecutionError::DuplicateEdge(format!(
196
-
"({} -> {})",
197
-
source, sink,
198
-
)))
199
-
.with_context(|| {
200
-
(
201
-
prev_debug_info.unwrap().into(),
202
-
self.debug_info.clone().into(),
203
-
)
204
-
.into()
205
-
});
206
-
}
230
+
Ok(edge) | Err(edge) => edge,
207
231
};
208
232
edge.attributes = self.attributes.clone();
209
233
Ok(())
+14
-6
src/execution/lazy/store.rs
+14
-6
src/execution/lazy/store.rs
···
193
193
) -> Result<HashMap<SyntaxNodeID, LazyValue>, ExecutionError> {
194
194
match values {
195
195
ScopedValues::Unforced(pairs) => {
196
-
let mut map = HashMap::new();
196
+
let mut values = HashMap::new();
197
197
let mut debug_infos = HashMap::new();
198
198
for (scope, value, debug_info) in pairs.into_iter() {
199
199
let node = scope
200
200
.evaluate_as_syntax_node(exec)
201
201
.with_context(|| format!("Evaluating scope of variable _.{}", name,).into())
202
202
.with_context(|| debug_info.0.clone().into())?;
203
-
let prev_debug_info = debug_infos.insert(node, debug_info.clone());
204
-
match map.insert(node.index, value.clone()) {
205
-
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)) => {
206
208
return Err(ExecutionError::DuplicateVariable(format!(
207
209
"{}.{}",
208
210
node, name,
209
211
)))
210
-
.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
+
)
211
219
}
212
220
_ => {}
213
221
};
214
222
}
215
-
Ok(map)
223
+
Ok(values)
216
224
}
217
225
ScopedValues::Forcing => Err(ExecutionError::RecursivelyDefinedScopedVariable(
218
226
format!("_.{}", name),
+36
-10
src/execution/lazy.rs
+36
-10
src/execution/lazy.rs
···
18
18
use tree_sitter::QueryMatch;
19
19
use tree_sitter::Tree;
20
20
21
+
use streaming_iterator::StreamingIterator;
22
+
21
23
use crate::ast;
22
24
use crate::execution::error::ExecutionError;
23
25
use crate::execution::error::ResultWithExecutionError;
···
60
62
lazy: config.lazy,
61
63
location_attr: config.location_attr.clone(),
62
64
variable_name_attr: config.variable_name_attr.clone(),
65
+
match_node_attr: config.match_node_attr.clone(),
63
66
};
64
67
65
68
let mut locals = VariableMap::new();
66
69
let mut store = LazyStore::new();
67
70
let mut scoped_store = LazyScopedVariables::new();
68
-
let mut lazy_graph = Vec::new();
71
+
let mut lazy_graph = LazyGraph::new();
69
72
let mut function_parameters = Vec::new();
70
73
let mut prev_element_debug_info = HashMap::new();
71
74
···
99
102
prev_element_debug_info: &mut prev_element_debug_info,
100
103
cancellation_flag,
101
104
};
102
-
for graph_stmt in &lazy_graph {
103
-
graph_stmt.evaluate(&mut exec)?;
104
-
}
105
+
lazy_graph.evaluate(&mut exec)?;
105
106
// make sure any unforced values are now forced, to surface any problems
106
107
// hidden by the fact that the values were unused
107
108
store.evaluate_all(&mut exec)?;
···
117
118
mut visit: F,
118
119
) -> Result<(), E>
119
120
where
120
-
F: FnMut(&ast::Stanza, QueryMatch<'_, 'tree>) -> Result<(), E>,
121
+
F: FnMut(&ast::Stanza, &QueryMatch<'_, 'tree>) -> Result<(), E>,
121
122
{
122
123
let mut cursor = QueryCursor::new();
123
124
let query = self.query.as_ref().unwrap();
124
-
let matches = cursor.matches(query, tree.root_node(), source.as_bytes());
125
-
for mat in matches {
125
+
let mut matches = cursor.matches(query, tree.root_node(), source.as_bytes());
126
+
while let Some(mat) = matches.next() {
126
127
let stanza = &self.stanzas[mat.pattern_index];
127
128
visit(stanza, mat)?;
128
129
}
···
138
139
locals: &'a mut dyn MutVariables<LazyValue>,
139
140
current_regex_captures: &'a Vec<String>,
140
141
mat: &'a QueryMatch<'a, 'tree>,
142
+
full_match_file_capture_index: usize,
141
143
store: &'a mut LazyStore,
142
144
scoped_store: &'a mut LazyScopedVariables,
143
-
lazy_graph: &'a mut Vec<LazyStatement>,
145
+
lazy_graph: &'a mut LazyGraph,
144
146
function_parameters: &'a mut Vec<graph::Value>, // re-usable buffer to reduce memory allocations
145
147
prev_element_debug_info: &'a mut HashMap<GraphElementKey, DebugInfo>,
146
148
error_context: StatementContext,
···
165
167
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
166
168
pub(super) enum GraphElementKey {
167
169
NodeAttribute(graph::GraphNodeRef, Identifier),
168
-
Edge(graph::GraphNodeRef, graph::GraphNodeRef),
169
170
EdgeAttribute(graph::GraphNodeRef, graph::GraphNodeRef, Identifier),
170
171
}
171
172
···
179
180
locals: &mut VariableMap<'l, LazyValue>,
180
181
store: &mut LazyStore,
181
182
scoped_store: &mut LazyScopedVariables,
182
-
lazy_graph: &mut Vec<LazyStatement>,
183
+
lazy_graph: &mut LazyGraph,
183
184
function_parameters: &mut Vec<graph::Value>,
184
185
prev_element_debug_info: &mut HashMap<GraphElementKey, DebugInfo>,
185
186
inherited_variables: &HashSet<Identifier>,
···
203
204
locals,
204
205
current_regex_captures: ¤t_regex_captures,
205
206
mat,
207
+
full_match_file_capture_index: self.full_match_file_capture_index,
206
208
store,
207
209
scoped_store,
208
210
lazy_graph,
···
229
231
Self::DeclareImmutable(statement) => statement.execute_lazy(exec),
230
232
Self::DeclareMutable(statement) => statement.execute_lazy(exec),
231
233
Self::Assign(statement) => statement.execute_lazy(exec),
234
+
Self::Expr(statement) => statement.value.evaluate_lazy(exec).map(|_| ()),
232
235
Self::CreateGraphNode(statement) => statement.execute_lazy(exec),
233
236
Self::AddGraphNodeAttribute(statement) => statement.execute_lazy(exec),
234
237
Self::CreateEdge(statement) => statement.execute_lazy(exec),
···
267
270
let graph_node = exec.graph.add_graph_node();
268
271
self.node
269
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
+
}
270
290
self.node.add_lazy(exec, graph_node.into(), false)
271
291
}
272
292
}
···
367
387
locals: &mut arm_locals,
368
388
current_regex_captures: ¤t_regex_captures,
369
389
mat: exec.mat,
390
+
full_match_file_capture_index: exec.full_match_file_capture_index,
370
391
store: exec.store,
371
392
scoped_store: exec.scoped_store,
372
393
lazy_graph: exec.lazy_graph,
···
433
454
locals: &mut arm_locals,
434
455
current_regex_captures: exec.current_regex_captures,
435
456
mat: exec.mat,
457
+
full_match_file_capture_index: exec.full_match_file_capture_index,
436
458
store: exec.store,
437
459
scoped_store: exec.scoped_store,
438
460
lazy_graph: exec.lazy_graph,
···
480
502
locals: &mut loop_locals,
481
503
current_regex_captures: exec.current_regex_captures,
482
504
mat: exec.mat,
505
+
full_match_file_capture_index: exec.full_match_file_capture_index,
483
506
store: exec.store,
484
507
scoped_store: exec.scoped_store,
485
508
lazy_graph: exec.lazy_graph,
···
574
597
locals: &mut loop_locals,
575
598
current_regex_captures: exec.current_regex_captures,
576
599
mat: exec.mat,
600
+
full_match_file_capture_index: exec.full_match_file_capture_index,
577
601
store: exec.store,
578
602
scoped_store: exec.scoped_store,
579
603
lazy_graph: exec.lazy_graph,
···
617
641
locals: &mut loop_locals,
618
642
current_regex_captures: exec.current_regex_captures,
619
643
mat: exec.mat,
644
+
full_match_file_capture_index: exec.full_match_file_capture_index,
620
645
store: exec.store,
621
646
scoped_store: exec.scoped_store,
622
647
lazy_graph: exec.lazy_graph,
···
832
857
locals: &mut shorthand_locals,
833
858
current_regex_captures: exec.current_regex_captures,
834
859
mat: exec.mat,
860
+
full_match_file_capture_index: exec.full_match_file_capture_index,
835
861
store: exec.store,
836
862
scoped_store: exec.scoped_store,
837
863
lazy_graph: exec.lazy_graph,
+41
-11
src/execution/strict.rs
+41
-11
src/execution/strict.rs
···
8
8
use std::collections::BTreeSet;
9
9
use std::collections::HashMap;
10
10
use std::collections::HashSet;
11
+
use streaming_iterator::StreamingIterator;
11
12
use tree_sitter::QueryCursor;
12
13
use tree_sitter::QueryMatch;
13
14
use tree_sitter::Tree;
···
15
16
use crate::ast::AddEdgeAttribute;
16
17
use crate::ast::AddGraphNodeAttribute;
17
18
use crate::ast::Assign;
19
+
use crate::ast::ExpressionStatement;
18
20
use crate::ast::Attribute;
19
21
use crate::ast::AttributeShorthand;
20
22
use crate::ast::AttributeShorthands;
···
81
83
lazy: config.lazy,
82
84
location_attr: config.location_attr.clone(),
83
85
variable_name_attr: config.variable_name_attr.clone(),
86
+
match_node_attr: config.match_node_attr.clone(),
84
87
};
85
88
86
89
let mut locals = VariableMap::new();
···
114
117
mut visit: F,
115
118
) -> Result<(), E>
116
119
where
117
-
F: FnMut(&Stanza, QueryMatch<'_, 'tree>) -> Result<(), E>,
120
+
F: FnMut(&Stanza, &QueryMatch<'_, 'tree>) -> Result<(), E>,
118
121
{
119
122
for stanza in &self.stanzas {
120
123
stanza.try_visit_matches_strict(tree, source, |mat| visit(stanza, mat))?;
···
133
136
current_regex_captures: &'a Vec<String>,
134
137
function_parameters: &'a mut Vec<Value>,
135
138
mat: &'a QueryMatch<'a, 'tree>,
139
+
full_match_stanza_capture_index: usize,
136
140
error_context: StatementContext,
137
141
inherited_variables: &'a HashSet<Identifier>,
138
142
shorthands: &'a AttributeShorthands,
···
192
196
current_regex_captures,
193
197
function_parameters,
194
198
mat: &mat,
199
+
full_match_stanza_capture_index: self.full_match_stanza_capture_index,
195
200
error_context,
196
201
inherited_variables,
197
202
shorthands,
···
211
216
mut visit: F,
212
217
) -> Result<(), E>
213
218
where
214
-
F: FnMut(QueryMatch<'_, 'tree>) -> Result<(), E>,
219
+
F: FnMut(&QueryMatch<'_, 'tree>) -> Result<(), E>,
215
220
{
216
221
let mut cursor = QueryCursor::new();
217
-
let matches = cursor.matches(&self.query, tree.root_node(), source.as_bytes());
218
-
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() {
219
224
visit(mat)?;
220
225
}
221
226
Ok(())
···
228
233
Statement::DeclareImmutable(s) => s.location,
229
234
Statement::DeclareMutable(s) => s.location,
230
235
Statement::Assign(s) => s.location,
236
+
Statement::Expr(s) => s.location,
231
237
Statement::CreateGraphNode(s) => s.location,
232
238
Statement::AddGraphNodeAttribute(s) => s.location,
233
239
Statement::CreateEdge(s) => s.location,
···
245
251
Statement::DeclareImmutable(statement) => statement.execute(exec),
246
252
Statement::DeclareMutable(statement) => statement.execute(exec),
247
253
Statement::Assign(statement) => statement.execute(exec),
254
+
Statement::Expr(statement) => statement.execute(exec),
248
255
Statement::CreateGraphNode(statement) => statement.execute(exec),
249
256
Statement::AddGraphNodeAttribute(statement) => statement.execute(exec),
250
257
Statement::CreateEdge(statement) => statement.execute(exec),
···
278
285
}
279
286
}
280
287
288
+
impl ExpressionStatement {
289
+
fn execute(&self, exec: &mut ExecutionContext) -> Result<(), ExecutionError> {
290
+
self.value.evaluate(exec).map(|_| ())
291
+
}
292
+
}
293
+
281
294
impl CreateGraphNode {
282
295
fn execute(&self, exec: &mut ExecutionContext) -> Result<(), ExecutionError> {
283
296
let graph_node = exec.graph.add_graph_node();
284
297
self.node
285
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
+
}
286
316
let value = Value::GraphNode(graph_node);
287
317
self.node.add(exec, value, false)
288
318
}
···
314
344
let source = self.source.evaluate(exec)?.into_graph_node_ref()?;
315
345
let sink = self.sink.evaluate(exec)?.into_graph_node_ref()?;
316
346
let edge = match exec.graph[source].add_edge(sink) {
317
-
Ok(edge) => edge,
318
-
Err(_) => {
319
-
return Err(ExecutionError::DuplicateEdge(format!(
320
-
"({} -> {}) in {}",
321
-
source, sink, self,
322
-
)))?
323
-
}
347
+
Ok(edge) | Err(edge) => edge,
324
348
};
325
349
self.add_debug_attrs(&mut edge.attributes, exec.config)?;
326
350
Ok(())
···
408
432
current_regex_captures: ¤t_regex_captures,
409
433
function_parameters: exec.function_parameters,
410
434
mat: exec.mat,
435
+
full_match_stanza_capture_index: exec.full_match_stanza_capture_index,
411
436
error_context: exec.error_context.clone(),
412
437
inherited_variables: exec.inherited_variables,
413
438
shorthands: exec.shorthands,
···
468
493
current_regex_captures: exec.current_regex_captures,
469
494
function_parameters: exec.function_parameters,
470
495
mat: exec.mat,
496
+
full_match_stanza_capture_index: exec.full_match_stanza_capture_index,
471
497
error_context: exec.error_context.clone(),
472
498
inherited_variables: exec.inherited_variables,
473
499
shorthands: exec.shorthands,
···
510
536
current_regex_captures: exec.current_regex_captures,
511
537
function_parameters: exec.function_parameters,
512
538
mat: exec.mat,
539
+
full_match_stanza_capture_index: exec.full_match_stanza_capture_index,
513
540
error_context: exec.error_context.clone(),
514
541
inherited_variables: exec.inherited_variables,
515
542
shorthands: exec.shorthands,
···
585
612
current_regex_captures: exec.current_regex_captures,
586
613
function_parameters: exec.function_parameters,
587
614
mat: exec.mat,
615
+
full_match_stanza_capture_index: exec.full_match_stanza_capture_index,
588
616
error_context: exec.error_context.clone(),
589
617
inherited_variables: exec.inherited_variables,
590
618
shorthands: exec.shorthands,
···
625
653
current_regex_captures: exec.current_regex_captures,
626
654
function_parameters: exec.function_parameters,
627
655
mat: exec.mat,
656
+
full_match_stanza_capture_index: exec.full_match_stanza_capture_index,
628
657
error_context: exec.error_context.clone(),
629
658
inherited_variables: exec.inherited_variables,
630
659
shorthands: exec.shorthands,
···
882
911
current_regex_captures: exec.current_regex_captures,
883
912
function_parameters: exec.function_parameters,
884
913
mat: exec.mat,
914
+
full_match_stanza_capture_index: exec.full_match_stanza_capture_index,
885
915
error_context: exec.error_context.clone(),
886
916
inherited_variables: exec.inherited_variables,
887
917
shorthands: exec.shorthands,
+15
-10
src/execution.rs
+15
-10
src/execution.rs
···
119
119
.iter()
120
120
.map(|name| {
121
121
let index = file_query
122
-
.capture_index_for_name(name)
122
+
.capture_index_for_name(*name)
123
123
.expect("missing index for capture");
124
124
let quantifier =
125
125
file_query.capture_quantifiers(mat.pattern_index)[index as usize];
126
-
(name, quantifier, index)
126
+
(*name, quantifier, index)
127
127
})
128
128
.filter(|c| c.2 != stanza.full_match_file_capture_index as u32)
129
129
.collect();
···
143
143
.map(|name| {
144
144
let index = stanza
145
145
.query
146
-
.capture_index_for_name(name)
146
+
.capture_index_for_name(*name)
147
147
.expect("missing index for capture");
148
148
let quantifier = stanza.query.capture_quantifiers(0)[index as usize];
149
-
(name, quantifier, index)
149
+
(*name, quantifier, index)
150
150
})
151
151
.filter(|c| c.2 != stanza.full_match_stanza_capture_index as u32)
152
152
.collect();
···
179
179
.map(|name| {
180
180
let index = self
181
181
.query
182
-
.capture_index_for_name(name)
182
+
.capture_index_for_name(*name)
183
183
.expect("missing index for capture");
184
184
let quantifier = self.query.capture_quantifiers(0)[index as usize];
185
-
(name, quantifier, index)
185
+
(*name, quantifier, index)
186
186
})
187
187
.filter(|c| c.2 != self.full_match_stanza_capture_index as u32)
188
188
.collect();
···
197
197
}
198
198
199
199
pub struct Match<'a, 'tree> {
200
-
mat: QueryMatch<'a, 'tree>,
200
+
mat: &'a QueryMatch<'a, 'tree>,
201
201
full_capture_index: u32,
202
-
named_captures: Vec<(&'a String, CaptureQuantifier, u32)>,
202
+
named_captures: Vec<(&'a str, CaptureQuantifier, u32)>,
203
203
query_location: Location,
204
204
}
205
205
···
217
217
&'s self,
218
218
) -> impl Iterator<
219
219
Item = (
220
-
&String,
220
+
&'a str,
221
221
CaptureQuantifier,
222
222
impl Iterator<Item = Node<'tree>> + 's,
223
223
),
···
239
239
}
240
240
241
241
/// Return an iterator over all capture names.
242
-
pub fn capture_names(&self) -> impl Iterator<Item = &String> {
242
+
pub fn capture_names(&self) -> impl Iterator<Item = &str> {
243
243
self.named_captures.iter().map(|c| c.0)
244
244
}
245
245
···
256
256
pub(crate) lazy: bool,
257
257
pub(crate) location_attr: Option<Identifier>,
258
258
pub(crate) variable_name_attr: Option<Identifier>,
259
+
pub(crate) match_node_attr: Option<Identifier>,
259
260
}
260
261
261
262
impl<'a, 'g> ExecutionConfig<'a, 'g> {
···
266
267
lazy: false,
267
268
location_attr: None,
268
269
variable_name_attr: None,
270
+
match_node_attr: None,
269
271
}
270
272
}
271
273
···
273
275
self,
274
276
location_attr: Identifier,
275
277
variable_name_attr: Identifier,
278
+
match_node_attr: Identifier,
276
279
) -> Self {
277
280
Self {
278
281
functions: self.functions,
···
280
283
lazy: self.lazy,
281
284
location_attr: location_attr.into(),
282
285
variable_name_attr: variable_name_attr.into(),
286
+
match_node_attr: match_node_attr.into(),
283
287
}
284
288
}
285
289
···
290
294
lazy,
291
295
location_attr: self.location_attr,
292
296
variable_name_attr: self.variable_name_attr,
297
+
match_node_attr: self.match_node_attr,
293
298
}
294
299
}
295
300
}
+1
-1
src/functions.rs
+1
-1
src/functions.rs
+7
-3
src/graph.rs
+7
-3
src/graph.rs
···
275
275
276
276
/// Adds an attribute to this attribute set. If there was already an attribute with the same
277
277
/// name, replaces its value and returns `Err`.
278
-
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> {
279
279
match self.values.entry(name) {
280
280
Entry::Occupied(mut o) => {
281
-
o.insert(value.into());
282
-
Err(())
281
+
let value = value.into();
282
+
if o.get() != &value {
283
+
Err(o.insert(value.into()))
284
+
} else {
285
+
Ok(())
286
+
}
283
287
}
284
288
Entry::Vacant(v) => {
285
289
v.insert(value.into());
+17
-17
src/parse_error.rs
+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
}
+12
-4
src/parser.rs
+12
-4
src/parser.rs
···
305
305
let name = self.parse_identifier("inherit")?;
306
306
file.inherited_variables.insert(name);
307
307
} else {
308
-
let stanza = self.parse_stanza(file.language)?;
308
+
let stanza = self.parse_stanza(&file.language)?;
309
309
file.stanzas.push(stanza);
310
310
}
311
311
self.consume_whitespace();
312
312
}
313
313
// we can unwrap here because all queries have already been parsed before
314
-
file.query = Some(Query::new(file.language, &self.query_source).unwrap());
314
+
file.query = Some(Query::new(&file.language, &self.query_source).unwrap());
315
315
Ok(())
316
316
}
317
317
···
369
369
Ok(quantifier)
370
370
}
371
371
372
-
fn parse_stanza(&mut self, language: Language) -> Result<ast::Stanza, ParseError> {
372
+
fn parse_stanza(&mut self, language: &Language) -> Result<ast::Stanza, ParseError> {
373
373
let start = self.location;
374
374
let (query, full_match_stanza_capture_index) = self.parse_query(language)?;
375
375
self.consume_whitespace();
···
385
385
})
386
386
}
387
387
388
-
fn parse_query(&mut self, language: Language) -> Result<(Query, usize), ParseError> {
388
+
fn parse_query(&mut self, language: &Language) -> Result<(Query, usize), ParseError> {
389
389
let location = self.location;
390
390
let query_start = self.offset;
391
391
self.skip_query()?;
···
482
482
}
483
483
484
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
+
}
485
493
let keyword_location = self.location;
486
494
let keyword = self.parse_name("keyword")?;
487
495
self.consume_whitespace();
+1
-1
src/variables.rs
+1
-1
src/variables.rs
+68
-2
tests/it/execution.rs
+68
-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
···
1008
1010
"#},
1009
1011
);
1010
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
+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
+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();
+71
-5
tests/it/lazy_execution.rs
+71
-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
···
1527
1529
"#},
1528
1530
);
1529
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
+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
+64
-39
tests/it/parser.rs
+64
-39
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");
1601
1625
}
1602
1626
1603
1627
#[test]
···
1605
1629
let source = r#"
1606
1630
inherit .scope
1607
1631
"#;
1608
-
let file = File::from_str(tree_sitter_python::language(), source).expect("parse to succeed");
1632
+
let file =
1633
+
File::from_str(tree_sitter_python::LANGUAGE.into(), source).expect("parse to succeed");
1609
1634
assert!(file.inherited_variables.contains("scope".into()));
1610
1635
}
+6
vscode/CHANGELOG.md
+6
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
+
8
14
## 0.1.1 -- 2023-06-01
9
15
10
16
### Added
+1
-1
vscode/package.json
+1
-1
vscode/package.json
+1
-1
vscode/syntaxes/tree-sitter-graph.tmLanguage.json
+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|inherit|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": {