blob: fe194050a947c39f103be13728d8b178f6462d8f [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 <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);
const auto& children = node.children();
if (children.size() == 1 && children.front() != nullptr) {
// If only child is non-null, descend.
return DescendThroughSingletons(*children.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 : 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));
} else {
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 TreeVisitorRecursive because it is
// only intended for searching a tree given a predicate.
class FirstSubtreeFinder : public TreeVisitorRecursive {
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;
};
} // 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();
}
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);
// 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);
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