From 973fc88c4fcb6569c390f3f403cf7e292c164078 Mon Sep 17 00:00:00 2001 From: Dmitri Plotnikov Date: Mon, 31 Aug 2026 14:54:19 -0700 Subject: [PATCH] [Pratt parser] Improve compatibility with ANTLR parser PiperOrigin-RevId: 974093125 --- parser/internal/pratt_parser_test.cc | 176 ++++++++++++++----------- parser/internal/pratt_parser_worker.cc | 9 +- parser/internal/pratt_parser_worker.h | 39 +++--- parser/parser_test.cc | 30 ++--- 4 files changed, 143 insertions(+), 111 deletions(-) diff --git a/parser/internal/pratt_parser_test.cc b/parser/internal/pratt_parser_test.cc index 908690bc8..422a5d19f 100644 --- a/parser/internal/pratt_parser_test.cc +++ b/parser/internal/pratt_parser_test.cc @@ -1222,21 +1222,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" " | ........^", }, @@ -1248,10 +1251,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]", @@ -1261,9 +1264,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", @@ -1274,17 +1278,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" " | ......^", }, @@ -1296,7 +1302,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" " | .....^", }, @@ -1308,13 +1314,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" " | .........^", }, @@ -1326,26 +1333,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" " | ......^", }, @@ -1357,35 +1367,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`", @@ -1440,7 +1454,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, @@ -1448,73 +1463,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 = "-", @@ -1526,15 +1550,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" + " | ........^", }, }; } @@ -1892,9 +1918,9 @@ TEST(PrattParserMacroErrorTest, ReportError) { auto ast = parser->Parse(*source, &issues); EXPECT_THAT(ast, StatusIs(absl::StatusCode::kInvalidArgument)); EXPECT_EQ(FormatIssues(*source, issues), - "ERROR: :1:6: custom macro error\n" + "ERROR: :1:15: custom macro error\n" " | 42 + bad_macro(x)\n" - " | .....^"); + " | ..............^"); } TEST(PrattParserMacroErrorTest, ReportErrorAt) { @@ -1941,7 +1967,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 1f99984c5..966717af1 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; } @@ -357,8 +358,8 @@ ExprNode PrattParserWorker::ParseExpr() { template void PrattParserWorker::ParseTernary(ExprNode& lhs) { - NextToken(); - int64_t op_id = NextId(); + Token op_tok = NextToken(); + int64_t op_id = NextId(op_tok); ExprNode true_expr = ParseBinaryAndTernary(1); if (!Expect(TokenType::kColon, "expected ':' in conditional expression")) { return; @@ -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; @@ -643,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)); } @@ -655,16 +656,17 @@ ExprNode PrattParserWorker::ParseIdentOrCall() { } std::string name = leading_dot ? absl::StrCat(".", id_text) : std::string(id_text); - int64_t id = NextId(leading_dot ? first_tok : id_tok); if (peek_token_.type == TokenType::kLeftParen) { - NextToken(); + Token lparen = NextToken(); + int64_t call_id = NextId(lparen); std::vector args = ParseArguments(TokenType::kRightParen); - if (auto expanded = TryExpandMacro(id, name, nullptr, args); + if (auto expanded = TryExpandMacro(call_id, name, nullptr, args); expanded.has_value()) { return std::move(*expanded); } - return ast_factory_.NewCall(id, name, std::move(args)); + return ast_factory_.NewCall(call_id, name, std::move(args)); } + int64_t id = NextId(leading_dot ? first_tok : id_tok); return ast_factory_.NewIdent(id, std::move(name)); } @@ -717,11 +719,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)); @@ -815,7 +816,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; } @@ -879,7 +880,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)); } @@ -913,7 +914,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)); } @@ -933,7 +934,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)); } @@ -946,7 +947,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)); } @@ -958,7 +959,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 dbac2b02f..cc9fbc5fe 100644 --- a/parser/parser_test.cc +++ b/parser/parser_test.cc @@ -212,7 +212,7 @@ std::vector test_cases = { " | .^", "", "", "", "", // PRATT PARSER ERROR MESSAGE - "ERROR: :1:2: expected '}'\n" + "ERROR: :1:2: Syntax error: expected '}'\n" " | {\n" " | .^"}, @@ -480,10 +480,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", "", @@ -495,7 +495,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" " | ..^"}, {"?", "", @@ -508,7 +508,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}", "", @@ -519,7 +519,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,)", "", @@ -696,7 +696,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()", @@ -718,7 +718,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" @@ -729,7 +729,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#"}, @@ -870,7 +870,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\"", @@ -885,7 +885,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#"}, @@ -985,10 +985,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", "", @@ -1041,7 +1041,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", "", @@ -1129,7 +1129,7 @@ std::vector test_cases = { " | ..^", "", "", "", "", // PRATT PARSER ERROR MESSAGE - "ERROR: :6:3: expected ']'\n" + "ERROR: :6:3: Syntax error: expected ']'\n" " | \r\n" " | ..^"},