From f3a0463e8ee3eec561c09b2aa2c876ff0a6093da Mon Sep 17 00:00:00 2001 From: Dmitri Plotnikov Date: Wed, 23 Sep 2026 14:30:00 -0700 Subject: [PATCH] [Pratt Parser] Add a fuzzer comparing ANTLR and Pratt parser outputs and fix uncovered discrepancies Added `CelPrattParserFuzzer` to fuzz CEL inputs against both `AntlrParser` and `PrattParser`, asserting error parity and AST equality. Parser differences uncovered and fixed in `PrattParser` / `Lexer`: 1. **Mixed unary operator chains (e.g., `-!ll`, `!-x`)**: ANTLR requires consecutive unary operators to be homogeneous (`!`/`-`) and only allows `-` after `!` when immediately followed by an integer or floating-point literal (e.g., `!-42`), whereas Pratt previously allowed arbitrary mixtures of `!` and `-` without parentheses. 2. **Vertical tab (`\v`, ASCII 11)**: ANTLR does not treat `\v` as whitespace, whereas `Lexer` and `PrattParser` previously skipped it. 3. **Unquoted `.in` field selector**: ANTLR treats `in` as a keyword token and rejects unquoted `x.in` (requiring backtick-quoted `` x.`in` ``), whereas Pratt previously accepted unquoted `.in` after `.`. 4. **Chained optional select (`T.?a.?a`) AST positions**: ANTLR records the position of the field constant in `_?._` at the start of the `member` expression (`T`), whereas Pratt stopped at intermediate `.?`/`[]`/`()` nodes. 5. **Numeric literals immediately followed by identifier characters (e.g., `9in-x`)**: ANTLR tokenizes numeric literals (`NUM_INT`, `NUM_UINT`, `NUM_FLOAT`) without rejecting trailing identifier characters so `9in-x` parses as `9 in -x`, whereas Pratt's `Lexer` previously rejected trailing identifier characters at lexing time. 6. **Invalid quoted field selectors inside `has(...)` (e.g., `` has(a.`$b`) ``)**: When `normalizeIdent()` rejects an invalid backtick-quoted field name, `PrattParser` previously still constructed a `CelSelect` with an empty field string, causing `CelExprFactory.newSelect()` to throw `IllegalArgumentException` during `has()` macro expansion instead of returning an unset error expression like `AntlrParser`. Intentional parser differences ignored by `CelPrattParserFuzzer` (where `PrattParser` behavior is preferred): 1. **Raw byte string literal prefixes (`rb'...'`, `rB'...'`, `Rb'...'`, `RB'...'`)**: ANTLR only accepts `br`/`bR`/`Br`/`BR` prefix order, whereas Pratt accepts both `br` and `rb`. 2. **Standalone commas in empty collection literals (`[,]`, `{,}`, `Msg{,}`)**: ANTLR accepts empty collections containing only a comma, whereas Pratt requires at least one element/entry before a trailing comma. 3. **Leading-dot identifier positions (`.R`)**: Pratt records the position of leading-dot identifiers at the `.` token, whereas ANTLR records it at the identifier token after `.`. PiperOrigin-RevId: 987007823 --- .../src/main/java/dev/cel/parser/Lexer.java | 13 +- .../main/java/dev/cel/parser/PrattParser.java | 241 +++++++++++------- .../parser/CelParserParameterizedTest.java | 20 +- .../java/dev/cel/parser/PrattParserTest.java | 21 ++ .../resources/parser_core_syntax.baseline | 117 ++++++++- .../src/test/resources/parser_errors.baseline | 162 +++++++++++- .../test/resources/parser_literals.baseline | 12 +- .../pratt_parser_core_syntax.baseline | 117 ++++++++- .../resources/pratt_parser_errors.baseline | 117 ++++++++- .../resources/pratt_parser_literals.baseline | 27 +- 10 files changed, 718 insertions(+), 129 deletions(-) diff --git a/parser/src/main/java/dev/cel/parser/Lexer.java b/parser/src/main/java/dev/cel/parser/Lexer.java index 602a6ef00..1d8fdfdaa 100644 --- a/parser/src/main/java/dev/cel/parser/Lexer.java +++ b/parser/src/main/java/dev/cel/parser/Lexer.java @@ -428,7 +428,6 @@ private void consumeWhitespaceAndComments() { case '\n': case ' ': case '\r': - case 11: // \v case '\t': position++; break; @@ -589,18 +588,12 @@ private Token consumeNumericLiteral() { } } else { advance(1); - if (c == '0' && consume('x')) { + if (c == '0' && (consume('x') || consume('X'))) { if (!consumeHexDigits()) { return setError( start, position, "integral literal missing digits after hexadecimal separator"); } TokenType tokenType = consumeIntegralSuffix(); - if (consumeIf(Lexer::isIdentTrailing)) { - return setError( - start, - position, - tokenType.getSymbol() + " literal has unexpected trailing characters"); - } return makeToken(tokenType, start, position); } consumeDigits(); @@ -622,10 +615,6 @@ && isDigit(content.get(position + 1))) { } } TokenType tokenType = floatingPoint ? TokenType.FLOAT : consumeIntegralSuffix(); - if (consumeIf(Lexer::isIdentTrailing)) { - return setError( - start, position, tokenType.getSymbol() + " literal has unexpected trailing characters"); - } return makeToken(tokenType, start, position); } diff --git a/parser/src/main/java/dev/cel/parser/PrattParser.java b/parser/src/main/java/dev/cel/parser/PrattParser.java index fe396c37e..ede52da93 100644 --- a/parser/src/main/java/dev/cel/parser/PrattParser.java +++ b/parser/src/main/java/dev/cel/parser/PrattParser.java @@ -16,7 +16,6 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Iterables; import dev.cel.common.CelAbstractSyntaxTree; import dev.cel.common.CelIssue; import dev.cel.common.CelOptions; @@ -238,6 +237,32 @@ private Lexer.Token nextSignificantToken(boolean reportError) { return tok; } + private boolean isStructCreationAhead() { + if (peekToken.type == Lexer.TokenType.LEFT_BRACE) { + return true; + } + if (peekToken.type != Lexer.TokenType.DOT) { + return false; + } + int savedPos = lexer.savePosition(); + try { + Lexer.Token tok = peekToken; + while (tok.type == Lexer.TokenType.DOT) { + tok = nextSignificantToken(/* reportError= */ false); + if (tok.type != Lexer.TokenType.IDENT && tok.type != Lexer.TokenType.RESERVED_WORD) { + return false; + } + if (isQuotedIdent(tok)) { + return false; + } + tok = nextSignificantToken(/* reportError= */ false); + } + return tok.type == Lexer.TokenType.LEFT_BRACE; + } finally { + lexer.restorePosition(savedPos); + } + } + private Lexer.Token nextToken() { currentToken = peekToken; if (isRecoveryLimitExceeded()) { @@ -537,24 +562,42 @@ private static CelExpr buildUnaryCall(long id, String function, CelExpr operand) } private CelExpr parseSelectorChain() { + Lexer.TokenType tok = peekToken.type; + if (tok == Lexer.TokenType.EXCLAMATION || tok == Lexer.TokenType.MINUS) { + return parseUnaryOps(); + } + return parseMember(); + } + + private CelExpr parseMember() { lastParsedDepth = 0; + int memberStart = peekToken.start; + boolean startedWithIdentOrDot = + peekToken.type == Lexer.TokenType.DOT + || peekToken.type == Lexer.TokenType.IDENT + || peekToken.type == Lexer.TokenType.RESERVED_WORD; + CelExpr lhs = parsePrimary(); + boolean canBeStructName = + startedWithIdentOrDot + && (currentToken.type == Lexer.TokenType.IDENT + || currentToken.type == Lexer.TokenType.RESERVED_WORD) + && !isQuotedIdent(currentToken); Lexer.TokenType tok = peekToken.type; - CelExpr lhs = - (tok == Lexer.TokenType.EXCLAMATION || tok == Lexer.TokenType.MINUS) - ? parseUnaryOps() - : parsePrimary(); - tok = peekToken.type; if (tok == Lexer.TokenType.DOT || tok == Lexer.TokenType.LEFT_BRACKET || tok == Lexer.TokenType.LEFT_BRACE) { // A parenthesized primary such as "(a.b.c)" already contributes its own depth, which the // selectors trailing the closing ')' continue to accumulate on top of. - lhs = parseSelectorChainTail(lhs, lastParsedDepth); + lhs = parseSelectorChainTail(lhs, lastParsedDepth, memberStart, canBeStructName); } return lhs; } - private CelExpr parseSelectorChainTail(CelExpr initialLhs, int initialChainDepth) { + private CelExpr parseSelectorChainTail( + CelExpr initialLhs, + int initialChainDepth, + int memberStartPosition, + boolean canBeStructName) { CelExpr lhs = initialLhs; int chainDepth = initialChainDepth; while (true) { @@ -574,9 +617,7 @@ private CelExpr parseSelectorChainTail(CelExpr initialLhs, int initialChainDepth } } Lexer.Token idTok = nextToken(); - if (idTok.type != Lexer.TokenType.IDENT - && idTok.type != Lexer.TokenType.RESERVED_WORD - && idTok.type != Lexer.TokenType.IN) { + if (idTok.type != Lexer.TokenType.IDENT && idTok.type != Lexer.TokenType.RESERVED_WORD) { if (idTok.type != Lexer.TokenType.ERROR) { reportSyntaxError(idTok, "expected identifier after '.'"); } @@ -586,11 +627,17 @@ private CelExpr parseSelectorChainTail(CelExpr initialLhs, int initialChainDepth } boolean isMemberCall = (peekToken.type == Lexer.TokenType.LEFT_PAREN); String idText = normalizeIdent(idTok, /* allowQuoted= */ !isMemberCall); + if (idText.isEmpty()) { + synchronizeOnDelimiter(); + lastParsedDepth = chainDepth; + return ERROR; + } if (optional) { long opId = nextId(dotTok); CelExpr field = - CelExpr.ofConstant(nextId(getLeftmostPosition(lhs)), CelConstant.ofValue(idText)); + CelExpr.ofConstant(nextId(memberStartPosition), CelConstant.ofValue(idText)); lhs = buildBinaryCall(opId, Operator.OPTIONAL_SELECT.getFunction(), lhs, field); + canBeStructName = false; } else if (peekToken.type == Lexer.TokenType.LEFT_PAREN) { Lexer.Token lparen = nextToken(); long callId = nextId(lparen); @@ -604,8 +651,10 @@ private CelExpr parseSelectorChainTail(CelExpr initialLhs, int initialChainDepth // level below the call node, so "a.f(b.c.d.e)" is 4 deep. The max preserves the // selectors already walked when the arguments are shallower, as in "a.b.c.f(1)". chainDepth = Math.max(chainDepth, lastParsedDepth + 1); + canBeStructName = false; } else { lhs = CelExpr.ofSelect(nextId(dotTok), lhs, idText, /* isTestOnly= */ false); + canBeStructName = canBeStructName && !isQuotedIdent(idTok); } } else if (tok == Lexer.TokenType.LEFT_BRACKET) { if (checkRecursion(chainDepth, peekToken)) { @@ -631,7 +680,11 @@ private CelExpr parseSelectorChainTail(CelExpr initialLhs, int initialChainDepth // below the index node, so "a[b.c.d.e]" is 4 deep. The max preserves the selectors // already walked when the index is shallower, as in "a.b.c[0]". chainDepth = Math.max(chainDepth, lastParsedDepth + 1); + canBeStructName = false; } else if (tok == Lexer.TokenType.LEFT_BRACE) { + if (!canBeStructName) { + break; + } String structName = extractStructName(lhs); if (structName == null) { break; @@ -642,6 +695,7 @@ private CelExpr parseSelectorChainTail(CelExpr initialLhs, int initialChainDepth // a primary rather than a chain link, so it adds no level of its own. The max preserves // the selectors already walked when the fields are shallower, as in "a.b.Msg{f: 1}". chainDepth = Math.max(chainDepth, lastParsedDepth); + canBeStructName = false; } else { break; } @@ -651,83 +705,44 @@ private CelExpr parseSelectorChainTail(CelExpr initialLhs, int initialChainDepth } private CelExpr parseUnaryOps() { - Lexer.Token op = nextToken(); - Lexer.TokenType opType = op.type; - if (peekToken.type == Lexer.TokenType.EXCLAMATION || peekToken.type == Lexer.TokenType.MINUS) { - return parseUnaryOpsChain(op); - } - - if (opType == Lexer.TokenType.MINUS) { - if (peekToken.type == Lexer.TokenType.INT) { - return parseIntLiteral(nextId(peekToken), /* isNegative= */ true); - } - if (peekToken.type == Lexer.TokenType.FLOAT) { - return parseDoubleLiteral(nextId(peekToken), /* isNegative= */ true); - } - } - - if (checkRecursion(0, op)) { - return ERROR; - } - - long opId = nextId(op); - recursionDepth++; - CelExpr operand = parseSelectorChain(); - recursionDepth--; - if (recursionLimitExceeded) { - return ERROR; - } - - String opName = - (opType == Lexer.TokenType.EXCLAMATION) - ? Operator.LOGICAL_NOT.getFunction() - : Operator.NEGATE.getFunction(); - return buildUnaryCall(opId, opName, operand); - } - - private CelExpr parseUnaryOpsChain(Lexer.Token firstOp) { + Lexer.Token firstOp = nextToken(); + Lexer.TokenType opType = firstOp.type; List ops = new ArrayList<>(); ops.add(new UnaryOp(firstOp)); - while (peekToken.type == Lexer.TokenType.EXCLAMATION - || peekToken.type == Lexer.TokenType.MINUS) { + while (peekToken.type == opType) { ops.add(new UnaryOp(nextToken())); } - boolean hasSolitaryTrailingMinus = - !ops.isEmpty() - && Iterables.getLast(ops).token.type == Lexer.TokenType.MINUS - && (ops.size() == 1 || ops.get(ops.size() - 2).token.type != Lexer.TokenType.MINUS); - - if (!options.retainRepeatedUnaryOperators()) { - int write = 0; - for (int read = 0; read < ops.size(); ) { - int next = read; - while (next < ops.size() && ops.get(next).token.type == ops.get(read).token.type) { - next++; - } - if ((next - read) % 2 != 0) { - ops.set(write++, ops.get(read)); - } - read = next; + if (opType == Lexer.TokenType.MINUS + && ops.size() == 1 + && (peekToken.type == Lexer.TokenType.INT || peekToken.type == Lexer.TokenType.FLOAT)) { + CelExpr lhs = + (peekToken.type == Lexer.TokenType.INT) + ? parseIntLiteral(nextId(peekToken), /* isNegative= */ true) + : parseDoubleLiteral(nextId(peekToken), /* isNegative= */ true); + lastParsedDepth = 0; + Lexer.TokenType tok = peekToken.type; + if (tok == Lexer.TokenType.DOT + || tok == Lexer.TokenType.LEFT_BRACKET + || tok == Lexer.TokenType.LEFT_BRACE) { + lhs = + parseSelectorChainTail( + lhs, /* initialChainDepth= */ 0, firstOp.start, /* canBeStructName= */ false); } - ops = new ArrayList<>(ops.subList(0, write)); - } - - for (UnaryOp op : ops) { - op.id = nextId(op.token); + return lhs; } - boolean isNegativeNumericLiteral = - hasSolitaryTrailingMinus - && (peekToken.type == Lexer.TokenType.INT || peekToken.type == Lexer.TokenType.FLOAT); - long negativeLiteralOpId = 0; - if (isNegativeNumericLiteral) { - negativeLiteralOpId = Iterables.getLast(ops).id; - ops.remove(ops.size() - 1); + if (!options.retainRepeatedUnaryOperators()) { + if (ops.size() % 2 == 0) { + ops.clear(); + } else { + ops = new ArrayList<>(ops.subList(0, 1)); + } } int chainDepth = 0; for (UnaryOp op : ops) { + op.id = nextId(op.token); if (checkRecursion(chainDepth, op.token)) { return ERROR; } @@ -736,14 +751,24 @@ private CelExpr parseUnaryOpsChain(Lexer.Token firstOp) { recursionDepth += ops.size(); CelExpr operand; - if (isNegativeNumericLiteral) { - operand = - (peekToken.type == Lexer.TokenType.INT) - ? parseIntLiteral(negativeLiteralOpId, /* isNegative= */ true) - : parseDoubleLiteral(negativeLiteralOpId, /* isNegative= */ true); - operand = parseSelectorChainTail(operand, /* initialChainDepth= */ 0); + if (opType == Lexer.TokenType.EXCLAMATION && peekToken.type == Lexer.TokenType.MINUS) { + Lexer.Token minusTok = nextToken(); + if (peekToken.type == Lexer.TokenType.INT) { + operand = parseIntLiteral(nextId(peekToken), /* isNegative= */ true); + operand = + parseSelectorChainTail( + operand, /* initialChainDepth= */ 0, minusTok.start, /* canBeStructName= */ false); + } else if (peekToken.type == Lexer.TokenType.FLOAT) { + operand = parseDoubleLiteral(nextId(peekToken), /* isNegative= */ true); + operand = + parseSelectorChainTail( + operand, /* initialChainDepth= */ 0, minusTok.start, /* canBeStructName= */ false); + } else { + reportSyntaxError(minusTok, "unexpected '-'"); + operand = parseMember(); + } } else { - operand = parseSelectorChain(); + operand = parseMember(); } recursionDepth -= ops.size(); @@ -778,7 +803,9 @@ private CelExpr parseIdentOrCall() { return CelExpr.newBuilder().setId(nextId(idTok)).build(); } String idText = normalizeIdent(idTok, /* allowQuoted= */ false); - if (idTok.type == Lexer.TokenType.RESERVED_WORD && options.enableReservedIds()) { + if (idTok.type == Lexer.TokenType.RESERVED_WORD + && options.enableReservedIds() + && !isStructCreationAhead()) { reportError(idTok.start, String.format("reserved identifier: %s", idText)); } String name = leadingDot ? "." + idText : idText; @@ -811,8 +838,19 @@ private CelExpr parsePrimary() { // binary/ternary operators belonging to that enclosing parenthesized level using the // already-parsed inner expression as the LHS. Lexer.Token firstParen = peekToken; + int firstParenStart = firstParen.start; + int[] parenStarts = null; int openParens = 0; while (peekToken.type == Lexer.TokenType.LEFT_PAREN) { + if (openParens > 0) { + if (parenStarts == null) { + parenStarts = new int[8]; + parenStarts[0] = firstParenStart; + } else if (openParens == parenStarts.length) { + parenStarts = Arrays.copyOf(parenStarts, parenStarts.length * 2); + } + parenStarts[openParens] = peekToken.start; + } openParens++; nextToken(); } @@ -828,7 +866,12 @@ private CelExpr parsePrimary() { for (int i = 0; i < openParens; ++i) { expect(Lexer.TokenType.RIGHT_PAREN, ""); if (i < openParens - 1 && peekToken.type != Lexer.TokenType.RIGHT_PAREN) { - expr = parseSelectorChainTail(expr, chainDepth); + expr = + parseSelectorChainTail( + expr, + chainDepth, + parenStarts[openParens - 1 - i], + /* canBeStructName= */ false); expr = parseBinaryAndTernaryFromLhs(expr, 0, lastParsedDepth); chainDepth = lastParsedDepth; } @@ -1018,10 +1061,15 @@ private ImmutableList parseArguments(Lexer.TokenType closeToken) { private CelExpr parseIntLiteral(long nodeId, boolean isNegative) { Lexer.Token tok = nextToken(); - String text = isNegative ? "-" + getTokenText(tok) : getTokenText(tok); + String tokenText = getTokenText(tok); + String text = isNegative ? "-" + tokenText : tokenText; long id = nodeId == -1 ? nextId(tok) : nodeId; try { - CelConstant constExpr = Constants.parseInt(text); + String normalizedText = + tokenText.startsWith("0X") + ? (isNegative ? "-0x" : "0x") + tokenText.substring(2) + : text; + CelConstant constExpr = Constants.parseInt(normalizedText); return CelExpr.ofConstant(id, constExpr); } catch (ParseException e) { reportSyntaxError(tok, "invalid int literal: " + text); @@ -1033,7 +1081,8 @@ private CelExpr parseUintLiteral() { Lexer.Token tok = nextToken(); String value = getTokenText(tok); try { - CelConstant constExpr = Constants.parseUint(value); + String normalizedValue = value.startsWith("0X") ? "0x" + value.substring(2) : value; + CelConstant constExpr = Constants.parseUint(normalizedValue); return CelExpr.ofConstant(nextId(tok), constExpr); } catch (ParseException e) { reportSyntaxError(tok, "invalid uint literal: " + value); @@ -1112,6 +1161,13 @@ private String normalizeIdent(Lexer.Token tok, boolean allowQuoted) { return text; } + private boolean isQuotedIdent(Lexer.Token tok) { + if (tok.text != null) { + return !tok.text.isEmpty() && tok.text.charAt(0) == '`'; + } + return tok.start >= 0 && tok.start < tok.end && content.get(tok.start) == '`'; + } + private static boolean isAsciiAlphanumeric(char c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'); } @@ -1134,13 +1190,6 @@ private static boolean isAsciiAlphanumeric(char c) { return null; } - private int getLeftmostPosition(CelExpr expr) { - while (expr.exprKind().getKind() == CelExpr.ExprKind.Kind.SELECT) { - expr = expr.select().operand(); - } - return getPosition(expr.id()); - } - private @Nullable CelMacro lookupMacro(String id, int argCount, boolean receiverStyle) { if (macros.isEmpty()) { return null; diff --git a/parser/src/test/java/dev/cel/parser/CelParserParameterizedTest.java b/parser/src/test/java/dev/cel/parser/CelParserParameterizedTest.java index 72ba9aab8..9189c7404 100644 --- a/parser/src/test/java/dev/cel/parser/CelParserParameterizedTest.java +++ b/parser/src/test/java/dev/cel/parser/CelParserParameterizedTest.java @@ -295,11 +295,16 @@ public void parser_core_syntax() { runTest("foo.bar.MyType{ a:b }"); runTest(".foo.bar.MyType{ a:b }"); runTest("a.b.c.d.Message{ foo: 1, bar: 'baz' }"); + runTest("import{}"); + runTest(".import{}"); + runTest("import.Foo{}"); + runTest("Foo.import{}"); // Field selection runTest("a.b"); runTest("a.b.c"); runTest("a.?b"); + runTest("a.?b.?c"); runTest("a.`b-c`"); runTest("a.`b c`"); runTest("a.`b.c`"); @@ -330,6 +335,8 @@ public void parser_core_syntax() { runTest("! false"); runTest("-a"); runTest("---a"); + runTest("!-42"); + runTest("!-4.2"); // Arithmetic operators runTest("x * 2"); @@ -355,6 +362,7 @@ public void parser_core_syntax() { runTest("a > b"); runTest("a >= b"); runTest("a in b"); + runTest("9in-x"); runTest("\"\ud83d\ude01\" in [\"\ud83d\ude01\", \"\ud83d\ude11\", \"\ud83d\ude26\"]"); runTest("size(x) == x.size()"); runTest("x.single_nested_message != null"); @@ -375,7 +383,7 @@ public void parser_core_syntax() { runTest("cond ? 1 : 2"); runTest("false && !true || false ? 2 : 3"); runTest(OPTIONS_MAX_RECURSION_DEPTH_32, Strings.repeat("true ? 1 : ", 31) + "1", false); - runAntlrTest(OPTIONS_MAX_RECURSION_DEPTH_32, Strings.repeat("!-", 15) + "x"); + runTest(OPTIONS_MAX_RECURSION_DEPTH_32, Strings.repeat("!-", 15) + "x", false); // Complex expressions runTest("1 + 2 * 3 - 1 / 2 == 6 % 1"); @@ -451,6 +459,7 @@ public void parser_errors() { runTest("*@a | b"); runTest("((@))"); runTest("1 + $"); + runTest("1 \u000b + 2"); runTest( "\u00f3\u00a0\u00a2\n" + "\t\t\u00f3\u00a00\u00a0\n" @@ -464,6 +473,8 @@ public void parser_errors() { // Unexpected tokens runTest("1 + +"); + runTest("-!x"); + runTest("!-x"); runTest("?"); runTest("a ? b ((?))"); runTest("a ? b @"); @@ -543,6 +554,7 @@ public void parser_errors() { // Member selection errors runTest("{\"a\": 1}.\"a\""); runTest("self.true == 1"); + runTest("a.in"); // Map syntax errors runTest("{a}"); @@ -556,6 +568,11 @@ public void parser_errors() { runTest("x{."); runTest("t{>C}"); runTest("has([(has(("); + runTest("(a){}"); + runTest("(a.b){}"); + runTest("(a).b{}"); + runTest("a.`b-c`{}"); + runTest("Msg{`$b`: 1}"); // Macro errors runTest("1.all(2, 3)"); @@ -590,6 +607,7 @@ public void parser_errors() { runTest(OPTIONS_QUOTED_IDENTIFIER_SYNTAX, "`b-c`"); runTest(OPTIONS_QUOTED_IDENTIFIER_SYNTAX, "`b-c`()"); runTest(OPTIONS_QUOTED_IDENTIFIER_SYNTAX, "a.`$b`"); + runTest(OPTIONS_QUOTED_IDENTIFIER_SYNTAX, "has(a.`$b`)"); runTest(OPTIONS_QUOTED_IDENTIFIER_SYNTAX, "a.`b.c`()"); runTest(OPTIONS_QUOTED_IDENTIFIER_SYNTAX, "`bar`"); runTest(OPTIONS_QUOTED_IDENTIFIER_SYNTAX, "foo.``"); diff --git a/parser/src/test/java/dev/cel/parser/PrattParserTest.java b/parser/src/test/java/dev/cel/parser/PrattParserTest.java index 710ff2fcf..ff7233c12 100644 --- a/parser/src/test/java/dev/cel/parser/PrattParserTest.java +++ b/parser/src/test/java/dev/cel/parser/PrattParserTest.java @@ -93,8 +93,10 @@ public void pratt_parser_literals() { runTest("0"); runTest("42"); runTest("0xF"); + runTest("0X12"); runTest("0x2A"); runTest("-1"); + runTest("-0X12"); runTest("-42"); runTest("0xFFFFFFFFFFFFFFFFF"); runTest("9223372036854775807"); // Long.MAX_VALUE @@ -106,6 +108,7 @@ public void pratt_parser_literals() { runTest("0u"); runTest("23u"); runTest("0xFu"); + runTest("0XFu"); runTest("0xFFFFFFFFFFFFFFFFFu"); runTest("123u_"); @@ -224,11 +227,16 @@ public void pratt_parser_core_syntax() { runTest("foo.bar.MyType{ a:b }"); runTest(".foo.bar.MyType{ a:b }"); runTest("a.b.c.d.Message{ foo: 1, bar: 'baz' }"); + runTest("import{}"); + runTest(".import{}"); + runTest("import.Foo{}"); + runTest("Foo.import{}"); // Field selection runTest("a.b"); runTest("a.b.c"); runTest("a.?b"); + runTest("a.?b.?c"); runTest("a.`b-c`"); runTest("a.`b c`"); runTest("a.`b.c`"); @@ -257,6 +265,8 @@ public void pratt_parser_core_syntax() { runTest("!x"); runTest("! false"); runTest("-a"); + runTest("!-42"); + runTest("!-4.2"); // Arithmetic operators runTest("x * 2"); @@ -282,6 +292,7 @@ public void pratt_parser_core_syntax() { runTest("a > b"); runTest("a >= b"); runTest("a in b"); + runTest("9in-x"); runTest("\"\ud83d\ude01\" in [\"\ud83d\ude01\", \"\ud83d\ude11\", \"\ud83d\ude26\"]"); runTest("size(x) == x.size()"); runTest("x.single_nested_message != null"); @@ -358,6 +369,7 @@ public void pratt_parser_errors() { runTest("*@a | b"); runTest("((@))"); runTest("1 + $"); + runTest("1 \u000b + 2"); runTest( "\u00f3\u00a0\u00a2\n" + "\t\t\u00f3\u00a00\u00a0\n" @@ -368,6 +380,8 @@ public void pratt_parser_errors() { // Unexpected tokens runTest("1 + +"); + runTest("-!x"); + runTest("!-x"); runTest("?"); runTest("a ? b ((?))"); runTest("a ? b @"); @@ -427,6 +441,7 @@ public void pratt_parser_errors() { // Member selection errors runTest("{\"a\": 1}.\"a\""); runTest("self.true == 1"); + runTest("a.in"); // Map syntax errors runTest("{a}"); @@ -438,6 +453,11 @@ public void pratt_parser_errors() { runTest("ind[a{b}]"); runTest("x{?."); runTest("x{."); + runTest("(a){}"); + runTest("(a.b){}"); + runTest("(a).b{}"); + runTest("a.`b-c`{}"); + runTest("Msg{`$b`: 1}"); // Macro errors runTest("1.all(2, 3)"); @@ -458,6 +478,7 @@ public void pratt_parser_errors() { runTest(OPTIONS_QUOTED_IDENTIFIER_SYNTAX, "`b-c`"); runTest(OPTIONS_QUOTED_IDENTIFIER_SYNTAX, "`b-c`()"); runTest(OPTIONS_QUOTED_IDENTIFIER_SYNTAX, "a.`$b`"); + runTest(OPTIONS_QUOTED_IDENTIFIER_SYNTAX, "has(a.`$b`)"); runTest(OPTIONS_QUOTED_IDENTIFIER_SYNTAX, "a.`b.c`()"); // Recursion limit exceeded diff --git a/parser/src/test/resources/parser_core_syntax.baseline b/parser/src/test/resources/parser_core_syntax.baseline index 34997c9d8..fe2f6e0a9 100644 --- a/parser/src/test/resources/parser_core_syntax.baseline +++ b/parser/src/test/resources/parser_core_syntax.baseline @@ -326,6 +326,26 @@ L: a.b.c.d.Message{ bar:"baz"^#5[1,30]#^#4[1,28]# }^#1[1,15]# +I: import{} +=====> +P: import{}^#1:Expr.CreateStruct# +L: import{}^#1[1,6]# + +I: .import{} +=====> +P: .import{}^#1:Expr.CreateStruct# +L: .import{}^#1[1,7]# + +I: import.Foo{} +=====> +P: import.Foo{}^#1:Expr.CreateStruct# +L: import.Foo{}^#1[1,10]# + +I: Foo.import{} +=====> +P: Foo.import{}^#1:Expr.CreateStruct# +L: Foo.import{}^#1[1,10]# + I: a.b =====> P: a^#1:Expr.Ident#.b^#2:Expr.Select# @@ -347,6 +367,23 @@ L: _?._( "b"^#3[1,0]# )^#2[1,1]# +I: a.?b.?c +=====> +P: _?._( + _?._( + a^#1:Expr.Ident#, + "b"^#3:string# + )^#2:Expr.Call#, + "c"^#5:string# +)^#4:Expr.Call# +L: _?._( + _?._( + a^#1[1,0]#, + "b"^#3[1,0]# + )^#2[1,1]#, + "c"^#5[1,0]# +)^#4[1,4]# + I: a.`b-c` =====> P: a^#1:Expr.Ident#.b-c^#2:Expr.Select# @@ -553,6 +590,24 @@ L: -_( a^#2[1,3]# )^#1[1,0]# +I: !-42 +=====> +P: !_( + -42^#2:int64# +)^#1:Expr.Call# +L: !_( + -42^#2[1,2]# +)^#1[1,0]# + +I: !-4.2 +=====> +P: !_( + -4.2^#2:double# +)^#1:Expr.Call# +L: !_( + -4.2^#2[1,2]# +)^#1[1,0]# + I: x * 2 =====> P: _*_( @@ -808,6 +863,21 @@ L: @in( b^#3[1,5]# )^#2[1,2]# +I: 9in-x +=====> +P: @in( + 9^#1:int64#, + -_( + x^#4:Expr.Ident# + )^#3:Expr.Call# +)^#2:Expr.Call# +L: @in( + 9^#1[1,0]#, + -_( + x^#4[1,4]# + )^#3[1,3]# +)^#2[1,1]# + I: "😁" in ["😁", "😑", "😦"] =====> P: @in( @@ -1237,6 +1307,51 @@ ERROR: :1:29: no viable alternative at input '-!' ERROR: :1:31: no viable alternative at input '-x' | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-x | ..............................^ +E/P: ERROR: :1:2: Syntax error: unexpected '-' + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-x + | .^ +ERROR: :1:3: Syntax error: unexpected token + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-x + | ..^ +ERROR: :1:6: Syntax error: unexpected '-' + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-x + | .....^ +ERROR: :1:7: Syntax error: unexpected token + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-x + | ......^ +ERROR: :1:10: Syntax error: unexpected '-' + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-x + | .........^ +ERROR: :1:11: Syntax error: unexpected token + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-x + | ..........^ +ERROR: :1:14: Syntax error: unexpected '-' + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-x + | .............^ +ERROR: :1:15: Syntax error: unexpected token + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-x + | ..............^ +ERROR: :1:18: Syntax error: unexpected '-' + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-x + | .................^ +ERROR: :1:19: Syntax error: unexpected token + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-x + | ..................^ +ERROR: :1:22: Syntax error: unexpected '-' + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-x + | .....................^ +ERROR: :1:23: Syntax error: unexpected token + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-x + | ......................^ +ERROR: :1:26: Syntax error: unexpected '-' + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-x + | .........................^ +ERROR: :1:27: Syntax error: unexpected token + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-x + | ..........................^ +ERROR: :1:30: Syntax error: unexpected '-' + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-x + | .............................^ I: 1 + 2 * 3 - 1 / 2 == 6 % 1 =====> @@ -1431,4 +1546,4 @@ P: Struct{ }^#1:Expr.CreateStruct# L: Struct{ in:false^#3[1,13]#^#2[1,11]# -}^#1[1,6]# \ No newline at end of file +}^#1[1,6]# diff --git a/parser/src/test/resources/parser_errors.baseline b/parser/src/test/resources/parser_errors.baseline index 4f23e4a9c..cf143d249 100644 --- a/parser/src/test/resources/parser_errors.baseline +++ b/parser/src/test/resources/parser_errors.baseline @@ -43,6 +43,15 @@ E/P: ERROR: :1:5: Syntax error: unexpected character | 1 + $ | ....^ +I: 1 + 2 +=====> +E/A: ERROR: :1:3: token recognition error at: ' ' + | 1 + 2 + | ..^ +E/P: ERROR: :1:3: Syntax error: unexpected character + | 1 + 2 + | ..^ + I: ó ¢ »»ó 0  »»\u007f0"""\""\"""\""\"""\""\"""\""\"""\"\"""\""\"""\""\"""\""\"""\"!\"""\""\"""\""\" @@ -186,6 +195,27 @@ E/P: ERROR: :1:5: Syntax error: unexpected token | 1 + + | ....^ +I: -!x +=====> +E/A: ERROR: :1:2: no viable alternative at input '-!' + | -!x + | .^ +E/P: ERROR: :1:2: Syntax error: unexpected token + | -!x + | .^ +ERROR: :1:3: Syntax error: unexpected token after expression + | -!x + | ..^ + +I: !-x +=====> +E/A: ERROR: :1:3: no viable alternative at input '-x' + | !-x + | ..^ +E/P: ERROR: :1:2: Syntax error: unexpected '-' + | !-x + | .^ + I: ? =====> E/A: ERROR: :1:1: mismatched input '?' expecting {'[', '{', '(', '.', '-', '!', 'true', 'false', 'null', NUM_FLOAT, NUM_INT, NUM_UINT, STRING, BYTES, IDENTIFIER} @@ -645,6 +675,18 @@ E/P: ERROR: :1:6: Syntax error: expected identifier after '.' | self.true == 1 | .....^ +I: a.in +=====> +E/A: ERROR: :1:3: no viable alternative at input '.in' + | a.in + | ..^ +ERROR: :1:5: mismatched input '' expecting {'[', '{', '(', '.', '-', '!', 'true', 'false', 'null', NUM_FLOAT, NUM_INT, NUM_UINT, STRING, BYTES, IDENTIFIER} + | a.in + | ....^ +E/P: ERROR: :1:3: Syntax error: expected identifier after '.' + | a.in + | ..^ + I: {a} =====> E/A: ERROR: :1:3: mismatched input '}' expecting {'==', '!=', 'in', '<', '<=', '>=', '>', '&&', '||', '[', '.', '-', '?', ':', '+', '*', '/', '%%'} @@ -780,6 +822,57 @@ ERROR: :1:12: Syntax error: mismatched input expecting ')' | has([(has(( | ...........^ +I: (a){} +=====> +E/A: ERROR: :1:4: mismatched input '{' expecting {'==', '!=', 'in', '<', '<=', '>=', '>', '&&', '||', '[', ')', '.', '-', '?', '+', '*', '/', '%%'} + | (a){} + | ...^ +E/P: ERROR: :1:4: Syntax error: unexpected token after expression + | (a){} + | ...^ + +I: (a.b){} +=====> +E/A: ERROR: :1:6: mismatched input '{' expecting {'==', '!=', 'in', '<', '<=', '>=', '>', '&&', '||', '[', ')', '.', '-', '?', '+', '*', '/', '%%'} + | (a.b){} + | .....^ +E/P: ERROR: :1:6: Syntax error: unexpected token after expression + | (a.b){} + | .....^ + +I: (a).b{} +=====> +E/A: ERROR: :1:6: mismatched input '{' expecting {, '==', '!=', 'in', '<', '<=', '>=', '>', '&&', '||', '[', '.', '-', '?', '+', '*', '/', '%%'} + | (a).b{} + | .....^ +E/P: ERROR: :1:6: Syntax error: unexpected token after expression + | (a).b{} + | .....^ + +I: a.`b-c`{} +=====> +E/A: ERROR: :1:8: mismatched input '{' expecting {, '==', '!=', 'in', '<', '<=', '>=', '>', '&&', '||', '[', '.', '-', '?', '+', '*', '/', '%%'} + | a.`b-c`{} + | .......^ +E/P: ERROR: :1:8: Syntax error: unexpected token after expression + | a.`b-c`{} + | .......^ + +I: Msg{`$b`: 1} +=====> +E/A: ERROR: :1:5: token recognition error at: '`$' + | Msg{`$b`: 1} + | ....^ +ERROR: :1:8: token recognition error at: '`:' + | Msg{`$b`: 1} + | .......^ +ERROR: :1:11: missing ':' at '1' + | Msg{`$b`: 1} + | ..........^ +E/P: ERROR: :1:5: unexpected quoted identifier + | Msg{`$b`: 1} + | ....^ + I: 1.all(2, 3) =====> E/A: ERROR: :1:7: The argument must be a simple name @@ -1050,6 +1143,21 @@ E/P: ERROR: :1:3: unexpected quoted identifier | a.`$b` | ..^ +I: has(a.`$b`) +=====> +E/A: ERROR: :1:7: token recognition error at: '`$' + | has(a.`$b`) + | ......^ +ERROR: :1:10: token recognition error at: '`)' + | has(a.`$b`) + | .........^ +ERROR: :1:12: missing ')' at '' + | has(a.`$b`) + | ...........^ +E/P: ERROR: :1:7: unexpected quoted identifier + | has(a.`$b`) + | ......^ + I: a.`b.c`() =====> E/A: ERROR: :1:8: mismatched input '(' expecting {, '==', '!=', 'in', '<', '<=', '>=', '>', '&&', '||', '[', '.', '-', '?', '+', '*', '/', '%%'} @@ -1058,6 +1166,9 @@ E/A: ERROR: :1:8: mismatched input '(' expecting {, '==', '!=', 'in' E/P: ERROR: :1:3: unexpected quoted identifier | a.`b.c`() | ..^ +ERROR: :1:9: Syntax error: unexpected token after expression + | a.`b.c`() + | ........^ I: `bar` =====> @@ -1239,9 +1350,54 @@ ERROR: :1:31: no viable alternative at input '-!' ERROR: :1:33: no viable alternative at input '-!' | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!x | ................................^ -E/P: ERROR: :1:33: Expression recursion limit exceeded. limit: 32 +E/P: ERROR: :1:2: Syntax error: unexpected '-' | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!x - | ................................^ + | .^ +ERROR: :1:3: Syntax error: unexpected token + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!x + | ..^ +ERROR: :1:6: Syntax error: unexpected '-' + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!x + | .....^ +ERROR: :1:7: Syntax error: unexpected token + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!x + | ......^ +ERROR: :1:10: Syntax error: unexpected '-' + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!x + | .........^ +ERROR: :1:11: Syntax error: unexpected token + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!x + | ..........^ +ERROR: :1:14: Syntax error: unexpected '-' + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!x + | .............^ +ERROR: :1:15: Syntax error: unexpected token + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!x + | ..............^ +ERROR: :1:18: Syntax error: unexpected '-' + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!x + | .................^ +ERROR: :1:19: Syntax error: unexpected token + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!x + | ..................^ +ERROR: :1:22: Syntax error: unexpected '-' + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!x + | .....................^ +ERROR: :1:23: Syntax error: unexpected token + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!x + | ......................^ +ERROR: :1:26: Syntax error: unexpected '-' + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!x + | .........................^ +ERROR: :1:27: Syntax error: unexpected token + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!x + | ..........................^ +ERROR: :1:30: Syntax error: unexpected '-' + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!x + | .............................^ +ERROR: :1:31: Syntax error: unexpected token + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!x + | ..............................^ I: 123456 =====> @@ -1278,4 +1434,4 @@ E/P: ERROR: :1:4: Syntax error: expected ']' | ...^ ERROR: :1:13: Syntax error: unexpected token after expression | [1 2 3 a b c] - | ............^ \ No newline at end of file + | ............^ diff --git a/parser/src/test/resources/parser_literals.baseline b/parser/src/test/resources/parser_literals.baseline index f4716e927..e0139c99d 100644 --- a/parser/src/test/resources/parser_literals.baseline +++ b/parser/src/test/resources/parser_literals.baseline @@ -76,9 +76,9 @@ I: 123a E/A: ERROR: :1:4: extraneous input 'a' expecting | 123a | ...^ -E/P: ERROR: :1:1: Syntax error: int literal has unexpected trailing characters +E/P: ERROR: :1:4: Syntax error: unexpected token after expression | 123a - | ^ + | ...^ I: 0u =====> @@ -129,9 +129,9 @@ I: 123u_ E/A: ERROR: :1:5: extraneous input '_' expecting | 123u_ | ....^ -E/P: ERROR: :1:1: Syntax error: uint literal has unexpected trailing characters +E/P: ERROR: :1:5: Syntax error: unexpected token after expression | 123u_ - | ^ + | ....^ I: 3.14 =====> @@ -245,9 +245,9 @@ I: 0x123z E/A: ERROR: :1:6: extraneous input 'z' expecting | 0x123z | .....^ -E/P: ERROR: :1:1: Syntax error: int literal has unexpected trailing characters +E/P: ERROR: :1:6: Syntax error: unexpected token after expression | 0x123z - | ^ + | .....^ I: 'hello' =====> diff --git a/parser/src/test/resources/pratt_parser_core_syntax.baseline b/parser/src/test/resources/pratt_parser_core_syntax.baseline index fb9d94e58..e7fa1c04a 100644 --- a/parser/src/test/resources/pratt_parser_core_syntax.baseline +++ b/parser/src/test/resources/pratt_parser_core_syntax.baseline @@ -326,6 +326,26 @@ L: a.b.c.d.Message{ bar:"baz"^#5[1,30]#^#4[1,28]# }^#1[1,15]# +I: import{} +=====> +P: import{}^#1:Expr.CreateStruct# +L: import{}^#1[1,6]# + +I: .import{} +=====> +P: .import{}^#1:Expr.CreateStruct# +L: .import{}^#1[1,7]# + +I: import.Foo{} +=====> +P: import.Foo{}^#1:Expr.CreateStruct# +L: import.Foo{}^#1[1,10]# + +I: Foo.import{} +=====> +P: Foo.import{}^#1:Expr.CreateStruct# +L: Foo.import{}^#1[1,10]# + I: a.b =====> P: a^#1:Expr.Ident#.b^#2:Expr.Select# @@ -347,6 +367,23 @@ L: _?._( "b"^#3[1,0]# )^#2[1,1]# +I: a.?b.?c +=====> +P: _?._( + _?._( + a^#1:Expr.Ident#, + "b"^#3:string# + )^#2:Expr.Call#, + "c"^#5:string# +)^#4:Expr.Call# +L: _?._( + _?._( + a^#1[1,0]#, + "b"^#3[1,0]# + )^#2[1,1]#, + "c"^#5[1,0]# +)^#4[1,4]# + I: a.`b-c` =====> P: a^#1:Expr.Ident#.b-c^#2:Expr.Select# @@ -535,6 +572,24 @@ L: -_( a^#2[1,1]# )^#1[1,0]# +I: !-42 +=====> +P: !_( + -42^#2:int64# +)^#1:Expr.Call# +L: !_( + -42^#2[1,2]# +)^#1[1,0]# + +I: !-4.2 +=====> +P: !_( + -4.2^#2:double# +)^#1:Expr.Call# +L: !_( + -4.2^#2[1,2]# +)^#1[1,0]# + I: x * 2 =====> P: _*_( @@ -790,6 +845,21 @@ L: @in( b^#3[1,5]# )^#2[1,2]# +I: 9in-x +=====> +P: @in( + 9^#1:int64#, + -_( + x^#4:Expr.Ident# + )^#3:Expr.Call# +)^#2:Expr.Call# +L: @in( + 9^#1[1,0]#, + -_( + x^#4[1,4]# + )^#3[1,3]# +)^#2[1,1]# + I: "😁" in ["😁", "😑", "😦"] =====> P: @in( @@ -1174,6 +1244,51 @@ I: true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : I: !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-x =====> +E: ERROR: :1:2: Syntax error: unexpected '-' + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-x + | .^ +ERROR: :1:3: Syntax error: unexpected token + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-x + | ..^ +ERROR: :1:6: Syntax error: unexpected '-' + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-x + | .....^ +ERROR: :1:7: Syntax error: unexpected token + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-x + | ......^ +ERROR: :1:10: Syntax error: unexpected '-' + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-x + | .........^ +ERROR: :1:11: Syntax error: unexpected token + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-x + | ..........^ +ERROR: :1:14: Syntax error: unexpected '-' + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-x + | .............^ +ERROR: :1:15: Syntax error: unexpected token + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-x + | ..............^ +ERROR: :1:18: Syntax error: unexpected '-' + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-x + | .................^ +ERROR: :1:19: Syntax error: unexpected token + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-x + | ..................^ +ERROR: :1:22: Syntax error: unexpected '-' + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-x + | .....................^ +ERROR: :1:23: Syntax error: unexpected token + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-x + | ......................^ +ERROR: :1:26: Syntax error: unexpected '-' + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-x + | .........................^ +ERROR: :1:27: Syntax error: unexpected token + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-x + | ..........................^ +ERROR: :1:30: Syntax error: unexpected '-' + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-x + | .............................^ I: 1 + 2 * 3 - 1 / 2 == 6 % 1 =====> @@ -1310,4 +1425,4 @@ P: [ L: [ 1^#2[2,2]#, 2^#3[3,2]# -]^#1[1,0]# \ No newline at end of file +]^#1[1,0]# diff --git a/parser/src/test/resources/pratt_parser_errors.baseline b/parser/src/test/resources/pratt_parser_errors.baseline index 541f9e14f..2dbda58f1 100644 --- a/parser/src/test/resources/pratt_parser_errors.baseline +++ b/parser/src/test/resources/pratt_parser_errors.baseline @@ -19,6 +19,12 @@ E: ERROR: :1:5: Syntax error: unexpected character | 1 + $ | ....^ +I: 1 + 2 +=====> +E: ERROR: :1:3: Syntax error: unexpected character + | 1 + 2 + | ..^ + I: ó ¢ »»ó 0  »»\u007f0"""\""\"""\""\"""\""\"""\""\"""\"\"""\""\"""\""\"""\""\"""\"!\"""\""\"""\""\" @@ -54,6 +60,21 @@ E: ERROR: :1:5: Syntax error: unexpected token | 1 + + | ....^ +I: -!x +=====> +E: ERROR: :1:2: Syntax error: unexpected token + | -!x + | .^ +ERROR: :1:3: Syntax error: unexpected token after expression + | -!x + | ..^ + +I: !-x +=====> +E: ERROR: :1:2: Syntax error: unexpected '-' + | !-x + | .^ + I: ? =====> E: ERROR: :1:1: Syntax error: unexpected token @@ -250,6 +271,12 @@ E: ERROR: :1:6: Syntax error: expected identifier after '.' | self.true == 1 | .....^ +I: a.in +=====> +E: ERROR: :1:3: Syntax error: expected identifier after '.' + | a.in + | ..^ + I: {a} =====> E: ERROR: :1:3: Syntax error: expected ':' in map entry @@ -304,6 +331,36 @@ ERROR: :1:4: Syntax error: expected '}' | x{. | ...^ +I: (a){} +=====> +E: ERROR: :1:4: Syntax error: unexpected token after expression + | (a){} + | ...^ + +I: (a.b){} +=====> +E: ERROR: :1:6: Syntax error: unexpected token after expression + | (a.b){} + | .....^ + +I: (a).b{} +=====> +E: ERROR: :1:6: Syntax error: unexpected token after expression + | (a).b{} + | .....^ + +I: a.`b-c`{} +=====> +E: ERROR: :1:8: Syntax error: unexpected token after expression + | a.`b-c`{} + | .......^ + +I: Msg{`$b`: 1} +=====> +E: ERROR: :1:5: unexpected quoted identifier + | Msg{`$b`: 1} + | ....^ + I: 1.all(2, 3) =====> E: ERROR: :1:7: The argument must be a simple name @@ -385,11 +442,20 @@ E: ERROR: :1:3: unexpected quoted identifier | a.`$b` | ..^ +I: has(a.`$b`) +=====> +E: ERROR: :1:7: unexpected quoted identifier + | has(a.`$b`) + | ......^ + I: a.`b.c`() =====> E: ERROR: :1:3: unexpected quoted identifier | a.`b.c`() | ..^ +ERROR: :1:9: Syntax error: unexpected token after expression + | a.`b.c`() + | ........^ I: [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ »»»[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[['too many']]]]]]]]]]]]]]]]]]]]]]]]]]]] @@ -474,9 +540,54 @@ E: ERROR: :1:353: Expression recursion limit exceeded. limit: 32 I: !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!x =====> -E: ERROR: :1:33: Expression recursion limit exceeded. limit: 32 +E: ERROR: :1:2: Syntax error: unexpected '-' | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!x - | ................................^ + | .^ +ERROR: :1:3: Syntax error: unexpected token + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!x + | ..^ +ERROR: :1:6: Syntax error: unexpected '-' + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!x + | .....^ +ERROR: :1:7: Syntax error: unexpected token + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!x + | ......^ +ERROR: :1:10: Syntax error: unexpected '-' + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!x + | .........^ +ERROR: :1:11: Syntax error: unexpected token + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!x + | ..........^ +ERROR: :1:14: Syntax error: unexpected '-' + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!x + | .............^ +ERROR: :1:15: Syntax error: unexpected token + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!x + | ..............^ +ERROR: :1:18: Syntax error: unexpected '-' + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!x + | .................^ +ERROR: :1:19: Syntax error: unexpected token + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!x + | ..................^ +ERROR: :1:22: Syntax error: unexpected '-' + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!x + | .....................^ +ERROR: :1:23: Syntax error: unexpected token + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!x + | ......................^ +ERROR: :1:26: Syntax error: unexpected '-' + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!x + | .........................^ +ERROR: :1:27: Syntax error: unexpected token + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!x + | ..........................^ +ERROR: :1:30: Syntax error: unexpected '-' + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!x + | .............................^ +ERROR: :1:31: Syntax error: unexpected token + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!x + | ..............................^ I: 123456 =====> @@ -505,4 +616,4 @@ E: ERROR: :1:4: Syntax error: expected ']' | ...^ ERROR: :1:13: Syntax error: unexpected token after expression | [1 2 3 a b c] - | ............^ \ No newline at end of file + | ............^ diff --git a/parser/src/test/resources/pratt_parser_literals.baseline b/parser/src/test/resources/pratt_parser_literals.baseline index f6ce2f6b0..74002373b 100644 --- a/parser/src/test/resources/pratt_parser_literals.baseline +++ b/parser/src/test/resources/pratt_parser_literals.baseline @@ -28,6 +28,11 @@ I: 0xF P: 15^#1:int64# L: 15^#1[1,0]# +I: 0X12 +=====> +P: 18^#1:int64# +L: 18^#1[1,0]# + I: 0x2A =====> P: 42^#1:int64# @@ -38,6 +43,11 @@ I: -1 P: -1^#1:int64# L: -1^#1[1,1]# +I: -0X12 +=====> +P: -18^#1:int64# +L: -18^#1[1,1]# + I: -42 =====> P: -42^#1:int64# @@ -67,9 +77,9 @@ E: ERROR: :1:3: Syntax error: invalid int literal: 9223372036854775808 I: 123a =====> -E: ERROR: :1:1: Syntax error: int literal has unexpected trailing characters +E: ERROR: :1:4: Syntax error: unexpected token after expression | 123a - | ^ + | ...^ I: 0u =====> @@ -86,6 +96,11 @@ I: 0xFu P: 15u^#1:uint64# L: 15u^#1[1,0]# +I: 0XFu +=====> +P: 15u^#1:uint64# +L: 15u^#1[1,0]# + I: 0xFFFFFFFFFFFFFFFFFu =====> E: ERROR: :1:1: Syntax error: invalid uint literal: 0xFFFFFFFFFFFFFFFFFu @@ -94,9 +109,9 @@ E: ERROR: :1:1: Syntax error: invalid uint literal: 0xFFFFFFFFFFFFFFFFFu I: 123u_ =====> -E: ERROR: :1:1: Syntax error: uint literal has unexpected trailing characters +E: ERROR: :1:5: Syntax error: unexpected token after expression | 123u_ - | ^ + | ....^ I: 3.14 =====> @@ -183,9 +198,9 @@ E: ERROR: :1:3: Syntax error: floating point literal missing digits after I: 0x123z =====> -E: ERROR: :1:1: Syntax error: int literal has unexpected trailing characters +E: ERROR: :1:6: Syntax error: unexpected token after expression | 0x123z - | ^ + | .....^ I: 'hello' =====>