diff --git a/starlark/parser.h b/starlark/parser.h index 3890fed..0e2d5cd 100644 --- a/starlark/parser.h +++ b/starlark/parser.h @@ -272,63 +272,77 @@ class Parser { } std::optional parse_expression(Token &token) { - auto operand = parse_operand(token); - if (!operand) { + auto expr = parse_operand(token); + if (!expr) { std::cerr << "Failed to parse operand.\n"; return std::nullopt; } - auto next = next_token(); - if (!next) { - // This is fine. - } else if (std::holds_alternative(*next)) { - auto args = parse_argument_list(); - if (!args) { - std::cerr << "Failed to parse argument list.\n"; - return std::nullopt; - } - - return CallExpr{ - .target = std::make_shared(std::move(*operand)), - .args = std::move(*args), - }; - } else if (std::holds_alternative(*next)) { - next = next_token(); + while (true) { + auto next = next_token(); if (!next) { - std::cerr << "Unexpected end of input after '['.\n"; - return std::nullopt; + return expr; } - auto index_expr = parse_expression(*next); - if (!index_expr) { - std::cerr << "Failed to parse index expression.\n"; - return std::nullopt; + if (std::holds_alternative(*next)) { + auto args = parse_argument_list(); + if (!args) { + std::cerr << "Failed to parse argument list.\n"; + return std::nullopt; + } + + expr = CallExpr{ + .target = std::make_shared(std::move(*expr)), + .args = std::move(*args), + }; + + continue; } - if (!expect_next_token(token::RBracket{})) { - return std::nullopt; + if (std::holds_alternative(*next)) { + next = next_token(); + if (!next) { + std::cerr << "Unexpected end of input after '['.\n"; + return std::nullopt; + } + + auto index_expr = parse_expression(*next); + if (!index_expr) { + std::cerr << "Failed to parse index expression.\n"; + return std::nullopt; + } + + if (!expect_next_token(token::RBracket{})) { + return std::nullopt; + } + + expr = SliceExpr{ + .target = std::make_shared(std::move(*expr)), + .index = std::make_shared(std::move(*index_expr)), + }; + + continue; } - return SliceExpr{ - .target = std::make_shared(std::move(*operand)), - .index = std::make_shared(std::move(*index_expr)), - }; - } else if (std::holds_alternative(*next)) { - auto member_token = next_token(); - if (!member_token || !std::holds_alternative(*member_token)) { - std::cerr << "Expected identifier after '.'.\n"; - return std::nullopt; + if (std::holds_alternative(*next)) { + auto member_token = next_token(); + if (!member_token || !std::holds_alternative(*member_token)) { + std::cerr << "Expected identifier after '.'.\n"; + return std::nullopt; + } + + expr = MemberExpr{ + .target = std::make_shared(std::move(*expr)), + .member = Identifier{.name = std::get(*member_token).name}, + }; + + continue; } - return MemberExpr{ - .target = std::make_shared(std::move(*operand)), - .member = Identifier{.name = std::get(*member_token).name}, - }; - } else { + // We didn't end up using the next token, so put it back for later. reconsume(std::move(*next)); + return expr; } - - return operand; } std::optional> parse_argument_list() { diff --git a/starlark/parser_test.cc b/starlark/parser_test.cc index dfbeeff..1fa44da 100644 --- a/starlark/parser_test.cc +++ b/starlark/parser_test.cc @@ -292,6 +292,39 @@ int main() { }, }, }, + { + "foo.baz[3].qux().bar", + starlark::Program{ + .statements{ + starlark::ExpressionStmt{ + .expr{ + starlark::MemberExpr{ + .target = std::make_shared(starlark::CallExpr{ + .target = + std::make_shared(starlark::MemberExpr{ + .target = std::make_shared< + starlark::Expression>(starlark::SliceExpr{ + .target = std::make_shared( + starlark::MemberExpr{ + .target = + std::make_shared( + starlark::Identifier{"foo"}), + .member = starlark::Identifier{"baz"}, + }), + .index = std::make_shared( + starlark::IntLiteral{3}), + }), + .member = starlark::Identifier{"qux"}, + }), + .args{}, + }), + .member = starlark::Identifier{"bar"}, + }, + }, + }, + }, + }, + }, }); // TODO(robinlinden): Return error codes from parser and use that here.