Skip to content

Commit 58bf0fc

Browse files
committed
fix: DAG scopes #457
1 parent 83477be commit 58bf0fc

File tree

5 files changed

+319
-32
lines changed

5 files changed

+319
-32
lines changed

vtl-engine/src/main/java/fr/insee/vtl/engine/visitors/DAGBuildingVisitor.java

Lines changed: 100 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import java.util.Set;
1010
import java.util.stream.Collectors;
1111
import java.util.stream.Stream;
12+
import org.antlr.v4.runtime.RuleContext;
1213
import org.antlr.v4.runtime.Token;
1314
import org.antlr.v4.runtime.tree.RuleNode;
1415
import org.antlr.v4.runtime.tree.TerminalNode;
@@ -154,10 +155,10 @@ protected List<DAGStatement> defaultResult() {
154155
* <code>VarIDsExtractingVisitor</code> is the visitor for extracting the used VarIds from VTL
155156
* statements.
156157
*/
157-
private static class IdentifierExtractingVisitor
158-
extends VtlBaseVisitor<Set<DAGStatement.Identifier>> {
158+
static class IdentifierExtractingVisitor extends VtlBaseVisitor<Set<DAGStatement.Identifier>> {
159159

160160
private final Set<String> ignoreInnerScopedVarIdentifiers;
161+
private int componentContextDepth = 0;
161162

162163
public IdentifierExtractingVisitor() {
163164
this(Set.of());
@@ -168,51 +169,127 @@ public IdentifierExtractingVisitor(Set<String> ignoreInnerScopedVarIdentifiers)
168169
this.ignoreInnerScopedVarIdentifiers = ignoreInnerScopedVarIdentifiers;
169170
}
170171

172+
private Set<DAGStatement.Identifier> enterRestrictedContext(RuleContext ctx) {
173+
componentContextDepth++;
174+
Set<DAGStatement.Identifier> result = super.visitChildren(ctx);
175+
componentContextDepth--;
176+
return result;
177+
}
178+
171179
@Override
172180
public Set<DAGStatement.Identifier> visitVarID(VtlParser.VarIDContext node) {
173181
final var currentVarIdentifier = node.IDENTIFIER().getSymbol().getText();
174-
final Set<DAGStatement.Identifier> thisResult =
175-
ignoreInnerScopedVarIdentifiers.contains(currentVarIdentifier)
176-
? Set.of()
177-
: Set.of(
178-
new DAGStatement.Identifier(
179-
DAGStatement.Identifier.Type.VARIABLE, currentVarIdentifier));
180-
final Set<DAGStatement.Identifier> subResult = this.visitChildren(node);
181-
return aggregateResult(thisResult, subResult);
182+
183+
// If we are inside a component context (depth > 0), we ignore this identifier
184+
// because it is a component name, not a dataset dependency, according to the unadjusted VTL
185+
// syntax
186+
if (componentContextDepth > 0) {
187+
return Set.of();
188+
}
189+
190+
return ignoreInnerScopedVarIdentifiers.contains(currentVarIdentifier)
191+
? Set.of()
192+
: Set.of(
193+
new DAGStatement.Identifier(
194+
DAGStatement.Identifier.Type.VARIABLE, currentVarIdentifier));
182195
}
183196

197+
// Workaround for https://github.com/InseeFr/Trevas/issues/457, as long the open points in here
198+
// are not clarified and https://github.com/InseeFr/Trevas/issues/355 is not implemented
199+
// If we are inside a component context (depth > 0), we ignore this identifier
200+
// because it is a component name, not a dataset dependency, according to the unadjusted VTL
201+
// syntax
184202
@Override
185-
public Set<DAGStatement.Identifier> visitOperatorID(VtlParser.OperatorIDContext node) {
186-
final Set<DAGStatement.Identifier> thisResult =
187-
Set.of(
188-
new DAGStatement.Identifier(
189-
DAGStatement.Identifier.Type.OPERATOR, node.IDENTIFIER().getSymbol().getText()));
190-
final Set<DAGStatement.Identifier> subResult = this.visitChildren(node);
191-
return aggregateResult(thisResult, subResult);
203+
public Set<DAGStatement.Identifier> visitFilterClause(VtlParser.FilterClauseContext ctx) {
204+
return enterRestrictedContext(ctx);
205+
}
206+
207+
@Override
208+
public Set<DAGStatement.Identifier> visitCalcClauseItem(VtlParser.CalcClauseItemContext ctx) {
209+
return enterRestrictedContext(ctx);
210+
}
211+
212+
@Override
213+
public Set<DAGStatement.Identifier> visitHavingClause(VtlParser.HavingClauseContext ctx) {
214+
return enterRestrictedContext(ctx);
215+
}
216+
217+
@Override
218+
public Set<DAGStatement.Identifier> visitJoinApplyClause(VtlParser.JoinApplyClauseContext ctx) {
219+
return enterRestrictedContext(ctx);
220+
}
221+
222+
@Override
223+
public Set<DAGStatement.Identifier> visitAggrFunctionClause(
224+
VtlParser.AggrFunctionClauseContext ctx) {
225+
return enterRestrictedContext(ctx);
226+
}
227+
228+
// Aggregate & Analytic Functions (First arg is Dataset, rest are components)
229+
@Override
230+
public Set<DAGStatement.Identifier> visitAggrDataset(VtlParser.AggrDatasetContext ctx) {
231+
Set<DAGStatement.Identifier> datasetRef = visit(ctx.expr());
232+
return aggregateResult(datasetRef, enterRestrictedContext(ctx));
233+
}
234+
235+
@Override
236+
public Set<DAGStatement.Identifier> visitAnSimpleFunction(
237+
VtlParser.AnSimpleFunctionContext ctx) {
238+
Set<DAGStatement.Identifier> datasetRef = visit(ctx.expr());
239+
return aggregateResult(datasetRef, enterRestrictedContext(ctx));
240+
}
241+
242+
@Override
243+
public Set<DAGStatement.Identifier> visitLagOrLeadAn(VtlParser.LagOrLeadAnContext ctx) {
244+
Set<DAGStatement.Identifier> datasetRef = visit(ctx.expr());
245+
return aggregateResult(datasetRef, enterRestrictedContext(ctx));
246+
}
247+
248+
@Override
249+
public Set<DAGStatement.Identifier> visitRatioToReportAn(VtlParser.RatioToReportAnContext ctx) {
250+
Set<DAGStatement.Identifier> datasetRef = visit(ctx.expr());
251+
return aggregateResult(datasetRef, enterRestrictedContext(ctx));
252+
}
253+
254+
@Override
255+
public Set<DAGStatement.Identifier> visitRankAn(VtlParser.RankAnContext ctx) {
256+
257+
return enterRestrictedContext(ctx);
258+
}
259+
260+
@Override
261+
public Set<DAGStatement.Identifier> visitMembershipExpr(VtlParser.MembershipExprContext ctx) {
262+
// Only visit the dataset (left side), ignore the component (right side)
263+
return visit(ctx.expr());
192264
}
193265

194266
@Override
195267
public Set<DAGStatement.Identifier> visitValidateDPruleset(
196268
VtlParser.ValidateDPrulesetContext node) {
197-
final Set<DAGStatement.Identifier> thisResult =
269+
Set<DAGStatement.Identifier> rulesetRef =
198270
Set.of(
199271
new DAGStatement.Identifier(
200272
DAGStatement.Identifier.Type.RULESET_DATAPOINT,
201273
node.IDENTIFIER().getSymbol().getText()));
202-
final Set<DAGStatement.Identifier> subResult = this.visitChildren(node);
203-
return aggregateResult(thisResult, subResult);
274+
return aggregateResult(rulesetRef, visit(node.expr()));
204275
}
205276

206277
@Override
207278
public Set<DAGStatement.Identifier> visitValidateHRruleset(
208279
VtlParser.ValidateHRrulesetContext node) {
209-
final Set<DAGStatement.Identifier> thisResult =
280+
Set<DAGStatement.Identifier> rulesetRef =
210281
Set.of(
211282
new DAGStatement.Identifier(
212283
DAGStatement.Identifier.Type.RULESET_HIERARCHICAL,
213284
node.IDENTIFIER().getSymbol().getText()));
214-
final Set<DAGStatement.Identifier> subResult = this.visitChildren(node);
215-
return aggregateResult(thisResult, subResult);
285+
return aggregateResult(rulesetRef, visit(node.expr()));
286+
}
287+
288+
@Override
289+
public Set<DAGStatement.Identifier> visitOperatorID(VtlParser.OperatorIDContext node) {
290+
return Set.of(
291+
new DAGStatement.Identifier(
292+
DAGStatement.Identifier.Type.OPERATOR, node.IDENTIFIER().getSymbol().getText()));
216293
}
217294

218295
@Override

vtl-engine/src/test/java/fr/insee/vtl/engine/utils/dag/DagDefineStatementsTest.java

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@
66
import fr.insee.vtl.model.exceptions.VtlScriptException;
77
import fr.insee.vtl.parser.VtlLexer;
88
import fr.insee.vtl.parser.VtlParser;
9-
import java.util.*;
9+
import java.util.Set;
1010
import java.util.stream.Stream;
1111
import javax.script.ScriptException;
12-
import org.antlr.v4.runtime.*;
12+
import org.antlr.v4.runtime.CharStreams;
13+
import org.antlr.v4.runtime.CodePointCharStream;
14+
import org.antlr.v4.runtime.CommonTokenStream;
1315
import org.antlr.v4.runtime.tree.ParseTree;
1416
import org.antlr.v4.runtime.tree.RuleNode;
1517
import org.antlr.v4.runtime.tree.TerminalNode;
@@ -21,15 +23,19 @@ public class DagDefineStatementsTest {
2123

2224
private static String performDagReordering(final String script, final Set<String> bindingVars)
2325
throws VtlScriptException {
24-
final CodePointCharStream stream = CharStreams.fromString(script);
25-
VtlLexer lexer = new VtlLexer(stream);
26-
VtlParser parser = new VtlParser(new CommonTokenStream(lexer));
27-
var start = parser.start();
26+
VtlParser.StartContext start = parseScript(script);
2827
VtlSyntaxPreprocessor syntaxPreprocessor = new VtlSyntaxPreprocessor(start, bindingVars);
2928
VtlParser.StartContext res = syntaxPreprocessor.checkForMultipleAssignmentsAndReorderScript();
3029
return parseTreeToText(res);
3130
}
3231

32+
public static VtlParser.StartContext parseScript(String script) {
33+
final CodePointCharStream stream = CharStreams.fromString(script);
34+
VtlLexer lexer = new VtlLexer(stream);
35+
VtlParser parser = new VtlParser(new CommonTokenStream(lexer));
36+
return parser.start();
37+
}
38+
3339
private static String parseTreeToText(ParseTree child) {
3440
StringBuilder result = new StringBuilder();
3541
if (child instanceof TerminalNode) {

vtl-engine/src/test/java/fr/insee/vtl/engine/utils/dag/DagTest.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -297,8 +297,6 @@ void testMultipleCycles() {
297297
void testDagDoubleAssignment() {
298298
ScriptContext context = engine.getContext();
299299
context.setAttribute("a", 1L, ScriptContext.ENGINE_SCOPE);
300-
// Note that the double assignment is not detected while building the DAG but later during
301-
// execution
302300
assertThatThrownBy(() -> engine.eval("b := a; b := 1;"))
303301
.isInstanceOf(VtlScriptException.class)
304302
.hasMessage("Dataset b has already been assigned");

0 commit comments

Comments
 (0)