| // 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 |