// Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include #include #include #include "common/error.h" #include "common/struct_reflection.h" #include "toolchain/parse/tree.h" #include "toolchain/parse/typed_nodes.h" namespace Carbon::Parse { // A trait type that should be specialized by types that can be extracted // from a parse tree. A specialization should provide the following API: // // ```cpp // template<> // struct Extractable { // // Extract a value of this type from the sequence of nodes starting at // // `it`, and increment `it` past this type. Returns `std::nullopt` if // // the tree is malformed. If `trace != nullptr`, writes what actions // // were taken to `*trace`. // static auto Extract(Tree* tree, Tree::SiblingIterator& it, // Tree::SiblingIterator end, // ErrorBuilder* trace) -> std::optional; // }; // ``` // // Note that `Tree::SiblingIterator`s iterate in reverse order through the // children of a node. // // This class is only in this file. template struct Extractable; // Extract a `NodeId` as a single child. template <> struct Extractable { static auto Extract(const Tree* tree, Tree::SiblingIterator& it, Tree::SiblingIterator end, ErrorBuilder* trace) -> std::optional { if (it == end) { if (trace) { *trace << "NodeId error: no more children\n"; } return std::nullopt; } if (trace) { *trace << "NodeId: " << tree->node_kind(*it) << " consumed\n"; } return *it++; } }; static auto NodeIdForKindAccept(const NodeKind& kind, const Tree* tree, const Tree::SiblingIterator& it, Tree::SiblingIterator end, ErrorBuilder* trace) -> bool { if (it == end || tree->node_kind(*it) != kind) { if (trace) { if (it == end) { *trace << "NodeIdForKind error: no more children, expected " << kind << "\n"; } else { *trace << "NodeIdForKind error: wrong kind " << tree->node_kind(*it) << ", expected " << kind << "\n"; } } return false; } if (trace) { *trace << "NodeIdForKind: " << kind << " consumed\n"; } return true; } // Extract a `FooId`, which is the same as `NodeIdForKind`, // as a single required child. template struct Extractable> { static auto Extract(const Tree* tree, Tree::SiblingIterator& it, Tree::SiblingIterator end, ErrorBuilder* trace) -> std::optional> { if (NodeIdForKindAccept(Kind, tree, it, end, trace)) { return NodeIdForKind(*it++); } else { return std::nullopt; } } }; static auto NodeIdInCategoryAccept(NodeCategory category, const Tree* tree, const Tree::SiblingIterator& it, Tree::SiblingIterator end, ErrorBuilder* trace) -> bool { if (it == end || !(tree->node_kind(*it).category() & category)) { if (trace) { *trace << "NodeIdInCategory " << category << " error: "; if (it == end) { *trace << "no more children\n"; } else { *trace << "kind " << tree->node_kind(*it) << " doesn't match\n"; } } return false; } if (trace) { *trace << "NodeIdInCategory " << category << ": kind " << tree->node_kind(*it) << " consumed\n"; } return true; } // Extract a `NodeIdInCategory` as a single child. template struct Extractable> { static auto Extract(const Tree* tree, Tree::SiblingIterator& it, Tree::SiblingIterator end, ErrorBuilder* trace) -> std::optional> { if (NodeIdInCategoryAccept(Category, tree, it, end, trace)) { return NodeIdInCategory(*it++); } else { return std::nullopt; } } }; static auto NodeIdOneOfAccept(std::initializer_list kinds, const Tree* tree, const Tree::SiblingIterator& it, Tree::SiblingIterator end, ErrorBuilder* trace) -> bool { auto trace_kinds = [&] { llvm::ListSeparator sep(" or "); for (auto kind : kinds) { *trace << sep << kind; } }; auto kind = tree->node_kind(*it); if (it == end || std::find(kinds.begin(), kinds.end(), kind) == kinds.end()) { if (trace) { if (it == end) { *trace << "NodeIdOneOf error: no more children, expected "; trace_kinds(); *trace << "\n"; } else { *trace << "NodeIdOneOf error: wrong kind " << tree->node_kind(*it) << ", expected "; trace_kinds(); *trace << "\n"; } } return false; } if (trace) { *trace << "NodeIdOneOf "; trace_kinds(); *trace << ": " << tree->node_kind(*it) << " consumed\n"; } return true; } // Extract a `NodeIdOneOf` as a single required child. template struct Extractable> { static auto Extract(const Tree* tree, Tree::SiblingIterator& it, Tree::SiblingIterator end, ErrorBuilder* trace) -> std::optional> { if (NodeIdOneOfAccept({T::Kind...}, tree, it, end, trace)) { return NodeIdOneOf(*it++); } else { return std::nullopt; } } }; // Extract a `NodeIdNot` as a single required child. // Note: this is only instantiated once, so no need to create a helper function. template struct Extractable> { static auto Extract(const Tree* tree, Tree::SiblingIterator& it, Tree::SiblingIterator end, ErrorBuilder* trace) -> std::optional> { if (it == end || tree->node_kind(*it) == T::Kind) { if (trace) { if (it == end) { *trace << "NodeIdNot " << T::Kind << " error: no more children\n"; } else { *trace << "NodeIdNot error: unexpected " << T::Kind << "\n"; } } return std::nullopt; } if (trace) { *trace << "NodeIdNot " << T::Kind << ": " << tree->node_kind(*it) << " consumed\n"; } return NodeIdNot(*it++); } }; // Extract an `llvm::SmallVector` by extracting `T`s until we can't. template struct Extractable> { static auto Extract(const Tree* tree, Tree::SiblingIterator& it, Tree::SiblingIterator end, ErrorBuilder* trace) -> std::optional> { if (trace) { *trace << "Vector: begin\n"; } llvm::SmallVector result; while (it != end) { auto old_it = it; auto item = Extractable::Extract(tree, it, end, trace); if (!item.has_value()) { it = old_it; break; } result.push_back(*item); } std::reverse(result.begin(), result.end()); if (trace) { *trace << "Vector: end\n"; } return result; } }; // Extract an `optional` from a list of child nodes by attempting to extract // a `T`, and extracting nothing if that fails. template struct Extractable> { static auto Extract(const Tree* tree, Tree::SiblingIterator& it, Tree::SiblingIterator end, ErrorBuilder* trace) -> std::optional> { if (trace) { *trace << "Optional " << typeid(T).name() << ": begin\n"; } auto old_it = it; std::optional value = Extractable::Extract(tree, it, end, trace); if (value) { if (trace) { *trace << "Optional " << typeid(T).name() << ": found\n"; } return value; } if (trace) { *trace << "Optional " << typeid(T).name() << ": missing\n"; } it = old_it; return value; } }; template static auto ExtractTupleLikeType(const Tree* tree, Tree::SiblingIterator& it, Tree::SiblingIterator end, ErrorBuilder* trace, std::index_sequence /*indices*/, std::tuple* /*type*/) -> std::optional { std::tuple...> fields; if (trace) { *trace << "Aggregate " << typeid(T).name() << ": begin\n"; } // Use a fold over the `=` operator to parse fields from right to left. [[maybe_unused]] int unused; bool ok = true; static_cast( ((ok && (ok = (std::get(fields) = Extractable::Extract(tree, it, end, trace)) .has_value()), unused) = ... = 0)); if (!ok) { if (trace) { *trace << "Aggregate " << typeid(T).name() << ": error\n"; } return std::nullopt; } if (trace) { *trace << "Aggregate " << typeid(T).name() << ": success\n"; } return T{std::move(std::get(fields).value())...}; } // Extract the fields of a simple aggregate type. template struct Extractable { static_assert(std::is_aggregate_v, "Unsupported child type"); static auto ExtractImpl(const Tree* tree, Tree::SiblingIterator& it, Tree::SiblingIterator end, ErrorBuilder* trace) -> std::optional { // Compute the corresponding tuple type. using TupleType = decltype(StructReflection::AsTuple(std::declval())); return ExtractTupleLikeType( tree, it, end, trace, std::make_index_sequence>(), static_cast(nullptr)); } static auto Extract(const Tree* tree, Tree::SiblingIterator& it, Tree::SiblingIterator end, ErrorBuilder* trace) -> std::optional { static_assert(!HasKindMember, "Missing Id suffix"); return ExtractImpl(tree, it, end, trace); } }; template auto Tree::TryExtractNodeFromChildren( llvm::iterator_range children, ErrorBuilder* trace) const -> std::optional { auto it = children.begin(); auto result = Extractable::ExtractImpl(this, it, children.end(), trace); if (it != children.end()) { if (trace) { *trace << "Error: " << node_kind(*it) << " node left unconsumed."; } return std::nullopt; } return result; } // Manually instantiate Tree::TryExtractNodeFromChildren #define CARBON_PARSE_NODE_KIND(KindName) \ template auto Tree::TryExtractNodeFromChildren( \ llvm::iterator_range children, \ ErrorBuilder * trace) const -> std::optional; // Also instantiate for `File`, even though it isn't a parse node. CARBON_PARSE_NODE_KIND(File) #include "toolchain/parse/node_kind.def" auto Tree::ExtractFile() const -> File { return ExtractNodeFromChildren(roots()); } } // namespace Carbon::Parse