| // 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/analysis/matcher/descent_path.h" | |
| #include <iterator> | |
| #include <memory> | |
| #include <vector> | |
| #include "common/text/concrete_syntax_tree.h" | |
| #include "common/text/symbol.h" | |
| namespace verible { | |
| namespace matcher { | |
| // AggregateAllDescendantsFromPath is a local helper for | |
| // GetAllDescendantsFromPath. It minimizes copying by using iterators | |
| // and pushing all discovered symbols onto a single vector. | |
| // | |
| // Adds all descendants of symbol that are precisely along path to target | |
| // children must have matching SymbolTag to the last element of path. | |
| // | |
| // Position should point to the start position of your path and | |
| // end should point to the end position. | |
| // | |
| // Discovered children are pushed back onto target | |
| static void AggregateAllDescendantsFromPath( | |
| const Symbol& symbol, const DescentPath::const_iterator& position, | |
| const DescentPath::const_iterator& end, std::vector<const Symbol*>* target); | |
| std::vector<const Symbol*> GetAllDescendantsFromPath(const Symbol& symbol, | |
| const DescentPath& path) { | |
| std::vector<const Symbol*> target; | |
| if (symbol.Kind() == SymbolKind::kNode) { | |
| const auto* node = down_cast<const SyntaxTreeNode*>(&symbol); | |
| for (const auto& child : node->children()) { | |
| if (child) { | |
| AggregateAllDescendantsFromPath(*child, path.begin(), path.end(), | |
| &target); | |
| } | |
| } | |
| } | |
| return target; | |
| } | |
| static void AggregateAllDescendantsFromPath( | |
| const Symbol& symbol, const DescentPath::const_iterator& position, | |
| const DescentPath::const_iterator& end, | |
| std::vector<const Symbol*>* target) { | |
| // If we are somehow operating on empty vector, stop recursion. | |
| if (position == end) { | |
| return; | |
| } | |
| // If we're at the last SymbolTag, stop recursion and check if we need to add | |
| // symbol to target. | |
| if (position + 1 == end) { | |
| if (symbol.Tag() == (*position)) { | |
| target->push_back(&symbol); | |
| } | |
| return; | |
| } | |
| // In order to recursively check descendants, symbol needs to be a node | |
| if (symbol.Kind() != SymbolKind::kNode) { | |
| return; | |
| } | |
| const auto* node = down_cast<const SyntaxTreeNode*>(&symbol); | |
| // If the cast fails or the node does not have the required tag or kind, | |
| // then stop recursion | |
| const auto tag_kind = node->Tag(); | |
| if (tag_kind != *position) { | |
| return; | |
| } | |
| // Recurse on children | |
| for (const auto& child : node->children()) { | |
| if (child) { | |
| AggregateAllDescendantsFromPath(*child, position + 1, end, target); | |
| } | |
| } | |
| } | |
| } // namespace matcher | |
| } // namespace verible |