+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
+78
CHANGELOG.md
+78
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
+
8
86
## v0.10.0 -- 2023-05-10
9
87
10
88
### DSL
+20
-44
Cargo.toml
+20
-44
Cargo.toml
···
1
1
[package]
2
2
name = "tree-sitter-graph"
3
-
version = "0.10.0"
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
}
+79
-27
src/execution/lazy.rs
+79
-27
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;
···
25
27
use crate::execution::ExecutionConfig;
26
28
use crate::functions::Functions;
27
29
use crate::graph;
30
+
use crate::graph::Attributes;
28
31
use crate::graph::Graph;
29
32
use crate::graph::Value;
30
33
use crate::variables::Globals;
···
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 = Value::from_nodes(graph, self.full_capture_from_file_match(mat), One);
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
};
···
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
};
+156
-85
src/execution/strict.rs
+156
-85
src/execution/strict.rs
···
7
7
8
8
use std::collections::BTreeSet;
9
9
use std::collections::HashMap;
10
+
use std::collections::HashSet;
11
+
use streaming_iterator::StreamingIterator;
10
12
use tree_sitter::QueryCursor;
11
13
use tree_sitter::QueryMatch;
12
14
use tree_sitter::Tree;
···
14
16
use crate::ast::AddEdgeAttribute;
15
17
use crate::ast::AddGraphNodeAttribute;
16
18
use crate::ast::Assign;
19
+
use crate::ast::ExpressionStatement;
17
20
use crate::ast::Attribute;
18
21
use crate::ast::AttributeShorthand;
19
22
use crate::ast::AttributeShorthands;
···
47
50
use crate::execution::error::StatementContext;
48
51
use crate::execution::CancellationFlag;
49
52
use crate::execution::ExecutionConfig;
50
-
use crate::graph::Attributes;
51
53
use crate::graph::Graph;
54
+
use crate::graph::SyntaxNodeID;
52
55
use crate::graph::SyntaxNodeRef;
53
56
use crate::graph::Value;
54
57
use crate::variables::Globals;
···
80
83
lazy: config.lazy,
81
84
location_attr: config.location_attr.clone(),
82
85
variable_name_attr: config.variable_name_attr.clone(),
86
+
match_node_attr: config.match_node_attr.clone(),
83
87
};
84
88
85
89
let mut locals = VariableMap::new();
86
90
let mut scoped = ScopedVariables::new();
87
91
let current_regex_captures = Vec::new();
88
92
let mut function_parameters = Vec::new();
89
-
let mut cursor = QueryCursor::new();
90
-
for stanza in &self.stanzas {
91
-
cancellation_flag.check("executing stanza")?;
93
+
94
+
self.try_visit_matches_strict(tree, source, |stanza, mat| {
92
95
stanza.execute(
93
-
tree,
94
96
source,
97
+
&mat,
95
98
graph,
96
99
&mut config,
97
100
&mut locals,
98
101
&mut scoped,
99
102
¤t_regex_captures,
100
103
&mut function_parameters,
101
-
&mut cursor,
104
+
&self.inherited_variables,
102
105
&self.shorthands,
103
106
cancellation_flag,
104
-
)?;
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))?;
105
124
}
106
125
Ok(())
107
126
}
···
117
136
current_regex_captures: &'a Vec<String>,
118
137
function_parameters: &'a mut Vec<Value>,
119
138
mat: &'a QueryMatch<'a, 'tree>,
139
+
full_match_stanza_capture_index: usize,
120
140
error_context: StatementContext,
141
+
inherited_variables: &'a HashSet<Identifier>,
121
142
shorthands: &'a AttributeShorthands,
122
143
cancellation_flag: &'a dyn CancellationFlag,
123
144
}
124
145
125
146
struct ScopedVariables<'a> {
126
-
scopes: HashMap<SyntaxNodeRef, VariableMap<'a, Value>>,
147
+
scopes: HashMap<SyntaxNodeID, VariableMap<'a, Value>>,
127
148
}
128
149
129
150
impl<'a> ScopedVariables<'a> {
···
133
154
}
134
155
}
135
156
136
-
fn get(&mut self, scope: SyntaxNodeRef) -> &mut VariableMap<'a, Value> {
137
-
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)
138
163
}
139
164
}
140
165
141
166
impl Stanza {
142
167
fn execute<'a, 'g, 'l, 's, 'tree>(
143
168
&self,
144
-
tree: &'tree Tree,
145
169
source: &'tree str,
170
+
mat: &QueryMatch<'_, 'tree>,
146
171
graph: &mut Graph<'tree>,
147
172
config: &ExecutionConfig<'_, 'g>,
148
173
locals: &mut VariableMap<'l, Value>,
149
174
scoped: &mut ScopedVariables<'s>,
150
175
current_regex_captures: &Vec<String>,
151
176
function_parameters: &mut Vec<Value>,
152
-
cursor: &mut QueryCursor,
177
+
inherited_variables: &HashSet<Identifier>,
153
178
shorthands: &AttributeShorthands,
154
179
cancellation_flag: &dyn CancellationFlag,
155
180
) -> Result<(), ExecutionError> {
156
-
let matches = cursor.matches(&self.query, tree.root_node(), source.as_bytes());
157
-
for mat in matches {
158
-
cancellation_flag.check("processing matches")?;
159
-
locals.clear();
160
-
for statement in &self.statements {
161
-
let error_context = {
162
-
let node = mat
163
-
.captures
164
-
.iter()
165
-
.find(|c| c.index as usize == self.full_match_stanza_capture_index)
166
-
.expect("missing capture for full match")
167
-
.node;
168
-
StatementContext::new(&statement, &self, &node)
169
-
};
170
-
let mut exec = ExecutionContext {
171
-
source,
172
-
graph,
173
-
config,
174
-
locals,
175
-
scoped,
176
-
current_regex_captures,
177
-
function_parameters,
178
-
mat: &mat,
179
-
error_context,
180
-
shorthands,
181
-
cancellation_flag,
182
-
};
183
-
statement
184
-
.execute(&mut exec)
185
-
.with_context(|| exec.error_context.into())?;
186
-
}
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)?;
187
225
}
188
226
Ok(())
189
227
}
···
195
233
Statement::DeclareImmutable(s) => s.location,
196
234
Statement::DeclareMutable(s) => s.location,
197
235
Statement::Assign(s) => s.location,
236
+
Statement::Expr(s) => s.location,
198
237
Statement::CreateGraphNode(s) => s.location,
199
238
Statement::AddGraphNodeAttribute(s) => s.location,
200
239
Statement::CreateEdge(s) => s.location,
···
212
251
Statement::DeclareImmutable(statement) => statement.execute(exec),
213
252
Statement::DeclareMutable(statement) => statement.execute(exec),
214
253
Statement::Assign(statement) => statement.execute(exec),
254
+
Statement::Expr(statement) => statement.execute(exec),
215
255
Statement::CreateGraphNode(statement) => statement.execute(exec),
216
256
Statement::AddGraphNodeAttribute(statement) => statement.execute(exec),
217
257
Statement::CreateEdge(statement) => statement.execute(exec),
···
245
285
}
246
286
}
247
287
288
+
impl ExpressionStatement {
289
+
fn execute(&self, exec: &mut ExecutionContext) -> Result<(), ExecutionError> {
290
+
self.value.evaluate(exec).map(|_| ())
291
+
}
292
+
}
293
+
248
294
impl CreateGraphNode {
249
295
fn execute(&self, exec: &mut ExecutionContext) -> Result<(), ExecutionError> {
250
296
let graph_node = exec.graph.add_graph_node();
251
297
self.node
252
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
+
}
253
316
let value = Value::GraphNode(graph_node);
254
317
self.node.add(exec, value, false)
255
318
}
···
280
343
fn execute(&self, exec: &mut ExecutionContext) -> Result<(), ExecutionError> {
281
344
let source = self.source.evaluate(exec)?.into_graph_node_ref()?;
282
345
let sink = self.sink.evaluate(exec)?.into_graph_node_ref()?;
283
-
if let Err(_) = exec.graph[source].add_edge(sink) {
284
-
Err(ExecutionError::DuplicateEdge(format!(
285
-
"({} -> {}) in {}",
286
-
source, sink, self,
287
-
)))?;
288
-
}
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)?;
289
350
Ok(())
290
351
}
291
352
}
···
371
432
current_regex_captures: ¤t_regex_captures,
372
433
function_parameters: exec.function_parameters,
373
434
mat: exec.mat,
435
+
full_match_stanza_capture_index: exec.full_match_stanza_capture_index,
374
436
error_context: exec.error_context.clone(),
437
+
inherited_variables: exec.inherited_variables,
375
438
shorthands: exec.shorthands,
376
439
cancellation_flag: exec.cancellation_flag,
377
440
};
···
430
493
current_regex_captures: exec.current_regex_captures,
431
494
function_parameters: exec.function_parameters,
432
495
mat: exec.mat,
496
+
full_match_stanza_capture_index: exec.full_match_stanza_capture_index,
433
497
error_context: exec.error_context.clone(),
498
+
inherited_variables: exec.inherited_variables,
434
499
shorthands: exec.shorthands,
435
500
cancellation_flag: exec.cancellation_flag,
436
501
};
···
471
536
current_regex_captures: exec.current_regex_captures,
472
537
function_parameters: exec.function_parameters,
473
538
mat: exec.mat,
539
+
full_match_stanza_capture_index: exec.full_match_stanza_capture_index,
474
540
error_context: exec.error_context.clone(),
541
+
inherited_variables: exec.inherited_variables,
475
542
shorthands: exec.shorthands,
476
543
cancellation_flag: exec.cancellation_flag,
477
544
};
···
545
612
current_regex_captures: exec.current_regex_captures,
546
613
function_parameters: exec.function_parameters,
547
614
mat: exec.mat,
615
+
full_match_stanza_capture_index: exec.full_match_stanza_capture_index,
548
616
error_context: exec.error_context.clone(),
617
+
inherited_variables: exec.inherited_variables,
549
618
shorthands: exec.shorthands,
550
619
cancellation_flag: exec.cancellation_flag,
551
620
};
···
584
653
current_regex_captures: exec.current_regex_captures,
585
654
function_parameters: exec.function_parameters,
586
655
mat: exec.mat,
656
+
full_match_stanza_capture_index: exec.full_match_stanza_capture_index,
587
657
error_context: exec.error_context.clone(),
658
+
inherited_variables: exec.inherited_variables,
588
659
shorthands: exec.shorthands,
589
660
cancellation_flag: exec.cancellation_flag,
590
661
};
···
682
753
)))
683
754
}
684
755
};
685
-
let variables = exec.scoped.get(scope);
686
-
if let Some(value) = variables.get(&self.name) {
687
-
Ok(value)
688
-
} else {
689
-
Err(ExecutionError::UndefinedVariable(format!(
690
-
"{} on node {}",
691
-
self, scope
692
-
)))
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);
693
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
+
)))
694
789
}
695
790
696
791
fn add(
···
709
804
)))
710
805
}
711
806
};
712
-
let variables = exec.scoped.get(scope);
807
+
let variables = exec.scoped.get_mut(scope);
713
808
variables
714
809
.add(self.name.clone(), value, mutable)
715
810
.map_err(|_| ExecutionError::DuplicateVariable(format!("{}", self)))
···
726
821
)))
727
822
}
728
823
};
729
-
let variables = exec.scoped.get(scope);
824
+
let variables = exec.scoped.get_mut(scope);
730
825
variables
731
826
.set(self.name.clone(), value)
732
827
.map_err(|_| ExecutionError::DuplicateVariable(format!("{}", self)))
···
777
872
}
778
873
}
779
874
780
-
impl Variable {
781
-
pub(crate) fn add_debug_attrs(
782
-
&self,
783
-
attributes: &mut Attributes,
784
-
config: &ExecutionConfig,
785
-
) -> Result<(), ExecutionError> {
786
-
if let Some(variable_name_attr) = &config.variable_name_attr {
787
-
attributes
788
-
.add(variable_name_attr.clone(), format!("{}", self))
789
-
.map_err(|_| {
790
-
ExecutionError::DuplicateAttribute(variable_name_attr.as_str().into())
791
-
})?;
792
-
}
793
-
if let Some(location_attr) = &config.location_attr {
794
-
let location = match &self {
795
-
Variable::Scoped(v) => v.location,
796
-
Variable::Unscoped(v) => v.location,
797
-
};
798
-
attributes
799
-
.add(location_attr.clone(), format!("{}", location))
800
-
.map_err(|_| ExecutionError::DuplicateAttribute(location_attr.as_str().into()))?;
801
-
}
802
-
Ok(())
803
-
}
804
-
}
805
-
806
875
impl Attribute {
807
876
fn execute<F>(
808
877
&self,
···
842
911
current_regex_captures: exec.current_regex_captures,
843
912
function_parameters: exec.function_parameters,
844
913
mat: exec.mat,
914
+
full_match_stanza_capture_index: exec.full_match_stanza_capture_index,
845
915
error_context: exec.error_context.clone(),
916
+
inherited_variables: exec.inherited_variables,
846
917
shorthands: exec.shorthands,
847
918
cancellation_flag: exec.cancellation_flag,
848
919
};
+208
-18
src/execution.rs
+208
-18
src/execution.rs
···
11
11
use tree_sitter::QueryMatch;
12
12
use tree_sitter::Tree;
13
13
14
+
use crate::ast::CreateEdge;
14
15
use crate::ast::File;
16
+
use crate::ast::Stanza;
17
+
use crate::ast::Variable;
15
18
use crate::execution::error::ExecutionError;
16
19
use crate::functions::Functions;
20
+
use crate::graph::Attributes;
17
21
use crate::graph::Graph;
18
22
use crate::graph::Value;
19
23
use crate::variables::Globals;
20
24
use crate::Identifier;
25
+
use crate::Location;
21
26
22
27
pub(crate) mod error;
23
28
mod lazy;
···
94
99
95
100
Ok(())
96
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
249
+
}
97
250
}
98
251
99
252
/// Configuration for the execution of a File
···
103
256
pub(crate) lazy: bool,
104
257
pub(crate) location_attr: Option<Identifier>,
105
258
pub(crate) variable_name_attr: Option<Identifier>,
259
+
pub(crate) match_node_attr: Option<Identifier>,
106
260
}
107
261
108
262
impl<'a, 'g> ExecutionConfig<'a, 'g> {
···
113
267
lazy: false,
114
268
location_attr: None,
115
269
variable_name_attr: None,
270
+
match_node_attr: None,
116
271
}
117
272
}
118
273
···
120
275
self,
121
276
location_attr: Identifier,
122
277
variable_name_attr: Identifier,
278
+
match_node_attr: Identifier,
123
279
) -> Self {
124
280
Self {
125
281
functions: self.functions,
···
127
283
lazy: self.lazy,
128
284
location_attr: location_attr.into(),
129
285
variable_name_attr: variable_name_attr.into(),
286
+
match_node_attr: match_node_attr.into(),
130
287
}
131
288
}
132
289
···
137
294
lazy,
138
295
location_attr: self.location_attr,
139
296
variable_name_attr: self.variable_name_attr,
297
+
match_node_attr: self.match_node_attr,
140
298
}
141
299
}
142
300
}
···
157
315
#[error("Cancelled at \"{0}\"")]
158
316
pub struct CancellationError(pub &'static str);
159
317
160
-
impl crate::ast::Stanza {
161
-
/// Return the top-level matched node from a file query match result.
162
-
pub fn full_capture_from_file_match<'a, 'cursor: 'a, 'tree: 'a>(
163
-
&self,
164
-
mat: &'a QueryMatch<'cursor, 'tree>,
165
-
) -> impl Iterator<Item = Node<'tree>> + 'a {
166
-
mat.nodes_for_capture_index(self.full_match_file_capture_index as u32)
167
-
}
168
-
169
-
/// Return the top-level matched node from a stanza query match result.
170
-
pub fn full_capture_from_stanza_match<'a, 'cursor: 'a, 'tree: 'a>(
171
-
&self,
172
-
mat: &'a QueryMatch<'cursor, 'tree>,
173
-
) -> impl Iterator<Item = Node<'tree>> + 'a {
174
-
mat.nodes_for_capture_index(self.full_match_stanza_capture_index as u32)
175
-
}
176
-
}
177
-
178
318
impl Value {
179
319
pub fn from_nodes<'tree, NI: IntoIterator<Item = Node<'tree>>>(
180
320
graph: &mut Graph<'tree>,
···
204
344
}
205
345
}
206
346
}
347
+
348
+
impl CreateEdge {
349
+
pub(crate) fn add_debug_attrs(
350
+
&self,
351
+
attributes: &mut Attributes,
352
+
config: &ExecutionConfig,
353
+
) -> Result<(), ExecutionError> {
354
+
if let Some(location_attr) = &config.location_attr {
355
+
attributes
356
+
.add(
357
+
location_attr.clone(),
358
+
format!(
359
+
"line {} column {}",
360
+
self.location.row + 1,
361
+
self.location.column + 1
362
+
),
363
+
)
364
+
.map_err(|_| ExecutionError::DuplicateAttribute(location_attr.as_str().into()))?;
365
+
}
366
+
Ok(())
367
+
}
368
+
}
369
+
impl Variable {
370
+
pub(crate) fn add_debug_attrs(
371
+
&self,
372
+
attributes: &mut Attributes,
373
+
config: &ExecutionConfig,
374
+
) -> Result<(), ExecutionError> {
375
+
if let Some(variable_name_attr) = &config.variable_name_attr {
376
+
attributes
377
+
.add(variable_name_attr.clone(), format!("{}", self))
378
+
.map_err(|_| {
379
+
ExecutionError::DuplicateAttribute(variable_name_attr.as_str().into())
380
+
})?;
381
+
}
382
+
if let Some(location_attr) = &config.location_attr {
383
+
let location = match &self {
384
+
Variable::Scoped(v) => v.location,
385
+
Variable::Unscoped(v) => v.location,
386
+
};
387
+
attributes
388
+
.add(
389
+
location_attr.clone(),
390
+
format!("line {} column {}", location.row + 1, location.column + 1),
391
+
)
392
+
.map_err(|_| ExecutionError::DuplicateAttribute(location_attr.as_str().into()))?;
393
+
}
394
+
Ok(())
395
+
}
396
+
}
+1
-1
src/functions.rs
+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": {