blob: 67f4119168ebf8e6ea32bb52cec8c436d37982d0 [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 <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_