diff --git a/parser/internal/pratt_parser_test.cc b/parser/internal/pratt_parser_test.cc index 6c85dd1d7..2b0f2ebec 100644 --- a/parser/internal/pratt_parser_test.cc +++ b/parser/internal/pratt_parser_test.cc @@ -482,6 +482,39 @@ std::vector GetParserTestCases() { )^#1:Expr.Call# )", }, + TestCase{ + .source = "-.2.V", + .expected_ast = R"( + -0.2^#1:double#.V^#2:Expr.Select# + )", + }, + TestCase{ + .source = "!-.2.V", + .expected_ast = R"( + !_( + -0.2^#2:double#.V^#3:Expr.Select# + )^#1:Expr.Call# + )", + }, + TestCase{ + .source = "!-2.V", + .expected_ast = R"( + !_( + -2^#2:int64#.V^#3:Expr.Select# + )^#1:Expr.Call# + )", + }, + TestCase{ + .source = "!-.2[0]", + .expected_ast = R"( + !_( + _[_]( + -0.2^#2:double#, + 0^#4:int64# + )^#3:Expr.Call# + )^#1:Expr.Call# + )", + }, TestCase{ .source = "a + b", .expected_ast = R"( @@ -1156,21 +1189,24 @@ std::vector GetErrorTestCases() { ErrorTestCase{ .source = "1 + 2 * 3 4", .expected_error = - "ERROR: :1:11: unexpected token after expression\n" + "ERROR: :1:11: Syntax error: unexpected token after " + "expression\n" " | 1 + 2 * 3 4\n" " | ..........^", }, ErrorTestCase{ .source = "1{}", .expected_error = - "ERROR: :1:2: unexpected token after expression\n" + "ERROR: :1:2: Syntax error: unexpected token after " + "expression\n" " | 1{}\n" " | .^", }, ErrorTestCase{ .source = "true ? 1", .expected_error = - "ERROR: :1:9: expected ':' in conditional expression\n" + "ERROR: :1:9: Syntax error: expected ':' in conditional " + "expression\n" " | true ? 1\n" " | ........^", }, @@ -1182,10 +1218,10 @@ std::vector GetErrorTestCases() { }, ErrorTestCase{ .source = "a.", - .expected_error = - "ERROR: :1:3: expected identifier after '.'\n" - " | a.\n" - " | ..^", + .expected_error = "ERROR: :1:3: Syntax error: expected " + "identifier after '.'\n" + " | a.\n" + " | ..^", }, ErrorTestCase{ .source = "a[?0]", @@ -1195,9 +1231,10 @@ std::vector GetErrorTestCases() { }, ErrorTestCase{ .source = ". *", - .expected_error = "ERROR: :1:3: expected identifier\n" - " | . *\n" - " | ..^", + .expected_error = + "ERROR: :1:3: Syntax error: expected identifier\n" + " | . *\n" + " | ..^", }, ErrorTestCase{ .source = ".as", @@ -1208,17 +1245,19 @@ std::vector GetErrorTestCases() { ErrorTestCase{ .source = "* 2", .expected_error = - "ERROR: :1:1: unexpected token\n" + "ERROR: :1:1: Syntax error: unexpected token\n" " | * 2\n" " | ^\n" - "ERROR: :1:3: unexpected token after expression\n" + "ERROR: :1:3: Syntax error: unexpected token after " + "expression\n" " | * 2\n" " | ..^", }, ErrorTestCase{ .source = "(1 + 2", .expected_error = - "ERROR: :1:7: mismatched input expecting ')'\n" + "ERROR: :1:7: Syntax error: mismatched input " + "expecting ')'\n" " | (1 + 2\n" " | ......^", }, @@ -1230,7 +1269,7 @@ std::vector GetErrorTestCases() { }, ErrorTestCase{ .source = "[1, 2", - .expected_error = "ERROR: :1:6: expected ']'\n" + .expected_error = "ERROR: :1:6: Syntax error: expected ']'\n" " | [1, 2\n" " | .....^", }, @@ -1242,13 +1281,14 @@ std::vector GetErrorTestCases() { }, ErrorTestCase{ .source = "{'k' 'v'}", - .expected_error = "ERROR: :1:6: expected ':' in map entry\n" - " | {'k' 'v'}\n" - " | .....^", + .expected_error = + "ERROR: :1:6: Syntax error: expected ':' in map entry\n" + " | {'k' 'v'}\n" + " | .....^", }, ErrorTestCase{ .source = "{'k': 'v'", - .expected_error = "ERROR: :1:10: expected '}'\n" + .expected_error = "ERROR: :1:10: Syntax error: expected '}'\n" " | {'k': 'v'\n" " | .........^", }, @@ -1260,26 +1300,29 @@ std::vector GetErrorTestCases() { }, ErrorTestCase{ .source = "Msg{1: 2}", - .expected_error = "ERROR: :1:5: expected struct field name\n" - " | Msg{1: 2}\n" - " | ....^", + .expected_error = + "ERROR: :1:5: Syntax error: expected struct field name\n" + " | Msg{1: 2}\n" + " | ....^", }, ErrorTestCase{ .source = "Msg{f 10}", - .expected_error = "ERROR: :1:7: expected ':' in struct field\n" - " | Msg{f 10}\n" - " | ......^", + .expected_error = + "ERROR: :1:7: Syntax error: expected ':' in struct field\n" + " | Msg{f 10}\n" + " | ......^", }, ErrorTestCase{ .source = "Msg{f: 10", - .expected_error = "ERROR: :1:10: expected '}'\n" + .expected_error = "ERROR: :1:10: Syntax error: expected '}'\n" " | Msg{f: 10\n" " | .........^", }, ErrorTestCase{ .source = "f(1, 2", .expected_error = - "ERROR: :1:7: mismatched input expecting ')'\n" + "ERROR: :1:7: Syntax error: mismatched input " + "expecting ')'\n" " | f(1, 2\n" " | ......^", }, @@ -1291,35 +1334,39 @@ std::vector GetErrorTestCases() { }, ErrorTestCase{ .source = "999999999999999999999999999999999999999", - .expected_error = "ERROR: :1:1: invalid int literal\n" - " | 999999999999999999999999999999999999999\n" - " | ^", + .expected_error = + "ERROR: :1:1: Syntax error: invalid int literal\n" + " | 999999999999999999999999999999999999999\n" + " | ^", }, ErrorTestCase{ .source = "999999999999999999999999999999999999999u", - .expected_error = "ERROR: :1:1: invalid uint literal\n" - " | 999999999999999999999999999999999999999u\n" - " | ^", + .expected_error = + "ERROR: :1:1: Syntax error: invalid uint literal\n" + " | 999999999999999999999999999999999999999u\n" + " | ^", }, ErrorTestCase{ .source = "1e", .expected_error = - "ERROR: :1:1: floating point literal missing digits after " - "exponent separator\n" + "ERROR: :1:1: Syntax error: floating point literal " + "missing digits after exponent separator\n" " | 1e\n" " | ^", }, ErrorTestCase{ .source = "\"unterminated", - .expected_error = "ERROR: :1:1: unterminated string literal\n" - " | \"unterminated\n" - " | ^", + .expected_error = + "ERROR: :1:1: Syntax error: unterminated string literal\n" + " | \"unterminated\n" + " | ^", }, ErrorTestCase{ .source = "b\"unterminated", - .expected_error = "ERROR: :1:1: unterminated bytes literal\n" - " | b\"unterminated\n" - " | ^", + .expected_error = + "ERROR: :1:1: Syntax error: unterminated bytes literal\n" + " | b\"unterminated\n" + " | ^", }, ErrorTestCase{ .source = "a.?`foo`", @@ -1374,7 +1421,8 @@ std::vector GetErrorTestCases() { ErrorTestCase{ .source = "`foo", .expected_error = - "ERROR: :1:1: unterminated quoted identifier\n" + "ERROR: :1:1: Syntax error: unterminated quoted " + "identifier\n" " | `foo\n" " | ^", .enable_quoted_identifiers = true, @@ -1382,73 +1430,82 @@ std::vector GetErrorTestCases() { ErrorTestCase{ .source = "f(*, 1e, {2 3})", .expected_error = - "ERROR: :1:3: unexpected token\n" + "ERROR: :1:3: Syntax error: unexpected token\n" " | f(*, 1e, {2 3})\n" " | ..^\n" - "ERROR: :1:6: floating point literal missing digits after " - "exponent separator\n" + "ERROR: :1:6: Syntax error: floating point literal " + "missing digits after exponent separator\n" " | f(*, 1e, {2 3})\n" " | .....^\n" - "ERROR: :1:13: expected ':' in map entry\n" + "ERROR: :1:13: Syntax error: expected ':' in map entry\n" " | f(*, 1e, {2 3})\n" " | ............^", }, ErrorTestCase{ .source = "(1 + *) + 2", - .expected_error = "ERROR: :1:6: unexpected token\n" - " | (1 + *) + 2\n" - " | .....^", + .expected_error = + "ERROR: :1:6: Syntax error: unexpected token\n" + " | (1 + *) + 2\n" + " | .....^", }, ErrorTestCase{ .source = "f(1 + *, 2)", - .expected_error = "ERROR: :1:7: unexpected token\n" - " | f(1 + *, 2)\n" - " | ......^", + .expected_error = + "ERROR: :1:7: Syntax error: unexpected token\n" + " | f(1 + *, 2)\n" + " | ......^", }, ErrorTestCase{ .source = "(a. + 1)", .expected_error = - "ERROR: :1:5: expected identifier after '.'\n" + "ERROR: :1:5: Syntax error: expected identifier after " + "'.'\n" " | (a. + 1)\n" " | ....^", }, ErrorTestCase{ .source = "f(a., 1)", .expected_error = - "ERROR: :1:5: expected identifier after '.'\n" + "ERROR: :1:5: Syntax error: expected identifier after " + "'.'\n" " | f(a., 1)\n" " | ....^", }, ErrorTestCase{ .source = "[a., 1]", .expected_error = - "ERROR: :1:4: expected identifier after '.'\n" + "ERROR: :1:4: Syntax error: expected identifier after " + "'.'\n" " | [a., 1]\n" " | ...^", }, ErrorTestCase{ .source = "-0x8000000000000001", - .expected_error = "ERROR: :1:2: invalid int literal\n" - " | -0x8000000000000001\n" - " | .^", + .expected_error = + "ERROR: :1:2: Syntax error: invalid int literal\n" + " | -0x8000000000000001\n" + " | .^", }, ErrorTestCase{ .source = "-0x10000000000000000", - .expected_error = "ERROR: :1:2: invalid int literal\n" - " | -0x10000000000000000\n" - " | .^", + .expected_error = + "ERROR: :1:2: Syntax error: invalid int literal\n" + " | -0x10000000000000000\n" + " | .^", }, ErrorTestCase{ .source = "-9223372036854775809", - .expected_error = "ERROR: :1:2: invalid int literal\n" - " | -9223372036854775809\n" - " | .^", + .expected_error = + "ERROR: :1:2: Syntax error: invalid int literal\n" + " | -9223372036854775809\n" + " | .^", }, ErrorTestCase{ .source = "-999999999999999999999999999999999999999", - .expected_error = "ERROR: :1:2: invalid int literal\n" - " | -999999999999999999999999999999999999999\n" - " | .^", + .expected_error = + "ERROR: :1:2: Syntax error: invalid int literal\n" + " | -999999999999999999999999999999999999999\n" + " | .^", }, ErrorTestCase{ .source = "-", @@ -1460,15 +1517,17 @@ std::vector GetErrorTestCases() { }, ErrorTestCase{ .source = "- *", - .expected_error = "ERROR: :1:3: unexpected token\n" - " | - *\n" - " | ..^", + .expected_error = + "ERROR: :1:3: Syntax error: unexpected token\n" + " | - *\n" + " | ..^", }, ErrorTestCase{ .source = "\"😀😀😀😀😀\" ~error", - .expected_error = "ERROR: :1:9: unexpected character\n" - " | \"😀😀😀😀😀\" ~error\n" - " | ........^", + .expected_error = + "ERROR: :1:9: Syntax error: unexpected character\n" + " | \"😀😀😀😀😀\" ~error\n" + " | ........^", }, }; } @@ -1875,7 +1934,7 @@ TEST(PrattParserErrorRecoveryTest, ErrorRecoveryLimitOne) { ASSERT_OK_AND_ASSIGN(auto source, cel::NewSource("......")); EXPECT_EQ(FormatIssues(*source, issues), "ERROR: :-1:0: Error recovery limit (1) exceeded\n" - "ERROR: :1:2: expected identifier\n" + "ERROR: :1:2: Syntax error: expected identifier\n" " | ......\n" " | .^"); } diff --git a/parser/internal/pratt_parser_worker.cc b/parser/internal/pratt_parser_worker.cc index b7356528d..198a70c4f 100644 --- a/parser/internal/pratt_parser_worker.cc +++ b/parser/internal/pratt_parser_worker.cc @@ -133,7 +133,7 @@ Token ParserWorker::NextSignificantToken(bool report_error) { continue; } if (tok.type == TokenType::kError && report_error) { - ReportError(tok, lexer_.GetError().message); + ReportSyntaxError(tok, lexer_.GetError().message); if (is_recovery_limit_exceeded()) { return Token{.type = TokenType::kEnd, .start = 0, .end = 0}; } @@ -177,7 +177,7 @@ bool ParserWorker::Expect(TokenType type, absl::string_view msg) { } else { err_msg = std::string(msg); } - ReportError(peek_token_, err_msg); + ReportSyntaxError(peek_token_, err_msg); } SynchronizeOnDelimiter(); return false; @@ -271,4 +271,9 @@ void ParserWorker::ReportError(const SourceLocation& loc, } } +void ParserWorker::ReportSyntaxError(const Token& token, + absl::string_view msg) { + ReportError(token.start, absl::StrCat("Syntax error: ", msg)); +} + } // namespace cel::parser_internal diff --git a/parser/internal/pratt_parser_worker.h b/parser/internal/pratt_parser_worker.h index f4886aa90..44690b06b 100644 --- a/parser/internal/pratt_parser_worker.h +++ b/parser/internal/pratt_parser_worker.h @@ -110,6 +110,7 @@ class ParserWorker { void ReportError(const Token& token, absl::string_view msg) { ReportError(token.start, msg); } + void ReportSyntaxError(const Token& token, absl::string_view msg); const cel::Source& source_; cel::ParserOptions options_; @@ -335,7 +336,7 @@ ExprNode PrattParserWorker::Parse() { } if (peek_token_.type != TokenType::kEnd && peek_token_.type != TokenType::kError) { - ReportError(peek_token_, "unexpected token after expression"); + ReportSyntaxError(peek_token_, "unexpected token after expression"); } return expr; } @@ -462,7 +463,7 @@ void PrattParserWorker::ParseSelectorChainTail(ExprNode& lhs) { if (id_tok.type != TokenType::kIdent && id_tok.type != TokenType::kReservedWord) { if (id_tok.type != TokenType::kError) { - ReportError(id_tok, "expected identifier after '.'"); + ReportSyntaxError(id_tok, "expected identifier after '.'"); } SynchronizeOnDelimiter(); return; @@ -556,10 +557,12 @@ ExprNode PrattParserWorker::ParseUnaryOpsChain(Token first_op) { int64_t op_id = ops.back().id; ops.pop_back(); operand = ParseNegativeIntLiteral(op_id); + ParseSelectorChainTail(operand); } else if (peek_token_.type == TokenType::kFloat) { int64_t op_id = ops.back().id; ops.pop_back(); operand = ParseNegativeDoubleLiteral(op_id); + ParseSelectorChainTail(operand); } else { operand = ParseSelectorChain(); } @@ -641,7 +644,7 @@ ExprNode PrattParserWorker::ParseIdentOrCall() { if (id_tok.type != TokenType::kIdent && id_tok.type != TokenType::kReservedWord) { if (id_tok.type != TokenType::kError) { - ReportError(id_tok, "expected identifier"); + ReportSyntaxError(id_tok, "expected identifier"); } return ast_factory_.NewUnspecified(NextId(id_tok)); } @@ -715,11 +718,10 @@ ExprNode PrattParserWorker::ParsePrimary() { Token bad_tok = NextToken(); if (bad_tok.type != TokenType::kError) { if (bad_tok.type == TokenType::kEnd) { - ReportError( - bad_tok, - "Syntax error: mismatched input '' expecting expression"); + ReportSyntaxError(bad_tok, + "mismatched input '' expecting expression"); } else { - ReportError(bad_tok, "unexpected token"); + ReportSyntaxError(bad_tok, "unexpected token"); } } return ast_factory_.NewUnspecified(NextId(bad_tok)); @@ -813,7 +815,7 @@ ExprNode PrattParserWorker::ParseStruct( Token field_tok = NextToken(); if (field_tok.type != TokenType::kIdent && field_tok.type != TokenType::kReservedWord) { - ReportError(field_tok, "expected struct field name"); + ReportSyntaxError(field_tok, "expected struct field name"); SynchronizeOnDelimiter(); break; } @@ -877,7 +879,7 @@ ExprNode PrattParserWorker::ParseIntLiteral() { } else if (absl::SimpleAtoi(value, &int_val)) { return ast_factory_.NewIntConst(NextId(tok), int_val); } - ReportError(tok, "invalid int literal"); + ReportSyntaxError(tok, "invalid int literal"); return ast_factory_.NewUnspecified(NextId(tok)); } @@ -911,7 +913,7 @@ ExprNode PrattParserWorker::ParseNegativeIntLiteral(int64_t node_id) { if (success) { return ast_factory_.NewIntConst(node_id, int_val); } - ReportError(lit_tok, "invalid int literal"); + ReportSyntaxError(lit_tok, "invalid int literal"); return ast_factory_.NewUnspecified(NextId(lit_tok)); } @@ -931,7 +933,7 @@ ExprNode PrattParserWorker::ParseUintLiteral() { } else if (absl::SimpleAtoi(value, &uint_val)) { return ast_factory_.NewUintConst(NextId(tok), uint_val); } - ReportError(tok, "invalid uint literal"); + ReportSyntaxError(tok, "invalid uint literal"); return ast_factory_.NewUnspecified(NextId(tok)); } @@ -944,7 +946,7 @@ ExprNode PrattParserWorker::ParseDoubleLiteral() { if (absl::SimpleAtod(value, &double_val)) { return ast_factory_.NewDoubleConst(NextId(tok), double_val); } - ReportError(tok, "invalid double literal"); + ReportSyntaxError(tok, "invalid double literal"); return ast_factory_.NewUnspecified(NextId(tok)); } @@ -956,7 +958,7 @@ ExprNode PrattParserWorker::ParseNegativeDoubleLiteral( if (absl::SimpleAtod(GetTokenText(lit_tok), &double_val)) { return ast_factory_.NewDoubleConst(node_id, -double_val); } - ReportError(lit_tok, "invalid double literal"); + ReportSyntaxError(lit_tok, "invalid double literal"); return ast_factory_.NewUnspecified(NextId(lit_tok)); } diff --git a/parser/parser_test.cc b/parser/parser_test.cc index e47557e6b..ce9ff58ee 100644 --- a/parser/parser_test.cc +++ b/parser/parser_test.cc @@ -191,7 +191,7 @@ std::vector test_cases = { " | .^", "", "", "", "", // PRATT PARSER ERROR MESSAGE - "ERROR: :1:2: expected '}'\n" + "ERROR: :1:2: Syntax error: expected '}'\n" " | {\n" " | .^"}, @@ -459,10 +459,10 @@ std::vector test_cases = { " | ......^", "", "", "", "", // PRATT PARSER ERROR MESSAGE - "ERROR: :1:1: unexpected token\n" + "ERROR: :1:1: Syntax error: unexpected token\n" " | *@a | b\n" " | ^\n" - "ERROR: :1:2: unexpected character\n" + "ERROR: :1:2: Syntax error: unexpected character\n" " | *@a | b\n" " | .^"}, {"a | b", "", @@ -474,7 +474,7 @@ std::vector test_cases = { " | ....^", "", "", "", "", // PRATT PARSER ERROR MESSAGE - "ERROR: :1:3: unexpected single '|', expected '||'\n" + "ERROR: :1:3: Syntax error: unexpected single '|', expected '||'\n" " | a | b\n" " | ..^"}, {"?", "", @@ -487,7 +487,7 @@ std::vector test_cases = { "ERROR: :4294967295:0: <> parsetree", "", "", "", "", // PRATT PARSER ERROR MESSAGE - "ERROR: :1:1: unexpected token\n" + "ERROR: :1:1: Syntax error: unexpected token\n" " | ?\n" " | ^"}, {"t{>C}", "", @@ -498,7 +498,7 @@ std::vector test_cases = { "mismatched input '}' expecting ':'\n | t{>C}\n | ....^", "", "", "", "", // PRATT PARSER ERROR MESSAGE - "ERROR: :1:3: expected struct field name\n" + "ERROR: :1:3: Syntax error: expected struct field name\n" " | t{>C}\n" " | ..^"}, {"foo(a,b,)", "", @@ -675,7 +675,7 @@ std::vector test_cases = { " | ..............^", "", "", "", "", // PRATT PARSER ERROR MESSAGE - "ERROR: :1:15: unexpected token after expression\n" + "ERROR: :1:15: Syntax error: unexpected token after expression\n" " | TestAllTypes(){single_int32: 1, single_int64: 2}\n" " | ..............^"}, {"size(x) == x.size()", @@ -697,7 +697,7 @@ std::vector test_cases = { " | .....^", "", "", "", "", // PRATT PARSER ERROR MESSAGE - "ERROR: :1:5: unexpected character\n" + "ERROR: :1:5: Syntax error: unexpected character\n" " | 1 + $\n" " | ....^"}, {"1 + 2\n" @@ -708,7 +708,7 @@ std::vector test_cases = { " | ^", "", "", "", "", // PRATT PARSER ERROR MESSAGE - "ERROR: :2:1: unexpected token after expression\n" + "ERROR: :2:1: Syntax error: unexpected token after expression\n" " | 3 +\n" " | ^"}, {"\"\\\"\"", "\"\\\"\"^#1:string#"}, @@ -849,7 +849,7 @@ std::vector test_cases = { " | .....^", "", "", "", "", // PRATT PARSER ERROR MESSAGE - "ERROR: :1:5: unexpected token\n" + "ERROR: :1:5: Syntax error: unexpected token\n" " | 1 + +\n" " | ....^"}, {"\"abc\" + \"def\"", @@ -864,7 +864,7 @@ std::vector test_cases = { " | .........^", "", "", "", "", // PRATT PARSER ERROR MESSAGE - "ERROR: :1:10: expected identifier after '.'\n" + "ERROR: :1:10: Syntax error: expected identifier after '.'\n" " | {\"a\": 1}.\"a\"\n" " | .........^"}, {"\"\\xC3\\XBF\"", "\"ÿ\"^#1:string#"}, @@ -964,10 +964,10 @@ std::vector test_cases = { " | ..........^", "", "", "", "", // PRATT PARSER ERROR MESSAGE - "ERROR: :2:7: unexpected token\n" + "ERROR: :2:7: Syntax error: unexpected token\n" " | && in.😁\n" " | ......^\n" - "ERROR: :2:10: unexpected character\n" + "ERROR: :2:10: Syntax error: unexpected character\n" " | && in.😁\n" " | .........^"}, {"as", "", @@ -1020,7 +1020,7 @@ std::vector test_cases = { " | ..^", "", "", "", "", // PRATT PARSER ERROR MESSAGE - "ERROR: :1:1: unexpected token\n" + "ERROR: :1:1: Syntax error: unexpected token\n" " | in\n" " | ^"}, {"let", "", @@ -1108,7 +1108,7 @@ std::vector test_cases = { " | ..^", "", "", "", "", // PRATT PARSER ERROR MESSAGE - "ERROR: :6:3: expected ']'\n" + "ERROR: :6:3: Syntax error: expected ']'\n" " | \r\n" " | ..^"},