| // 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. | |
| #ifndef VERIBLE_COMMON_UTIL_VECTOR_TREE_H_ | |
| #define VERIBLE_COMMON_UTIL_VECTOR_TREE_H_ | |
| #include <algorithm> | |
| #include <cstddef> | |
| #include <functional> | |
| #include <iosfwd> // IWYU pragma: keep | |
| #include <iterator> | |
| #include <set> | |
| #include <utility> | |
| #include <vector> | |
| #include "common/util/iterator_range.h" | |
| #include "common/util/logging.h" | |
| #include "common/util/spacer.h" | |
| namespace verible { | |
| // This helper class contains private template functions for many pairs | |
| // of const/mutable methods of VectorTree. This reduces the amount of code | |
| // duplication for similar methods. | |
| // In all template functions, type TP is either 'VectorTree' or | |
| // 'const VectorTree', and is inferrable from template argument deduction. | |
| // Recursive calls automatically call the correct variant. | |
| class _VectorTreeImpl { | |
| protected: | |
| // Returns pointer root node, following ancestry chain. | |
| template <typename TP> | |
| static TP* _Root(TP* node) { | |
| while (node->Parent() != nullptr) { | |
| node = node->Parent(); | |
| } | |
| return node; | |
| } | |
| // Returns pointer to next sibling if it exists, else nullptr. | |
| template <typename TP> | |
| static TP* _NextSibling(TP* node) { | |
| if (node->Parent() == nullptr) { | |
| return nullptr; | |
| } | |
| const size_t birth_rank = node->BirthRank(); | |
| const size_t next_rank = birth_rank + 1; | |
| if (next_rank == node->Parent()->Children().size()) { | |
| return nullptr; // This is the last child of the Parent(). | |
| } | |
| // More children follow this one. | |
| return &node->Parent()->Children()[next_rank]; | |
| } | |
| // Returns pointer to previous sibling if it exists, else nullptr. | |
| template <typename TP> | |
| static TP* _PreviousSibling(TP* node) { | |
| if (node->Parent() == nullptr) { | |
| return nullptr; | |
| } | |
| const size_t birth_rank = node->BirthRank(); | |
| if (birth_rank == 0) { | |
| return nullptr; | |
| } | |
| // More children precede this one. | |
| return &node->Parent()->Children()[birth_rank - 1]; | |
| } | |
| // Descend through children using indices specified by iterator range. | |
| // Iter type dereferences to an integral value. | |
| // This works on any internal node, not just the root. | |
| template <typename TP, typename Iter> | |
| static TP& _DescendPath(TP* node, Iter start, Iter end) { | |
| for (; start != end; ++start) { | |
| auto& children = node->Children(); | |
| node = &children[*start]; // descend | |
| } | |
| return *node; | |
| } | |
| // Returns the node reached by descending through Children().front(). | |
| template <typename TP> | |
| static TP* _LeftmostDescendant(TP* node) { | |
| while (!node->Children().empty()) { | |
| node = &node->Children().front(); | |
| } | |
| return node; | |
| } | |
| // Returns the node reached by descending through Children().back(). | |
| template <typename TP> | |
| static TP* _RightmostDescendant(TP* node) { | |
| while (!node->Children().empty()) { | |
| node = &node->Children().back(); | |
| } | |
| return node; | |
| } | |
| // Navigates to the next leaf (node without Children()) in the tree | |
| // (if it exists), else returns nullptr. | |
| template <typename TP> | |
| static TP* _NextLeaf(TP* node) { | |
| auto* parent = node->Parent(); | |
| if (parent == nullptr) { | |
| // Root node has no next sibling, this is the end(). | |
| return nullptr; | |
| } | |
| // Find the next sibling, if there is one. | |
| auto& siblings = parent->Children(); | |
| const size_t birth_rank = node->BirthRank(); | |
| const size_t next_rank = birth_rank + 1; | |
| if (next_rank != siblings.size()) { | |
| // More children follow this one. | |
| return siblings[next_rank].LeftmostDescendant(); | |
| } | |
| // This is the last child of the group. | |
| // Find the nearest parent that has a next child (ascending). | |
| // TODO(fangism): rewrite without recursion | |
| auto* next_ancestor = parent->NextLeaf(); | |
| if (next_ancestor == nullptr) return nullptr; | |
| // next_ancestor is the NearestCommonAncestor() to the original | |
| // node and the resulting node. | |
| return next_ancestor->LeftmostDescendant(); | |
| } | |
| // Navigates to the previous leaf (node without Children()) in the tree | |
| // (if it exists), else returns nullptr. | |
| template <typename TP> | |
| static TP* _PreviousLeaf(TP* node) { | |
| auto* parent = node->Parent(); | |
| if (parent == nullptr) { | |
| // Root node has no previous sibling, this is the reverse-end(). | |
| return nullptr; | |
| } | |
| // Find the next sibling, if there is one. | |
| auto& siblings = parent->Children(); | |
| const size_t birth_rank = node->BirthRank(); | |
| if (birth_rank > 0) { | |
| // More children precede this one. | |
| return siblings[birth_rank - 1].RightmostDescendant(); | |
| } | |
| // This is the first child of the group. | |
| // Find the nearest parent that has a previous child (descending). | |
| // TODO(fangism): rewrite without recursion | |
| auto* prev_ancestor = parent->PreviousLeaf(); | |
| if (prev_ancestor == nullptr) return nullptr; | |
| // prev_ancestor is the NearestCommonAncestor() to the original | |
| // node and the resulting node. | |
| return prev_ancestor->RightmostDescendant(); | |
| } | |
| // Returns the nearest common ancestor node to two nodes if a common ancestor | |
| // exists, else nullptr. | |
| // Run time: let L and R be the number of ancestors of left and right, | |
| // respectively. In the worst case, there will be L and R set insertions, | |
| // so O(L lg L) + I(R lg R) and L and R membership checks, so O(L lg R) + O(R | |
| // lg L). With K = max(L, R), overall this is no worse than O(K lg K). | |
| template <typename TP> | |
| static TP* _NearestCommonAncestor(TP* left, TP* right) { | |
| std::set<TP*> left_ancestors, right_ancestors; | |
| // In alternation, insert left/right into its respective set of ancestors, | |
| // and check for membership in the other ancestor set. | |
| // Return as soon as one is found in the other's set of ancestors. | |
| while (left != nullptr || right != nullptr) { | |
| if (left != nullptr) { | |
| left_ancestors.insert(left); | |
| if (right_ancestors.find(left) != right_ancestors.end()) { | |
| return left; | |
| } | |
| left = left->Parent(); | |
| } | |
| if (right != nullptr) { | |
| right_ancestors.insert(right); | |
| if (left_ancestors.find(right) != left_ancestors.end()) { | |
| return right; | |
| } | |
| right = right->Parent(); | |
| } | |
| } | |
| // Once this point is reached, there are no common ancestors. | |
| return nullptr; | |
| } | |
| }; | |
| // VectorTree is a hierarchical representation of information. | |
| // While it may be useful to maintain some invariant relationship between | |
| // parents and children nodes, it is not required for this class. | |
| // The traversal methods ApplyPreOrder/ApplyPostOrder could be used to | |
| // maintain or verify parent-child invariants. | |
| // The VectorTree class is itself also the same as the internally used node | |
| // class; i.e. there is no separate node class. | |
| // | |
| // Example applications (with some parent-child invariant relationship): | |
| // | |
| // * Range interval tree -- A numeric range from [0:N] could be sub-divided | |
| // into smaller ranges [0:k], [k:N] for some 0 < k < N, or multiple | |
| // monotonically increasing k's. This class is a generalization of an | |
| // interval tree concept. | |
| // | |
| // * Lexical tokens output -- some tokens could be further tokenized or | |
| // expanded, but the choice of view depends on consumer and application. | |
| // This can be useful for embedding snippets of one language within another; | |
| // further lexing/parsing of an unexpanded substring can be deferred. | |
| // | |
| // * Lexical token range partitions -- token sub-range partitioning is a | |
| // critical step in a formatting strategy; the decision to partition | |
| // a particular range may be difficult to determine statically, so it | |
| // is best left undecided until a later heuristic pass. | |
| // | |
| // Construction: | |
| // This implementation gives the user the liberty to construct the tree | |
| // nodes in any order and any manner. A top-down construction may guarantee | |
| // that a parent is well-formed before creating its children, whereas a | |
| // bottom-up construction completes creation of children before the enveloping | |
| // parent. | |
| template <typename T> | |
| class VectorTree : private _VectorTreeImpl { | |
| typedef VectorTree<T> this_type; | |
| // Self-recursive type that represents children in an expanded view. | |
| typedef std::vector<this_type> subnodes_type; | |
| typedef _VectorTreeImpl impl_type; | |
| // Private default constructor. | |
| VectorTree() = default; | |
| public: | |
| typedef T value_type; | |
| // Deep copy-constructor. | |
| VectorTree(const this_type& other) | |
| : node_value_(other.node_value_), | |
| children_(other.children_), | |
| parent_(other.parent_) { | |
| Relink(); | |
| } | |
| VectorTree(this_type&& other) | |
| : node_value_(std::move(other.node_value_)), | |
| children_(std::move(other.children_)), | |
| parent_(other.parent_) { | |
| Relink(); | |
| } | |
| // This constructor can be used to recursively build trees. | |
| // e.g. | |
| // // looks like function-call, but just invokes constructor: | |
| // typedef VectorTree<Foo> FooNode; | |
| // auto foo_tree = FooNode({value-initializer}, | |
| // FooNode({value-initializer}, /* children nodes... */ ), | |
| // FooNode({value-initializer}, /* children nodes... */ ) | |
| // ); | |
| template <typename... Args> | |
| explicit VectorTree(const value_type& v, Args&&... args) | |
| : node_value_(v), children_() { | |
| AdoptSubtree(std::forward<Args>(args)...); | |
| } | |
| template <typename... Args> | |
| explicit VectorTree(value_type&& v, Args&&... args) | |
| : node_value_(std::move(v)), children_() { | |
| AdoptSubtree(std::forward<Args>(args)...); | |
| } | |
| ~VectorTree() { CHECK(CheckIntegrity()); } | |
| // Swaps values and subtrees of two nodes. | |
| // This operation is safe for unrelated trees (no common ancestor). | |
| // This operation is safe when the two nodes share a common ancestor, | |
| // excluding the case where one node is a direct ancestor of the other. | |
| // TODO(fangism): Add a proper check for this property, and test. | |
| void swap(this_type& other) { | |
| // parent_'s are unchanged. | |
| std::swap(node_value_, other.node_value_); | |
| children_.swap(other.children_); // efficient O(1) vector::swap | |
| Relink(); // O(|children|) | |
| other.Relink(); // O(|children|) | |
| } | |
| // Copy value and children, but relink new children to this node. | |
| VectorTree& operator=(const this_type& source) { | |
| node_value_ = source.node_value_; | |
| children_ = source.Children(); | |
| Relink(); | |
| return *this; | |
| } | |
| // Explicit move-assignability needed for vector::erase() | |
| // No need to change parent links when children keep same parent. | |
| VectorTree& operator=(this_type&& source) { | |
| // Keep this->parent_. Ignore source.parent_. | |
| node_value_ = std::move(source.node_value_); | |
| children_.clear(); | |
| // vector::swap only moves pointers, not elements | |
| children_.swap(source.children_); | |
| Relink(); // because the address of 'this' changes. | |
| return *this; | |
| } | |
| // Builders | |
| // Appends a new child node to the tree at this level. | |
| // 'this' node is the parent of the new child. | |
| // Returns a pointer to the newly added child. | |
| // This invalidates previous iterators/pointers to sibling children. | |
| template <typename... Args> | |
| this_type* NewChild(Args&&... args) { | |
| // emplace_back() may cause realloc | |
| children_.emplace_back(std::forward<Args>(args)...); | |
| // Relink child to parent. | |
| auto& newest = children_.back(); | |
| newest.parent_ = this; | |
| return &newest; | |
| } | |
| // Appends a new child node to the parent of this node. | |
| // Returns a pointer to the newly added sibling. | |
| // This invalidates previous iterators/pointers to sibling children. | |
| template <typename... Args> | |
| this_type* NewSibling(Args&&... args) { | |
| return ABSL_DIE_IF_NULL(parent_)->NewChild(std::forward<Args>(args)...); | |
| } | |
| // No-op base case for variadic AdoptSubtree. | |
| void AdoptSubtree() const {} | |
| // Appends one or more sub-trees at this level. | |
| // Variadic template handles one argument at a time. | |
| // This invalidates previous iterators/pointers to sibling children. | |
| template <typename F, typename... Args> | |
| void AdoptSubtree(F&& first, Args&&... args) { | |
| children_.emplace_back(std::forward<F>(first)); // may cause realloc | |
| // Relink child to parent. | |
| auto& newest = children_.back(); | |
| newest.parent_ = this; | |
| AdoptSubtree(std::forward<Args>(args)...); | |
| } | |
| // This node takes/moves subtrees from another node (concatenates). | |
| // There need not be any relationship between this node and the other. | |
| void AdoptSubtreesFrom(this_type* other) { | |
| auto& src_children = other->Children(); | |
| children_.reserve(children_.size() + src_children.size()); | |
| AdoptSubtreesFromUnreserved(&src_children); | |
| } | |
| // Accessors | |
| T& Value() { return node_value_; } | |
| const T& Value() const { return node_value_; } | |
| this_type* Parent() { return parent_; } | |
| const this_type* Parent() const { return parent_; } | |
| subnodes_type& Children() { return children_; } | |
| const subnodes_type& Children() const { return children_; } | |
| // Properties | |
| // Returns the index of this node relative to parent's children. | |
| // An only-child or first-child will have birth rank 0. | |
| size_t BirthRank() const { | |
| if (parent_ != nullptr) { | |
| // Parent() must have Children(); this is one of them. | |
| // Storage of siblings must be contiguous, so that pointer | |
| // distance translates to index. | |
| return std::distance(&Parent()->Children().front(), this); | |
| } | |
| return 0; | |
| } | |
| // Returns the number of parents between this node and the root. | |
| size_t NumAncestors() const { | |
| size_t depth = 0; | |
| for (const this_type* iter = Parent(); iter != nullptr; | |
| iter = iter->Parent()) { | |
| ++depth; | |
| } | |
| return depth; | |
| } | |
| // Returns true if 'other' is an ancestor of this node, in other words, | |
| // this node is descended from 'other'. | |
| // This method could have been named IsDescendedFrom(). | |
| // nullptr is never considered an ancestor of any node. | |
| // 'this' node is not considered an ancestor of itself. | |
| bool ContainsAncestor(const this_type* other) const { | |
| if (other == nullptr) return false; | |
| for (const this_type* iter = Parent(); iter != nullptr; | |
| iter = iter->Parent()) { | |
| if (iter == other) return true; | |
| } | |
| return false; | |
| } | |
| // Returns pointer to the tree root, the greatest ancestor of this node. | |
| const this_type* Root() const { return impl_type::_Root(this); } | |
| // Returns mutable pointer to the tree root. | |
| this_type* Root() { return impl_type::_Root(this); } | |
| // Returns the closest common ancestor to this and the other, else nullptr. | |
| // This is the const pointer overload. | |
| const this_type* NearestCommonAncestor(const this_type* other) const { | |
| return impl_type::_NearestCommonAncestor(this, other); | |
| } | |
| // Returns the closest common ancestor to this and the other, else nullptr. | |
| // This is the mutable pointer overload. | |
| this_type* NearestCommonAncestor(this_type* other) { | |
| return impl_type::_NearestCommonAncestor(this, other); | |
| } | |
| // Construct a path of BirthRank()s from root to this. | |
| // Root node's 'path' is empty. Passing the resulting path to | |
| // root.DescendPath() gets you back to this node. | |
| // PathType can be any container with a push_back() interface. | |
| template <class PathType> | |
| void Path(PathType& path) const { | |
| if (Parent() != nullptr) { | |
| Parent()->Path(path); | |
| path.push_back(BirthRank()); | |
| } | |
| } | |
| // Descend through children using indices specified by iterator range. | |
| // Iter type dereferences to an integral value. | |
| // This works on any internal node, not just the root. | |
| template <class Iter> | |
| const this_type& DescendPath(Iter start, Iter end) const { | |
| return impl_type::_DescendPath(this, start, end); | |
| } | |
| // Returns the next sibling node if it exists, else nullptr. | |
| const this_type* NextSibling() const { return impl_type::_NextSibling(this); } | |
| // Returns the next sibling node if it exists, else nullptr. | |
| this_type* NextSibling() { return impl_type::_NextSibling(this); } | |
| // Returns the previous sibling node if it exists, else nullptr. | |
| const this_type* PreviousSibling() const { | |
| return impl_type::_PreviousSibling(this); | |
| } | |
| // Returns the previous sibling node if it exists, else nullptr. | |
| this_type* PreviousSibling() { return impl_type::_PreviousSibling(this); } | |
| // Mutable variant of DescendPath. | |
| template <class Iter> | |
| this_type& DescendPath(Iter start, Iter end) { | |
| return impl_type::_DescendPath(this, start, end); | |
| } | |
| // Returns the node reached by descending through Children().front(). | |
| const this_type* LeftmostDescendant() const { | |
| return impl_type::_LeftmostDescendant(this); | |
| } | |
| // Returns the node reached by descending through Children().front(), mutable | |
| // variant. | |
| this_type* LeftmostDescendant() { | |
| return impl_type::_LeftmostDescendant(this); | |
| } | |
| // Returns the node reached by descending through Children().back(). | |
| const this_type* RightmostDescendant() const { | |
| return impl_type::_RightmostDescendant(this); | |
| } | |
| // Returns the node reached by descending through Children().back(), mutable | |
| // variant. | |
| this_type* RightmostDescendant() { | |
| return impl_type::_RightmostDescendant(this); | |
| } | |
| // Returns true if this node has no parent, or it has BirthRank 0. | |
| bool IsFirstChild() const { | |
| if (Parent() == nullptr) return true; | |
| return BirthRank() == 0; | |
| } | |
| // Returns true if this node has no parent, or it is the last child of its | |
| // parent. | |
| bool IsLastChild() const { | |
| if (Parent() == nullptr) return true; | |
| return BirthRank() == Parent()->Children().size() - 1; | |
| } | |
| // Navigates to the next leaf (node without Children()) in the tree | |
| // (if it exists), else returns nullptr. | |
| const this_type* NextLeaf() const { return impl_type::_NextLeaf(this); } | |
| // Mutable variant of NextLeaf(). | |
| this_type* NextLeaf() { return impl_type::_NextLeaf(this); } | |
| // Navigates to the previous leaf (node without Children()) in the tree | |
| // (if it exists), else returns nullptr. | |
| const this_type* PreviousLeaf() const { | |
| return impl_type::_PreviousLeaf(this); | |
| } | |
| // Mutable variant of PreviousLeaf(). | |
| this_type* PreviousLeaf() { return impl_type::_PreviousLeaf(this); } | |
| // Removes this node from its parent, and shifts ths siblings that follow this | |
| // node lower in BirthRank(). | |
| // Any iterators that pointed to this node or its later siblings are | |
| // invalidated. | |
| // This node is destroyed in the process. | |
| // This operation is only valid on non-root nodes. | |
| // It is the caller's responsibility to maintain invariants before | |
| // destroying this node. | |
| void RemoveSelfFromParent() { | |
| auto& siblings = ABSL_DIE_IF_NULL(Parent())->Children(); | |
| auto self_iter = siblings.begin() + BirthRank(); | |
| CHECK_EQ(&*self_iter, this); | |
| siblings.erase(self_iter); | |
| } | |
| // TODO(fangism): provide unidirectional iterator views, forward and reversed, | |
| // using NextLeaf() and PreviousLeaf(). | |
| // Returns true if parent-child links are valid in entire tree. | |
| bool CheckIntegrity() const { | |
| for (const auto& child : children_) { | |
| CHECK_EQ(child.Parent(), this) | |
| << "Inconsistency: child's parent does not point back to this node!"; | |
| if (!child.CheckIntegrity()) return false; | |
| } | |
| return true; | |
| } | |
| // Function-application traversals. | |
| // * Apply in pre-order vs. post-order. | |
| // * Apply to attached value at each node vs. the whole node itself. | |
| // Whole node gives access to immediate children (and subtree). | |
| // * Apply function is const vs. mutating. | |
| // All combinations of the above are provided. | |
| // Visits all tree nodes in pre-order traversal applying function to all nodes | |
| // (non-modifying). Useful for checking invariants between parents and | |
| // their children. | |
| void ApplyPreOrder(const std::function<void(const this_type&)>& f) const { | |
| f(*this); | |
| for (const auto& child : Children()) { | |
| child.ApplyPreOrder(f); | |
| } | |
| } | |
| // This variant of ApplyPreOrder expects a function on the underlying | |
| // value_type (const&). | |
| void ApplyPreOrder(const std::function<void(const value_type&)>& f) const { | |
| ApplyPreOrder([&f](const this_type& t) { f(t.Value()); }); | |
| } | |
| // Visits all tree nodes in pre-order traversal applying function to all nodes | |
| // (modifying). | |
| void ApplyPreOrder(const std::function<void(this_type&)>& f) { | |
| f(*this); | |
| for (auto& child : Children()) { | |
| child.ApplyPreOrder(f); | |
| } | |
| } | |
| // Visits all tree nodes in pre-order traversal applying function to all node | |
| // values (modifying). Useful for applying transformations. | |
| void ApplyPreOrder(const std::function<void(value_type&)>& f) { | |
| ApplyPreOrder([&f](this_type& t) { f(t.Value()); }); | |
| } | |
| // Visits all tree nodes in post-order traversal applying function to all | |
| // nodes (non-modifying). Useful for checking invariants between parents and | |
| // their children. | |
| void ApplyPostOrder(const std::function<void(const this_type&)>& f) const { | |
| for (const auto& child : Children()) { | |
| child.ApplyPostOrder(f); | |
| } | |
| f(*this); | |
| } | |
| // This variant of ApplyPostOrder expects a function on the underlying | |
| // value_type (const&). | |
| void ApplyPostOrder(const std::function<void(const value_type&)>& f) const { | |
| ApplyPostOrder([&f](const this_type& t) { f(t.Value()); }); | |
| } | |
| // Visits all tree nodes in post-order traversal applying function to all | |
| // nodes (modifying). Useful for applying transformations. | |
| void ApplyPostOrder(const std::function<void(this_type&)>& f) { | |
| for (auto& child : Children()) { | |
| child.ApplyPostOrder(f); | |
| } | |
| f(*this); | |
| } | |
| // Visits all tree nodes in post-order traversal applying function to all node | |
| // values (modifying). Useful for applying transformations. | |
| void ApplyPostOrder(const std::function<void(value_type&)>& f) { | |
| ApplyPostOrder([&f](this_type& t) { f(t.Value()); }); | |
| } | |
| // Recursively transform a VectorTree<T> to VectorTree<S>. | |
| // This cannot be expressed as a constructor because template constructor | |
| // calls must be type-deducible from its constructor args, and there's no | |
| // way to explicitly invoke a constructor with specific template args. | |
| // The resulting tree is always StructureEqual() to the original tree. | |
| // | |
| // Usage: | |
| // auto tree_other = orig_tree.Transform<OtherType>( | |
| // [](const VectorTree<OrigType>& node) { return ... }); | |
| template <typename S> | |
| VectorTree<S> Transform( | |
| const std::function<S(const this_type& node)>& f) const { | |
| VectorTree<S> return_tree(f(*this)); // sets Value() | |
| auto& return_children = return_tree.Children(); | |
| return_children.reserve(Children().size()); | |
| // Construct children subtree. | |
| for (const auto& child : Children()) { | |
| return_tree.AdoptSubtree(child.template Transform<S>(f)); | |
| } | |
| return return_tree; // Rely on copy-elision. | |
| } | |
| // If this node has exactly one child, replace this node with that child | |
| // and return true, otherwise, do nothing and return false. | |
| bool HoistOnlyChild() { | |
| if (Children().size() != 1) return false; | |
| auto& only = Children().front(); | |
| only.parent_ = nullptr; // disconnect from parent | |
| std::swap(node_value_, only.node_value_); | |
| // children_.swap(only.children_); // but this leaks | |
| // so we swap through a temporary, which still avoids any copying. | |
| subnodes_type temp; | |
| temp.swap(only.children_); | |
| children_.swap(temp); | |
| Relink(); | |
| return true; | |
| } | |
| // Combines the Nth and (N+1) sibling using a custom function 'joiner' on the | |
| // nodes' values, and the Nth sibling will adopt N+1's children. | |
| // The 'joiner' function does: *left = f(*left, *right); | |
| // The (N+1) sibling will be erased in the process, and every sibling | |
| // thereafter will be shifted back one position (same inefficiency as shifting | |
| // vector contents). This invalidates all iterators after position N, | |
| // and iterators to the Nth node's children (possible realloc). | |
| void MergeConsecutiveSiblings( | |
| size_t N, std::function<void(value_type*, const value_type&)> joiner) { | |
| CHECK_LT(N + 1, children_.size()); | |
| // Combine value into node[N]. | |
| joiner(&Children()[N].node_value_, Children()[N + 1].node_value_); | |
| // Move-concatenate children to node[N]. | |
| const auto next_iter = children_.begin() + N + 1; | |
| Children()[N].AdoptSubtreesFrom(&*next_iter); | |
| // Shift-left children_ by 1 beyond N. | |
| children_.erase(next_iter); // done via move-assignment | |
| } | |
| // Replace all direct children of this node with concatenated grandchildren. | |
| // Retains the value of this node. Discards direct childrens' values. | |
| void FlattenOnce() { | |
| // Emancipate children from this node without discarding them yet. | |
| subnodes_type temp; | |
| temp.swap(children_); | |
| // Reserve space for all grandchildren. | |
| { | |
| size_t num_grandchildren = 0; | |
| for (const auto& child : temp) { | |
| num_grandchildren += child.Children().size(); | |
| } | |
| children_.reserve(num_grandchildren); | |
| } | |
| // Concatenate all grandchildren. | |
| for (auto& child : temp) { | |
| AdoptSubtreesFromUnreserved(&child.Children()); | |
| } | |
| // temp, which holds the discarded children, is destroyed. | |
| } | |
| // For every child, if that child has grandchildren, replace that child with | |
| // its grandchildren, else preserve that child. | |
| // If new_offsets is provided, populate that array with indices into the | |
| // resulting children that correspond to the start locations of the original | |
| // children's children. This lets the caller reference and operate on | |
| // subranges of the original set of grandchildren. | |
| void FlattenOnlyChildrenWithChildren( | |
| std::vector<size_t>* new_offsets = nullptr) { | |
| // Emancipate children from this node without discarding them yet. | |
| subnodes_type temp; | |
| temp.swap(children_); | |
| // Reserve space. | |
| { | |
| if (new_offsets != nullptr) { | |
| new_offsets->clear(); | |
| new_offsets->reserve(temp.size()); | |
| } | |
| size_t num_grandchildren = 0; | |
| for (const auto& child : temp) { | |
| if (new_offsets != nullptr) new_offsets->push_back(num_grandchildren); | |
| num_grandchildren += std::max(child.Children().size(), size_t(1)); | |
| } | |
| children_.reserve(num_grandchildren); | |
| } | |
| // Concatenate children-without-grandchildren and grandchildren. | |
| for (auto& child : temp) { | |
| if (child.Children().empty()) { | |
| children_.emplace_back(std::move(child)); | |
| } else { | |
| AdoptSubtreesFromUnreserved(&child.Children()); | |
| } | |
| } | |
| // temp, which holds the discarded children, is destroyed. | |
| } | |
| // Replace the i'th child with its children. This may result in increasing | |
| // the number of direct children of this node. | |
| void FlattenOneChild(size_t i) { | |
| const size_t original_size = Children().size(); | |
| CHECK_LT(i, original_size); | |
| // Unlink the grandchildren that will be adopted (in staging area). | |
| subnodes_type adopted_grandchildren; | |
| adopted_grandchildren.swap(Children()[i].Children()); | |
| { | |
| // Remove i'th child. | |
| const auto insert_iter = children_.begin() + i; | |
| children_.erase(insert_iter); | |
| // Allocate room for new children. | |
| this_type dummy_node; | |
| children_.insert(insert_iter, adopted_grandchildren.size(), dummy_node); | |
| // insert_iter is invalidated here, due to potential re-allocation | |
| } | |
| { | |
| // Insert adopted grandchildren (via move-swap to avoid copying). | |
| const auto insert_iter = children_.begin() + i; | |
| std::swap_ranges(adopted_grandchildren.begin(), | |
| adopted_grandchildren.end(), insert_iter); | |
| // Relink them to this node. | |
| for (auto& child : verible::make_range( | |
| insert_iter, insert_iter + adopted_grandchildren.size())) { | |
| child.parent_ = this; | |
| } | |
| } | |
| } | |
| // Pretty-print in tree-form. Value() is enclosed in parens, and the whole | |
| // node is enclosed in braces. | |
| std::ostream& PrintTree(std::ostream* stream, size_t indent = 0) const { | |
| *stream << Spacer(indent) << "{ (" << Value() << ')'; | |
| if (Children().empty()) { | |
| *stream << " }"; | |
| } else { | |
| *stream << '\n'; | |
| for (const auto& child : Children()) { | |
| child.PrintTree(stream, indent + 2) << '\n'; | |
| } | |
| *stream << Spacer(indent) << '}'; | |
| } | |
| return *stream; | |
| } | |
| private: | |
| // Establish parent-child links. | |
| void Relink() { | |
| for (auto& child : children_) { | |
| child.parent_ = this; | |
| } | |
| } | |
| // This node takes/moves subtrees from another array of node (concatenates). | |
| void AdoptSubtreesFromUnreserved(subnodes_type* other) { | |
| for (auto& child : *other) { | |
| AdoptSubtree(std::move(child)); // already Relink()s | |
| } | |
| other->clear(); | |
| } | |
| // Singular value stored at this node. | |
| value_type node_value_; | |
| // Array of nodes/subtrees. | |
| subnodes_type children_; | |
| // Pointer up to parent node. | |
| // Only the root node of a tree has a nullptr parent_. | |
| this_type* parent_ = nullptr; | |
| }; | |
| // Stream-printable representation of the location of a node under its | |
| // greatest ancestor (root). | |
| // Usage: stream << NodePath(vector_tree); | |
| struct NodePath { | |
| template <class T> | |
| explicit NodePath(const VectorTree<T>& node) { | |
| node.Path(path); | |
| } | |
| std::vector<size_t> path; | |
| }; | |
| std::ostream& operator<<(std::ostream& stream, const NodePath& p); | |
| template <typename T> | |
| std::ostream& operator<<(std::ostream& stream, const VectorTree<T>& node) { | |
| return node.PrintTree(&stream); | |
| } | |
| // Binary operations | |
| // Struct used to point to corresponding two nodes in different trees. | |
| // This could be interpreted as a difference object. | |
| // Both pointers should be nullptr when DeepEqual() finds no differences between | |
| // any corresponding pair of tree nodes, or non-nullptr when some difference is | |
| // found. When they are both non-nullptr, left != right by some user-defined | |
| // comparison function, and their ->Path()s are equivalent (as ensured by | |
| // DeepEqual's simultaneous traversal). | |
| // This is analogous to std::mismatch()'s return type: a pair of non-end() | |
| // iterators pointing to the first difference found in a dual-linear | |
| // traversal. | |
| template <typename LT, typename RT> | |
| struct VectorTreeNodePair { | |
| VectorTreeNodePair() {} | |
| VectorTreeNodePair(const LT* l, const RT* r) : left(l), right(r) {} | |
| const LT* left = nullptr; | |
| const RT* right = nullptr; | |
| }; | |
| // Recursively compares two trees node-for-node, checking their values | |
| // and substructure. The value types of the trees being compared need not be | |
| // the same, as long as there exists a comparison function for them. | |
| // The comparison function should be an equality function, i.e. it should | |
| // return true when values are considered equal, otherwise false. | |
| // Traversal order: pre-order (compare parents before children) | |
| // Returns pair of pointers pointing to first encountered difference, | |
| // or pair of nullptr when everything matches. | |
| // If you want more detail about value differences, then capture them in the | |
| // comparison function's closure before returning. | |
| template <typename LT, typename RT> | |
| VectorTreeNodePair<LT, RT> DeepEqual( | |
| const LT& left, const RT& right, | |
| const std::function<bool(const typename LT::value_type&, | |
| const typename RT::value_type&)>& comp) { | |
| using result_type = VectorTreeNodePair<LT, RT>; | |
| // Node value comparison at current level. | |
| if (!comp(left.Value(), right.Value())) { | |
| return result_type{&left, &right}; | |
| } | |
| // Subtree comparison: check number of children first, returning early if | |
| // different. | |
| const auto& left_children = left.Children(); | |
| const auto& right_children = right.Children(); | |
| if (left_children.size() != right_children.size()) { | |
| return result_type{&left, &right}; | |
| } | |
| // Number of children match. Find first differing children. | |
| // The iterators returned by std::mismatch() do not propagate the | |
| // deep children nodes that differ, so we must use a lambda with capture. | |
| result_type first_diff; // initially nullptrs | |
| std::mismatch(left_children.begin(), left_children.end(), | |
| right_children.begin(), | |
| [&comp, &first_diff](const LT& l, const RT& r) -> bool { | |
| const auto result = DeepEqual(l, r, comp); | |
| if (result.left == nullptr) { | |
| return true; | |
| } else { | |
| first_diff = result; // Capture first difference. | |
| return false; | |
| } | |
| // When this returns true, no further comparisons will be | |
| // called, so the assignment to first_diff will contain the | |
| // desired result. | |
| }); | |
| // When every subtree matches, first_diff will hold nullptrs. | |
| // Otherwise it will point to the first mismatched nodes. | |
| return first_diff; | |
| } | |
| // Overload for the case when left and right tree types have a defined equality | |
| // operator. This also works when the left and right types are the same. | |
| template <typename LT, typename RT> | |
| VectorTreeNodePair<LT, RT> DeepEqual(const LT& left, const RT& right) { | |
| return DeepEqual(left, right, | |
| [](const typename LT::value_type& l, | |
| const typename RT::value_type& r) { return l == r; }); | |
| } | |
| // If both trees are structurally the same, node-for-node, returns a pair of | |
| // nullptrs. Otherwise, returns with pointers to the first encountered nodes | |
| // that differ in substructure. Traversal order: pre-order. | |
| // Implementation: this is just a degenerate form of DeepEqual, where the value | |
| // comparison is ignored (always returns true). | |
| template <typename LT, typename RT> | |
| VectorTreeNodePair<LT, RT> StructureEqual(const LT& left, const RT& right) { | |
| // Ignore node values by always treating them as equal. | |
| return DeepEqual(left, right, | |
| [](const typename LT::value_type&, | |
| const typename RT::value_type&) { return true; }); | |
| } | |
| // Provide ADL-enabled overload for use by swap implementations. | |
| template <class T> | |
| void swap(VectorTree<T>& left, VectorTree<T>& right) { | |
| left.swap(right); | |
| } | |
| } // namespace verible | |
| #endif // VERIBLE_COMMON_UTIL_VECTOR_TREE_H_ |