blob: 208c392d660c1a7e61d0db3ca8f9733433b0ae35 [file] [log] [blame]
// 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/tree_operations.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 WrappedNodeType>
class TreeViewNodeInfo {
using TreeTraits = TreeNodeTraits<WrappedNodeType>;
using value_const_reference = typename TreeTraits::Value::const_reference;
public:
explicit TreeViewNodeInfo(const WrappedNodeType &node) : node_(&node) {}
void Unexpand() { expand_ = false; }
void Expand() { expand_ = true; }
bool IsExpanded() const { return expand_; }
value_const_reference Value() const { return node_->Value(); }
private:
// Immutable pointer to corresponding VectorTree node.
// This promises to never modify the node's data.
const WrappedNodeType *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 WrappedNodeType>
class ExpandableTreeView {
using TreeTraits = TreeNodeTraits<WrappedNodeType>;
using value_type = typename TreeTraits::Value::type;
using this_type = ExpandableTreeView;
// The type of the tree being pointed to.
// This class treats the underlying tree as read-only.
using tree_type = WrappedNodeType;
// 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.
using node_type = TreeViewNodeInfo<WrappedNodeType>;
// The tree implementation type for the collection of expandable tree
// nodes that parallel the original tree.
using impl_type = VectorTree<node_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_(
Transform<impl_type>(tree, [](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(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) {
verible::ApplyPreOrder(view_, f);
}
// Apply a mutating transformation to this tree view, post-order traversal.
void ApplyPostOrder(const std::function<void(impl_type &)> &f) {
verible::ApplyPostOrder(view_, 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() && !is_leaf(current)) {
// Let compiler to tail-call optimize self-recursion.
return first_unexpanded_child(current.Children().front());
}
return &current;
}
// 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) return nullptr;
// Find the next sibling, if there is one.
const size_t birth_rank = verible::BirthRank(current);
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());
return next_ancestor ? first_unexpanded_child(*next_ancestor) : nullptr;
}
// More children follow this one.
return first_unexpanded_child(current.Parent()->Children()[next_rank]);
}
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 WrappedNodeType>
class ExpandableTreeView<WrappedNodeType>::iterator {
friend class ExpandableTreeView<WrappedNodeType>; // grant access to private
// constructor
public:
using iterator_category = std::forward_iterator_tag;
using value_type = typename TreeTraits::Value::type;
using difference_type = int;
using pointer = typename TreeTraits::Value::type *;
using reference = typename TreeTraits::Value::type &;
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_