From f23864418de9c4bcbdff6454d8b1dbfb370fbd12 Mon Sep 17 00:00:00 2001
From: opaopa6969 <182220+opaopa6969@users.noreply.github.com>
Date: Wed, 2 Sep 2026 19:06:32 +0900
Subject: [PATCH 01/10] fix: use public unlaxer 3.0.15 mapper API
---
pom.xml | 4 +-
.../p4/P4PreferredAstMapper.java | 140 +++---------------
2 files changed, 23 insertions(+), 121 deletions(-)
diff --git a/pom.xml b/pom.xml
index c5793fb9..1b2d6b25 100644
--- a/pom.xml
+++ b/pom.xml
@@ -63,12 +63,12 @@
org.unlaxer
unlaxer-common
- 3.0.14
+ 3.0.15
org.unlaxer
unlaxer-dsl
- 3.0.14
+ 3.0.15
org.jetbrains
diff --git a/src/main/java/org/unlaxer/tinyexpression/p4/P4PreferredAstMapper.java b/src/main/java/org/unlaxer/tinyexpression/p4/P4PreferredAstMapper.java
index b443dc40..53ad023b 100644
--- a/src/main/java/org/unlaxer/tinyexpression/p4/P4PreferredAstMapper.java
+++ b/src/main/java/org/unlaxer/tinyexpression/p4/P4PreferredAstMapper.java
@@ -1,7 +1,5 @@
package org.unlaxer.tinyexpression.p4;
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
@@ -15,7 +13,6 @@
import org.unlaxer.tinyexpression.generated.p4.TinyExpressionP4Mapper;
import org.unlaxer.tinyexpression.generated.p4.TinyExpressionP4Parsers;
import org.unlaxer.tinyexpression.parser.ExpressionType;
-import org.unlaxer.tinyexpression.parser.TinyExpressionParserCapabilities;
/**
* Selects a more specific generated AST root when the generic mapper would
@@ -267,41 +264,31 @@ private static TinyExpressionP4AST parseViaMapperCompat(
private static ParsedAst parseMappedCandidates(
String source, List candidates, boolean allowDefault, long deadlineNanos) {
- // unlaxer-dsl 3.0.14 emits only the // branch of @comment even though the
- // javaStyle contract also includes block comments. Preserve layout so mapper
- // source spans remain valid. This is one lexical input pass, not a retry or an
- // alternate parser path; remove it when the generator's delimiter is corrected.
- String parserSource = TinyExpressionParserCapabilities.stripJavaStyleCommentsPreservingLayout(source);
- Token rootToken = parseRootToken(parserSource, deadlineNanos);
+ Token rootToken = parseRootToken(source, deadlineNanos);
RuntimeException lastFailure = null;
- try {
- for (String candidate : candidates) {
- if (candidate == null || candidate.isBlank()) {
+ for (String candidate : candidates) {
+ if (candidate == null || candidate.isBlank()) {
+ continue;
+ }
+ try {
+ TinyExpressionP4Mapper.MappedAst mappedAst =
+ TinyExpressionP4Mapper.mapParsedToken(rootToken, candidate);
+ if (!coversWholeSource(source, mappedAst.token())) {
continue;
}
- try {
- clearMapperSourceSpans();
- Token bestMappedToken = invokeFindBestMappedToken(rootToken, candidate);
- if (!coversWholeSource(parserSource, bestMappedToken)) {
- continue;
- }
- TinyExpressionP4AST mapped = invokeMapToken(bestMappedToken);
- if (mapped != null && candidate.equals(mapped.getClass().getSimpleName())) {
- return new ParsedAst(mapped, "preferred:" + candidate);
- }
- } catch (RuntimeException failure) {
- lastFailure = failure;
+ TinyExpressionP4AST mapped = mappedAst.ast();
+ if (mapped != null && candidate.equals(mapped.getClass().getSimpleName())) {
+ return new ParsedAst(mapped, "preferred:" + candidate);
}
+ } catch (RuntimeException failure) {
+ lastFailure = failure;
}
- if (allowDefault) {
- clearMapperSourceSpans();
- TinyExpressionP4AST mapped = invokeMapToken(invokeFindBestMappedToken(rootToken, null));
- if (mapped != null) {
- return new ParsedAst(mapped, "default");
- }
+ }
+ if (allowDefault) {
+ TinyExpressionP4AST mapped = TinyExpressionP4Mapper.mapParsedToken(rootToken).ast();
+ if (mapped != null) {
+ return new ParsedAst(mapped, "default");
}
- } finally {
- clearMapperSourceSpans();
}
if (lastFailure != null) {
throw toParseFailure(lastFailure);
@@ -434,39 +421,6 @@ private static void closeParseContextQuietly(ParseContext context) {
}
}
- private static void clearMapperSourceSpans() {
- try {
- Field field = TinyExpressionP4Mapper.class.getDeclaredField("NODE_SOURCE_SPANS");
- field.setAccessible(true);
- Object value = field.get(null);
- if (value instanceof java.util.Map, ?> map) {
- map.clear();
- }
- } catch (ReflectiveOperationException ignored) {
- }
- }
-
- private static Token invokeFindBestMappedToken(Token rootToken, String preferredAstSimpleName) {
- try {
- Method method = TinyExpressionP4Mapper.class.getDeclaredMethod(
- "findBestMappedToken", Token.class, String.class);
- method.setAccessible(true);
- return (Token) method.invoke(null, rootToken, preferredAstSimpleName);
- } catch (ReflectiveOperationException e) {
- throw new IllegalArgumentException("Failed to resolve mapped token", e);
- }
- }
-
- private static TinyExpressionP4AST invokeMapToken(Token token) {
- try {
- Method method = TinyExpressionP4Mapper.class.getDeclaredMethod("mapToken", Token.class);
- method.setAccessible(true);
- return (TinyExpressionP4AST) method.invoke(null, token);
- } catch (ReflectiveOperationException e) {
- throw new IllegalArgumentException("Failed to map parse tree", e);
- }
- }
-
private static int consumedLengthCompat(Token token) {
String text = tokenTextCompat(token);
return text == null ? 0 : text.length();
@@ -476,63 +430,11 @@ private static String tokenTextCompat(Token token) {
if (token == null) {
return null;
}
- try {
- Method method = token.getClass().getMethod("getToken");
- Object value = method.invoke(token);
- if (value instanceof java.util.Optional> optional && optional.isPresent()) {
- Object tokenValue = optional.get();
- return tokenValue == null ? null : String.valueOf(tokenValue);
- }
- } catch (ReflectiveOperationException ignored) {
- }
- try {
- Field field = token.getClass().getField("tokenString");
- Object value = field.get(token);
- if (value instanceof java.util.Optional> optional && optional.isPresent()) {
- Object tokenValue = optional.get();
- return tokenValue == null ? null : String.valueOf(tokenValue);
- }
- } catch (ReflectiveOperationException ignored) {
- }
- try {
- Field field = token.getClass().getField("source");
- Object source = field.get(token);
- if (source != null) {
- Method method = source.getClass().getMethod("sourceAsString");
- Object value = method.invoke(source);
- return value == null ? null : String.valueOf(value);
- }
- } catch (ReflectiveOperationException ignored) {
- }
- return null;
+ return token.getToken().orElse(null);
}
private static StringSource createRootSourceCompat(String source) {
- try {
- Method method = StringSource.class.getMethod("createRootSource", String.class);
- Object value = method.invoke(null, source);
- if (value instanceof StringSource stringSource) {
- return stringSource;
- }
- } catch (ReflectiveOperationException ignored) {
- }
- try {
- for (java.lang.reflect.Constructor> constructor : StringSource.class.getDeclaredConstructors()) {
- Class>[] types = constructor.getParameterTypes();
- if (types.length == 0 || types[0] != String.class) {
- continue;
- }
- Object[] args = new Object[types.length];
- args[0] = source;
- constructor.setAccessible(true);
- Object value = constructor.newInstance(args);
- if (value instanceof StringSource stringSource) {
- return stringSource;
- }
- }
- } catch (ReflectiveOperationException ignored) {
- }
- throw new IllegalStateException("No compatible StringSource initializer found");
+ return StringSource.createRootSource(source);
}
private record MatchBody(String body, int bodyStartOffset) {}
From e302adb146f162c8eec39acf4d019054d22446d8 Mon Sep 17 00:00:00 2001
From: opaopa6969 <182220+opaopa6969@users.noreply.github.com>
Date: Wed, 2 Sep 2026 19:08:15 +0900
Subject: [PATCH 02/10] fix: preserve preferred AST selection around comments
---
.../unlaxer/tinyexpression/p4/P4PreferredAstMapper.java | 8 --------
1 file changed, 8 deletions(-)
diff --git a/src/main/java/org/unlaxer/tinyexpression/p4/P4PreferredAstMapper.java b/src/main/java/org/unlaxer/tinyexpression/p4/P4PreferredAstMapper.java
index 53ad023b..9639a90e 100644
--- a/src/main/java/org/unlaxer/tinyexpression/p4/P4PreferredAstMapper.java
+++ b/src/main/java/org/unlaxer/tinyexpression/p4/P4PreferredAstMapper.java
@@ -273,9 +273,6 @@ private static ParsedAst parseMappedCandidates(
try {
TinyExpressionP4Mapper.MappedAst mappedAst =
TinyExpressionP4Mapper.mapParsedToken(rootToken, candidate);
- if (!coversWholeSource(source, mappedAst.token())) {
- continue;
- }
TinyExpressionP4AST mapped = mappedAst.ast();
if (mapped != null && candidate.equals(mapped.getClass().getSimpleName())) {
return new ParsedAst(mapped, "preferred:" + candidate);
@@ -296,11 +293,6 @@ private static ParsedAst parseMappedCandidates(
throw new IllegalArgumentException("No whole-source generated AST mapping found: " + source);
}
- private static boolean coversWholeSource(String source, Token token) {
- String mappedSource = tokenTextCompat(token);
- return mappedSource != null && source.strip().equals(mappedSource.strip());
- }
-
/**
* Parses {@code source} fully and returns the root token.
*
From 847a3212afeaca35e57557d5081fd7b8416b7452 Mon Sep 17 00:00:00 2001
From: opaopa6969 <182220+opaopa6969@users.noreply.github.com>
Date: Wed, 2 Sep 2026 19:09:53 +0900
Subject: [PATCH 03/10] fix: ignore comments when validating mapped source span
---
.../tinyexpression/p4/P4PreferredAstMapper.java | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/src/main/java/org/unlaxer/tinyexpression/p4/P4PreferredAstMapper.java b/src/main/java/org/unlaxer/tinyexpression/p4/P4PreferredAstMapper.java
index 9639a90e..f5a325bc 100644
--- a/src/main/java/org/unlaxer/tinyexpression/p4/P4PreferredAstMapper.java
+++ b/src/main/java/org/unlaxer/tinyexpression/p4/P4PreferredAstMapper.java
@@ -13,6 +13,7 @@
import org.unlaxer.tinyexpression.generated.p4.TinyExpressionP4Mapper;
import org.unlaxer.tinyexpression.generated.p4.TinyExpressionP4Parsers;
import org.unlaxer.tinyexpression.parser.ExpressionType;
+import org.unlaxer.tinyexpression.parser.TinyExpressionParserCapabilities;
/**
* Selects a more specific generated AST root when the generic mapper would
@@ -265,6 +266,11 @@ private static TinyExpressionP4AST parseViaMapperCompat(
private static ParsedAst parseMappedCandidates(
String source, List candidates, boolean allowDefault, long deadlineNanos) {
Token rootToken = parseRootToken(source, deadlineNanos);
+ // Comments are consumed by the 3.0.15 grammar but are not part of the mapped
+ // node's token text. Normalize only for the selection check; the parser still
+ // receives the original source, preserving source spans and block comments.
+ String sourceWithoutComments = TinyExpressionParserCapabilities
+ .stripJavaStyleCommentsPreservingLayout(source);
RuntimeException lastFailure = null;
for (String candidate : candidates) {
if (candidate == null || candidate.isBlank()) {
@@ -273,6 +279,9 @@ private static ParsedAst parseMappedCandidates(
try {
TinyExpressionP4Mapper.MappedAst mappedAst =
TinyExpressionP4Mapper.mapParsedToken(rootToken, candidate);
+ if (!coversWholeSource(sourceWithoutComments, mappedAst.token())) {
+ continue;
+ }
TinyExpressionP4AST mapped = mappedAst.ast();
if (mapped != null && candidate.equals(mapped.getClass().getSimpleName())) {
return new ParsedAst(mapped, "preferred:" + candidate);
@@ -293,6 +302,11 @@ private static ParsedAst parseMappedCandidates(
throw new IllegalArgumentException("No whole-source generated AST mapping found: " + source);
}
+ private static boolean coversWholeSource(String source, Token token) {
+ String mappedSource = tokenTextCompat(token);
+ return mappedSource != null && source.strip().equals(mappedSource.strip());
+ }
+
/**
* Parses {@code source} fully and returns the root token.
*
From dc7b8f77729f1df9bbadeb98f88dc36052ef8258 Mon Sep 17 00:00:00 2001
From: opaopa6969 <182220+opaopa6969@users.noreply.github.com>
Date: Wed, 2 Sep 2026 19:11:39 +0900
Subject: [PATCH 04/10] fix: use unlaxer javaStyle comment defaults
---
.../tinyexpression-p4-lsp-vscode/grammar/tinyexpression-p4.ubnf | 1 -
1 file changed, 1 deletion(-)
diff --git a/tools/tinyexpression-p4-lsp-vscode/grammar/tinyexpression-p4.ubnf b/tools/tinyexpression-p4-lsp-vscode/grammar/tinyexpression-p4.ubnf
index 84d7776e..a7077c07 100644
--- a/tools/tinyexpression-p4-lsp-vscode/grammar/tinyexpression-p4.ubnf
+++ b/tools/tinyexpression-p4-lsp-vscode/grammar/tinyexpression-p4.ubnf
@@ -2,7 +2,6 @@ grammar TinyExpressionP4 {
@package: org.unlaxer.tinyexpression.generated.p4
@whitespace: javaStyle
- @comment: { line: '//' }
token NUMBER = org.unlaxer.parser.elementary.NumberParser
token IDENTIFIER = org.unlaxer.parser.clang.IdentifierParser
From a1e805e7b2b7cf71a1b70d9761b86535d800477d Mon Sep 17 00:00:00 2001
From: opaopa6969 <182220+opaopa6969@users.noreply.github.com>
Date: Wed, 2 Sep 2026 19:13:59 +0900
Subject: [PATCH 05/10] fix: interleave javaStyle delimiters at formula root
---
.../tinyexpression-p4-lsp-vscode/grammar/tinyexpression-p4.ubnf | 1 +
1 file changed, 1 insertion(+)
diff --git a/tools/tinyexpression-p4-lsp-vscode/grammar/tinyexpression-p4.ubnf b/tools/tinyexpression-p4-lsp-vscode/grammar/tinyexpression-p4.ubnf
index a7077c07..e97d006c 100644
--- a/tools/tinyexpression-p4-lsp-vscode/grammar/tinyexpression-p4.ubnf
+++ b/tools/tinyexpression-p4-lsp-vscode/grammar/tinyexpression-p4.ubnf
@@ -12,6 +12,7 @@ grammar TinyExpressionP4 {
token EOF = org.unlaxer.parser.elementary.EndOfSourceParser
@root
+ @interleave(profile=javaStyle)
@scopeTree(mode=lexical)
@mapping(FormulaExpr, params=[imports, declarations, expression, methods])
Formula ::= { CodeBlock } { ImportDeclaration @imports } { VariableDeclaration @declarations }
From 26e3187be929380e3f42c191fd81cb3823494ba0 Mon Sep 17 00:00:00 2001
From: opaopa6969 <182220+opaopa6969@users.noreply.github.com>
Date: Wed, 2 Sep 2026 19:15:24 +0900
Subject: [PATCH 06/10] fix: keep comment layout compatible with mapped spans
---
.../unlaxer/tinyexpression/p4/P4PreferredAstMapper.java | 9 +++------
1 file changed, 3 insertions(+), 6 deletions(-)
diff --git a/src/main/java/org/unlaxer/tinyexpression/p4/P4PreferredAstMapper.java b/src/main/java/org/unlaxer/tinyexpression/p4/P4PreferredAstMapper.java
index f5a325bc..3d25591d 100644
--- a/src/main/java/org/unlaxer/tinyexpression/p4/P4PreferredAstMapper.java
+++ b/src/main/java/org/unlaxer/tinyexpression/p4/P4PreferredAstMapper.java
@@ -265,12 +265,9 @@ private static TinyExpressionP4AST parseViaMapperCompat(
private static ParsedAst parseMappedCandidates(
String source, List candidates, boolean allowDefault, long deadlineNanos) {
- Token rootToken = parseRootToken(source, deadlineNanos);
- // Comments are consumed by the 3.0.15 grammar but are not part of the mapped
- // node's token text. Normalize only for the selection check; the parser still
- // receives the original source, preserving source spans and block comments.
- String sourceWithoutComments = TinyExpressionParserCapabilities
- .stripJavaStyleCommentsPreservingLayout(source);
+ String parserSource = TinyExpressionParserCapabilities.stripJavaStyleCommentsPreservingLayout(source);
+ Token rootToken = parseRootToken(parserSource, deadlineNanos);
+ String sourceWithoutComments = parserSource;
RuntimeException lastFailure = null;
for (String candidate : candidates) {
if (candidate == null || candidate.isBlank()) {
From 30b7e31cce540fd633cccfceb9937c70173854dc Mon Sep 17 00:00:00 2001
From: opaopa6969 <182220+opaopa6969@users.noreply.github.com>
Date: Wed, 2 Sep 2026 20:14:37 +0900
Subject: [PATCH 07/10] fix: align p4 lsp generator with unlaxer 3.0.15
---
.../org/unlaxer/tinyexpression/p4/P4PreferredAstMapper.java | 5 ++---
tools/tinyexpression-p4-lsp-vscode/pom.xml | 2 +-
2 files changed, 3 insertions(+), 4 deletions(-)
diff --git a/src/main/java/org/unlaxer/tinyexpression/p4/P4PreferredAstMapper.java b/src/main/java/org/unlaxer/tinyexpression/p4/P4PreferredAstMapper.java
index 3d25591d..0f96dffc 100644
--- a/src/main/java/org/unlaxer/tinyexpression/p4/P4PreferredAstMapper.java
+++ b/src/main/java/org/unlaxer/tinyexpression/p4/P4PreferredAstMapper.java
@@ -265,9 +265,8 @@ private static TinyExpressionP4AST parseViaMapperCompat(
private static ParsedAst parseMappedCandidates(
String source, List candidates, boolean allowDefault, long deadlineNanos) {
- String parserSource = TinyExpressionParserCapabilities.stripJavaStyleCommentsPreservingLayout(source);
- Token rootToken = parseRootToken(parserSource, deadlineNanos);
- String sourceWithoutComments = parserSource;
+ Token rootToken = parseRootToken(source, deadlineNanos);
+ String sourceWithoutComments = source;
RuntimeException lastFailure = null;
for (String candidate : candidates) {
if (candidate == null || candidate.isBlank()) {
diff --git a/tools/tinyexpression-p4-lsp-vscode/pom.xml b/tools/tinyexpression-p4-lsp-vscode/pom.xml
index 48b2fb18..35f81a5b 100644
--- a/tools/tinyexpression-p4-lsp-vscode/pom.xml
+++ b/tools/tinyexpression-p4-lsp-vscode/pom.xml
@@ -23,7 +23,7 @@
org.unlaxer
unlaxer-dsl
- 3.0.14
+ 3.0.15
org.unlaxer
From be5ae4570c19ff3159211579dad5ce98a693da73 Mon Sep 17 00:00:00 2001
From: opaopa6969 <182220+opaopa6969@users.noreply.github.com>
Date: Wed, 2 Sep 2026 20:16:26 +0900
Subject: [PATCH 08/10] fix: accept comments in generated mapper spans
---
.../unlaxer/tinyexpression/p4/P4PreferredAstMapper.java | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/src/main/java/org/unlaxer/tinyexpression/p4/P4PreferredAstMapper.java b/src/main/java/org/unlaxer/tinyexpression/p4/P4PreferredAstMapper.java
index 0f96dffc..3582b6f7 100644
--- a/src/main/java/org/unlaxer/tinyexpression/p4/P4PreferredAstMapper.java
+++ b/src/main/java/org/unlaxer/tinyexpression/p4/P4PreferredAstMapper.java
@@ -267,6 +267,11 @@ private static ParsedAst parseMappedCandidates(
String source, List candidates, boolean allowDefault, long deadlineNanos) {
Token rootToken = parseRootToken(source, deadlineNanos);
String sourceWithoutComments = source;
+ // Interleave removes comments from mapped token text. Normalize only the span
+ // comparison; parsing itself must receive the original source so 3.0.15 can
+ // preserve comment-aware token layout.
+ String sourceForSpanComparison =
+ TinyExpressionParserCapabilities.stripJavaStyleCommentsPreservingLayout(sourceWithoutComments);
RuntimeException lastFailure = null;
for (String candidate : candidates) {
if (candidate == null || candidate.isBlank()) {
@@ -275,7 +280,7 @@ private static ParsedAst parseMappedCandidates(
try {
TinyExpressionP4Mapper.MappedAst mappedAst =
TinyExpressionP4Mapper.mapParsedToken(rootToken, candidate);
- if (!coversWholeSource(sourceWithoutComments, mappedAst.token())) {
+ if (!coversWholeSource(sourceForSpanComparison, mappedAst.token())) {
continue;
}
TinyExpressionP4AST mapped = mappedAst.ast();
From 41b18b77fbe31356c57d3bb5aa9f6a71ddaa97cd Mon Sep 17 00:00:00 2001
From: opaopa6969 <182220+opaopa6969@users.noreply.github.com>
Date: Wed, 2 Sep 2026 20:18:08 +0900
Subject: [PATCH 09/10] fix: interleave comments in structured p4 expressions
---
.../grammar/tinyexpression-p4.ubnf | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/tools/tinyexpression-p4-lsp-vscode/grammar/tinyexpression-p4.ubnf b/tools/tinyexpression-p4-lsp-vscode/grammar/tinyexpression-p4.ubnf
index e97d006c..79c50dc8 100644
--- a/tools/tinyexpression-p4-lsp-vscode/grammar/tinyexpression-p4.ubnf
+++ b/tools/tinyexpression-p4-lsp-vscode/grammar/tinyexpression-p4.ubnf
@@ -561,6 +561,7 @@ grammar TinyExpressionP4 {
// ── Control flow ───────────────────────────────────────────────────────────
+ @interleave(profile=javaStyle)
@mapping(IfExpr, params=[condition, thenExpr, elseExpr])
IfExpression ::=
'if' '(' BooleanExpression @condition ')'
@@ -585,6 +586,7 @@ grammar TinyExpressionP4 {
// ── Match expressions ──────────────────────────────────────────────────────
+ @interleave(profile=javaStyle)
@mapping(NumberMatchExpr, params=[firstCase, moreCases, defaultCase])
NumberMatchExpression ::=
'match' '{'
@@ -599,6 +601,7 @@ grammar TinyExpressionP4 {
@mapping(NumberCaseValueExpr, params=[value])
NumberCaseValue ::= NumberExpression @value ;
+ @interleave(profile=javaStyle)
@mapping(StringMatchExpr, params=[firstCase, moreCases, defaultCase])
StringMatchExpression ::=
'match' '{'
@@ -613,6 +616,7 @@ grammar TinyExpressionP4 {
@mapping(StringCaseValueExpr, params=[value])
StringCaseValue ::= StringExpression @value ;
+ @interleave(profile=javaStyle)
@mapping(BooleanMatchExpr, params=[firstCase, moreCases, defaultCase])
BooleanMatchExpression ::=
'match' '{'
From eea88f32a74a330d3f41be174dc87fb52c68d1fb Mon Sep 17 00:00:00 2001
From: opaopa6969 <182220+opaopa6969@users.noreply.github.com>
Date: Wed, 2 Sep 2026 20:19:43 +0900
Subject: [PATCH 10/10] fix: preserve comment parsing compatibility
---
.../tinyexpression/p4/P4PreferredAstMapper.java | 12 +++++-------
.../grammar/tinyexpression-p4.ubnf | 4 ----
2 files changed, 5 insertions(+), 11 deletions(-)
diff --git a/src/main/java/org/unlaxer/tinyexpression/p4/P4PreferredAstMapper.java b/src/main/java/org/unlaxer/tinyexpression/p4/P4PreferredAstMapper.java
index 3582b6f7..e8e8d3cf 100644
--- a/src/main/java/org/unlaxer/tinyexpression/p4/P4PreferredAstMapper.java
+++ b/src/main/java/org/unlaxer/tinyexpression/p4/P4PreferredAstMapper.java
@@ -265,13 +265,11 @@ private static TinyExpressionP4AST parseViaMapperCompat(
private static ParsedAst parseMappedCandidates(
String source, List candidates, boolean allowDefault, long deadlineNanos) {
- Token rootToken = parseRootToken(source, deadlineNanos);
- String sourceWithoutComments = source;
- // Interleave removes comments from mapped token text. Normalize only the span
- // comparison; parsing itself must receive the original source so 3.0.15 can
- // preserve comment-aware token layout.
- String sourceForSpanComparison =
- TinyExpressionParserCapabilities.stripJavaStyleCommentsPreservingLayout(sourceWithoutComments);
+ // Keep source offsets stable while accepting comments in positions where the generated
+ // grammar's interleave metadata is not applied to nested alternatives.
+ String parserSource = TinyExpressionParserCapabilities.stripJavaStyleCommentsPreservingLayout(source);
+ Token rootToken = parseRootToken(parserSource, deadlineNanos);
+ String sourceForSpanComparison = parserSource;
RuntimeException lastFailure = null;
for (String candidate : candidates) {
if (candidate == null || candidate.isBlank()) {
diff --git a/tools/tinyexpression-p4-lsp-vscode/grammar/tinyexpression-p4.ubnf b/tools/tinyexpression-p4-lsp-vscode/grammar/tinyexpression-p4.ubnf
index 79c50dc8..e97d006c 100644
--- a/tools/tinyexpression-p4-lsp-vscode/grammar/tinyexpression-p4.ubnf
+++ b/tools/tinyexpression-p4-lsp-vscode/grammar/tinyexpression-p4.ubnf
@@ -561,7 +561,6 @@ grammar TinyExpressionP4 {
// ── Control flow ───────────────────────────────────────────────────────────
- @interleave(profile=javaStyle)
@mapping(IfExpr, params=[condition, thenExpr, elseExpr])
IfExpression ::=
'if' '(' BooleanExpression @condition ')'
@@ -586,7 +585,6 @@ grammar TinyExpressionP4 {
// ── Match expressions ──────────────────────────────────────────────────────
- @interleave(profile=javaStyle)
@mapping(NumberMatchExpr, params=[firstCase, moreCases, defaultCase])
NumberMatchExpression ::=
'match' '{'
@@ -601,7 +599,6 @@ grammar TinyExpressionP4 {
@mapping(NumberCaseValueExpr, params=[value])
NumberCaseValue ::= NumberExpression @value ;
- @interleave(profile=javaStyle)
@mapping(StringMatchExpr, params=[firstCase, moreCases, defaultCase])
StringMatchExpression ::=
'match' '{'
@@ -616,7 +613,6 @@ grammar TinyExpressionP4 {
@mapping(StringCaseValueExpr, params=[value])
StringCaseValue ::= StringExpression @value ;
- @interleave(profile=javaStyle)
@mapping(BooleanMatchExpr, params=[firstCase, moreCases, defaultCase])
BooleanMatchExpression ::=
'match' '{'