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..e8e8d3cf 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;
@@ -267,41 +265,35 @@ 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.
+ // 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;
- 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(sourceForSpanComparison, 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 +426,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 +435,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) {}
diff --git a/tools/tinyexpression-p4-lsp-vscode/grammar/tinyexpression-p4.ubnf b/tools/tinyexpression-p4-lsp-vscode/grammar/tinyexpression-p4.ubnf
index 84d7776e..e97d006c 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
@@ -13,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 }
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