blob: b418e3f87f14e428dc7bf564d543137aaffa9559 [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_VECTOR_TREE_H_
#define VERIBLE_COMMON_UTIL_VECTOR_TREE_H_
#include <algorithm>
#include <cstddef>
#include <functional>
#include <iosfwd> // IWYU pragma: keep
#include <iterator>
#include <numeric>
#include <set>
#include <utility>
#include <vector>
#include "common/util/container_proxy.h"
#include "common/util/iterator_range.h"
#include "common/util/logging.h"
#include "common/util/type_traits.h"
namespace verible {
// 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 vector_tree_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 {
typedef VectorTree<T> this_type;
// Forward declaration
class ChildrenList;
public:
// Self-recursive type that represents children in an expanded view.
typedef std::vector<this_type> subnodes_type;
typedef T value_type;
VectorTree() : children_(*this) {}
// Deep copy-constructor.
VectorTree(const this_type& other)
: node_value_(other.node_value_),
parent_(other.parent_),
children_(*this, other.children_) {}
VectorTree(this_type&& other) noexcept
: node_value_(std::move(other.node_value_)),
parent_(other.parent_),
children_(*this, std::move(other.children_)) {}
// 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_(*this) {
children_.reserve(sizeof...(args));
(children_.emplace_back(std::forward<Args>(args)), ...);
}
template <typename... Args>
explicit VectorTree(value_type&& v, Args&&... args)
: node_value_(std::move(v)), children_(*this) {
children_.reserve(sizeof...(args));
(children_.emplace_back(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) {
std::swap(node_value_, other.node_value_);
children_.swap(other.children_); // efficient O(1) vector::swap
// + O(|children|) linking to parent
}
// Copy value and children, but relink new children to this node.
VectorTree& operator=(const this_type& source) {
if (this == &source) return *this;
node_value_ = source.node_value_;
children_ = source.children_;
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) noexcept {
node_value_ = std::move(source.node_value_);
children_ = std::move(source.children_);
return *this;
}
// 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_; }
ChildrenList& Children() { return children_; }
const ChildrenList& Children() const { return children_; }
private:
// 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;
}
// A wrapper of a sequence container for storing VectorTree nodes that sets
// correct parent pointer in each inserted node. The reference to the
// "correct" parent is passed to a constructor.
//
// The sole purpose of this class is to function as a children list in
// VectorTree, both as a storage and as a public interface for tree
// manipulation (through reference).
//
// This class handles parent pointer assignment for all cases where the
// children list itself is modified. However, it does not clear or otherwise
// change parent pointer in removed nodes.
class ChildrenList : ContainerProxyBase<ChildrenList, subnodes_type> {
using Base = ContainerProxyBase<ChildrenList, subnodes_type>;
friend Base;
public:
using typename Base::container_type;
// Import (via `using`) ContainerProxy members supported by std::vector.
USING_CONTAINER_PROXY_STD_VECTOR_MEMBERS(Base)
// Move-cast to wrapped container's type. Moves out the container.
explicit operator container_type() && { return std::move(container_); }
protected:
// ContainerProxy interface
container_type& underlying_container() { return container_; }
const container_type& underlying_container() const { return container_; }
void ElementsInserted(iterator first, iterator last) {
LinkChildrenToParent(iterator_range(first, last));
}
// Unused:
// void ElementsBeingRemoved(iterator first, iterator last)
// Unused:
// void ElementsBeingReplaced()
void ElementsWereReplaced() { LinkChildrenToParent(container_); }
private:
// Sets parent pointer of nodes from `children` range to address of `node_`.
template <class Range>
void LinkChildrenToParent(Range&& children) {
for (auto& child : children) {
child.parent_ = &node_;
}
}
// Allow construction, assignment and direct access to `container_` inside
// VectorTree.
friend VectorTree;
// Hide constructors and assignments from the world. This object is created
// and assigned-to only in VectorTree.
explicit ChildrenList(VectorTree& node) : node_(node) {}
// Construction requires parent node reference.
ChildrenList(const ChildrenList&) = delete;
ChildrenList(VectorTree& node, const ChildrenList& other)
: node_(node), container_(other.container_) {
LinkChildrenToParent(container_);
}
ChildrenList& operator=(const ChildrenList& other) {
container_ = other.container_;
LinkChildrenToParent(container_);
return *this;
}
// Construction requires parent node reference.
ChildrenList(ChildrenList&&) = delete;
ChildrenList(VectorTree& node, ChildrenList&& other) noexcept
: node_(node), container_(std::move(other.container_)) {
// Note: `other` is not notified about the change because it ends up in
// undefined state as a result of the move.
LinkChildrenToParent(container_);
}
ChildrenList& operator=(ChildrenList&& other) noexcept {
// Note: `other` is not notified about the change because it ends up in
// undefined state as a result of the move.
container_ = std::move(other.container_);
LinkChildrenToParent(container_);
return *this;
}
// Reference to a VectorTree node in which this object represents a list of
// children.
// TODO(mglb): try to get rid of this reference. See:
// https://github.com/chipsalliance/verible/pull/1252#discussion_r825196108
// Also look at MapTree::KeyValuePair() - `offsetof` is already used there
// so it could be user here too.
VectorTree& node_;
// Actual data container where the nodes are stored.
subnodes_type container_;
};
// Singular value stored at this node.
value_type node_value_;
// Pointer up to parent node.
// Only the root node of a tree has a nullptr parent_.
// This value is managed by ChildrenList, constructors, and
// operator=(). There should be no need to set it manually in other places.
this_type* parent_ = nullptr;
// Array of nodes/subtrees.
ChildrenList children_;
};
// 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_