+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
+98
CHANGELOG.md
+98
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
+
78
+
## v0.10.1 -- 2023-05-15
79
+
80
+
### Library
81
+
82
+
#### Added
83
+
84
+
- The `ast::File::try_visit_matches` method can be used to execute the stanza query matching without executing the stanza bodies.
85
+
86
+
## v0.10.0 -- 2023-05-10
87
+
88
+
### DSL
89
+
90
+
#### Changed
91
+
92
+
- Unused captures in patterns are now considered errors, unless they start with an underscore.
93
+
94
+
### Library
95
+
96
+
#### Fixed
97
+
98
+
- Some execution errors were not always reported in lazy execution mode, depending on whether variables were used or not. This has been fixed to behave more consistently, so that errors are always reported regardless of variable use.
99
+
100
+
### CLI
101
+
102
+
#### Added
103
+
104
+
- Errors from setting a scoped variable twice on the same node are now reported with source snippets for both statements involved.
105
+
8
106
## v0.9.2 -- 2023-04-14
9
107
10
108
### Library
+20
-44
Cargo.toml
+20
-44
Cargo.toml
···
1
1
[package]
2
2
name = "tree-sitter-graph"
3
-
version = "0.9.2"
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
+31
-1
src/ast.rs
+31
-1
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;
15
16
use tree_sitter::Query;
16
17
18
+
use crate::parser::Range;
17
19
use crate::Identifier;
18
20
use crate::Location;
19
21
···
23
25
pub language: Language,
24
26
/// The expected global variables used in this file
25
27
pub globals: Vec<Global>,
28
+
/// The scoped variables that are inherited by child nodes
29
+
pub inherited_variables: HashSet<Identifier>,
26
30
/// The combined query of all stanzas in the file
27
31
pub query: Option<Query>,
28
32
/// The list of stanzas in the file
···
36
40
File {
37
41
language,
38
42
globals: Vec::new(),
43
+
inherited_variables: HashSet::new(),
39
44
query: None,
40
45
stanzas: Vec::new(),
41
46
shorthands: AttributeShorthands::new(),
···
66
71
pub full_match_stanza_capture_index: usize,
67
72
/// Capture index of the full match in the file query
68
73
pub full_match_file_capture_index: usize,
69
-
pub location: Location,
74
+
pub range: Range,
70
75
}
71
76
72
77
/// A statement that can appear in a graph DSL stanza
···
76
81
DeclareImmutable(DeclareImmutable),
77
82
DeclareMutable(DeclareMutable),
78
83
Assign(Assign),
84
+
Expr(ExpressionStatement),
79
85
// Graph nodes
80
86
CreateGraphNode(CreateGraphNode),
81
87
AddGraphNodeAttribute(AddGraphNodeAttribute),
···
98
104
Self::DeclareImmutable(stmt) => stmt.fmt(f),
99
105
Self::DeclareMutable(stmt) => stmt.fmt(f),
100
106
Self::Assign(stmt) => stmt.fmt(f),
107
+
Self::Expr(stmt) => stmt.fmt(f),
101
108
Self::CreateGraphNode(stmt) => stmt.fmt(f),
102
109
Self::AddGraphNodeAttribute(stmt) => stmt.fmt(f),
103
110
Self::CreateEdge(stmt) => stmt.fmt(f),
···
156
163
write!(f, " {}", attr)?;
157
164
}
158
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
+
)
159
189
}
160
190
}
161
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()))?;
+12
-2
src/checker.rs
+12
-2
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)
···
198
198
if !unused_captures.is_empty() {
199
199
return Err(CheckError::UnusedCaptures(
200
200
unused_captures.join(" "),
201
-
self.location,
201
+
self.range.start,
202
202
));
203
203
}
204
204
···
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
}
+1
-1
src/execution/error.rs
+1
-1
src/execution/error.rs
···
95
95
Self {
96
96
statement: format!("{}", stmt),
97
97
statement_location: stmt.location(),
98
-
stanza_location: stanza.location,
98
+
stanza_location: stanza.range.start,
99
99
source_location: Location::from(source_node.range().start_point),
100
100
node_kind: source_node.kind().to_string(),
101
101
}
+100
-40
src/execution/lazy/statements.rs
+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
+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
+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
}
+84
-32
src/execution/lazy.rs
+84
-32
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
-
use tree_sitter::CaptureQuantifier::One;
17
17
use tree_sitter::QueryCursor;
18
18
use tree_sitter::QueryMatch;
19
19
use tree_sitter::Tree;
20
+
21
+
use streaming_iterator::StreamingIterator;
20
22
21
23
use crate::ast;
22
24
use crate::execution::error::ExecutionError;
23
25
use crate::execution::error::ResultWithExecutionError;
24
26
use crate::execution::error::StatementContext;
25
-
use crate::execution::query_capture_value;
26
27
use crate::execution::ExecutionConfig;
27
28
use crate::functions::Functions;
28
29
use crate::graph;
30
+
use crate::graph::Attributes;
29
31
use crate::graph::Graph;
32
+
use crate::graph::Value;
30
33
use crate::variables::Globals;
31
34
use crate::variables::MutVariables;
32
35
use crate::variables::VariableMap;
···
59
62
lazy: config.lazy,
60
63
location_attr: config.location_attr.clone(),
61
64
variable_name_attr: config.variable_name_attr.clone(),
65
+
match_node_attr: config.match_node_attr.clone(),
62
66
};
63
67
64
68
let mut locals = VariableMap::new();
65
-
let mut cursor = QueryCursor::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
72
-
let query = &self.query.as_ref().unwrap();
73
-
let matches = cursor.matches(query, tree.root_node(), source.as_bytes());
74
-
for mat in matches {
75
+
self.try_visit_matches_lazy(tree, source, |stanza, mat| {
75
76
cancellation_flag.check("processing matches")?;
76
-
let stanza = &self.stanzas[mat.pattern_index];
77
77
stanza.execute_lazy(
78
78
source,
79
79
&mat,
···
85
85
&mut lazy_graph,
86
86
&mut function_parameters,
87
87
&mut prev_element_debug_info,
88
+
&self.inherited_variables,
88
89
&self.shorthands,
89
90
cancellation_flag,
90
-
)?;
91
-
}
91
+
)
92
+
})?;
92
93
93
94
let mut exec = EvaluationContext {
94
95
source,
···
96
97
functions: config.functions,
97
98
store: &store,
98
99
scoped_store: &scoped_store,
100
+
inherited_variables: &self.inherited_variables,
99
101
function_parameters: &mut function_parameters,
100
102
prev_element_debug_info: &mut prev_element_debug_info,
101
103
cancellation_flag,
102
104
};
103
-
for graph_stmt in &lazy_graph {
104
-
graph_stmt.evaluate(&mut exec)?;
105
-
}
105
+
lazy_graph.evaluate(&mut exec)?;
106
106
// make sure any unforced values are now forced, to surface any problems
107
107
// hidden by the fact that the values were unused
108
108
store.evaluate_all(&mut exec)?;
···
110
110
111
111
Ok(())
112
112
}
113
+
114
+
pub(super) fn try_visit_matches_lazy<'tree, E, F>(
115
+
&self,
116
+
tree: &'tree Tree,
117
+
source: &'tree str,
118
+
mut visit: F,
119
+
) -> Result<(), E>
120
+
where
121
+
F: FnMut(&ast::Stanza, &QueryMatch<'_, 'tree>) -> Result<(), E>,
122
+
{
123
+
let mut cursor = QueryCursor::new();
124
+
let query = self.query.as_ref().unwrap();
125
+
let mut matches = cursor.matches(query, tree.root_node(), source.as_bytes());
126
+
while let Some(mat) = matches.next() {
127
+
let stanza = &self.stanzas[mat.pattern_index];
128
+
visit(stanza, mat)?;
129
+
}
130
+
Ok(())
131
+
}
113
132
}
114
133
115
134
/// Context for execution, which executes stanzas to build the lazy graph
···
120
139
locals: &'a mut dyn MutVariables<LazyValue>,
121
140
current_regex_captures: &'a Vec<String>,
122
141
mat: &'a QueryMatch<'a, 'tree>,
142
+
full_match_file_capture_index: usize,
123
143
store: &'a mut LazyStore,
124
144
scoped_store: &'a mut LazyScopedVariables,
125
-
lazy_graph: &'a mut Vec<LazyStatement>,
145
+
lazy_graph: &'a mut LazyGraph,
126
146
function_parameters: &'a mut Vec<graph::Value>, // re-usable buffer to reduce memory allocations
127
147
prev_element_debug_info: &'a mut HashMap<GraphElementKey, DebugInfo>,
128
148
error_context: StatementContext,
149
+
inherited_variables: &'a HashSet<Identifier>,
129
150
shorthands: &'a ast::AttributeShorthands,
130
151
cancellation_flag: &'a dyn CancellationFlag,
131
152
}
···
137
158
pub functions: &'a Functions,
138
159
pub store: &'a LazyStore,
139
160
pub scoped_store: &'a LazyScopedVariables,
161
+
pub inherited_variables: &'a HashSet<Identifier>,
140
162
pub function_parameters: &'a mut Vec<graph::Value>, // re-usable buffer to reduce memory allocations
141
163
pub prev_element_debug_info: &'a mut HashMap<GraphElementKey, DebugInfo>,
142
164
pub cancellation_flag: &'a dyn CancellationFlag,
···
145
167
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
146
168
pub(super) enum GraphElementKey {
147
169
NodeAttribute(graph::GraphNodeRef, Identifier),
148
-
Edge(graph::GraphNodeRef, graph::GraphNodeRef),
149
170
EdgeAttribute(graph::GraphNodeRef, graph::GraphNodeRef, Identifier),
150
171
}
151
172
···
159
180
locals: &mut VariableMap<'l, LazyValue>,
160
181
store: &mut LazyStore,
161
182
scoped_store: &mut LazyScopedVariables,
162
-
lazy_graph: &mut Vec<LazyStatement>,
183
+
lazy_graph: &mut LazyGraph,
163
184
function_parameters: &mut Vec<graph::Value>,
164
185
prev_element_debug_info: &mut HashMap<GraphElementKey, DebugInfo>,
186
+
inherited_variables: &HashSet<Identifier>,
165
187
shorthands: &ast::AttributeShorthands,
166
188
cancellation_flag: &dyn CancellationFlag,
167
189
) -> Result<(), ExecutionError> {
168
190
let current_regex_captures = vec![];
169
191
locals.clear();
170
-
let node = query_capture_value(self.full_match_file_capture_index, One, &mat, graph);
171
-
debug!("match {} at {}", node, self.location);
192
+
let node = mat
193
+
.nodes_for_capture_index(self.full_match_file_capture_index as u32)
194
+
.next()
195
+
.expect("missing capture for full match");
196
+
debug!("match {:?} at {}", node, self.range.start);
172
197
trace!("{{");
173
198
for statement in &self.statements {
174
-
let error_context = {
175
-
let node = mat
176
-
.captures
177
-
.iter()
178
-
.find(|c| c.index as usize == self.full_match_file_capture_index)
179
-
.expect("missing capture for full match")
180
-
.node;
181
-
StatementContext::new(&statement, &self, &node)
182
-
};
199
+
let error_context = { StatementContext::new(&statement, &self, &node) };
183
200
let mut exec = ExecutionContext {
184
201
source,
185
202
graph,
···
187
204
locals,
188
205
current_regex_captures: ¤t_regex_captures,
189
206
mat,
207
+
full_match_file_capture_index: self.full_match_file_capture_index,
190
208
store,
191
209
scoped_store,
192
210
lazy_graph,
193
211
function_parameters,
194
212
prev_element_debug_info,
195
213
error_context,
214
+
inherited_variables,
196
215
shorthands,
197
216
cancellation_flag,
198
217
};
···
212
231
Self::DeclareImmutable(statement) => statement.execute_lazy(exec),
213
232
Self::DeclareMutable(statement) => statement.execute_lazy(exec),
214
233
Self::Assign(statement) => statement.execute_lazy(exec),
234
+
Self::Expr(statement) => statement.value.evaluate_lazy(exec).map(|_| ()),
215
235
Self::CreateGraphNode(statement) => statement.execute_lazy(exec),
216
236
Self::AddGraphNodeAttribute(statement) => statement.execute_lazy(exec),
217
237
Self::CreateEdge(statement) => statement.execute_lazy(exec),
···
250
270
let graph_node = exec.graph.add_graph_node();
251
271
self.node
252
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
+
}
253
290
self.node.add_lazy(exec, graph_node.into(), false)
254
291
}
255
292
}
···
273
310
fn execute_lazy(&self, exec: &mut ExecutionContext) -> Result<(), ExecutionError> {
274
311
let source = self.source.evaluate_lazy(exec)?;
275
312
let sink = self.sink.evaluate_lazy(exec)?;
276
-
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());
277
316
exec.lazy_graph.push(stmt.into());
278
317
Ok(())
279
318
}
···
348
387
locals: &mut arm_locals,
349
388
current_regex_captures: ¤t_regex_captures,
350
389
mat: exec.mat,
390
+
full_match_file_capture_index: exec.full_match_file_capture_index,
351
391
store: exec.store,
352
392
scoped_store: exec.scoped_store,
353
393
lazy_graph: exec.lazy_graph,
354
394
function_parameters: exec.function_parameters,
355
395
prev_element_debug_info: exec.prev_element_debug_info,
356
396
error_context: exec.error_context.clone(),
397
+
inherited_variables: exec.inherited_variables,
357
398
shorthands: exec.shorthands,
358
399
cancellation_flag: exec.cancellation_flag,
359
400
};
···
413
454
locals: &mut arm_locals,
414
455
current_regex_captures: exec.current_regex_captures,
415
456
mat: exec.mat,
457
+
full_match_file_capture_index: exec.full_match_file_capture_index,
416
458
store: exec.store,
417
459
scoped_store: exec.scoped_store,
418
460
lazy_graph: exec.lazy_graph,
419
461
function_parameters: exec.function_parameters,
420
462
prev_element_debug_info: exec.prev_element_debug_info,
421
463
error_context: exec.error_context.clone(),
464
+
inherited_variables: exec.inherited_variables,
422
465
shorthands: exec.shorthands,
423
466
cancellation_flag: exec.cancellation_flag,
424
467
};
···
459
502
locals: &mut loop_locals,
460
503
current_regex_captures: exec.current_regex_captures,
461
504
mat: exec.mat,
505
+
full_match_file_capture_index: exec.full_match_file_capture_index,
462
506
store: exec.store,
463
507
scoped_store: exec.scoped_store,
464
508
lazy_graph: exec.lazy_graph,
465
509
function_parameters: exec.function_parameters,
466
510
prev_element_debug_info: exec.prev_element_debug_info,
467
511
error_context: exec.error_context.clone(),
512
+
inherited_variables: exec.inherited_variables,
468
513
shorthands: exec.shorthands,
469
514
cancellation_flag: exec.cancellation_flag,
470
515
};
···
508
553
functions: exec.config.functions,
509
554
store: exec.store,
510
555
scoped_store: exec.scoped_store,
556
+
inherited_variables: exec.inherited_variables,
511
557
function_parameters: exec.function_parameters,
512
558
prev_element_debug_info: exec.prev_element_debug_info,
513
559
cancellation_flag: exec.cancellation_flag,
···
551
597
locals: &mut loop_locals,
552
598
current_regex_captures: exec.current_regex_captures,
553
599
mat: exec.mat,
600
+
full_match_file_capture_index: exec.full_match_file_capture_index,
554
601
store: exec.store,
555
602
scoped_store: exec.scoped_store,
556
603
lazy_graph: exec.lazy_graph,
557
604
function_parameters: exec.function_parameters,
558
605
prev_element_debug_info: exec.prev_element_debug_info,
559
606
error_context: exec.error_context.clone(),
607
+
inherited_variables: exec.inherited_variables,
560
608
shorthands: exec.shorthands,
561
609
cancellation_flag: exec.cancellation_flag,
562
610
};
···
593
641
locals: &mut loop_locals,
594
642
current_regex_captures: exec.current_regex_captures,
595
643
mat: exec.mat,
644
+
full_match_file_capture_index: exec.full_match_file_capture_index,
596
645
store: exec.store,
597
646
scoped_store: exec.scoped_store,
598
647
lazy_graph: exec.lazy_graph,
599
648
function_parameters: exec.function_parameters,
600
649
prev_element_debug_info: exec.prev_element_debug_info,
601
650
error_context: exec.error_context.clone(),
651
+
inherited_variables: exec.inherited_variables,
602
652
shorthands: exec.shorthands,
603
653
cancellation_flag: exec.cancellation_flag,
604
654
};
···
613
663
614
664
impl ast::Capture {
615
665
fn evaluate_lazy(&self, exec: &mut ExecutionContext) -> Result<LazyValue, ExecutionError> {
616
-
Ok(query_capture_value(
617
-
self.file_capture_index,
618
-
self.quantifier,
619
-
exec.mat,
666
+
Ok(Value::from_nodes(
620
667
exec.graph,
668
+
exec.mat
669
+
.nodes_for_capture_index(self.file_capture_index as u32),
670
+
self.quantifier,
621
671
)
622
672
.into())
623
673
}
···
807
857
locals: &mut shorthand_locals,
808
858
current_regex_captures: exec.current_regex_captures,
809
859
mat: exec.mat,
860
+
full_match_file_capture_index: exec.full_match_file_capture_index,
810
861
store: exec.store,
811
862
scoped_store: exec.scoped_store,
812
863
lazy_graph: exec.lazy_graph,
813
864
function_parameters: exec.function_parameters,
814
865
prev_element_debug_info: exec.prev_element_debug_info,
815
866
error_context: exec.error_context.clone(),
867
+
inherited_variables: exec.inherited_variables,
816
868
shorthands: exec.shorthands,
817
869
cancellation_flag: exec.cancellation_flag,
818
870
};
+163
-93
src/execution/strict.rs
+163
-93
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;
···
44
47
use crate::ast::Variable;
45
48
use crate::execution::error::ExecutionError;
46
49
use crate::execution::error::ResultWithExecutionError;
47
-
use crate::execution::query_capture_value;
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;
···
58
61
use crate::Identifier;
59
62
use crate::Location;
60
63
61
-
use super::error::StatementContext;
62
-
63
64
impl File {
64
65
/// Executes this graph DSL file against a source file, saving the results into an existing
65
66
/// `Graph` instance. You must provide the parsed syntax tree (`tree`) as well as the source
···
82
83
lazy: config.lazy,
83
84
location_attr: config.location_attr.clone(),
84
85
variable_name_attr: config.variable_name_attr.clone(),
86
+
match_node_attr: config.match_node_attr.clone(),
85
87
};
86
88
87
89
let mut locals = VariableMap::new();
88
90
let mut scoped = ScopedVariables::new();
89
91
let current_regex_captures = Vec::new();
90
92
let mut function_parameters = Vec::new();
91
-
let mut cursor = QueryCursor::new();
92
-
for stanza in &self.stanzas {
93
-
cancellation_flag.check("executing stanza")?;
93
+
94
+
self.try_visit_matches_strict(tree, source, |stanza, mat| {
94
95
stanza.execute(
95
-
tree,
96
96
source,
97
+
&mat,
97
98
graph,
98
99
&mut config,
99
100
&mut locals,
100
101
&mut scoped,
101
102
¤t_regex_captures,
102
103
&mut function_parameters,
103
-
&mut cursor,
104
+
&self.inherited_variables,
104
105
&self.shorthands,
105
106
cancellation_flag,
106
-
)?;
107
+
)
108
+
})?;
109
+
110
+
Ok(())
111
+
}
112
+
113
+
pub(super) fn try_visit_matches_strict<'tree, E, F>(
114
+
&self,
115
+
tree: &'tree Tree,
116
+
source: &'tree str,
117
+
mut visit: F,
118
+
) -> Result<(), E>
119
+
where
120
+
F: FnMut(&Stanza, &QueryMatch<'_, 'tree>) -> Result<(), E>,
121
+
{
122
+
for stanza in &self.stanzas {
123
+
stanza.try_visit_matches_strict(tree, source, |mat| visit(stanza, mat))?;
107
124
}
108
125
Ok(())
109
126
}
···
119
136
current_regex_captures: &'a Vec<String>,
120
137
function_parameters: &'a mut Vec<Value>,
121
138
mat: &'a QueryMatch<'a, 'tree>,
139
+
full_match_stanza_capture_index: usize,
122
140
error_context: StatementContext,
141
+
inherited_variables: &'a HashSet<Identifier>,
123
142
shorthands: &'a AttributeShorthands,
124
143
cancellation_flag: &'a dyn CancellationFlag,
125
144
}
126
145
127
146
struct ScopedVariables<'a> {
128
-
scopes: HashMap<SyntaxNodeRef, VariableMap<'a, Value>>,
147
+
scopes: HashMap<SyntaxNodeID, VariableMap<'a, Value>>,
129
148
}
130
149
131
150
impl<'a> ScopedVariables<'a> {
···
135
154
}
136
155
}
137
156
138
-
fn get(&mut self, scope: SyntaxNodeRef) -> &mut VariableMap<'a, Value> {
139
-
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)
140
163
}
141
164
}
142
165
143
166
impl Stanza {
144
167
fn execute<'a, 'g, 'l, 's, 'tree>(
145
168
&self,
146
-
tree: &'tree Tree,
147
169
source: &'tree str,
170
+
mat: &QueryMatch<'_, 'tree>,
148
171
graph: &mut Graph<'tree>,
149
172
config: &ExecutionConfig<'_, 'g>,
150
173
locals: &mut VariableMap<'l, Value>,
151
174
scoped: &mut ScopedVariables<'s>,
152
175
current_regex_captures: &Vec<String>,
153
176
function_parameters: &mut Vec<Value>,
154
-
cursor: &mut QueryCursor,
177
+
inherited_variables: &HashSet<Identifier>,
155
178
shorthands: &AttributeShorthands,
156
179
cancellation_flag: &dyn CancellationFlag,
157
180
) -> Result<(), ExecutionError> {
158
-
let matches = cursor.matches(&self.query, tree.root_node(), source.as_bytes());
159
-
for mat in matches {
160
-
cancellation_flag.check("processing matches")?;
161
-
locals.clear();
162
-
for statement in &self.statements {
163
-
let error_context = {
164
-
let node = mat
165
-
.captures
166
-
.iter()
167
-
.find(|c| c.index as usize == self.full_match_stanza_capture_index)
168
-
.expect("missing capture for full match")
169
-
.node;
170
-
StatementContext::new(&statement, &self, &node)
171
-
};
172
-
let mut exec = ExecutionContext {
173
-
source,
174
-
graph,
175
-
config,
176
-
locals,
177
-
scoped,
178
-
current_regex_captures,
179
-
function_parameters,
180
-
mat: &mat,
181
-
error_context,
182
-
shorthands,
183
-
cancellation_flag,
184
-
};
185
-
statement
186
-
.execute(&mut exec)
187
-
.with_context(|| exec.error_context.into())?;
188
-
}
181
+
locals.clear();
182
+
for statement in &self.statements {
183
+
let error_context = {
184
+
let node = mat
185
+
.nodes_for_capture_index(self.full_match_stanza_capture_index as u32)
186
+
.next()
187
+
.expect("missing full capture");
188
+
StatementContext::new(&statement, &self, &node)
189
+
};
190
+
let mut exec = ExecutionContext {
191
+
source,
192
+
graph,
193
+
config,
194
+
locals,
195
+
scoped,
196
+
current_regex_captures,
197
+
function_parameters,
198
+
mat: &mat,
199
+
full_match_stanza_capture_index: self.full_match_stanza_capture_index,
200
+
error_context,
201
+
inherited_variables,
202
+
shorthands,
203
+
cancellation_flag,
204
+
};
205
+
statement
206
+
.execute(&mut exec)
207
+
.with_context(|| exec.error_context.into())?;
208
+
}
209
+
Ok(())
210
+
}
211
+
212
+
pub(super) fn try_visit_matches_strict<'tree, E, F>(
213
+
&self,
214
+
tree: &'tree Tree,
215
+
source: &'tree str,
216
+
mut visit: F,
217
+
) -> Result<(), E>
218
+
where
219
+
F: FnMut(&QueryMatch<'_, 'tree>) -> Result<(), E>,
220
+
{
221
+
let mut cursor = QueryCursor::new();
222
+
let mut matches = cursor.matches(&self.query, tree.root_node(), source.as_bytes());
223
+
while let Some(mat) = matches.next() {
224
+
visit(mat)?;
189
225
}
190
226
Ok(())
191
227
}
···
197
233
Statement::DeclareImmutable(s) => s.location,
198
234
Statement::DeclareMutable(s) => s.location,
199
235
Statement::Assign(s) => s.location,
236
+
Statement::Expr(s) => s.location,
200
237
Statement::CreateGraphNode(s) => s.location,
201
238
Statement::AddGraphNodeAttribute(s) => s.location,
202
239
Statement::CreateEdge(s) => s.location,
···
214
251
Statement::DeclareImmutable(statement) => statement.execute(exec),
215
252
Statement::DeclareMutable(statement) => statement.execute(exec),
216
253
Statement::Assign(statement) => statement.execute(exec),
254
+
Statement::Expr(statement) => statement.execute(exec),
217
255
Statement::CreateGraphNode(statement) => statement.execute(exec),
218
256
Statement::AddGraphNodeAttribute(statement) => statement.execute(exec),
219
257
Statement::CreateEdge(statement) => statement.execute(exec),
···
244
282
fn execute(&self, exec: &mut ExecutionContext) -> Result<(), ExecutionError> {
245
283
let value = self.value.evaluate(exec)?;
246
284
self.variable.set(exec, value)
285
+
}
286
+
}
287
+
288
+
impl ExpressionStatement {
289
+
fn execute(&self, exec: &mut ExecutionContext) -> Result<(), ExecutionError> {
290
+
self.value.evaluate(exec).map(|_| ())
247
291
}
248
292
}
249
293
···
252
296
let graph_node = exec.graph.add_graph_node();
253
297
self.node
254
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
+
}
255
316
let value = Value::GraphNode(graph_node);
256
317
self.node.add(exec, value, false)
257
318
}
···
282
343
fn execute(&self, exec: &mut ExecutionContext) -> Result<(), ExecutionError> {
283
344
let source = self.source.evaluate(exec)?.into_graph_node_ref()?;
284
345
let sink = self.sink.evaluate(exec)?.into_graph_node_ref()?;
285
-
if let Err(_) = exec.graph[source].add_edge(sink) {
286
-
Err(ExecutionError::DuplicateEdge(format!(
287
-
"({} -> {}) in {}",
288
-
source, sink, self,
289
-
)))?;
290
-
}
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)?;
291
350
Ok(())
292
351
}
293
352
}
···
373
432
current_regex_captures: ¤t_regex_captures,
374
433
function_parameters: exec.function_parameters,
375
434
mat: exec.mat,
435
+
full_match_stanza_capture_index: exec.full_match_stanza_capture_index,
376
436
error_context: exec.error_context.clone(),
437
+
inherited_variables: exec.inherited_variables,
377
438
shorthands: exec.shorthands,
378
439
cancellation_flag: exec.cancellation_flag,
379
440
};
···
432
493
current_regex_captures: exec.current_regex_captures,
433
494
function_parameters: exec.function_parameters,
434
495
mat: exec.mat,
496
+
full_match_stanza_capture_index: exec.full_match_stanza_capture_index,
435
497
error_context: exec.error_context.clone(),
498
+
inherited_variables: exec.inherited_variables,
436
499
shorthands: exec.shorthands,
437
500
cancellation_flag: exec.cancellation_flag,
438
501
};
···
473
536
current_regex_captures: exec.current_regex_captures,
474
537
function_parameters: exec.function_parameters,
475
538
mat: exec.mat,
539
+
full_match_stanza_capture_index: exec.full_match_stanza_capture_index,
476
540
error_context: exec.error_context.clone(),
541
+
inherited_variables: exec.inherited_variables,
477
542
shorthands: exec.shorthands,
478
543
cancellation_flag: exec.cancellation_flag,
479
544
};
···
547
612
current_regex_captures: exec.current_regex_captures,
548
613
function_parameters: exec.function_parameters,
549
614
mat: exec.mat,
615
+
full_match_stanza_capture_index: exec.full_match_stanza_capture_index,
550
616
error_context: exec.error_context.clone(),
617
+
inherited_variables: exec.inherited_variables,
551
618
shorthands: exec.shorthands,
552
619
cancellation_flag: exec.cancellation_flag,
553
620
};
···
586
653
current_regex_captures: exec.current_regex_captures,
587
654
function_parameters: exec.function_parameters,
588
655
mat: exec.mat,
656
+
full_match_stanza_capture_index: exec.full_match_stanza_capture_index,
589
657
error_context: exec.error_context.clone(),
658
+
inherited_variables: exec.inherited_variables,
590
659
shorthands: exec.shorthands,
591
660
cancellation_flag: exec.cancellation_flag,
592
661
};
···
600
669
601
670
impl Capture {
602
671
fn evaluate(&self, exec: &mut ExecutionContext) -> Result<Value, ExecutionError> {
603
-
Ok(query_capture_value(
604
-
self.stanza_capture_index,
605
-
self.quantifier,
606
-
exec.mat,
672
+
Ok(Value::from_nodes(
607
673
exec.graph,
608
-
))
674
+
exec.mat
675
+
.nodes_for_capture_index(self.stanza_capture_index as u32),
676
+
self.quantifier,
677
+
)
678
+
.into())
609
679
}
610
680
}
611
681
···
683
753
)))
684
754
}
685
755
};
686
-
let variables = exec.scoped.get(scope);
687
-
if let Some(value) = variables.get(&self.name) {
688
-
Ok(value)
689
-
} else {
690
-
Err(ExecutionError::UndefinedVariable(format!(
691
-
"{} on node {}",
692
-
self, scope
693
-
)))
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);
694
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
+
)))
695
789
}
696
790
697
791
fn add(
···
710
804
)))
711
805
}
712
806
};
713
-
let variables = exec.scoped.get(scope);
807
+
let variables = exec.scoped.get_mut(scope);
714
808
variables
715
809
.add(self.name.clone(), value, mutable)
716
810
.map_err(|_| ExecutionError::DuplicateVariable(format!("{}", self)))
···
727
821
)))
728
822
}
729
823
};
730
-
let variables = exec.scoped.get(scope);
824
+
let variables = exec.scoped.get_mut(scope);
731
825
variables
732
826
.set(self.name.clone(), value)
733
827
.map_err(|_| ExecutionError::DuplicateVariable(format!("{}", self)))
···
778
872
}
779
873
}
780
874
781
-
impl Variable {
782
-
pub(crate) fn add_debug_attrs(
783
-
&self,
784
-
attributes: &mut Attributes,
785
-
config: &ExecutionConfig,
786
-
) -> Result<(), ExecutionError> {
787
-
if let Some(variable_name_attr) = &config.variable_name_attr {
788
-
attributes
789
-
.add(variable_name_attr.clone(), format!("{}", self))
790
-
.map_err(|_| {
791
-
ExecutionError::DuplicateAttribute(variable_name_attr.as_str().into())
792
-
})?;
793
-
}
794
-
if let Some(location_attr) = &config.location_attr {
795
-
let location = match &self {
796
-
Variable::Scoped(v) => v.location,
797
-
Variable::Unscoped(v) => v.location,
798
-
};
799
-
attributes
800
-
.add(location_attr.clone(), format!("{}", location))
801
-
.map_err(|_| ExecutionError::DuplicateAttribute(location_attr.as_str().into()))?;
802
-
}
803
-
Ok(())
804
-
}
805
-
}
806
-
807
875
impl Attribute {
808
876
fn execute<F>(
809
877
&self,
···
843
911
current_regex_captures: exec.current_regex_captures,
844
912
function_parameters: exec.function_parameters,
845
913
mat: exec.mat,
914
+
full_match_stanza_capture_index: exec.full_match_stanza_capture_index,
846
915
error_context: exec.error_context.clone(),
916
+
inherited_variables: exec.inherited_variables,
847
917
shorthands: exec.shorthands,
848
918
cancellation_flag: exec.cancellation_flag,
849
919
};
+234
-29
src/execution.rs
+234
-29
src/execution.rs
···
7
7
8
8
use thiserror::Error;
9
9
use tree_sitter::CaptureQuantifier;
10
+
use tree_sitter::Node;
10
11
use tree_sitter::QueryMatch;
11
12
use tree_sitter::Tree;
12
13
14
+
use crate::ast::CreateEdge;
13
15
use crate::ast::File;
16
+
use crate::ast::Stanza;
17
+
use crate::ast::Variable;
14
18
use crate::execution::error::ExecutionError;
15
19
use crate::functions::Functions;
20
+
use crate::graph::Attributes;
16
21
use crate::graph::Graph;
17
22
use crate::graph::Value;
18
23
use crate::variables::Globals;
19
24
use crate::Identifier;
25
+
use crate::Location;
20
26
21
27
pub(crate) mod error;
22
28
mod lazy;
···
92
98
}
93
99
94
100
Ok(())
101
+
}
102
+
103
+
pub fn try_visit_matches<'tree, E, F>(
104
+
&self,
105
+
tree: &'tree Tree,
106
+
source: &'tree str,
107
+
lazy: bool,
108
+
mut visit: F,
109
+
) -> Result<(), E>
110
+
where
111
+
F: FnMut(Match<'_, 'tree>) -> Result<(), E>,
112
+
{
113
+
if lazy {
114
+
let file_query = self.query.as_ref().expect("missing file query");
115
+
self.try_visit_matches_lazy(tree, source, |stanza, mat| {
116
+
let named_captures = stanza
117
+
.query
118
+
.capture_names()
119
+
.iter()
120
+
.map(|name| {
121
+
let index = file_query
122
+
.capture_index_for_name(*name)
123
+
.expect("missing index for capture");
124
+
let quantifier =
125
+
file_query.capture_quantifiers(mat.pattern_index)[index as usize];
126
+
(*name, quantifier, index)
127
+
})
128
+
.filter(|c| c.2 != stanza.full_match_file_capture_index as u32)
129
+
.collect();
130
+
visit(Match {
131
+
mat,
132
+
full_capture_index: stanza.full_match_file_capture_index as u32,
133
+
named_captures,
134
+
query_location: stanza.range.start,
135
+
})
136
+
})
137
+
} else {
138
+
self.try_visit_matches_strict(tree, source, |stanza, mat| {
139
+
let named_captures = stanza
140
+
.query
141
+
.capture_names()
142
+
.iter()
143
+
.map(|name| {
144
+
let index = stanza
145
+
.query
146
+
.capture_index_for_name(*name)
147
+
.expect("missing index for capture");
148
+
let quantifier = stanza.query.capture_quantifiers(0)[index as usize];
149
+
(*name, quantifier, index)
150
+
})
151
+
.filter(|c| c.2 != stanza.full_match_stanza_capture_index as u32)
152
+
.collect();
153
+
visit(Match {
154
+
mat,
155
+
full_capture_index: stanza.full_match_stanza_capture_index as u32,
156
+
named_captures,
157
+
query_location: stanza.range.start,
158
+
})
159
+
})
160
+
}
161
+
}
162
+
}
163
+
164
+
impl Stanza {
165
+
pub fn try_visit_matches<'tree, E, F>(
166
+
&self,
167
+
tree: &'tree Tree,
168
+
source: &'tree str,
169
+
mut visit: F,
170
+
) -> Result<(), E>
171
+
where
172
+
F: FnMut(Match<'_, 'tree>) -> Result<(), E>,
173
+
{
174
+
self.try_visit_matches_strict(tree, source, |mat| {
175
+
let named_captures = self
176
+
.query
177
+
.capture_names()
178
+
.iter()
179
+
.map(|name| {
180
+
let index = self
181
+
.query
182
+
.capture_index_for_name(*name)
183
+
.expect("missing index for capture");
184
+
let quantifier = self.query.capture_quantifiers(0)[index as usize];
185
+
(*name, quantifier, index)
186
+
})
187
+
.filter(|c| c.2 != self.full_match_stanza_capture_index as u32)
188
+
.collect();
189
+
visit(Match {
190
+
mat,
191
+
full_capture_index: self.full_match_stanza_capture_index as u32,
192
+
named_captures,
193
+
query_location: self.range.start,
194
+
})
195
+
})
196
+
}
197
+
}
198
+
199
+
pub struct Match<'a, 'tree> {
200
+
mat: &'a QueryMatch<'a, 'tree>,
201
+
full_capture_index: u32,
202
+
named_captures: Vec<(&'a str, CaptureQuantifier, u32)>,
203
+
query_location: Location,
204
+
}
205
+
206
+
impl<'a, 'tree> Match<'a, 'tree> {
207
+
/// Return the top-level matched node.
208
+
pub fn full_capture(&self) -> Node<'tree> {
209
+
self.mat
210
+
.nodes_for_capture_index(self.full_capture_index)
211
+
.next()
212
+
.expect("missing full capture")
213
+
}
214
+
215
+
/// Return the matched nodes for a named capture.
216
+
pub fn named_captures<'s: 'a + 'tree>(
217
+
&'s self,
218
+
) -> impl Iterator<
219
+
Item = (
220
+
&'a str,
221
+
CaptureQuantifier,
222
+
impl Iterator<Item = Node<'tree>> + 's,
223
+
),
224
+
> {
225
+
self.named_captures
226
+
.iter()
227
+
.map(move |c| (c.0, c.1, self.mat.nodes_for_capture_index(c.2)))
228
+
}
229
+
230
+
/// Return the matched nodes for a named capture.
231
+
pub fn named_capture<'s: 'a + 'tree>(
232
+
&'s self,
233
+
name: &str,
234
+
) -> Option<(CaptureQuantifier, impl Iterator<Item = Node<'tree>> + 's)> {
235
+
self.named_captures
236
+
.iter()
237
+
.find(|c| c.0 == name)
238
+
.map(|c| (c.1, self.mat.nodes_for_capture_index(c.2)))
239
+
}
240
+
241
+
/// Return an iterator over all capture names.
242
+
pub fn capture_names(&self) -> impl Iterator<Item = &str> {
243
+
self.named_captures.iter().map(|c| c.0)
244
+
}
245
+
246
+
/// Return the query location.
247
+
pub fn query_location(&self) -> &Location {
248
+
&self.query_location
95
249
}
96
250
}
97
251
···
102
256
pub(crate) lazy: bool,
103
257
pub(crate) location_attr: Option<Identifier>,
104
258
pub(crate) variable_name_attr: Option<Identifier>,
259
+
pub(crate) match_node_attr: Option<Identifier>,
105
260
}
106
261
107
262
impl<'a, 'g> ExecutionConfig<'a, 'g> {
···
112
267
lazy: false,
113
268
location_attr: None,
114
269
variable_name_attr: None,
270
+
match_node_attr: None,
115
271
}
116
272
}
117
273
···
119
275
self,
120
276
location_attr: Identifier,
121
277
variable_name_attr: Identifier,
278
+
match_node_attr: Identifier,
122
279
) -> Self {
123
280
Self {
124
281
functions: self.functions,
···
126
283
lazy: self.lazy,
127
284
location_attr: location_attr.into(),
128
285
variable_name_attr: variable_name_attr.into(),
286
+
match_node_attr: match_node_attr.into(),
129
287
}
130
288
}
131
289
···
136
294
lazy,
137
295
location_attr: self.location_attr,
138
296
variable_name_attr: self.variable_name_attr,
297
+
match_node_attr: self.match_node_attr,
139
298
}
140
299
}
141
300
}
···
156
315
#[error("Cancelled at \"{0}\"")]
157
316
pub struct CancellationError(pub &'static str);
158
317
159
-
/// Get the value for the given capture, considering the suffix
160
-
pub(self) fn query_capture_value<'tree>(
161
-
index: usize,
162
-
quantifier: CaptureQuantifier,
163
-
mat: &QueryMatch<'_, 'tree>,
164
-
graph: &mut Graph<'tree>,
165
-
) -> Value {
166
-
let mut nodes = mat
167
-
.captures
168
-
.iter()
169
-
.filter(|c| c.index as usize == index)
170
-
.map(|c| c.node);
171
-
match quantifier {
172
-
CaptureQuantifier::Zero => unreachable!(),
173
-
CaptureQuantifier::One => {
174
-
let syntax_node = graph.add_syntax_node(nodes.next().expect("missing capture"));
175
-
syntax_node.into()
318
+
impl Value {
319
+
pub fn from_nodes<'tree, NI: IntoIterator<Item = Node<'tree>>>(
320
+
graph: &mut Graph<'tree>,
321
+
nodes: NI,
322
+
quantifier: CaptureQuantifier,
323
+
) -> Value {
324
+
let mut nodes = nodes.into_iter();
325
+
match quantifier {
326
+
CaptureQuantifier::Zero => unreachable!(),
327
+
CaptureQuantifier::One => {
328
+
let syntax_node = graph.add_syntax_node(nodes.next().expect("missing capture"));
329
+
syntax_node.into()
330
+
}
331
+
CaptureQuantifier::ZeroOrMore | CaptureQuantifier::OneOrMore => {
332
+
let syntax_nodes = nodes
333
+
.map(|n| graph.add_syntax_node(n.clone()).into())
334
+
.collect::<Vec<Value>>();
335
+
syntax_nodes.into()
336
+
}
337
+
CaptureQuantifier::ZeroOrOne => match nodes.next() {
338
+
None => Value::Null.into(),
339
+
Some(node) => {
340
+
let syntax_node = graph.add_syntax_node(node);
341
+
syntax_node.into()
342
+
}
343
+
},
344
+
}
345
+
}
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
+
})?;
176
381
}
177
-
CaptureQuantifier::ZeroOrMore | CaptureQuantifier::OneOrMore => {
178
-
let syntax_nodes = nodes
179
-
.map(|n| graph.add_syntax_node(n.clone()).into())
180
-
.collect::<Vec<Value>>();
181
-
syntax_nodes.into()
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()))?;
182
393
}
183
-
CaptureQuantifier::ZeroOrOne => match nodes.next() {
184
-
None => Value::Null.into(),
185
-
Some(node) => {
186
-
let syntax_node = graph.add_syntax_node(node);
187
-
syntax_node.into()
188
-
}
189
-
},
394
+
Ok(())
190
395
}
191
396
}
+1
-1
src/functions.rs
+1
-1
src/functions.rs
+11
-6
src/graph.rs
+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
}
+1
src/lib.rs
+1
src/lib.rs
+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
}
+42
-12
src/parser.rs
+42
-12
src/parser.rs
···
7
7
8
8
use std::fmt::Display;
9
9
use std::iter::Peekable;
10
-
use std::ops::Range;
11
10
use std::path::Path;
12
11
use std::str::Chars;
13
12
···
161
160
}
162
161
}
163
162
164
-
pub(crate) fn to_column_range(&self) -> Range<usize> {
163
+
pub(crate) fn to_column_range(&self) -> std::ops::Range<usize> {
165
164
self.column..self.column + 1
166
165
}
167
166
}
···
169
168
impl Display for Location {
170
169
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
171
170
write!(f, "({}, {})", self.row + 1, self.column + 1)
171
+
}
172
+
}
173
+
174
+
// ----------------------------------------------------------------------------
175
+
// Range
176
+
177
+
/// The range of a graph DSL entity within its file
178
+
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
179
+
pub struct Range {
180
+
pub start: Location,
181
+
pub end: Location,
182
+
}
183
+
184
+
impl Display for Range {
185
+
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
186
+
write!(f, "{} - {}", self.start, self.end)
172
187
}
173
188
}
174
189
···
276
291
fn parse_into_file(&mut self, file: &mut ast::File) -> Result<(), ParseError> {
277
292
self.consume_whitespace();
278
293
while self.try_peek().is_some() {
279
-
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") {
280
299
self.consume_whitespace();
281
300
let global = self.parse_global()?;
282
301
file.globals.push(global);
283
-
} else if let Ok(_) = self.consume_token("attribute") {
302
+
} else if let Ok(_) = self.consume_token("inherit") {
284
303
self.consume_whitespace();
285
-
let shorthand = self.parse_shorthand()?;
286
-
file.shorthands.add(shorthand);
304
+
self.consume_token(".")?;
305
+
let name = self.parse_identifier("inherit")?;
306
+
file.inherited_variables.insert(name);
287
307
} else {
288
-
let stanza = self.parse_stanza(file.language)?;
308
+
let stanza = self.parse_stanza(&file.language)?;
289
309
file.stanzas.push(stanza);
290
310
}
291
311
self.consume_whitespace();
292
312
}
293
313
// we can unwrap here because all queries have already been parsed before
294
-
file.query = Some(Query::new(file.language, &self.query_source).unwrap());
314
+
file.query = Some(Query::new(&file.language, &self.query_source).unwrap());
295
315
Ok(())
296
316
}
297
317
···
349
369
Ok(quantifier)
350
370
}
351
371
352
-
fn parse_stanza(&mut self, language: Language) -> Result<ast::Stanza, ParseError> {
353
-
let location = self.location;
372
+
fn parse_stanza(&mut self, language: &Language) -> Result<ast::Stanza, ParseError> {
373
+
let start = self.location;
354
374
let (query, full_match_stanza_capture_index) = self.parse_query(language)?;
355
375
self.consume_whitespace();
356
376
let statements = self.parse_statements()?;
377
+
let end = self.location;
378
+
let range = Range { start, end };
357
379
Ok(ast::Stanza {
358
380
query,
359
381
statements,
360
382
full_match_stanza_capture_index,
361
383
full_match_file_capture_index: usize::MAX, // set in checker
362
-
location,
384
+
range,
363
385
})
364
386
}
365
387
366
-
fn parse_query(&mut self, language: Language) -> Result<(Query, usize), ParseError> {
388
+
fn parse_query(&mut self, language: &Language) -> Result<(Query, usize), ParseError> {
367
389
let location = self.location;
368
390
let query_start = self.offset;
369
391
self.skip_query()?;
···
460
482
}
461
483
462
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
+
}
463
493
let keyword_location = self.location;
464
494
let keyword = self.parse_name("keyword")?;
465
495
self.consume_whitespace();
+1
-1
src/variables.rs
+1
-1
src/variables.rs
+140
-2
tests/it/execution.rs
+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
+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();
+143
-5
tests/it/lazy_execution.rs
+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
+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
+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
+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
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|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": {