| // Copyright 2017-2020 The Verible Authors. | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| // ConcreteSyntaxTree represents the structure of a body of text. | |
| // | |
| // This header also provides the following (owernship-transferring) functions | |
| // for constructing syntax trees in semantic action blocks: | |
| // | |
| // $$ = MakeNode($1, $2, ...); | |
| // $$ = MakeTaggedNode(kTag, $1, $2, ...); | |
| // $$ = ExtendNode($1, $2, ...); | |
| // $$ = MakeNode($1, ForwardChildren($2), $3, ...); | |
| // | |
| // As ownership is transferred exclusively, the pointers left behind are | |
| // null as a result. | |
| // | |
| // These functions are intended for use only in <language>.yc semantic actions. | |
| // | |
| // The std::move is automated for the sake of easy tree building. | |
| // Without the automation, the user would have to write: | |
| // $$ = MakeNode(std::move($1), std::move($2), ...); | |
| // which would be less readable than the above. | |
| #ifndef VERIBLE_COMMON_TEXT_CONCRETE_SYNTAX_TREE_H_ | |
| #define VERIBLE_COMMON_TEXT_CONCRETE_SYNTAX_TREE_H_ | |
| #include <algorithm> | |
| #include <cstddef> | |
| #include <initializer_list> | |
| #include <memory> | |
| #include <utility> | |
| #include <vector> | |
| #include "common/text/constants.h" | |
| #include "common/text/symbol.h" | |
| #include "common/text/tree_compare.h" | |
| #include "common/text/visitors.h" | |
| #include "common/util/casts.h" | |
| #include "common/util/logging.h" | |
| namespace verible { | |
| // Using unique_ptr in the symbol stack requires careful moving. | |
| using SymbolPtr = std::unique_ptr<Symbol>; | |
| // Currently, a tree *is* a tree-node, but this may change in the future. | |
| // Treat this as an opaque type. | |
| using ConcreteSyntaxTree = SymbolPtr; | |
| // Helper class for transferring ownership of children, which is | |
| // used as an overload to SyntaxTreeNode::AppendChild. | |
| // This takes over ownership of the symbol pointer. | |
| // $$ = MakeNode($1, $2, ForwardChildren($3), $4); | |
| struct ForwardChildren { | |
| explicit ForwardChildren(SymbolPtr& symbol) // NOLINT | |
| : node(std::move(symbol)) {} | |
| SymbolPtr node; | |
| }; | |
| // SyntaxTreeNode is a language-agnostic node structure, supporting an | |
| // arbitrary number of children. The 'tag' field is a node type enumeration | |
| // used by various language front-ends. | |
| class SyntaxTreeNode final : public Symbol { | |
| public: | |
| explicit SyntaxTreeNode(const int tag = kUntagged) : tag_(tag) {} | |
| const std::vector<SymbolPtr>& children() const { return children_; } | |
| std::vector<SymbolPtr>& mutable_children() { return children_; } | |
| // Transfer ownership of argument to this object. | |
| // Call MakeNode or ExtendNode instead of calling this directly. | |
| void AppendChild(SymbolPtr child) { children_.push_back(std::move(child)); } | |
| // Transfer ownership of argument's children to this object. | |
| // Call MakeNode or ExtendNode instead of calling this directly. | |
| // If node is actually a leaf, just append the leaf. | |
| void AppendChild(ForwardChildren forwarded_children) { | |
| if (forwarded_children.node == nullptr) return; | |
| auto* node = dynamic_cast<SyntaxTreeNode*>(forwarded_children.node.get()); | |
| if (node == nullptr) { | |
| // Could be a SyntaxTreeLeaf, for instance. | |
| children_.push_back(std::move(forwarded_children.node)); | |
| return; | |
| } | |
| const auto new_size = children_.size() + node->children_.size(); | |
| children_.reserve(new_size); | |
| for (auto& child : node->children_) { | |
| children_.push_back(std::move(child)); | |
| } | |
| // Remove all the vacated children slots left in the parent. | |
| node->children_.clear(); | |
| } | |
| // This no-op case is the base case for the variadic Append. | |
| void Append() const {} | |
| // Ownership of all arguments is transferred to this object. | |
| // Call MakeNode or ExtendNode instead of calling Append directly. | |
| template <typename T, typename... Args> | |
| void Append(T&& t, Args&&... args) { | |
| AppendChild(std::move(t)); // Append the first. | |
| Append(std::forward<Args>(args)...); // Append the rest. | |
| } | |
| // Children accessor (mutable). | |
| SymbolPtr& operator[](size_t i); | |
| // Children accessor (const). | |
| const SymbolPtr& operator[](size_t i) const; | |
| // Compares this node to an arbitrary symbol using the compare_tokens | |
| // function. | |
| bool equals(const Symbol* symbol, | |
| const TokenComparator& compare_tokens) const final; | |
| // Compares this node to another node. | |
| // Checks for recursive equality among all children of both nodes. | |
| bool equals(const SyntaxTreeNode* node, | |
| const TokenComparator& compare_tokens) const; | |
| // Uses passed TreeVisitorRecursive to visit all children recursively, | |
| // then visit itself. | |
| void Accept(TreeVisitorRecursive* visitor) const final; | |
| void Accept(MutableTreeVisitorRecursive* visitor, | |
| SymbolPtr* this_owned) final; | |
| // Accepting a symbol visitor does not recursively visit children. | |
| void Accept(SymbolVisitor* visitor) const final; | |
| // Method override that returns the Kind of Symbol | |
| SymbolKind Kind() const final { return SymbolKind::kNode; } | |
| SymbolTag Tag() const final { return NodeTag(tag_); } | |
| // MatchesTag returns true if the tag value matches the argument. | |
| // This is designed to work with any enumeration type. | |
| template <typename EnumType> | |
| bool MatchesTag(EnumType e) const { | |
| return tag_ == static_cast<int>(e); | |
| } | |
| template <typename EnumType> | |
| bool MatchesTagAnyOf(std::initializer_list<EnumType> enums) const { | |
| auto it = enums.begin(); | |
| // Unroll OR expression. Right now, we never have more than 4. | |
| switch (enums.size()) { | |
| case 4: | |
| if (*it++ == EnumType(tag_)) return true; | |
| ABSL_FALLTHROUGH_INTENDED; | |
| case 3: | |
| if (*it++ == EnumType(tag_)) return true; | |
| ABSL_FALLTHROUGH_INTENDED; | |
| case 2: | |
| if (*it++ == EnumType(tag_)) return true; | |
| ABSL_FALLTHROUGH_INTENDED; | |
| case 1: | |
| if (*it++ == EnumType(tag_)) return true; | |
| ABSL_FALLTHROUGH_INTENDED; | |
| case 0: | |
| return false; | |
| default: | |
| // TODO(hzeller): with constexpr magic, can we static_assert() this ? | |
| LOG(FATAL) << "need more choice " << enums.size(); | |
| return false; | |
| } | |
| } | |
| private: | |
| // This tag would really prefer to be a language-specific node enumeration | |
| // type, but that would (IMHO) create unecessary templating. | |
| // Decision: Keep this a generic int. | |
| int tag_; | |
| // Sequence of pointers to subtrees and nodes. | |
| std::vector<SymbolPtr> children_; | |
| }; | |
| // The following functions are intended for use in semantic action blocks | |
| // in yacc/bison grammar files (.yc). | |
| // Construct a syntax tree node with a tag. | |
| // Ownership of all args is transferred, and consumed by the new node. | |
| // Sample usage: $$ = MakeNode(TAG, $1, $2, $3); | |
| template <typename... Args> | |
| SymbolPtr MakeNode(Args&&... args) { | |
| std::unique_ptr<SyntaxTreeNode> node_pointer(new SyntaxTreeNode); | |
| node_pointer->Append(std::forward<Args>(args)...); | |
| return std::move(node_pointer); | |
| } | |
| // Construct a syntax tree node with a tag. | |
| // Ownership of all args is transferred, and consumed by the new node. | |
| // Sample usage: | |
| // $$ = MakeTaggedNode(TAG); // empty, no children | |
| // $$ = MakeTaggedNode(TAG, $1, $2, $3); | |
| template <typename Enum, typename... Args> | |
| SymbolPtr MakeTaggedNode(const Enum tag, Args&&... args) { | |
| std::unique_ptr<SyntaxTreeNode> node_pointer( | |
| new SyntaxTreeNode(static_cast<int>(tag))); | |
| node_pointer->Append(std::forward<Args>(args)...); | |
| return std::move(node_pointer); | |
| } | |
| // Extend the children of an existing node. | |
| // Equivalent to: $$ = std::move(ExtendNode($1, $2, $3)); | |
| // Ownership of all args is transferred, and consumed by the existing node. | |
| // $1 is transferred to $$. | |
| // Sample usage: $$ = ExtendNode($1, $2, $3); | |
| template <typename T, typename... Args> | |
| SymbolPtr ExtendNode(T&& list_ptr, Args&&... args) { | |
| return _ExtendNodeMoved(std::move(list_ptr), std::forward<Args>(args)...); | |
| } | |
| // Implementation detail, call ExtendNode instead for automatic std::move. | |
| template <typename... Args> | |
| SymbolPtr _ExtendNodeMoved(SymbolPtr list_ptr, Args&&... args) { | |
| CHECK(list_ptr->Kind() == SymbolKind::kNode); | |
| SyntaxTreeNode* node_pointer = down_cast<SyntaxTreeNode*>(list_ptr.get()); | |
| node_pointer->Append(std::forward<Args>(args)...); | |
| return list_ptr; | |
| } | |
| // Helper function to deal with move semantics and argument forwarding | |
| void SetChild_(const SymbolPtr& parent, int child_index, SymbolPtr new_child); | |
| // Sets the child at child_index of parent to new_child. | |
| // SetChild will crash when: | |
| // Child_index is out of range | |
| // Parent either a leaf or a nullptr | |
| // Preexisting data at target index is not a nullptr | |
| // Equivalent to: parent->children[child_index] = std::move(new_child); | |
| template <typename T> | |
| void SetChild(const SymbolPtr& parent, int child_index, T&& new_child) { | |
| SetChild_(parent, child_index, std::move(new_child)); | |
| } | |
| } // namespace verible | |
| #endif // VERIBLE_COMMON_TEXT_CONCRETE_SYNTAX_TREE_H_ |