| // 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_TEXT_SYNTAX_TREE_CONTEXT_H_ | |
| #define VERIBLE_COMMON_TEXT_SYNTAX_TREE_CONTEXT_H_ | |
| #include <algorithm> | |
| #include <cstddef> | |
| #include <initializer_list> | |
| #include <iterator> | |
| #include <vector> | |
| #include "common/text/concrete_syntax_tree.h" | |
| #include "common/util/auto_pop_stack.h" | |
| #include "common/util/iterator_adaptors.h" | |
| #include "common/util/logging.h" | |
| namespace verible { | |
| // Container with a stack of SyntaxTreeNodes and methods to verify the context | |
| // of a SyntaxTreeNode during traversal of a ConcreteSyntaxTree. | |
| // Note: Public methods are named to follow STL convention for std::stack. | |
| // TODO(fangism): implement a general ForwardMatcher and ReverseMatcher | |
| // interface that can express AND/OR/NOT. | |
| // Despite of implementation based on pointers. This class requires | |
| // that managed elements are non-nullptrs. | |
| class SyntaxTreeContext : public AutoPopStack<const SyntaxTreeNode*> { | |
| public: | |
| typedef AutoPopStack<const SyntaxTreeNode*> base_type; | |
| // member class to handle push and pop of stack safely | |
| using AutoPop = base_type::AutoPop; | |
| protected: | |
| // restrict access to AutoPopStack<>::top method only to this class | |
| using base_type::top; | |
| public: | |
| // returns the top SyntaxTreeNode of the stack | |
| const SyntaxTreeNode& top() const { | |
| return *ABSL_DIE_IF_NULL(base_type::top()); | |
| } | |
| // IsInside returns true if there is a node of the specified | |
| // tag on the TreeContext stack. Search traverses from the top of the | |
| // stack starting with offset and returns on the first match found. | |
| // Type parameter E can be a language-specific enum or plain integer type. | |
| template <typename E> | |
| bool IsInsideStartingFrom(E tag_enum, size_t reverse_offset) const { | |
| if (size() <= reverse_offset) return false; | |
| return std::any_of( | |
| rbegin() + reverse_offset, rend(), | |
| [=](const SyntaxTreeNode* node) { return node->MatchesTag(tag_enum); }); | |
| } | |
| // IsInside returns true if there is a node of the specified | |
| // tag on the TreeContext stack. Search occurs from the top of the | |
| // stack and returns on the first match found. | |
| // Type parameter E can be a language-specific enum or plain integer type. | |
| template <typename E> | |
| bool IsInside(E tag_enum) const { | |
| return IsInsideStartingFrom(tag_enum, 0); | |
| } | |
| // Returns true if current context is directly inside one of the includes | |
| // node types before any of the excludes node types. Search starts | |
| // from the top of the stack. | |
| template <typename E> | |
| bool IsInsideFirst(std::initializer_list<E> includes, | |
| std::initializer_list<E> excludes) const { | |
| for (const auto& type : reversed_view(*this)) { | |
| if (type->MatchesTagAnyOf(includes)) return true; | |
| if (type->MatchesTagAnyOf(excludes)) return false; | |
| } | |
| return false; | |
| } | |
| // Returns true if stack is not empty and top of stack matches tag_enum. | |
| template <typename E> | |
| bool DirectParentIs(E tag_enum) const { | |
| if (empty()) { | |
| return false; | |
| } | |
| return E(top().Tag().tag) == tag_enum; | |
| } | |
| // Returns true if stack is not empty and top of stack matches | |
| // one of the tag_enums. | |
| template <typename E> | |
| bool DirectParentIsOneOf(std::initializer_list<E> tag_enums) const { | |
| if (empty()) { | |
| return false; | |
| } | |
| return std::find(tag_enums.begin(), tag_enums.end(), E(top().Tag().tag)) != | |
| tag_enums.end(); | |
| } | |
| // Returns true if the immediate parents are the given sequence (top-down). | |
| // Sequence should be specified as: direct-parent, direct-grandparent, ... | |
| // In the degenerate empty-list case, this will return true. | |
| template <typename E> | |
| bool DirectParentsAre(std::initializer_list<E> tag_enums) const { | |
| if (tag_enums.size() > size()) return false; | |
| // top of stack is back of vector (direct parent) | |
| return std::equal(tag_enums.begin(), tag_enums.end(), rbegin(), | |
| [](E tag, const SyntaxTreeNode* node) { | |
| return E(node->Tag().tag) == tag; | |
| }); | |
| } | |
| }; | |
| } // namespace verible | |
| #endif // VERIBLE_COMMON_TEXT_SYNTAX_TREE_CONTEXT_H_ |