+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
+29
src/ast.rs
+29
src/ast.rs
···
9
9
10
10
use regex::Regex;
11
11
use std::collections::HashMap;
12
+
use std::collections::HashSet;
12
13
use std::fmt;
13
14
use tree_sitter::CaptureQuantifier;
14
15
use tree_sitter::Language;
···
24
25
pub language: Language,
25
26
/// The expected global variables used in this file
26
27
pub globals: Vec<Global>,
28
+
/// The scoped variables that are inherited by child nodes
29
+
pub inherited_variables: HashSet<Identifier>,
27
30
/// The combined query of all stanzas in the file
28
31
pub query: Option<Query>,
29
32
/// The list of stanzas in the file
···
37
40
File {
38
41
language,
39
42
globals: Vec::new(),
43
+
inherited_variables: HashSet::new(),
40
44
query: None,
41
45
stanzas: Vec::new(),
42
46
shorthands: AttributeShorthands::new(),
···
77
81
DeclareImmutable(DeclareImmutable),
78
82
DeclareMutable(DeclareMutable),
79
83
Assign(Assign),
84
+
Expr(ExpressionStatement),
80
85
// Graph nodes
81
86
CreateGraphNode(CreateGraphNode),
82
87
AddGraphNodeAttribute(AddGraphNodeAttribute),
···
99
104
Self::DeclareImmutable(stmt) => stmt.fmt(f),
100
105
Self::DeclareMutable(stmt) => stmt.fmt(f),
101
106
Self::Assign(stmt) => stmt.fmt(f),
107
+
Self::Expr(stmt) => stmt.fmt(f),
102
108
Self::CreateGraphNode(stmt) => stmt.fmt(f),
103
109
Self::AddGraphNodeAttribute(stmt) => stmt.fmt(f),
104
110
Self::CreateEdge(stmt) => stmt.fmt(f),
···
157
163
write!(f, " {}", attr)?;
158
164
}
159
165
write!(f, " at {}", self.location)
166
+
}
167
+
}
168
+
169
+
/// An `expression` statement whose output is ignored
170
+
#[derive(Debug, Eq, PartialEq)]
171
+
pub struct ExpressionStatement {
172
+
pub value: Expression,
173
+
pub location: Location,
174
+
}
175
+
176
+
impl From<ExpressionStatement> for Statement {
177
+
fn from(statement: ExpressionStatement) -> Statement {
178
+
Statement::Expr(statement)
179
+
}
180
+
}
181
+
182
+
impl std::fmt::Display for ExpressionStatement {
183
+
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
184
+
write!(
185
+
f,
186
+
"{} at {}",
187
+
self.value, self.location,
188
+
)
160
189
}
161
190
}
162
191
+3
-3
src/bin/tree-sitter-graph/main.rs
+3
-3
src/bin/tree-sitter-graph/main.rs
···
89
89
)?;
90
90
}
91
91
92
-
let config = Config::load()?;
92
+
let config = Config::load(None)?;
93
93
let mut loader = Loader::new()?;
94
94
let loader_config = config.get()?;
95
95
loader.find_all_languages(&loader_config)?;
···
98
98
let tsg = std::fs::read(tsg_path)
99
99
.with_context(|| format!("Cannot read TSG file {}", tsg_path.display()))?;
100
100
let tsg = String::from_utf8(tsg)?;
101
-
let file = match File::from_str(language, &tsg) {
101
+
let file = match File::from_str(language.clone(), &tsg) {
102
102
Ok(file) => file,
103
103
Err(err) => {
104
104
eprintln!("{}", err.display_pretty(tsg_path, &tsg));
···
110
110
.with_context(|| format!("Cannot read source file {}", source_path.display()))?;
111
111
let source = String::from_utf8(source)?;
112
112
let mut parser = Parser::new();
113
-
parser.set_language(language)?;
113
+
parser.set_language(&language)?;
114
114
let tree = parser
115
115
.parse(&source, None)
116
116
.ok_or_else(|| anyhow!("Cannot parse {}", source_path.display()))?;
+11
-1
src/checker.rs
+11
-1
src/checker.rs
···
188
188
.expect("capture should have index")
189
189
!= self.full_match_stanza_capture_index as u32
190
190
})
191
-
.map(|cn| Identifier::from(cn.as_str()))
191
+
.map(|cn| Identifier::from(*cn))
192
192
.collect::<HashSet<_>>();
193
193
let unused_captures = all_captures
194
194
.difference(&used_captures)
···
220
220
Self::DeclareImmutable(stmt) => stmt.check(ctx),
221
221
Self::DeclareMutable(stmt) => stmt.check(ctx),
222
222
Self::Assign(stmt) => stmt.check(ctx),
223
+
Self::Expr(stmt) => stmt.check(ctx),
223
224
Self::CreateGraphNode(stmt) => stmt.check(ctx),
224
225
Self::AddGraphNodeAttribute(stmt) => stmt.check(ctx),
225
226
Self::CreateEdge(stmt) => stmt.check(ctx),
···
261
262
used_captures.extend(value.used_captures.iter().cloned());
262
263
let var_result = self.variable.check_set(ctx, value.into())?;
263
264
used_captures.extend(var_result.used_captures);
265
+
Ok(StatementResult { used_captures })
266
+
}
267
+
}
268
+
269
+
impl ast::ExpressionStatement {
270
+
fn check(&mut self, ctx: &mut CheckContext) -> Result<StatementResult, CheckError> {
271
+
let mut used_captures = HashSet::new();
272
+
let value = self.value.check(ctx)?;
273
+
used_captures.extend(value.used_captures.iter().cloned());
264
274
Ok(StatementResult { used_captures })
265
275
}
266
276
}
+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
}
+54
-11
src/execution/lazy.rs
+54
-11
src/execution/lazy.rs
···
12
12
use log::{debug, trace};
13
13
14
14
use std::collections::HashMap;
15
+
use std::collections::HashSet;
15
16
16
17
use tree_sitter::QueryCursor;
17
18
use tree_sitter::QueryMatch;
18
19
use tree_sitter::Tree;
20
+
21
+
use streaming_iterator::StreamingIterator;
19
22
20
23
use crate::ast;
21
24
use crate::execution::error::ExecutionError;
···
24
27
use crate::execution::ExecutionConfig;
25
28
use crate::functions::Functions;
26
29
use crate::graph;
30
+
use crate::graph::Attributes;
27
31
use crate::graph::Graph;
28
32
use crate::graph::Value;
29
33
use crate::variables::Globals;
···
58
62
lazy: config.lazy,
59
63
location_attr: config.location_attr.clone(),
60
64
variable_name_attr: config.variable_name_attr.clone(),
65
+
match_node_attr: config.match_node_attr.clone(),
61
66
};
62
67
63
68
let mut locals = VariableMap::new();
64
69
let mut store = LazyStore::new();
65
70
let mut scoped_store = LazyScopedVariables::new();
66
-
let mut lazy_graph = Vec::new();
71
+
let mut lazy_graph = LazyGraph::new();
67
72
let mut function_parameters = Vec::new();
68
73
let mut prev_element_debug_info = HashMap::new();
69
74
···
80
85
&mut lazy_graph,
81
86
&mut function_parameters,
82
87
&mut prev_element_debug_info,
88
+
&self.inherited_variables,
83
89
&self.shorthands,
84
90
cancellation_flag,
85
91
)
···
91
97
functions: config.functions,
92
98
store: &store,
93
99
scoped_store: &scoped_store,
100
+
inherited_variables: &self.inherited_variables,
94
101
function_parameters: &mut function_parameters,
95
102
prev_element_debug_info: &mut prev_element_debug_info,
96
103
cancellation_flag,
97
104
};
98
-
for graph_stmt in &lazy_graph {
99
-
graph_stmt.evaluate(&mut exec)?;
100
-
}
105
+
lazy_graph.evaluate(&mut exec)?;
101
106
// make sure any unforced values are now forced, to surface any problems
102
107
// hidden by the fact that the values were unused
103
108
store.evaluate_all(&mut exec)?;
···
113
118
mut visit: F,
114
119
) -> Result<(), E>
115
120
where
116
-
F: FnMut(&ast::Stanza, QueryMatch<'_, 'tree>) -> Result<(), E>,
121
+
F: FnMut(&ast::Stanza, &QueryMatch<'_, 'tree>) -> Result<(), E>,
117
122
{
118
123
let mut cursor = QueryCursor::new();
119
124
let query = self.query.as_ref().unwrap();
120
-
let matches = cursor.matches(query, tree.root_node(), source.as_bytes());
121
-
for mat in matches {
125
+
let mut matches = cursor.matches(query, tree.root_node(), source.as_bytes());
126
+
while let Some(mat) = matches.next() {
122
127
let stanza = &self.stanzas[mat.pattern_index];
123
128
visit(stanza, mat)?;
124
129
}
···
134
139
locals: &'a mut dyn MutVariables<LazyValue>,
135
140
current_regex_captures: &'a Vec<String>,
136
141
mat: &'a QueryMatch<'a, 'tree>,
142
+
full_match_file_capture_index: usize,
137
143
store: &'a mut LazyStore,
138
144
scoped_store: &'a mut LazyScopedVariables,
139
-
lazy_graph: &'a mut Vec<LazyStatement>,
145
+
lazy_graph: &'a mut LazyGraph,
140
146
function_parameters: &'a mut Vec<graph::Value>, // re-usable buffer to reduce memory allocations
141
147
prev_element_debug_info: &'a mut HashMap<GraphElementKey, DebugInfo>,
142
148
error_context: StatementContext,
149
+
inherited_variables: &'a HashSet<Identifier>,
143
150
shorthands: &'a ast::AttributeShorthands,
144
151
cancellation_flag: &'a dyn CancellationFlag,
145
152
}
···
151
158
pub functions: &'a Functions,
152
159
pub store: &'a LazyStore,
153
160
pub scoped_store: &'a LazyScopedVariables,
161
+
pub inherited_variables: &'a HashSet<Identifier>,
154
162
pub function_parameters: &'a mut Vec<graph::Value>, // re-usable buffer to reduce memory allocations
155
163
pub prev_element_debug_info: &'a mut HashMap<GraphElementKey, DebugInfo>,
156
164
pub cancellation_flag: &'a dyn CancellationFlag,
···
159
167
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
160
168
pub(super) enum GraphElementKey {
161
169
NodeAttribute(graph::GraphNodeRef, Identifier),
162
-
Edge(graph::GraphNodeRef, graph::GraphNodeRef),
163
170
EdgeAttribute(graph::GraphNodeRef, graph::GraphNodeRef, Identifier),
164
171
}
165
172
···
173
180
locals: &mut VariableMap<'l, LazyValue>,
174
181
store: &mut LazyStore,
175
182
scoped_store: &mut LazyScopedVariables,
176
-
lazy_graph: &mut Vec<LazyStatement>,
183
+
lazy_graph: &mut LazyGraph,
177
184
function_parameters: &mut Vec<graph::Value>,
178
185
prev_element_debug_info: &mut HashMap<GraphElementKey, DebugInfo>,
186
+
inherited_variables: &HashSet<Identifier>,
179
187
shorthands: &ast::AttributeShorthands,
180
188
cancellation_flag: &dyn CancellationFlag,
181
189
) -> Result<(), ExecutionError> {
···
196
204
locals,
197
205
current_regex_captures: ¤t_regex_captures,
198
206
mat,
207
+
full_match_file_capture_index: self.full_match_file_capture_index,
199
208
store,
200
209
scoped_store,
201
210
lazy_graph,
202
211
function_parameters,
203
212
prev_element_debug_info,
204
213
error_context,
214
+
inherited_variables,
205
215
shorthands,
206
216
cancellation_flag,
207
217
};
···
221
231
Self::DeclareImmutable(statement) => statement.execute_lazy(exec),
222
232
Self::DeclareMutable(statement) => statement.execute_lazy(exec),
223
233
Self::Assign(statement) => statement.execute_lazy(exec),
234
+
Self::Expr(statement) => statement.value.evaluate_lazy(exec).map(|_| ()),
224
235
Self::CreateGraphNode(statement) => statement.execute_lazy(exec),
225
236
Self::AddGraphNodeAttribute(statement) => statement.execute_lazy(exec),
226
237
Self::CreateEdge(statement) => statement.execute_lazy(exec),
···
259
270
let graph_node = exec.graph.add_graph_node();
260
271
self.node
261
272
.add_debug_attrs(&mut exec.graph[graph_node].attributes, exec.config)?;
273
+
if let Some(match_node_attr) = &exec.config.match_node_attr {
274
+
let match_node = exec
275
+
.mat
276
+
.nodes_for_capture_index(exec.full_match_file_capture_index as u32)
277
+
.next()
278
+
.expect("missing capture for full match");
279
+
let syn_node = exec.graph.add_syntax_node(match_node);
280
+
exec.graph[graph_node]
281
+
.attributes
282
+
.add(match_node_attr.clone(), syn_node)
283
+
.map_err(|_| {
284
+
ExecutionError::DuplicateAttribute(format!(
285
+
" {} on graph node ({}) in {}",
286
+
match_node_attr, graph_node, self,
287
+
))
288
+
})?;
289
+
}
262
290
self.node.add_lazy(exec, graph_node.into(), false)
263
291
}
264
292
}
···
282
310
fn execute_lazy(&self, exec: &mut ExecutionContext) -> Result<(), ExecutionError> {
283
311
let source = self.source.evaluate_lazy(exec)?;
284
312
let sink = self.sink.evaluate_lazy(exec)?;
285
-
let stmt = LazyCreateEdge::new(source, sink, exec.error_context.clone().into());
313
+
let mut attributes = Attributes::new();
314
+
self.add_debug_attrs(&mut attributes, exec.config)?;
315
+
let stmt = LazyCreateEdge::new(source, sink, attributes, exec.error_context.clone().into());
286
316
exec.lazy_graph.push(stmt.into());
287
317
Ok(())
288
318
}
···
357
387
locals: &mut arm_locals,
358
388
current_regex_captures: ¤t_regex_captures,
359
389
mat: exec.mat,
390
+
full_match_file_capture_index: exec.full_match_file_capture_index,
360
391
store: exec.store,
361
392
scoped_store: exec.scoped_store,
362
393
lazy_graph: exec.lazy_graph,
363
394
function_parameters: exec.function_parameters,
364
395
prev_element_debug_info: exec.prev_element_debug_info,
365
396
error_context: exec.error_context.clone(),
397
+
inherited_variables: exec.inherited_variables,
366
398
shorthands: exec.shorthands,
367
399
cancellation_flag: exec.cancellation_flag,
368
400
};
···
422
454
locals: &mut arm_locals,
423
455
current_regex_captures: exec.current_regex_captures,
424
456
mat: exec.mat,
457
+
full_match_file_capture_index: exec.full_match_file_capture_index,
425
458
store: exec.store,
426
459
scoped_store: exec.scoped_store,
427
460
lazy_graph: exec.lazy_graph,
428
461
function_parameters: exec.function_parameters,
429
462
prev_element_debug_info: exec.prev_element_debug_info,
430
463
error_context: exec.error_context.clone(),
464
+
inherited_variables: exec.inherited_variables,
431
465
shorthands: exec.shorthands,
432
466
cancellation_flag: exec.cancellation_flag,
433
467
};
···
468
502
locals: &mut loop_locals,
469
503
current_regex_captures: exec.current_regex_captures,
470
504
mat: exec.mat,
505
+
full_match_file_capture_index: exec.full_match_file_capture_index,
471
506
store: exec.store,
472
507
scoped_store: exec.scoped_store,
473
508
lazy_graph: exec.lazy_graph,
474
509
function_parameters: exec.function_parameters,
475
510
prev_element_debug_info: exec.prev_element_debug_info,
476
511
error_context: exec.error_context.clone(),
512
+
inherited_variables: exec.inherited_variables,
477
513
shorthands: exec.shorthands,
478
514
cancellation_flag: exec.cancellation_flag,
479
515
};
···
517
553
functions: exec.config.functions,
518
554
store: exec.store,
519
555
scoped_store: exec.scoped_store,
556
+
inherited_variables: exec.inherited_variables,
520
557
function_parameters: exec.function_parameters,
521
558
prev_element_debug_info: exec.prev_element_debug_info,
522
559
cancellation_flag: exec.cancellation_flag,
···
560
597
locals: &mut loop_locals,
561
598
current_regex_captures: exec.current_regex_captures,
562
599
mat: exec.mat,
600
+
full_match_file_capture_index: exec.full_match_file_capture_index,
563
601
store: exec.store,
564
602
scoped_store: exec.scoped_store,
565
603
lazy_graph: exec.lazy_graph,
566
604
function_parameters: exec.function_parameters,
567
605
prev_element_debug_info: exec.prev_element_debug_info,
568
606
error_context: exec.error_context.clone(),
607
+
inherited_variables: exec.inherited_variables,
569
608
shorthands: exec.shorthands,
570
609
cancellation_flag: exec.cancellation_flag,
571
610
};
···
602
641
locals: &mut loop_locals,
603
642
current_regex_captures: exec.current_regex_captures,
604
643
mat: exec.mat,
644
+
full_match_file_capture_index: exec.full_match_file_capture_index,
605
645
store: exec.store,
606
646
scoped_store: exec.scoped_store,
607
647
lazy_graph: exec.lazy_graph,
608
648
function_parameters: exec.function_parameters,
609
649
prev_element_debug_info: exec.prev_element_debug_info,
610
650
error_context: exec.error_context.clone(),
651
+
inherited_variables: exec.inherited_variables,
611
652
shorthands: exec.shorthands,
612
653
cancellation_flag: exec.cancellation_flag,
613
654
};
···
816
857
locals: &mut shorthand_locals,
817
858
current_regex_captures: exec.current_regex_captures,
818
859
mat: exec.mat,
860
+
full_match_file_capture_index: exec.full_match_file_capture_index,
819
861
store: exec.store,
820
862
scoped_store: exec.scoped_store,
821
863
lazy_graph: exec.lazy_graph,
822
864
function_parameters: exec.function_parameters,
823
865
prev_element_debug_info: exec.prev_element_debug_info,
824
866
error_context: exec.error_context.clone(),
867
+
inherited_variables: exec.inherited_variables,
825
868
shorthands: exec.shorthands,
826
869
cancellation_flag: exec.cancellation_flag,
827
870
};
+97
-50
src/execution/strict.rs
+97
-50
src/execution/strict.rs
···
7
7
8
8
use std::collections::BTreeSet;
9
9
use std::collections::HashMap;
10
+
use std::collections::HashSet;
11
+
use streaming_iterator::StreamingIterator;
10
12
use tree_sitter::QueryCursor;
11
13
use tree_sitter::QueryMatch;
12
14
use tree_sitter::Tree;
···
14
16
use crate::ast::AddEdgeAttribute;
15
17
use crate::ast::AddGraphNodeAttribute;
16
18
use crate::ast::Assign;
19
+
use crate::ast::ExpressionStatement;
17
20
use crate::ast::Attribute;
18
21
use crate::ast::AttributeShorthand;
19
22
use crate::ast::AttributeShorthands;
···
47
50
use crate::execution::error::StatementContext;
48
51
use crate::execution::CancellationFlag;
49
52
use crate::execution::ExecutionConfig;
50
-
use crate::graph::Attributes;
51
53
use crate::graph::Graph;
54
+
use crate::graph::SyntaxNodeID;
52
55
use crate::graph::SyntaxNodeRef;
53
56
use crate::graph::Value;
54
57
use crate::variables::Globals;
···
80
83
lazy: config.lazy,
81
84
location_attr: config.location_attr.clone(),
82
85
variable_name_attr: config.variable_name_attr.clone(),
86
+
match_node_attr: config.match_node_attr.clone(),
83
87
};
84
88
85
89
let mut locals = VariableMap::new();
···
97
101
&mut scoped,
98
102
¤t_regex_captures,
99
103
&mut function_parameters,
104
+
&self.inherited_variables,
100
105
&self.shorthands,
101
106
cancellation_flag,
102
107
)
···
112
117
mut visit: F,
113
118
) -> Result<(), E>
114
119
where
115
-
F: FnMut(&Stanza, QueryMatch<'_, 'tree>) -> Result<(), E>,
120
+
F: FnMut(&Stanza, &QueryMatch<'_, 'tree>) -> Result<(), E>,
116
121
{
117
122
for stanza in &self.stanzas {
118
123
stanza.try_visit_matches_strict(tree, source, |mat| visit(stanza, mat))?;
···
131
136
current_regex_captures: &'a Vec<String>,
132
137
function_parameters: &'a mut Vec<Value>,
133
138
mat: &'a QueryMatch<'a, 'tree>,
139
+
full_match_stanza_capture_index: usize,
134
140
error_context: StatementContext,
141
+
inherited_variables: &'a HashSet<Identifier>,
135
142
shorthands: &'a AttributeShorthands,
136
143
cancellation_flag: &'a dyn CancellationFlag,
137
144
}
138
145
139
146
struct ScopedVariables<'a> {
140
-
scopes: HashMap<SyntaxNodeRef, VariableMap<'a, Value>>,
147
+
scopes: HashMap<SyntaxNodeID, VariableMap<'a, Value>>,
141
148
}
142
149
143
150
impl<'a> ScopedVariables<'a> {
···
147
154
}
148
155
}
149
156
150
-
fn get(&mut self, scope: SyntaxNodeRef) -> &mut VariableMap<'a, Value> {
151
-
self.scopes.entry(scope).or_insert(VariableMap::new())
157
+
fn get_mut(&mut self, scope: SyntaxNodeRef) -> &mut VariableMap<'a, Value> {
158
+
self.scopes.entry(scope.index).or_insert(VariableMap::new())
159
+
}
160
+
161
+
fn try_get(&self, index: SyntaxNodeID) -> Option<&VariableMap<'a, Value>> {
162
+
self.scopes.get(&index)
152
163
}
153
164
}
154
165
···
163
174
scoped: &mut ScopedVariables<'s>,
164
175
current_regex_captures: &Vec<String>,
165
176
function_parameters: &mut Vec<Value>,
177
+
inherited_variables: &HashSet<Identifier>,
166
178
shorthands: &AttributeShorthands,
167
179
cancellation_flag: &dyn CancellationFlag,
168
180
) -> Result<(), ExecutionError> {
···
184
196
current_regex_captures,
185
197
function_parameters,
186
198
mat: &mat,
199
+
full_match_stanza_capture_index: self.full_match_stanza_capture_index,
187
200
error_context,
201
+
inherited_variables,
188
202
shorthands,
189
203
cancellation_flag,
190
204
};
···
202
216
mut visit: F,
203
217
) -> Result<(), E>
204
218
where
205
-
F: FnMut(QueryMatch<'_, 'tree>) -> Result<(), E>,
219
+
F: FnMut(&QueryMatch<'_, 'tree>) -> Result<(), E>,
206
220
{
207
221
let mut cursor = QueryCursor::new();
208
-
let matches = cursor.matches(&self.query, tree.root_node(), source.as_bytes());
209
-
for mat in matches {
222
+
let mut matches = cursor.matches(&self.query, tree.root_node(), source.as_bytes());
223
+
while let Some(mat) = matches.next() {
210
224
visit(mat)?;
211
225
}
212
226
Ok(())
···
219
233
Statement::DeclareImmutable(s) => s.location,
220
234
Statement::DeclareMutable(s) => s.location,
221
235
Statement::Assign(s) => s.location,
236
+
Statement::Expr(s) => s.location,
222
237
Statement::CreateGraphNode(s) => s.location,
223
238
Statement::AddGraphNodeAttribute(s) => s.location,
224
239
Statement::CreateEdge(s) => s.location,
···
236
251
Statement::DeclareImmutable(statement) => statement.execute(exec),
237
252
Statement::DeclareMutable(statement) => statement.execute(exec),
238
253
Statement::Assign(statement) => statement.execute(exec),
254
+
Statement::Expr(statement) => statement.execute(exec),
239
255
Statement::CreateGraphNode(statement) => statement.execute(exec),
240
256
Statement::AddGraphNodeAttribute(statement) => statement.execute(exec),
241
257
Statement::CreateEdge(statement) => statement.execute(exec),
···
269
285
}
270
286
}
271
287
288
+
impl ExpressionStatement {
289
+
fn execute(&self, exec: &mut ExecutionContext) -> Result<(), ExecutionError> {
290
+
self.value.evaluate(exec).map(|_| ())
291
+
}
292
+
}
293
+
272
294
impl CreateGraphNode {
273
295
fn execute(&self, exec: &mut ExecutionContext) -> Result<(), ExecutionError> {
274
296
let graph_node = exec.graph.add_graph_node();
275
297
self.node
276
298
.add_debug_attrs(&mut exec.graph[graph_node].attributes, exec.config)?;
299
+
if let Some(match_node_attr) = &exec.config.match_node_attr {
300
+
let match_node = exec
301
+
.mat
302
+
.nodes_for_capture_index(exec.full_match_stanza_capture_index as u32)
303
+
.next()
304
+
.expect("missing capture for full match");
305
+
let syn_node = exec.graph.add_syntax_node(match_node);
306
+
exec.graph[graph_node]
307
+
.attributes
308
+
.add(match_node_attr.clone(), syn_node)
309
+
.map_err(|_| {
310
+
ExecutionError::DuplicateAttribute(format!(
311
+
" {} on graph node ({}) in {}",
312
+
match_node_attr, graph_node, self,
313
+
))
314
+
})?;
315
+
}
277
316
let value = Value::GraphNode(graph_node);
278
317
self.node.add(exec, value, false)
279
318
}
···
304
343
fn execute(&self, exec: &mut ExecutionContext) -> Result<(), ExecutionError> {
305
344
let source = self.source.evaluate(exec)?.into_graph_node_ref()?;
306
345
let sink = self.sink.evaluate(exec)?.into_graph_node_ref()?;
307
-
if let Err(_) = exec.graph[source].add_edge(sink) {
308
-
Err(ExecutionError::DuplicateEdge(format!(
309
-
"({} -> {}) in {}",
310
-
source, sink, self,
311
-
)))?;
312
-
}
346
+
let edge = match exec.graph[source].add_edge(sink) {
347
+
Ok(edge) | Err(edge) => edge,
348
+
};
349
+
self.add_debug_attrs(&mut edge.attributes, exec.config)?;
313
350
Ok(())
314
351
}
315
352
}
···
395
432
current_regex_captures: ¤t_regex_captures,
396
433
function_parameters: exec.function_parameters,
397
434
mat: exec.mat,
435
+
full_match_stanza_capture_index: exec.full_match_stanza_capture_index,
398
436
error_context: exec.error_context.clone(),
437
+
inherited_variables: exec.inherited_variables,
399
438
shorthands: exec.shorthands,
400
439
cancellation_flag: exec.cancellation_flag,
401
440
};
···
454
493
current_regex_captures: exec.current_regex_captures,
455
494
function_parameters: exec.function_parameters,
456
495
mat: exec.mat,
496
+
full_match_stanza_capture_index: exec.full_match_stanza_capture_index,
457
497
error_context: exec.error_context.clone(),
498
+
inherited_variables: exec.inherited_variables,
458
499
shorthands: exec.shorthands,
459
500
cancellation_flag: exec.cancellation_flag,
460
501
};
···
495
536
current_regex_captures: exec.current_regex_captures,
496
537
function_parameters: exec.function_parameters,
497
538
mat: exec.mat,
539
+
full_match_stanza_capture_index: exec.full_match_stanza_capture_index,
498
540
error_context: exec.error_context.clone(),
541
+
inherited_variables: exec.inherited_variables,
499
542
shorthands: exec.shorthands,
500
543
cancellation_flag: exec.cancellation_flag,
501
544
};
···
569
612
current_regex_captures: exec.current_regex_captures,
570
613
function_parameters: exec.function_parameters,
571
614
mat: exec.mat,
615
+
full_match_stanza_capture_index: exec.full_match_stanza_capture_index,
572
616
error_context: exec.error_context.clone(),
617
+
inherited_variables: exec.inherited_variables,
573
618
shorthands: exec.shorthands,
574
619
cancellation_flag: exec.cancellation_flag,
575
620
};
···
608
653
current_regex_captures: exec.current_regex_captures,
609
654
function_parameters: exec.function_parameters,
610
655
mat: exec.mat,
656
+
full_match_stanza_capture_index: exec.full_match_stanza_capture_index,
611
657
error_context: exec.error_context.clone(),
658
+
inherited_variables: exec.inherited_variables,
612
659
shorthands: exec.shorthands,
613
660
cancellation_flag: exec.cancellation_flag,
614
661
};
···
706
753
)))
707
754
}
708
755
};
709
-
let variables = exec.scoped.get(scope);
710
-
if let Some(value) = variables.get(&self.name) {
711
-
Ok(value)
712
-
} else {
713
-
Err(ExecutionError::UndefinedVariable(format!(
714
-
"{} on node {}",
715
-
self, scope
716
-
)))
756
+
757
+
// search this node
758
+
if let Some(value) = exec
759
+
.scoped
760
+
.try_get(scope.index)
761
+
.and_then(|v| v.get(&self.name))
762
+
{
763
+
return Ok(value);
717
764
}
765
+
766
+
// search parent nodes
767
+
if exec.inherited_variables.contains(&self.name) {
768
+
let mut parent = exec
769
+
.graph
770
+
.syntax_nodes
771
+
.get(&scope.index)
772
+
.and_then(|n| n.parent());
773
+
while let Some(scope) = parent {
774
+
if let Some(value) = exec
775
+
.scoped
776
+
.try_get(scope.id() as u32)
777
+
.and_then(|v| v.get(&self.name))
778
+
{
779
+
return Ok(value);
780
+
}
781
+
parent = scope.parent();
782
+
}
783
+
}
784
+
785
+
Err(ExecutionError::UndefinedVariable(format!(
786
+
"{} on node {}",
787
+
self, scope
788
+
)))
718
789
}
719
790
720
791
fn add(
···
733
804
)))
734
805
}
735
806
};
736
-
let variables = exec.scoped.get(scope);
807
+
let variables = exec.scoped.get_mut(scope);
737
808
variables
738
809
.add(self.name.clone(), value, mutable)
739
810
.map_err(|_| ExecutionError::DuplicateVariable(format!("{}", self)))
···
750
821
)))
751
822
}
752
823
};
753
-
let variables = exec.scoped.get(scope);
824
+
let variables = exec.scoped.get_mut(scope);
754
825
variables
755
826
.set(self.name.clone(), value)
756
827
.map_err(|_| ExecutionError::DuplicateVariable(format!("{}", self)))
···
801
872
}
802
873
}
803
874
804
-
impl Variable {
805
-
pub(crate) fn add_debug_attrs(
806
-
&self,
807
-
attributes: &mut Attributes,
808
-
config: &ExecutionConfig,
809
-
) -> Result<(), ExecutionError> {
810
-
if let Some(variable_name_attr) = &config.variable_name_attr {
811
-
attributes
812
-
.add(variable_name_attr.clone(), format!("{}", self))
813
-
.map_err(|_| {
814
-
ExecutionError::DuplicateAttribute(variable_name_attr.as_str().into())
815
-
})?;
816
-
}
817
-
if let Some(location_attr) = &config.location_attr {
818
-
let location = match &self {
819
-
Variable::Scoped(v) => v.location,
820
-
Variable::Unscoped(v) => v.location,
821
-
};
822
-
attributes
823
-
.add(location_attr.clone(), format!("{}", location))
824
-
.map_err(|_| ExecutionError::DuplicateAttribute(location_attr.as_str().into()))?;
825
-
}
826
-
Ok(())
827
-
}
828
-
}
829
-
830
875
impl Attribute {
831
876
fn execute<F>(
832
877
&self,
···
866
911
current_regex_captures: exec.current_regex_captures,
867
912
function_parameters: exec.function_parameters,
868
913
mat: exec.mat,
914
+
full_match_stanza_capture_index: exec.full_match_stanza_capture_index,
869
915
error_context: exec.error_context.clone(),
916
+
inherited_variables: exec.inherited_variables,
870
917
shorthands: exec.shorthands,
871
918
cancellation_flag: exec.cancellation_flag,
872
919
};
+84
-16
src/execution.rs
+84
-16
src/execution.rs
···
11
11
use tree_sitter::QueryMatch;
12
12
use tree_sitter::Tree;
13
13
14
+
use crate::ast::CreateEdge;
14
15
use crate::ast::File;
15
16
use crate::ast::Stanza;
17
+
use crate::ast::Variable;
16
18
use crate::execution::error::ExecutionError;
17
19
use crate::functions::Functions;
20
+
use crate::graph::Attributes;
18
21
use crate::graph::Graph;
19
22
use crate::graph::Value;
20
23
use crate::variables::Globals;
···
116
119
.iter()
117
120
.map(|name| {
118
121
let index = file_query
119
-
.capture_index_for_name(name)
122
+
.capture_index_for_name(*name)
120
123
.expect("missing index for capture");
121
-
(name, index)
124
+
let quantifier =
125
+
file_query.capture_quantifiers(mat.pattern_index)[index as usize];
126
+
(*name, quantifier, index)
122
127
})
123
-
.filter(|c| c.1 != stanza.full_match_file_capture_index as u32)
128
+
.filter(|c| c.2 != stanza.full_match_file_capture_index as u32)
124
129
.collect();
125
130
visit(Match {
126
131
mat,
···
138
143
.map(|name| {
139
144
let index = stanza
140
145
.query
141
-
.capture_index_for_name(name)
146
+
.capture_index_for_name(*name)
142
147
.expect("missing index for capture");
143
-
(name, index)
148
+
let quantifier = stanza.query.capture_quantifiers(0)[index as usize];
149
+
(*name, quantifier, index)
144
150
})
145
-
.filter(|c| c.1 != stanza.full_match_stanza_capture_index as u32)
151
+
.filter(|c| c.2 != stanza.full_match_stanza_capture_index as u32)
146
152
.collect();
147
153
visit(Match {
148
154
mat,
···
173
179
.map(|name| {
174
180
let index = self
175
181
.query
176
-
.capture_index_for_name(name)
182
+
.capture_index_for_name(*name)
177
183
.expect("missing index for capture");
178
-
(name, index)
184
+
let quantifier = self.query.capture_quantifiers(0)[index as usize];
185
+
(*name, quantifier, index)
179
186
})
180
-
.filter(|c| c.1 != self.full_match_stanza_capture_index as u32)
187
+
.filter(|c| c.2 != self.full_match_stanza_capture_index as u32)
181
188
.collect();
182
189
visit(Match {
183
190
mat,
···
190
197
}
191
198
192
199
pub struct Match<'a, 'tree> {
193
-
mat: QueryMatch<'a, 'tree>,
200
+
mat: &'a QueryMatch<'a, 'tree>,
194
201
full_capture_index: u32,
195
-
named_captures: Vec<(&'a String, u32)>,
202
+
named_captures: Vec<(&'a str, CaptureQuantifier, u32)>,
196
203
query_location: Location,
197
204
}
198
205
···
208
215
/// Return the matched nodes for a named capture.
209
216
pub fn named_captures<'s: 'a + 'tree>(
210
217
&'s self,
211
-
) -> impl Iterator<Item = (&String, impl Iterator<Item = Node<'tree>> + 's)> {
218
+
) -> impl Iterator<
219
+
Item = (
220
+
&'a str,
221
+
CaptureQuantifier,
222
+
impl Iterator<Item = Node<'tree>> + 's,
223
+
),
224
+
> {
212
225
self.named_captures
213
226
.iter()
214
-
.map(move |c| (c.0, self.mat.nodes_for_capture_index(c.1)))
227
+
.map(move |c| (c.0, c.1, self.mat.nodes_for_capture_index(c.2)))
215
228
}
216
229
217
230
/// Return the matched nodes for a named capture.
218
231
pub fn named_capture<'s: 'a + 'tree>(
219
232
&'s self,
220
233
name: &str,
221
-
) -> Option<impl Iterator<Item = Node<'tree>> + 's> {
234
+
) -> Option<(CaptureQuantifier, impl Iterator<Item = Node<'tree>> + 's)> {
222
235
self.named_captures
223
236
.iter()
224
237
.find(|c| c.0 == name)
225
-
.map(|c| self.mat.nodes_for_capture_index(c.1))
238
+
.map(|c| (c.1, self.mat.nodes_for_capture_index(c.2)))
226
239
}
227
240
228
241
/// Return an iterator over all capture names.
229
-
pub fn capture_names(&self) -> impl Iterator<Item = &String> {
242
+
pub fn capture_names(&self) -> impl Iterator<Item = &str> {
230
243
self.named_captures.iter().map(|c| c.0)
231
244
}
232
245
···
243
256
pub(crate) lazy: bool,
244
257
pub(crate) location_attr: Option<Identifier>,
245
258
pub(crate) variable_name_attr: Option<Identifier>,
259
+
pub(crate) match_node_attr: Option<Identifier>,
246
260
}
247
261
248
262
impl<'a, 'g> ExecutionConfig<'a, 'g> {
···
253
267
lazy: false,
254
268
location_attr: None,
255
269
variable_name_attr: None,
270
+
match_node_attr: None,
256
271
}
257
272
}
258
273
···
260
275
self,
261
276
location_attr: Identifier,
262
277
variable_name_attr: Identifier,
278
+
match_node_attr: Identifier,
263
279
) -> Self {
264
280
Self {
265
281
functions: self.functions,
···
267
283
lazy: self.lazy,
268
284
location_attr: location_attr.into(),
269
285
variable_name_attr: variable_name_attr.into(),
286
+
match_node_attr: match_node_attr.into(),
270
287
}
271
288
}
272
289
···
277
294
lazy,
278
295
location_attr: self.location_attr,
279
296
variable_name_attr: self.variable_name_attr,
297
+
match_node_attr: self.match_node_attr,
280
298
}
281
299
}
282
300
}
···
326
344
}
327
345
}
328
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
}
+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
}
+21
-8
src/parser.rs
+21
-8
src/parser.rs
···
291
291
fn parse_into_file(&mut self, file: &mut ast::File) -> Result<(), ParseError> {
292
292
self.consume_whitespace();
293
293
while self.try_peek().is_some() {
294
-
if let Ok(_) = self.consume_token("global") {
294
+
if let Ok(_) = self.consume_token("attribute") {
295
+
self.consume_whitespace();
296
+
let shorthand = self.parse_shorthand()?;
297
+
file.shorthands.add(shorthand);
298
+
} else if let Ok(_) = self.consume_token("global") {
295
299
self.consume_whitespace();
296
300
let global = self.parse_global()?;
297
301
file.globals.push(global);
298
-
} else if let Ok(_) = self.consume_token("attribute") {
302
+
} else if let Ok(_) = self.consume_token("inherit") {
299
303
self.consume_whitespace();
300
-
let shorthand = self.parse_shorthand()?;
301
-
file.shorthands.add(shorthand);
304
+
self.consume_token(".")?;
305
+
let name = self.parse_identifier("inherit")?;
306
+
file.inherited_variables.insert(name);
302
307
} else {
303
-
let stanza = self.parse_stanza(file.language)?;
308
+
let stanza = self.parse_stanza(&file.language)?;
304
309
file.stanzas.push(stanza);
305
310
}
306
311
self.consume_whitespace();
307
312
}
308
313
// we can unwrap here because all queries have already been parsed before
309
-
file.query = Some(Query::new(file.language, &self.query_source).unwrap());
314
+
file.query = Some(Query::new(&file.language, &self.query_source).unwrap());
310
315
Ok(())
311
316
}
312
317
···
364
369
Ok(quantifier)
365
370
}
366
371
367
-
fn parse_stanza(&mut self, language: Language) -> Result<ast::Stanza, ParseError> {
372
+
fn parse_stanza(&mut self, language: &Language) -> Result<ast::Stanza, ParseError> {
368
373
let start = self.location;
369
374
let (query, full_match_stanza_capture_index) = self.parse_query(language)?;
370
375
self.consume_whitespace();
···
380
385
})
381
386
}
382
387
383
-
fn parse_query(&mut self, language: Language) -> Result<(Query, usize), ParseError> {
388
+
fn parse_query(&mut self, language: &Language) -> Result<(Query, usize), ParseError> {
384
389
let location = self.location;
385
390
let query_start = self.offset;
386
391
self.skip_query()?;
···
477
482
}
478
483
479
484
fn parse_statement(&mut self) -> Result<ast::Statement, ParseError> {
485
+
if matches!(self.peek(), Ok( '#' | '"' | '@' | '$' | '(' | '[' | '{')) {
486
+
if let Ok(value) = self.parse_expression() {
487
+
return Ok(ast::Statement::Expr(ast::ExpressionStatement {
488
+
location: self.location,
489
+
value,
490
+
}));
491
+
}
492
+
}
480
493
let keyword_location = self.location;
481
494
let keyword = self.parse_name("keyword")?;
482
495
self.consume_whitespace();
+1
-1
src/variables.rs
+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": {