+12
CHANGELOG.md
+12
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.4 -- 2023-06-02
9
+
10
+
### Library
11
+
12
+
#### Added
13
+
14
+
- 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.
15
+
16
+
#### Fixed
17
+
18
+
- Ensure that edge attribute statements are executed after edges are created, to prevent non-deterministic ordering bugs in lazy execution mode.
19
+
8
20
## v0.10.3 -- 2023-06-01
9
21
10
22
### DSL
+1
-1
Cargo.toml
+1
-1
Cargo.toml
+4
-6
src/execution/lazy.rs
+4
-6
src/execution/lazy.rs
···
65
65
let mut locals = VariableMap::new();
66
66
let mut store = LazyStore::new();
67
67
let mut scoped_store = LazyScopedVariables::new();
68
-
let mut lazy_graph = Vec::new();
68
+
let mut lazy_graph = LazyGraph::new();
69
69
let mut function_parameters = Vec::new();
70
70
let mut prev_element_debug_info = HashMap::new();
71
71
···
99
99
prev_element_debug_info: &mut prev_element_debug_info,
100
100
cancellation_flag,
101
101
};
102
-
for graph_stmt in &lazy_graph {
103
-
graph_stmt.evaluate(&mut exec)?;
104
-
}
102
+
lazy_graph.evaluate(&mut exec)?;
105
103
// make sure any unforced values are now forced, to surface any problems
106
104
// hidden by the fact that the values were unused
107
105
store.evaluate_all(&mut exec)?;
···
140
138
mat: &'a QueryMatch<'a, 'tree>,
141
139
store: &'a mut LazyStore,
142
140
scoped_store: &'a mut LazyScopedVariables,
143
-
lazy_graph: &'a mut Vec<LazyStatement>,
141
+
lazy_graph: &'a mut LazyGraph,
144
142
function_parameters: &'a mut Vec<graph::Value>, // re-usable buffer to reduce memory allocations
145
143
prev_element_debug_info: &'a mut HashMap<GraphElementKey, DebugInfo>,
146
144
error_context: StatementContext,
···
179
177
locals: &mut VariableMap<'l, LazyValue>,
180
178
store: &mut LazyStore,
181
179
scoped_store: &mut LazyScopedVariables,
182
-
lazy_graph: &mut Vec<LazyStatement>,
180
+
lazy_graph: &mut LazyGraph,
183
181
function_parameters: &mut Vec<graph::Value>,
184
182
prev_element_debug_info: &mut HashMap<GraphElementKey, DebugInfo>,
185
183
inherited_variables: &HashSet<Identifier>,
+97
-33
src/execution/lazy/statements.rs
+97
-33
src/execution/lazy/statements.rs
···
22
22
use super::EvaluationContext;
23
23
use super::GraphElementKey;
24
24
25
+
/// Lazy graph
26
+
#[derive(Debug)]
27
+
pub(super) struct LazyGraph {
28
+
edge_statements: Vec<LazyStatement>,
29
+
attr_statements: Vec<LazyStatement>,
30
+
print_statements: Vec<LazyStatement>,
31
+
}
32
+
33
+
impl LazyGraph {
34
+
pub(super) fn new() -> Self {
35
+
LazyGraph {
36
+
edge_statements: Vec::new(),
37
+
attr_statements: Vec::new(),
38
+
print_statements: Vec::new(),
39
+
}
40
+
}
41
+
42
+
pub(super) fn push(&mut self, stmt: LazyStatement) {
43
+
match stmt {
44
+
LazyStatement::AddGraphNodeAttribute(_) => self.attr_statements.push(stmt),
45
+
LazyStatement::CreateEdge(_) => self.edge_statements.push(stmt),
46
+
LazyStatement::AddEdgeAttribute(_) => self.attr_statements.push(stmt),
47
+
LazyStatement::Print(_) => self.print_statements.push(stmt),
48
+
}
49
+
}
50
+
51
+
pub(super) fn evaluate(&self, exec: &mut EvaluationContext) -> Result<(), ExecutionError> {
52
+
for stmt in &self.edge_statements {
53
+
stmt.evaluate(exec)?;
54
+
}
55
+
for stmt in &self.attr_statements {
56
+
stmt.evaluate(exec)?;
57
+
}
58
+
for stmt in &self.print_statements {
59
+
stmt.evaluate(exec)?;
60
+
}
61
+
Ok(())
62
+
}
63
+
}
64
+
25
65
/// Lazy graph statements
26
66
#[derive(Debug)]
27
67
pub(super) enum LazyStatement {
···
112
152
}
113
153
114
154
pub(super) fn evaluate(&self, exec: &mut EvaluationContext) -> Result<(), ExecutionError> {
115
-
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())?;
116
159
for attribute in &self.attributes {
117
160
let value = attribute.value.evaluate(exec)?;
118
161
let prev_debug_info = exec.prev_element_debug_info.insert(
119
162
GraphElementKey::NodeAttribute(node, attribute.name.clone()),
120
163
self.debug_info.clone(),
121
164
);
122
-
exec.graph[node]
165
+
if let Err(_) = exec.graph[node]
123
166
.attributes
124
167
.add(attribute.name.clone(), value)
125
-
.map_err(|_| {
126
-
ExecutionError::DuplicateAttribute(format!(
127
-
"{} on {} at {} and {}",
128
-
attribute.name,
129
-
node,
130
-
prev_debug_info.unwrap(),
131
-
self.debug_info,
132
-
))
133
-
})?;
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
+
};
134
181
}
135
182
Ok(())
136
183
}
···
171
218
}
172
219
173
220
pub(super) fn evaluate(&self, exec: &mut EvaluationContext) -> Result<(), ExecutionError> {
174
-
let source = self.source.evaluate_as_graph_node(exec)?;
175
-
let sink = self.sink.evaluate_as_graph_node(exec)?;
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())?;
176
229
let prev_debug_info = exec
177
230
.prev_element_debug_info
178
231
.insert(GraphElementKey::Edge(source, sink), self.debug_info.clone());
···
180
233
Ok(edge) => edge,
181
234
Err(_) => {
182
235
return Err(ExecutionError::DuplicateEdge(format!(
183
-
"({} -> {}) at {} and {}",
184
-
source,
185
-
sink,
186
-
prev_debug_info.unwrap(),
187
-
self.debug_info,
188
-
)))?
236
+
"({} -> {})",
237
+
source, sink,
238
+
)))
239
+
.with_context(|| {
240
+
(
241
+
prev_debug_info.unwrap().into(),
242
+
self.debug_info.clone().into(),
243
+
)
244
+
.into()
245
+
});
189
246
}
190
247
};
191
248
edge.attributes = self.attributes.clone();
···
228
285
}
229
286
230
287
pub(super) fn evaluate(&self, exec: &mut EvaluationContext) -> Result<(), ExecutionError> {
231
-
let source = self.source.evaluate_as_graph_node(exec)?;
232
-
let sink = self.sink.evaluate_as_graph_node(exec)?;
288
+
let source = self
289
+
.source
290
+
.evaluate_as_graph_node(exec)
291
+
.with_context(|| "Evaluating edge source".to_string().into())?;
292
+
let sink = self
293
+
.sink
294
+
.evaluate_as_graph_node(exec)
295
+
.with_context(|| "Evaluating edge sink".to_string().into())?;
233
296
for attribute in &self.attributes {
234
297
let value = attribute.value.evaluate(exec)?;
235
298
let edge = match exec.graph[source].get_edge_mut(sink) {
···
243
306
GraphElementKey::EdgeAttribute(source, sink, attribute.name.clone()),
244
307
self.debug_info.clone(),
245
308
);
246
-
edge.attributes
247
-
.add(attribute.name.clone(), value)
248
-
.map_err(|_| {
249
-
ExecutionError::DuplicateAttribute(format!(
250
-
"{} on edge ({} -> {}) at {} and {}",
251
-
attribute.name,
252
-
source,
253
-
sink,
254
-
prev_debug_info.unwrap(),
255
-
self.debug_info,
256
-
))
257
-
})?;
309
+
if let Err(_) = edge.attributes.add(attribute.name.clone(), value) {
310
+
return Err(ExecutionError::DuplicateAttribute(format!(
311
+
"{} on edge ({} -> {})",
312
+
attribute.name, source, sink,
313
+
)))
314
+
.with_context(|| {
315
+
(
316
+
prev_debug_info.unwrap().into(),
317
+
self.debug_info.clone().into(),
318
+
)
319
+
.into()
320
+
});
321
+
}
258
322
}
259
323
Ok(())
260
324
}
+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
}