+8
CHANGELOG.md
+8
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.10.3 -- 2023-06-01
9
+
10
+
### DSL
11
+
12
+
#### Added
13
+
14
+
- 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.
15
+
8
16
## v0.10.2 -- 2023-05-25
9
17
10
18
### Library
+1
-1
Cargo.toml
+1
-1
Cargo.toml
+4
src/ast.rs
+4
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(),
+14
src/execution/lazy.rs
+14
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;
···
81
82
&mut lazy_graph,
82
83
&mut function_parameters,
83
84
&mut prev_element_debug_info,
85
+
&self.inherited_variables,
84
86
&self.shorthands,
85
87
cancellation_flag,
86
88
)
···
92
94
functions: config.functions,
93
95
store: &store,
94
96
scoped_store: &scoped_store,
97
+
inherited_variables: &self.inherited_variables,
95
98
function_parameters: &mut function_parameters,
96
99
prev_element_debug_info: &mut prev_element_debug_info,
97
100
cancellation_flag,
···
141
144
function_parameters: &'a mut Vec<graph::Value>, // re-usable buffer to reduce memory allocations
142
145
prev_element_debug_info: &'a mut HashMap<GraphElementKey, DebugInfo>,
143
146
error_context: StatementContext,
147
+
inherited_variables: &'a HashSet<Identifier>,
144
148
shorthands: &'a ast::AttributeShorthands,
145
149
cancellation_flag: &'a dyn CancellationFlag,
146
150
}
···
152
156
pub functions: &'a Functions,
153
157
pub store: &'a LazyStore,
154
158
pub scoped_store: &'a LazyScopedVariables,
159
+
pub inherited_variables: &'a HashSet<Identifier>,
155
160
pub function_parameters: &'a mut Vec<graph::Value>, // re-usable buffer to reduce memory allocations
156
161
pub prev_element_debug_info: &'a mut HashMap<GraphElementKey, DebugInfo>,
157
162
pub cancellation_flag: &'a dyn CancellationFlag,
···
177
182
lazy_graph: &mut Vec<LazyStatement>,
178
183
function_parameters: &mut Vec<graph::Value>,
179
184
prev_element_debug_info: &mut HashMap<GraphElementKey, DebugInfo>,
185
+
inherited_variables: &HashSet<Identifier>,
180
186
shorthands: &ast::AttributeShorthands,
181
187
cancellation_flag: &dyn CancellationFlag,
182
188
) -> Result<(), ExecutionError> {
···
203
209
function_parameters,
204
210
prev_element_debug_info,
205
211
error_context,
212
+
inherited_variables,
206
213
shorthands,
207
214
cancellation_flag,
208
215
};
···
366
373
function_parameters: exec.function_parameters,
367
374
prev_element_debug_info: exec.prev_element_debug_info,
368
375
error_context: exec.error_context.clone(),
376
+
inherited_variables: exec.inherited_variables,
369
377
shorthands: exec.shorthands,
370
378
cancellation_flag: exec.cancellation_flag,
371
379
};
···
431
439
function_parameters: exec.function_parameters,
432
440
prev_element_debug_info: exec.prev_element_debug_info,
433
441
error_context: exec.error_context.clone(),
442
+
inherited_variables: exec.inherited_variables,
434
443
shorthands: exec.shorthands,
435
444
cancellation_flag: exec.cancellation_flag,
436
445
};
···
477
486
function_parameters: exec.function_parameters,
478
487
prev_element_debug_info: exec.prev_element_debug_info,
479
488
error_context: exec.error_context.clone(),
489
+
inherited_variables: exec.inherited_variables,
480
490
shorthands: exec.shorthands,
481
491
cancellation_flag: exec.cancellation_flag,
482
492
};
···
520
530
functions: exec.config.functions,
521
531
store: exec.store,
522
532
scoped_store: exec.scoped_store,
533
+
inherited_variables: exec.inherited_variables,
523
534
function_parameters: exec.function_parameters,
524
535
prev_element_debug_info: exec.prev_element_debug_info,
525
536
cancellation_flag: exec.cancellation_flag,
···
569
580
function_parameters: exec.function_parameters,
570
581
prev_element_debug_info: exec.prev_element_debug_info,
571
582
error_context: exec.error_context.clone(),
583
+
inherited_variables: exec.inherited_variables,
572
584
shorthands: exec.shorthands,
573
585
cancellation_flag: exec.cancellation_flag,
574
586
};
···
611
623
function_parameters: exec.function_parameters,
612
624
prev_element_debug_info: exec.prev_element_debug_info,
613
625
error_context: exec.error_context.clone(),
626
+
inherited_variables: exec.inherited_variables,
614
627
shorthands: exec.shorthands,
615
628
cancellation_flag: exec.cancellation_flag,
616
629
};
···
825
838
function_parameters: exec.function_parameters,
826
839
prev_element_debug_info: exec.prev_element_debug_info,
827
840
error_context: exec.error_context.clone(),
841
+
inherited_variables: exec.inherited_variables,
828
842
shorthands: exec.shorthands,
829
843
cancellation_flag: exec.cancellation_flag,
830
844
};
+25
-11
src/execution/lazy/store.rs
+25
-11
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
196
let mut map = HashMap::new();
···
187
201
.with_context(|| format!("Evaluating scope of variable _.{}", name,).into())
188
202
.with_context(|| debug_info.0.clone().into())?;
189
203
let prev_debug_info = debug_infos.insert(node, debug_info.clone());
190
-
match map.insert(node, value.clone()) {
204
+
match map.insert(node.index, value.clone()) {
191
205
Some(_) => {
192
206
return Err(ExecutionError::DuplicateVariable(format!(
193
207
"{}.{}",
···
211
225
enum ScopedValues {
212
226
Unforced(Vec<(LazyValue, LazyValue, DebugInfo)>),
213
227
Forcing,
214
-
Forced(HashMap<SyntaxNodeRef, LazyValue>),
228
+
Forced(HashMap<SyntaxNodeID, LazyValue>),
215
229
}
216
230
217
231
impl ScopedValues {
+53
-13
src/execution/strict.rs
+53
-13
src/execution/strict.rs
···
7
7
8
8
use std::collections::BTreeSet;
9
9
use std::collections::HashMap;
10
+
use std::collections::HashSet;
10
11
use tree_sitter::QueryCursor;
11
12
use tree_sitter::QueryMatch;
12
13
use tree_sitter::Tree;
···
48
49
use crate::execution::CancellationFlag;
49
50
use crate::execution::ExecutionConfig;
50
51
use crate::graph::Graph;
52
+
use crate::graph::SyntaxNodeID;
51
53
use crate::graph::SyntaxNodeRef;
52
54
use crate::graph::Value;
53
55
use crate::variables::Globals;
···
96
98
&mut scoped,
97
99
¤t_regex_captures,
98
100
&mut function_parameters,
101
+
&self.inherited_variables,
99
102
&self.shorthands,
100
103
cancellation_flag,
101
104
)
···
131
134
function_parameters: &'a mut Vec<Value>,
132
135
mat: &'a QueryMatch<'a, 'tree>,
133
136
error_context: StatementContext,
137
+
inherited_variables: &'a HashSet<Identifier>,
134
138
shorthands: &'a AttributeShorthands,
135
139
cancellation_flag: &'a dyn CancellationFlag,
136
140
}
137
141
138
142
struct ScopedVariables<'a> {
139
-
scopes: HashMap<SyntaxNodeRef, VariableMap<'a, Value>>,
143
+
scopes: HashMap<SyntaxNodeID, VariableMap<'a, Value>>,
140
144
}
141
145
142
146
impl<'a> ScopedVariables<'a> {
···
146
150
}
147
151
}
148
152
149
-
fn get(&mut self, scope: SyntaxNodeRef) -> &mut VariableMap<'a, Value> {
150
-
self.scopes.entry(scope).or_insert(VariableMap::new())
153
+
fn get_mut(&mut self, scope: SyntaxNodeRef) -> &mut VariableMap<'a, Value> {
154
+
self.scopes.entry(scope.index).or_insert(VariableMap::new())
155
+
}
156
+
157
+
fn try_get(&self, index: SyntaxNodeID) -> Option<&VariableMap<'a, Value>> {
158
+
self.scopes.get(&index)
151
159
}
152
160
}
153
161
···
162
170
scoped: &mut ScopedVariables<'s>,
163
171
current_regex_captures: &Vec<String>,
164
172
function_parameters: &mut Vec<Value>,
173
+
inherited_variables: &HashSet<Identifier>,
165
174
shorthands: &AttributeShorthands,
166
175
cancellation_flag: &dyn CancellationFlag,
167
176
) -> Result<(), ExecutionError> {
···
184
193
function_parameters,
185
194
mat: &mat,
186
195
error_context,
196
+
inherited_variables,
187
197
shorthands,
188
198
cancellation_flag,
189
199
};
···
399
409
function_parameters: exec.function_parameters,
400
410
mat: exec.mat,
401
411
error_context: exec.error_context.clone(),
412
+
inherited_variables: exec.inherited_variables,
402
413
shorthands: exec.shorthands,
403
414
cancellation_flag: exec.cancellation_flag,
404
415
};
···
458
469
function_parameters: exec.function_parameters,
459
470
mat: exec.mat,
460
471
error_context: exec.error_context.clone(),
472
+
inherited_variables: exec.inherited_variables,
461
473
shorthands: exec.shorthands,
462
474
cancellation_flag: exec.cancellation_flag,
463
475
};
···
499
511
function_parameters: exec.function_parameters,
500
512
mat: exec.mat,
501
513
error_context: exec.error_context.clone(),
514
+
inherited_variables: exec.inherited_variables,
502
515
shorthands: exec.shorthands,
503
516
cancellation_flag: exec.cancellation_flag,
504
517
};
···
573
586
function_parameters: exec.function_parameters,
574
587
mat: exec.mat,
575
588
error_context: exec.error_context.clone(),
589
+
inherited_variables: exec.inherited_variables,
576
590
shorthands: exec.shorthands,
577
591
cancellation_flag: exec.cancellation_flag,
578
592
};
···
612
626
function_parameters: exec.function_parameters,
613
627
mat: exec.mat,
614
628
error_context: exec.error_context.clone(),
629
+
inherited_variables: exec.inherited_variables,
615
630
shorthands: exec.shorthands,
616
631
cancellation_flag: exec.cancellation_flag,
617
632
};
···
709
724
)))
710
725
}
711
726
};
712
-
let variables = exec.scoped.get(scope);
713
-
if let Some(value) = variables.get(&self.name) {
714
-
Ok(value)
715
-
} else {
716
-
Err(ExecutionError::UndefinedVariable(format!(
717
-
"{} on node {}",
718
-
self, scope
719
-
)))
727
+
728
+
// search this node
729
+
if let Some(value) = exec
730
+
.scoped
731
+
.try_get(scope.index)
732
+
.and_then(|v| v.get(&self.name))
733
+
{
734
+
return Ok(value);
720
735
}
736
+
737
+
// search parent nodes
738
+
if exec.inherited_variables.contains(&self.name) {
739
+
let mut parent = exec
740
+
.graph
741
+
.syntax_nodes
742
+
.get(&scope.index)
743
+
.and_then(|n| n.parent());
744
+
while let Some(scope) = parent {
745
+
if let Some(value) = exec
746
+
.scoped
747
+
.try_get(scope.id() as u32)
748
+
.and_then(|v| v.get(&self.name))
749
+
{
750
+
return Ok(value);
751
+
}
752
+
parent = scope.parent();
753
+
}
754
+
}
755
+
756
+
Err(ExecutionError::UndefinedVariable(format!(
757
+
"{} on node {}",
758
+
self, scope
759
+
)))
721
760
}
722
761
723
762
fn add(
···
736
775
)))
737
776
}
738
777
};
739
-
let variables = exec.scoped.get(scope);
778
+
let variables = exec.scoped.get_mut(scope);
740
779
variables
741
780
.add(self.name.clone(), value, mutable)
742
781
.map_err(|_| ExecutionError::DuplicateVariable(format!("{}", self)))
···
753
792
)))
754
793
}
755
794
};
756
-
let variables = exec.scoped.get(scope);
795
+
let variables = exec.scoped.get_mut(scope);
757
796
variables
758
797
.set(self.name.clone(), value)
759
798
.map_err(|_| ExecutionError::DuplicateVariable(format!("{}", self)))
···
844
883
function_parameters: exec.function_parameters,
845
884
mat: exec.mat,
846
885
error_context: exec.error_context.clone(),
886
+
inherited_variables: exec.inherited_variables,
847
887
shorthands: exec.shorthands,
848
888
cancellation_flag: exec.cancellation_flag,
849
889
};
+3
-3
src/graph.rs
+3
-3
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> {
···
635
635
/// A reference to a syntax node in a graph
636
636
#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
637
637
pub struct SyntaxNodeRef {
638
-
index: SyntaxNodeID,
638
+
pub(crate) index: SyntaxNodeID,
639
639
kind: &'static str,
640
640
position: tree_sitter::Point,
641
641
}
+9
-4
src/parser.rs
+9
-4
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
308
let stanza = self.parse_stanza(file.language)?;
304
309
file.stanzas.push(stanza);
+72
tests/it/execution.rs
+72
tests/it/execution.rs
···
936
936
"#},
937
937
);
938
938
}
939
+
940
+
#[test]
941
+
fn can_access_inherited_attribute() {
942
+
check_execution(
943
+
indoc! { r#"
944
+
def get_f():
945
+
pass
946
+
"#},
947
+
indoc! {r#"
948
+
inherit .test
949
+
(function_definition)@def {
950
+
node @def.test
951
+
attr (@def.test) in_def
952
+
}
953
+
(pass_statement)@pass {
954
+
attr (@pass.test) in_pass
955
+
}
956
+
"#},
957
+
indoc! {r#"
958
+
node 0
959
+
in_def: #true
960
+
in_pass: #true
961
+
"#},
962
+
);
963
+
}
964
+
965
+
#[test]
966
+
fn can_overwrite_inherited_attribute() {
967
+
check_execution(
968
+
indoc! { r#"
969
+
def get_f():
970
+
pass
971
+
"#},
972
+
indoc! {r#"
973
+
inherit .test
974
+
(function_definition)@def {
975
+
node @def.test
976
+
attr (@def.test) in_def
977
+
}
978
+
(pass_statement)@pass {
979
+
node @pass.test
980
+
}
981
+
(pass_statement)@pass {
982
+
attr (@pass.test) in_pass
983
+
}
984
+
"#},
985
+
indoc! {r#"
986
+
node 0
987
+
in_def: #true
988
+
node 1
989
+
in_pass: #true
990
+
"#},
991
+
);
992
+
}
993
+
994
+
#[test]
995
+
fn cannot_access_non_inherited_variable() {
996
+
fail_execution(
997
+
indoc! { r#"
998
+
def get_f():
999
+
pass
1000
+
"#},
1001
+
indoc! {r#"
1002
+
(function_definition)@def {
1003
+
node @def.test
1004
+
}
1005
+
(pass_statement)@pass {
1006
+
attr (@pass.test) in_pass
1007
+
}
1008
+
"#},
1009
+
);
1010
+
}
+72
tests/it/lazy_execution.rs
+72
tests/it/lazy_execution.rs
···
1455
1455
"#},
1456
1456
);
1457
1457
}
1458
+
1459
+
#[test]
1460
+
fn can_access_inherited_attribute() {
1461
+
check_execution(
1462
+
indoc! { r#"
1463
+
def get_f():
1464
+
pass
1465
+
"#},
1466
+
indoc! {r#"
1467
+
inherit .test
1468
+
(function_definition)@def {
1469
+
node @def.test
1470
+
attr (@def.test) in_def
1471
+
}
1472
+
(pass_statement)@pass {
1473
+
attr (@pass.test) in_pass
1474
+
}
1475
+
"#},
1476
+
indoc! {r#"
1477
+
node 0
1478
+
in_def: #true
1479
+
in_pass: #true
1480
+
"#},
1481
+
);
1482
+
}
1483
+
1484
+
#[test]
1485
+
fn can_overwrite_inherited_attribute() {
1486
+
check_execution(
1487
+
indoc! { r#"
1488
+
def get_f():
1489
+
pass
1490
+
"#},
1491
+
indoc! {r#"
1492
+
inherit .test
1493
+
(function_definition)@def {
1494
+
node @def.test
1495
+
attr (@def.test) in_def
1496
+
}
1497
+
(pass_statement)@pass {
1498
+
node @pass.test
1499
+
}
1500
+
(pass_statement)@pass {
1501
+
attr (@pass.test) in_pass
1502
+
}
1503
+
"#},
1504
+
indoc! {r#"
1505
+
node 0
1506
+
in_def: #true
1507
+
node 1
1508
+
in_pass: #true
1509
+
"#},
1510
+
);
1511
+
}
1512
+
1513
+
#[test]
1514
+
fn cannot_access_non_inherited_variable() {
1515
+
fail_execution(
1516
+
indoc! { r#"
1517
+
def get_f():
1518
+
pass
1519
+
"#},
1520
+
indoc! {r#"
1521
+
(function_definition)@def {
1522
+
node @def.test
1523
+
}
1524
+
(pass_statement)@pass {
1525
+
attr (@pass.test) in_pass
1526
+
}
1527
+
"#},
1528
+
);
1529
+
}
+9
tests/it/parser.rs
+9
tests/it/parser.rs
···
1599
1599
"#;
1600
1600
File::from_str(tree_sitter_python::language(), source).expect("parse to succeed");
1601
1601
}
1602
+
1603
+
#[test]
1604
+
fn can_parse_inherit_directives() {
1605
+
let source = r#"
1606
+
inherit .scope
1607
+
"#;
1608
+
let file = File::from_str(tree_sitter_python::language(), source).expect("parse to succeed");
1609
+
assert!(file.inherited_variables.contains("scope".into()));
1610
+
}
+6
vscode/CHANGELOG.md
+6
vscode/CHANGELOG.md
···
5
5
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
8
+
## 0.1.1 -- 2023-06-01
9
+
10
+
### Added
11
+
12
+
- Add new `inherit` keyword
13
+
8
14
## 0.1.0 -- 2022-05-11
9
15
10
16
### Added
+1
-1
vscode/package.json
+1
-1
vscode/package.json
+1
-1
vscode/syntaxes/tree-sitter-graph.tmLanguage.json
+1
-1
vscode/syntaxes/tree-sitter-graph.tmLanguage.json
···
16
16
"keywords": {
17
17
"patterns": [{
18
18
"name": "keyword.control.tsg",
19
-
"match": "^\\s*(attr|attribute|edge|for|global|if|let|node|none|print|scan|set|some|var)\\b"
19
+
"match": "^\\s*(attr|attribute|edge|for|global|if|inherit|let|node|none|print|scan|set|some|var)\\b"
20
20
}]
21
21
},
22
22
"functions": {