| // 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_ |