diff --git a/starlark/ast.h b/starlark/ast.h index b0e1e18..c2b8a15 100644 --- a/starlark/ast.h +++ b/starlark/ast.h @@ -35,6 +35,7 @@ struct DictExpr; struct ListExpr; struct ListComp; struct SliceExpr; +struct MemberExpr; using Expression = std::variant< CallExpr, @@ -44,7 +45,8 @@ using Expression = std::variant< ListComp, ListExpr, DictExpr, - SliceExpr>; + SliceExpr, + MemberExpr>; struct Argument; @@ -66,6 +68,12 @@ struct SliceExpr { constexpr bool operator==(SliceExpr const &) const; }; +struct MemberExpr { + std::shared_ptr target; + Identifier member; + constexpr bool operator==(MemberExpr const &) const; +}; + // TODO(robinlinden): shared_ptr is silly here, but right now the ast has to be // copyable for some reason. struct ListComp { @@ -100,6 +108,10 @@ constexpr bool ListComp::operator==(ListComp const &o) const { return *element == *o.element && iterator_var == o.iterator_var && *iterable == *o.iterable; } +constexpr bool MemberExpr::operator==(MemberExpr const &o) const { + return *target == *o.target && member == o.member; +} + struct AssignStmt { Identifier target; Expression value; diff --git a/starlark/parser.h b/starlark/parser.h index 46278e2..3890fed 100644 --- a/starlark/parser.h +++ b/starlark/parser.h @@ -313,6 +313,17 @@ class Parser { .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; + } + + return MemberExpr{ + .target = std::make_shared(std::move(*operand)), + .member = Identifier{.name = std::get(*member_token).name}, + }; } else { reconsume(std::move(*next)); } diff --git a/starlark/parser_test.cc b/starlark/parser_test.cc index 44e875c..dfbeeff 100644 --- a/starlark/parser_test.cc +++ b/starlark/parser_test.cc @@ -276,6 +276,22 @@ int main() { }, }, }, + { + "foo.bar", + starlark::Program{ + .statements{ + starlark::ExpressionStmt{ + .expr{ + starlark::MemberExpr{ + .target = std::make_shared( + starlark::Identifier{"foo"}), + .member = starlark::Identifier{"bar"}, + }, + }, + }, + }, + }, + }, }); // TODO(robinlinden): Return error codes from parser and use that here. @@ -326,6 +342,12 @@ int main() { "A = foo(", // Non-ident target. "\"A\" = B", + + // MemberExpr + // Missing member name. + "foo.", + // Invalid member name. + "foo.5", }); etest::Suite s{};