From 82179bc9039e3b39ce83305a9de45c7bc23050a2 Mon Sep 17 00:00:00 2001 From: josh11b Date: Thu, 4 Jan 2024 13:51:19 -0800 Subject: [PATCH] Add `Tree::TryAs` to cast to a typed parse node id (#3565) Co-authored-by: Richard Smith --- toolchain/parse/tree.h | 43 +++++++++++++++++++++++++++++++++++ toolchain/parse/tree_test.cpp | 41 +++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+) diff --git a/toolchain/parse/tree.h b/toolchain/parse/tree.h index a611de1209de..86e519eaff20 100644 --- a/toolchain/parse/tree.h +++ b/toolchain/parse/tree.h @@ -125,6 +125,27 @@ class Tree : public Printable { return !node_has_error(id); } + // Converts `n` to a constrained node id `T` if the `node_kind(n)` matches + // the constraint on `T`. + template + auto TryAs(NodeId n) const -> std::optional { + CARBON_DCHECK(n.is_valid()); + if (ConvertTo::AllowedFor(node_kind(n))) { + return T(n); + } else { + return std::nullopt; + } + } + + // Converts to `n` to a constrained node id `T`. Checks that the + // `node_kind(n)` matches the constraint on `T`. + template + auto As(NodeId n) const -> T { + CARBON_DCHECK(n.is_valid()); + CARBON_CHECK(ConvertTo::AllowedFor(node_kind(n))); + return T(n); + } + auto packaging_directive() const -> const std::optional& { return packaging_directive_; } @@ -226,6 +247,9 @@ class Tree : public Printable { private: friend class Context; + template + struct ConvertTo; + // The in-memory representation of data used for a particular node in the // tree. struct NodeImpl { @@ -452,6 +476,25 @@ auto Tree::Extract(IdT id) const return ExtractNodeFromChildren(children(id)); } +template +struct Tree::ConvertTo> { + static auto AllowedFor(NodeKind kind) -> bool { return kind == K; } +}; + +template +struct Tree::ConvertTo> { + static auto AllowedFor(NodeKind kind) -> bool { + return !!(kind.category() & C); + } +}; + +template +struct Tree::ConvertTo> { + static auto AllowedFor(NodeKind kind) -> bool { + return kind == T::Kind || kind == U::Kind; + } +}; + } // namespace Carbon::Parse #endif // CARBON_TOOLCHAIN_PARSE_TREE_H_ diff --git a/toolchain/parse/tree_test.cpp b/toolchain/parse/tree_test.cpp index dcbe0ad13bf6..075cbd954967 100644 --- a/toolchain/parse/tree_test.cpp +++ b/toolchain/parse/tree_test.cpp @@ -56,6 +56,47 @@ TEST_F(TreeTest, IsValid) { EXPECT_TRUE((*tree.postorder().begin()).is_valid()); } +TEST_F(TreeTest, AsAndTryAs) { + Lex::TokenizedBuffer& tokens = GetTokenizedBuffer("fn F();"); + Tree tree = Parse(tokens, consumer_, /*vlog_stream=*/nullptr); + ASSERT_FALSE(tree.has_errors()); + auto it = tree.roots().begin(); + // A FileEnd node, so won't match. + NodeId n = *it; + + // NodeIdForKind + std::optional fn_decl_id = tree.TryAs(n); + EXPECT_FALSE(fn_decl_id.has_value()); + // NodeIdOneOf + std::optional any_fn_decl_id = + tree.TryAs(n); + EXPECT_FALSE(any_fn_decl_id.has_value()); + // NodeIdInCategory + std::optional any_decl_id = tree.TryAs(n); + EXPECT_FALSE(any_decl_id.has_value()); + + ++it; + n = *it; + // A FunctionDecl node, so will match. + fn_decl_id = tree.TryAs(n); + ASSERT_TRUE(fn_decl_id.has_value()); + EXPECT_TRUE(*fn_decl_id == n); + FunctionDeclId fn_decl_id2 = tree.As(n); + EXPECT_TRUE(*fn_decl_id == fn_decl_id2); + + any_fn_decl_id = tree.TryAs(n); + ASSERT_TRUE(any_fn_decl_id.has_value()); + EXPECT_TRUE(*any_fn_decl_id == n); + AnyFunctionDeclId any_fn_decl_id2 = tree.As(n); + EXPECT_TRUE(*any_fn_decl_id == any_fn_decl_id2); + + any_decl_id = tree.TryAs(n); + ASSERT_TRUE(any_decl_id.has_value()); + EXPECT_TRUE(*any_decl_id == n); + AnyDeclId any_decl_id2 = tree.As(n); + EXPECT_TRUE(*any_decl_id == any_decl_id2); +} + TEST_F(TreeTest, PrintPostorderAsYAML) { Lex::TokenizedBuffer& tokens = GetTokenizedBuffer("fn F();"); Tree tree = Parse(tokens, consumer_, /*vlog_stream=*/nullptr);