From c1ef414f4c8e5f35e059638b127bc224156493df Mon Sep 17 00:00:00 2001 From: Ioannis Rosuochatzakis Date: Mon, 2 Mar 2026 11:37:53 +0100 Subject: [PATCH 1/2] TEDEFO-4941 Simplify preprocessor after grammar ambiguity removal --- .../efx/sdk2/EfxExpressionTranslatorV2.java | 45 +++++-------------- .../sdk2/PreprocessorTypeResolutionTest.java | 6 +++ 2 files changed, 16 insertions(+), 35 deletions(-) diff --git a/src/main/java/eu/europa/ted/efx/sdk2/EfxExpressionTranslatorV2.java b/src/main/java/eu/europa/ted/efx/sdk2/EfxExpressionTranslatorV2.java index 0e84f8b..5cb0b93 100644 --- a/src/main/java/eu/europa/ted/efx/sdk2/EfxExpressionTranslatorV2.java +++ b/src/main/java/eu/europa/ted/efx/sdk2/EfxExpressionTranslatorV2.java @@ -3077,7 +3077,7 @@ public void exitFieldReferenceWithVariableContextOverride( @Override public void enterLateBoundScalar(LateBoundScalarContext ctx) { this.typeResolutionStack.push( - this.adjustForGrammarAmbiguities(ctx, CardinalityResolutionContext.RESOLVE_SCALAR)); + this.adjustForGrammarAmbiguity(ctx, CardinalityResolutionContext.RESOLVE_SCALAR)); } @Override @@ -3088,7 +3088,7 @@ public void exitLateBoundScalar(LateBoundScalarContext ctx) { @Override public void enterLateBoundSequence(LateBoundSequenceContext ctx) { this.typeResolutionStack.push( - this.adjustForGrammarAmbiguities(ctx, CardinalityResolutionContext.RESOLVE_SEQUENCE)); + this.adjustForGrammarAmbiguity(ctx, CardinalityResolutionContext.RESOLVE_SEQUENCE)); } @Override @@ -3101,36 +3101,21 @@ public void exitLateBoundSequence(LateBoundSequenceContext ctx) { * cardinality is ambiguous, and returns {@code RESOLVE_EITHER} if so. * Otherwise returns the provided default context. * - * There are two grammar ambiguities that require this adjustment: - * - * Ambiguity 1: The {@code expression} rule accepts both - * {@code lateBoundScalar} and {@code lateBoundSequence} via - * {@code lateBoundExpression}. The parser picks one alternative, but the - * actual cardinality is unknown until the preprocessor resolves it. - * - * Ambiguity 2: The {@code for...return} construct has two rules: - * {@code lateBoundSequenceFromIteration} (scalar body) and - * {@code lateBoundSequenceFromConcatenatedIterations} (sequence body). - * The parser picks one, but the body cardinality is unknown until resolved. + * The {@code lateBoundExpression} rule accepts both {@code lateBoundScalar} + * and {@code lateBoundSequence}. The parser picks one alternative, but the + * actual cardinality is unknown until the preprocessor resolves it. This + * ambiguity applies wherever {@code lateBoundExpression} appears in the + * grammar: the top-level {@code expression} rule and the body of + * {@code lateBoundSequenceFromIteration}. * * @param ctx the late-bound parse tree node (scalar or sequence) * @param defaultContext the context to use when no ambiguity is detected * @return {@code RESOLVE_EITHER} if an ambiguity applies, otherwise * {@code defaultContext} */ - private CardinalityResolutionContext adjustForGrammarAmbiguities(ParserRuleContext ctx, + private CardinalityResolutionContext adjustForGrammarAmbiguity(ParserRuleContext ctx, CardinalityResolutionContext defaultContext) { - ParserRuleContext parent = ctx.getParent(); - - // Ambiguity 1: expression → lateBoundExpression → lateBoundScalar | lateBoundSequence - boolean ambiguity1 = (parent instanceof LateBoundExpressionContext) - && (parent.getParent() instanceof ExpressionContext); - - // Ambiguity 2: lateBoundSequenceFromIteration vs lateBoundSequenceFromConcatenatedIterations - boolean ambiguity2 = (parent instanceof LateBoundSequenceFromIterationContext) - || (parent instanceof LateBoundSequenceFromConcatenatedIterationsContext); - - return (ambiguity1 || ambiguity2) + return (ctx.getParent() instanceof LateBoundExpressionContext) ? CardinalityResolutionContext.RESOLVE_EITHER : defaultContext; } @@ -3769,16 +3754,6 @@ public void exitDurationSequenceFromConcatenatedIterations(DurationSequenceFromC this.stack.popStackFrame(); } - @Override - public void enterLateBoundSequenceFromConcatenatedIterations(LateBoundSequenceFromConcatenatedIterationsContext ctx) { - this.stack.pushStackFrame(); - } - - @Override - public void exitLateBoundSequenceFromConcatenatedIterations(LateBoundSequenceFromConcatenatedIterationsContext ctx) { - this.stack.popStackFrame(); - } - // #endregion Scope management -------------------------------------------- // #region Guards --------------------------------------------------------- diff --git a/src/test/java/eu/europa/ted/efx/sdk2/PreprocessorTypeResolutionTest.java b/src/test/java/eu/europa/ted/efx/sdk2/PreprocessorTypeResolutionTest.java index 125bdd6..376369b 100644 --- a/src/test/java/eu/europa/ted/efx/sdk2/PreprocessorTypeResolutionTest.java +++ b/src/test/java/eu/europa/ted/efx/sdk2/PreprocessorTypeResolutionTest.java @@ -92,6 +92,12 @@ void testResolveFieldReference_Either_NonRepeatableFieldInForReturn() { "for text:$x in BT-00-Text return BT-00-Text"); } + @Test + void testResolveFieldReference_Either_NestedForReturn() { + translateExpressionWithContext("ND-Root", + "for text:$x in BT-00-Text return (for text:$y in BT-00-Text return BT-00-Repeatable-Text)"); + } + // #endregion resolveFieldOrAttributeReference -------------------------------- // #region resolveFunctionInvocation ------------------------------------------ From b43c9d04bfbc4c4a944aacf0d8bdf36a76ddcbeb Mon Sep 17 00:00:00 2001 From: Ioannis Rosuochatzakis Date: Mon, 2 Mar 2026 13:46:17 +0100 Subject: [PATCH 2/2] Wrap nested for-return test with assertDoesNotThrow --- .../europa/ted/efx/sdk2/PreprocessorTypeResolutionTest.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/test/java/eu/europa/ted/efx/sdk2/PreprocessorTypeResolutionTest.java b/src/test/java/eu/europa/ted/efx/sdk2/PreprocessorTypeResolutionTest.java index 49e7025..650859a 100644 --- a/src/test/java/eu/europa/ted/efx/sdk2/PreprocessorTypeResolutionTest.java +++ b/src/test/java/eu/europa/ted/efx/sdk2/PreprocessorTypeResolutionTest.java @@ -102,8 +102,9 @@ void testResolveFieldReference_Either_NonRepeatableFieldInForReturn() { @Test void testResolveFieldReference_Either_NestedForReturn() { - translateExpressionWithContext("ND-Root", - "for text:$x in BT-00-Text return (for text:$y in BT-00-Text return BT-00-Repeatable-Text)"); + assertDoesNotThrow( + () -> translateExpressionWithContext("ND-Root", + "for text:$x in BT-00-Text return (for text:$y in BT-00-Text return BT-00-Repeatable-Text)")); } // #endregion resolveFieldOrAttributeReference --------------------------------