| // 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_EXPANDABLE_TREE_VIEW_H_ | |
| #define VERIBLE_COMMON_UTIL_EXPANDABLE_TREE_VIEW_H_ | |
| #include <cstddef> | |
| #include <functional> | |
| #include <iterator> | |
| #include "common/util/logging.h" | |
| #include "common/util/vector_tree.h" | |
| namespace verible { | |
| // TreeViewNodeInfo is a support structure for ExpandableTreeView. | |
| // This is the information that is attached to every node in an | |
| // ExpandableTreeView, and also contains a pointer to every corresponding | |
| // node in the viewed VectorTree. | |
| template <class T> | |
| class TreeViewNodeInfo { | |
| public: | |
| explicit TreeViewNodeInfo(const VectorTree<T>& node) : node_(&node) {} | |
| void Unexpand() { expand_ = false; } | |
| void Expand() { expand_ = true; } | |
| bool IsExpanded() const { return expand_; } | |
| const T& Value() const { return node_->Value(); } | |
| private: | |
| // Immutable pointer to corresponding VectorTree node. | |
| // This promises to never modify the node's data. | |
| const VectorTree<T>* const node_; | |
| // Current view of this particular subtree node. | |
| // If true, then traverse children_, otherwise visit this node as one element. | |
| // Never visit both this node and its children. | |
| bool expand_ = true; | |
| }; | |
| // ExpandableTreeView is a read-only view of a VectorTree structure. | |
| // Whereas VectorTree contains the information for a fully-expanded tree, | |
| // this class presents a potentially collapsed view thereof. | |
| // Whereas VectorTree represents the maximum extent to which a | |
| // hierarchical view *could* be expanded, this view class allows control | |
| // over which nodes are expanded. This allows expansions decisions to | |
| // occur dynamically. At any given level, you work with either a single | |
| // object (flat), or a sequence of objects that in some way represents | |
| // or expands upon the single object -- equivalence is subject to | |
| // interpretation. | |
| // | |
| // Illustrated example: partitions: | |
| // none expanded: |<-- -->| | |
| // one-level expanded: |<- ->|<- ->|<- ->| | |
| // selectively expanded: | | | | | | | |
| // fully expanded (VectorTree): | | | | | | | | | | | |
| // | |
| // No matter which view, the whole is representative of the sum of the parts. | |
| // | |
| // Implementation detail: | |
| // ExpandableTreeView itself uses a VectorTree to store a parallel tree | |
| // of references to another VectorTree, thanks to similiarity in structure. | |
| // Structural modifications to the original tree may invalidate an entire tree | |
| // view, due to the use of pointers in TreeViewNodeInfo node type. | |
| template <class T> | |
| class ExpandableTreeView { | |
| typedef T value_type; | |
| typedef ExpandableTreeView<value_type> this_type; | |
| // The type of the tree being pointed to. | |
| // This class treats the underlying tree as read-only. | |
| typedef VectorTree<value_type> tree_type; | |
| // Struct type that contains a pointer to the original tree type. | |
| // Every node in the original tree type corresponds to a node in the | |
| // view impl_type. | |
| typedef TreeViewNodeInfo<value_type> node_type; | |
| // The tree implementation type for the collection of expandable tree | |
| // nodes that parallel the original tree. | |
| typedef VectorTree<node_type> impl_type; | |
| public: | |
| class iterator; | |
| using const_iterator = iterator; | |
| // Constructs (recursively) a fully-expanded view from the input tree. | |
| explicit ExpandableTreeView(const tree_type& tree) | |
| : view_(tree.template Transform<node_type>( | |
| [](const tree_type& other) -> node_type { | |
| // Initialize a view node using the address of corresponding node | |
| // in the other tree. | |
| return node_type(other); | |
| })) { | |
| // Guarantee structural equivalence with original tree. | |
| CHECK_EQ(StructureEqual(view_, tree).left, nullptr); | |
| } | |
| // TODO(fangism): implement later as needed | |
| ExpandableTreeView(const this_type&) = delete; | |
| ExpandableTreeView(this_type&&) = delete; | |
| ~ExpandableTreeView() = default; | |
| // Accessors | |
| const node_type& Value() const { return view_.Value(); } | |
| node_type& Value() { return view_.Value(); } | |
| // Directly access children nodes by index. | |
| const impl_type& operator[](size_t i) const { return view_.Children()[i]; } | |
| impl_type& operator[](size_t i) { return view_.Children()[i]; } | |
| // Iteration | |
| iterator begin() const { return iterator(first_unexpanded_child(view_)); } | |
| iterator end() const { return iterator(nullptr); } | |
| // Transformation | |
| // Apply a mutating transformation to this tree view, pre-order traversal. | |
| void ApplyPreOrder(const std::function<void(impl_type&)>& f) { | |
| view_.ApplyPreOrder(f); | |
| } | |
| // Apply a mutating transformation to this tree view, post-order traversal. | |
| void ApplyPostOrder(const std::function<void(impl_type&)>& f) { | |
| view_.ApplyPostOrder(f); | |
| } | |
| // Properties | |
| private: // This section is only directly accessible to the iterator class. | |
| // Descend to the first node that is not expanded. | |
| // Recall that expanded nodes should *only* visit children, not self. | |
| // This behaves a lot like VectorTree::LeftmostDescendant(), only the | |
| // termination condition is different. | |
| static const impl_type* first_unexpanded_child(const impl_type& current) { | |
| const auto& info = current.Value(); | |
| if (info.IsExpanded() && !current.Children().empty()) { | |
| // Let compiler to tail-call optimize self-recursion. | |
| return first_unexpanded_child(current.Children().front()); | |
| } else { | |
| return ¤t; | |
| } | |
| } | |
| // Helper function for iterating to next node in sequence, which could be | |
| // sibling of the same parent or even a 'cousin'. (Naming this | |
| // next_sibling_or_cousin just sounds too verbose...) | |
| // This visits either a node or its children, but never both. | |
| // This behaves a lot like VectorTree::NextLeaf(). | |
| // \precondition this->parent_->expand_ is true (for all ancestors), | |
| // otherwise we would have never reached this node. | |
| static const impl_type* next_sibling(const impl_type& current) { | |
| if (current.Parent() != nullptr) { | |
| // Find the next sibling, if there is one. | |
| const size_t birth_rank = current.BirthRank(); | |
| const size_t next_rank = birth_rank + 1; | |
| if (next_rank == current.Parent()->Children().size()) { | |
| // This is the last child of the group. | |
| // Find the nearest parent that has a next child (ascending). | |
| auto* next_ancestor = next_sibling(*current.Parent()); | |
| if (next_ancestor != nullptr) { | |
| return first_unexpanded_child(*next_ancestor); | |
| } else { | |
| return nullptr; | |
| } | |
| } else { | |
| // More children follow this one. | |
| return first_unexpanded_child(current.Parent()->Children()[next_rank]); | |
| } | |
| } else { | |
| // Root node has no next sibling, this is the end(). | |
| return nullptr; | |
| } | |
| } | |
| private: | |
| // Parallel tree whose nodes point to original tree passed in constructor. | |
| // As a 'view' class, the original tree is never modified from this view. | |
| // Changes that restructure the original tree can invalidate an entire | |
| // tree view, so it is best to freeze a tree before constructing a view from | |
| // it. | |
| impl_type view_; | |
| }; | |
| // This iterator class implements a mostly-conventional N-ary tree iterator. | |
| // The major difference is that this is tailored to ExpandableTreeView (node), | |
| // in which its IsExpanded() property determines whether or not a particular | |
| // node is visited unexpanded, or that node's children is visited; iteration | |
| // never visits both a node AND its children, only one or the other. | |
| template <class T> | |
| class ExpandableTreeView<T>::iterator | |
| : public std::iterator<std::forward_iterator_tag, const value_type> { | |
| friend class ExpandableTreeView<T>; // grant access to private constructor | |
| private: | |
| explicit iterator(const impl_type* node) : node_(node) {} | |
| public: | |
| iterator(const iterator&) = default; | |
| // pre-increment | |
| iterator& operator++() { | |
| node_ = next_sibling(*node_); | |
| return *this; | |
| } | |
| // post-increment | |
| iterator operator++(int) { | |
| auto iter = *this; | |
| ++(*this); | |
| return iter; | |
| } | |
| // TODO(fangism): implement decrement operators, to support bidirectionality | |
| bool operator==(const iterator& rhs) const { return node_ == rhs.node_; } | |
| bool operator!=(const iterator& rhs) const { return !(*this == rhs); } | |
| const value_type& operator*() const { return node_->Value().Value(); } | |
| const value_type* operator->() const { return &node_->Value().Value(); } | |
| private: | |
| // From the node_ pointer alone, we have sufficient information to iterate. | |
| // For now, this supports forward-only iteration because, once this becomes | |
| // nullptr, there is no way to go backward. | |
| const impl_type* node_; | |
| }; | |
| } // namespace verible | |
| #endif // VERIBLE_COMMON_UTIL_EXPANDABLE_TREE_VIEW_H_ |