blob: a192704b9aff96606ae0425657b0cadc841a4454 [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.
#include "common/text/tree_utils.h"
#include <algorithm>
#include <cstdlib>
#include <functional>
#include <iostream>
#include <iterator>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "absl/strings/str_cat.h"
#include "common/text/concrete_syntax_leaf.h"
#include "common/text/concrete_syntax_tree.h"
#include "common/text/symbol.h"
#include "common/text/token_info.h"
#include "common/text/visitors.h"
#include "common/util/iterator_adaptors.h"
#include "common/util/logging.h"
#include "common/util/spacer.h"
#include "common/util/value_saver.h"
namespace verible {
const Symbol *DescendThroughSingletons(const Symbol &symbol) {
if (symbol.Kind() == SymbolKind::kLeaf) {
return &symbol;
}
// else is a kNode
const auto &node = SymbolCastToNode(symbol);
if (node.size() == 1 && node.front() != nullptr) {
// If only child is non-null, descend.
return DescendThroughSingletons(*node.front());
// TODO(fangism): rewrite non-recursively.
}
return &symbol;
}
const SyntaxTreeLeaf *GetRightmostLeaf(const Symbol &symbol) {
if (symbol.Kind() == SymbolKind::kLeaf) {
return &SymbolCastToLeaf(symbol);
}
const auto &node = SymbolCastToNode(symbol);
for (const auto &child : const_reversed_view(node.children())) {
if (child != nullptr) {
const auto *leaf = GetRightmostLeaf(*child);
if (leaf != nullptr) {
return leaf;
}
}
}
return nullptr;
}
const SyntaxTreeLeaf *GetLeftmostLeaf(const Symbol &symbol) {
if (symbol.Kind() == SymbolKind::kLeaf) {
return &SymbolCastToLeaf(symbol);
}
const auto &node = SymbolCastToNode(symbol);
for (const auto &child : node.children()) {
if (child != nullptr) {
const auto *leaf = GetLeftmostLeaf(*child);
if (leaf != nullptr) {
return leaf;
}
}
}
return nullptr;
}
absl::string_view StringSpanOfSymbol(const Symbol &symbol) {
return StringSpanOfSymbol(symbol, symbol);
}
absl::string_view StringSpanOfSymbol(const Symbol &lsym, const Symbol &rsym) {
const auto *left = GetLeftmostLeaf(lsym);
const auto *right = GetRightmostLeaf(rsym);
if (left != nullptr && right != nullptr) {
const auto range_begin = left->get().text().begin();
const auto range_end = right->get().text().end();
return absl::string_view(range_begin,
std::distance(range_begin, range_end));
}
return "";
}
const SyntaxTreeNode &SymbolCastToNode(const Symbol &symbol) {
// Assert the symbol is a node.
CHECK_EQ(symbol.Kind(), SymbolKind::kNode)
<< "got: " << RawTreePrinter(symbol);
return down_cast<const SyntaxTreeNode &>(symbol);
}
SyntaxTreeNode &SymbolCastToNode(Symbol &symbol) {
// Assert the symbol is a node.
CHECK_EQ(symbol.Kind(), SymbolKind::kNode)
<< "got: " << RawTreePrinter(symbol);
return down_cast<SyntaxTreeNode &>(symbol);
}
const SyntaxTreeLeaf &SymbolCastToLeaf(const Symbol &symbol) {
// Assert the symbol is a leaf.
CHECK_EQ(symbol.Kind(), SymbolKind::kLeaf)
<< "got: " << RawTreePrinter(symbol);
return down_cast<const SyntaxTreeLeaf &>(symbol);
}
namespace {
// FirstSubtreeFinderMutable is a visitor class that supports the implementation
// of FindFirstSubtreeMutable(). It is derived from
// MutableTreeVisitorRecursive because it is intended for use with pruning and
// modifying syntax trees.
class FirstSubtreeFinderMutable : public MutableTreeVisitorRecursive {
public:
explicit FirstSubtreeFinderMutable(const TreePredicate &predicate)
: predicate_(predicate) {}
void Visit(const SyntaxTreeNode &node, SymbolPtr *symbol_ptr) final {
CHECK_EQ(symbol_ptr->get(), &node); // symbol_ptr owns node.
if (result_ == nullptr) {
// If this node matches, return it, and skip evaluating children.
if (predicate_(node)) {
result_ = symbol_ptr;
} else {
// Cast the mutable copy of the node pointer (same object as &node).
auto *const mutable_node =
down_cast<SyntaxTreeNode *>(symbol_ptr->get());
for (SymbolPtr &child : mutable_node->mutable_children()) {
if (child != nullptr) {
child->Accept(this, &child);
}
// Stop as soon as first result is found.
if (result_ != nullptr) return;
}
}
}
}
void Visit(const SyntaxTreeLeaf &leaf, SymbolPtr *symbol_ptr) final {
CHECK_EQ(symbol_ptr->get(), &leaf); // symbol_ptr owns leaf.
// If already have a result, stop checking and return right away.
if (result_ == nullptr) {
if (predicate_(leaf)) {
result_ = symbol_ptr;
}
}
}
ConcreteSyntaxTree *result() const { return result_; }
private:
// Matching criterion.
TreePredicate predicate_;
// Contains first matching result found or nullptr if no match is found.
ConcreteSyntaxTree *result_ = nullptr;
};
// FirstSubtreeFinder is a visitor class that supports the implementation of
// FindFirstSubtree(). It is derived from SymbolVisitor so that it's able to
// exit early if a match is found.
class FirstSubtreeFinder : public SymbolVisitor {
public:
explicit FirstSubtreeFinder(const TreePredicate &predicate)
: predicate_(predicate) {}
void Visit(const SyntaxTreeNode &node) final {
if (result_ == nullptr) {
// If this node matches, return it, and skip evaluating children.
if (predicate_(node)) {
result_ = &node;
} else {
for (const SymbolPtr &child : node.children()) {
if (child != nullptr) {
child->Accept(this);
}
// Stop as soon as first result is found.
if (result_ != nullptr) return;
}
}
}
}
void Visit(const SyntaxTreeLeaf &leaf) final {
// If already have a result, stop checking and return right away.
if (result_ == nullptr) {
if (predicate_(leaf)) {
result_ = &leaf;
}
}
}
const Symbol *result() const { return result_; }
private:
// Matching criterion.
TreePredicate predicate_;
// Contains first matching result found or nullptr if no match is found.
const Symbol *result_ = nullptr;
};
// A visitor that finds the last matching node. Inherits from
// TreeVisitorRecursive, as it needs to visit all nodes in the tree.
class LastSubtreeFinder : public TreeVisitorRecursive {
public:
explicit LastSubtreeFinder(const TreePredicate &predicate)
: predicate_(predicate) {}
void Visit(const SyntaxTreeNode &node) final {
if (predicate_(node)) result_ = &node;
}
void Visit(const SyntaxTreeLeaf &leaf) final {
if (predicate_(leaf)) result_ = &leaf;
}
const Symbol *result() const { return result_; }
private:
// Matching criterion.
TreePredicate predicate_;
// Contains last matching result found or nullptr if no match is found.
const Symbol *result_ = nullptr;
};
} // namespace
ConcreteSyntaxTree *FindFirstSubtreeMutable(ConcreteSyntaxTree *tree,
const TreePredicate &pred) {
if (*ABSL_DIE_IF_NULL(tree) == nullptr) return nullptr;
FirstSubtreeFinderMutable finder(pred);
(*tree)->Accept(&finder, tree);
return finder.result();
}
const Symbol *FindFirstSubtree(const Symbol *tree, const TreePredicate &pred) {
if (tree == nullptr) return nullptr;
FirstSubtreeFinder finder(pred);
tree->Accept(&finder);
return finder.result();
}
const Symbol *FindLastSubtree(const Symbol *tree, const TreePredicate &pred) {
if (tree == nullptr) return nullptr;
LastSubtreeFinder finder(pred);
tree->Accept(&finder);
return finder.result();
}
ConcreteSyntaxTree *FindSubtreeStartingAtOffset(
ConcreteSyntaxTree *tree, const char *first_token_offset) {
auto predicate = [=](const Symbol &s) {
const SyntaxTreeLeaf *leftmost = GetLeftmostLeaf(s);
if (leftmost != nullptr) {
if (std::distance(first_token_offset, leftmost->get().text().begin()) >=
0) {
return true;
}
}
return false;
};
ConcreteSyntaxTree *result =
FindFirstSubtreeMutable(ABSL_DIE_IF_NULL(tree), predicate);
// This cannot return a null tree node because it would have been skipped
// by FirstSubtreeFinderMutable.
if (result != nullptr) CHECK(*result != nullptr);
return result;
}
// Helper function for PruneSyntaxTreeAfterOffset
namespace {
// Returns true if this node should be deleted by parent (pop_back).
bool PruneTreeFromRight(ConcreteSyntaxTree *tree, const char *offset) {
const auto kind = (*ABSL_DIE_IF_NULL(tree))->Kind();
switch (kind) {
case SymbolKind::kLeaf: {
auto *leaf = down_cast<SyntaxTreeLeaf *>(tree->get());
return std::distance(offset, leaf->get().text().end()) > 0;
}
case SymbolKind::kNode: {
auto &node = down_cast<SyntaxTreeNode &>(*tree->get());
auto &children = node.mutable_children();
for (auto &child : reversed_view(children)) {
if (child == nullptr) {
children.pop_back(); // pop_back() guaranteed to not realloc
} else {
if (PruneTreeFromRight(&child, offset)) {
children.pop_back();
} else {
// Since token locations are monotonic, we can stop checking
// as soon as the above function returns false.
break;
}
}
}
// If no children remain, tell caller to delete this node.
return children.empty();
}
}
std::cerr << "Unhandled SymbolKind: " << static_cast<int>(kind);
abort();
}
} // namespace
void PruneSyntaxTreeAfterOffset(ConcreteSyntaxTree *tree, const char *offset) {
PruneTreeFromRight(tree, offset);
}
// Helper functions for ZoomSyntaxTree
namespace {
// Return the upper bound offset of the rightmost token in the tree.
const char *RightmostOffset(const Symbol &symbol) {
const SyntaxTreeLeaf *leaf_ptr = verible::GetRightmostLeaf(symbol);
return ABSL_DIE_IF_NULL(leaf_ptr)->get().text().end();
}
// Return the first non-null child node/leaf of the immediate subtree.
ConcreteSyntaxTree *LeftSubtree(ConcreteSyntaxTree *tree) {
if ((ABSL_DIE_IF_NULL(*tree))->Kind() == verible::SymbolKind::kLeaf) {
// Leaves don't have subtrees.
return nullptr;
}
auto &children = down_cast<SyntaxTreeNode &>(*tree->get()).mutable_children();
for (auto &child : children) {
if (child != nullptr) return &child;
}
return nullptr;
}
} // namespace
ConcreteSyntaxTree *ZoomSyntaxTree(ConcreteSyntaxTree *tree,
absl::string_view trim_range) {
if (*tree == nullptr) return nullptr;
const auto left_offset = trim_range.begin();
// Find shallowest syntax tree node that starts at the given byte offset.
ConcreteSyntaxTree *match =
FindSubtreeStartingAtOffset(ABSL_DIE_IF_NULL(tree), left_offset);
// Take leftmost subtree until its right bound falls within offset.
const auto right_offset = trim_range.end();
while (match != nullptr && *match != nullptr &&
RightmostOffset(*ABSL_DIE_IF_NULL(*match)) > right_offset) {
match = LeftSubtree(match);
}
return match;
}
void TrimSyntaxTree(ConcreteSyntaxTree *tree, absl::string_view trim_range) {
auto *replacement = ZoomSyntaxTree(tree, trim_range);
if (replacement == nullptr || *replacement == nullptr) {
*tree = nullptr;
} else {
*tree = std::move(*replacement);
}
}
namespace {
// Applies one transformation to every leaf (token) in the syntax tree.
class LeafMutatorVisitor : public MutableTreeVisitorRecursive {
public:
// Maintains a reference but not ownership of the mutator, so the
// mutator must outlive this object.
explicit LeafMutatorVisitor(const LeafMutator *mutator)
: leaf_mutator_(*mutator) {}
void Visit(const SyntaxTreeNode &, SymbolPtr *) final {}
// Transforms a single leaf.
void Visit(const SyntaxTreeLeaf &leaf, SymbolPtr *leaf_owner) final {
CHECK_EQ(leaf_owner->get(), &leaf);
auto *const mutable_leaf = down_cast<SyntaxTreeLeaf *>(leaf_owner->get());
leaf_mutator_(ABSL_DIE_IF_NULL(mutable_leaf)->get_mutable());
}
private:
// Mutation to apply to every leaf token.
const LeafMutator &leaf_mutator_;
};
} // namespace
void MutateLeaves(ConcreteSyntaxTree *tree, const LeafMutator &mutator) {
if (*ABSL_DIE_IF_NULL(tree) != nullptr) {
LeafMutatorVisitor visitor(&mutator);
(*tree)->Accept(&visitor, tree);
}
}
//
// Implementation of printing functions
//
std::ostream &RawSymbolPrinter::auto_indent() {
return *stream_ << Spacer(indent_, ' ');
}
void RawSymbolPrinter::Visit(const SyntaxTreeLeaf &leaf) {
leaf.get().ToStream(auto_indent() << "Leaf @" << child_rank_ << ' ')
<< std::endl;
}
void PrettyPrinter::Visit(const SyntaxTreeLeaf &leaf) {
leaf.get().ToStream(auto_indent() << "Leaf @" << child_rank_ << ' ', context_)
<< std::endl;
}
void RawSymbolPrinter::Visit(const SyntaxTreeNode &node) {
std::string tag_info;
const int tag = node.Tag().tag;
if (tag != 0) tag_info = absl::StrCat("(tag: ", tag, ") ");
auto_indent() << "Node @" << child_rank_ << ' ' << tag_info << "{"
<< std::endl;
{
const ValueSaver<int> value_saver(&indent_, indent_ + 2);
const ValueSaver<int> rank_saver(&child_rank_, 0);
for (const auto &child : node.children()) {
if (child) {
child->Accept(this);
} else if (print_null_nodes_) {
auto_indent() << "NULL @" << child_rank_ << std::endl;
}
// Note that nullptrs will appear as gaps in the child rank sequence.
// nullptr nodes in tail position are not shown.
++child_rank_;
}
}
auto_indent() << "}" << std::endl;
}
std::ostream &RawTreePrinter::Print(std::ostream &stream) const {
RawSymbolPrinter printer(&stream, print_null_nodes_);
root_.Accept(&printer);
return stream;
}
std::ostream &operator<<(std::ostream &stream, const RawTreePrinter &printer) {
return printer.Print(stream);
}
void PrettyPrintTree(const Symbol &root, const TokenInfo::Context &context,
std::ostream *stream) {
PrettyPrinter printer(stream, context);
root.Accept(&printer);
}
std::ostream &TreePrettyPrinter::Print(std::ostream &stream) const {
PrettyPrintTree(root_, context_, &stream);
return stream;
}
std::ostream &operator<<(std::ostream &stream,
const TreePrettyPrinter &printer) {
return printer.Print(stream);
}
} // namespace verible