| // 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_VERILOG_TOOLS_KYTHE_SCOPE_RESOLVER_H_ |
| #define VERIBLE_VERILOG_TOOLS_KYTHE_SCOPE_RESOLVER_H_ |
| |
| #include <map> |
| #include <set> |
| #include <utility> |
| #include <vector> |
| |
| #include "absl/container/node_hash_map.h" |
| #include "absl/strings/string_view.h" |
| #include "common/util/auto_pop_stack.h" |
| #include "common/util/iterator_range.h" |
| #include "verilog/tools/kythe/kythe_facts.h" |
| |
| namespace verilog { |
| namespace kythe { |
| |
| // Used to wrap whatever needs to be recorded in a scope item. |
| // Right now, this is just a VName, if it will be more, make this a struct. |
| using ScopeMemberItem = VName; |
| |
| // Represents some scope in SystemVerilog code like class or module. |
| class Scope { |
| public: |
| Scope() = default; |
| explicit Scope(const Signature& signature) : signature_(signature) {} |
| |
| Scope(const Scope&) = default; |
| Scope(Scope&&) = default; |
| Scope& operator=(const Scope&) = default; |
| Scope& operator=(Scope&&) = default; |
| |
| // Appends the given scope item to the members of this scope. |
| void AddMemberItem(const ScopeMemberItem& member_item); |
| |
| // Appends the member of the given scope to the current scope. |
| void AppendScope(const Scope& scope); |
| |
| using MemberMap = absl::node_hash_map<absl::string_view, ScopeMemberItem>; |
| const MemberMap& Members() const { return members_; } |
| const Signature& GetSignature() const { return signature_; } |
| |
| // Searches for the given reference_name in the current scope and returns its |
| // VName or nullptr if not found. |
| const VName* SearchForDefinition(absl::string_view name) const; |
| |
| // Removes the given VName from the members. |
| void RemoveMember(const ScopeMemberItem& member); |
| |
| private: |
| // Signature of the owner of this scope. |
| Signature signature_; |
| |
| // list of the members inside this scope. |
| MemberMap members_; |
| }; |
| |
| // Container with a stack of Scopes to hold the accessible scopes during |
| // traversing an Indexing Facts Tree. |
| // This is used to get the definitions of some reference. |
| // |
| // This is modified during tree traversal because in case of entering new |
| // scope the new scope is resolved first and after that it's added to the |
| // containing scope and the next scope is being analyzed. |
| // e.g |
| // package pkg; |
| // int x; |
| // function int my_fun(); |
| // return x; |
| // endfunction |
| // endpackage |
| // |
| // Scope stack when extracting my_fun: |
| // The Global Scope |
| // { |
| // "pkg" |
| // } |
| // Pkg Scope |
| // { |
| // pkg#x |
| // } |
| // when trying to find a definition for "x" in "return x" ScopeContext |
| // try to find the first matching definition starting from the innermost/nearest |
| // scope or returns a nullptr if not found. |
| class ScopeContext : public verible::AutoPopStack<Scope*> { |
| public: |
| typedef verible::AutoPopStack<Scope*> base_type; |
| |
| ScopeContext() = default; |
| |
| ScopeContext(const ScopeContext&) = delete; |
| ScopeContext(ScopeContext&&) = delete; |
| ScopeContext& operator=(const ScopeContext&) = delete; |
| ScopeContext& operator=(ScopeContext&&) = delete; |
| |
| // member class to handle push and pop of stack safely |
| using AutoPop = base_type::AutoPop; |
| |
| // returns the top VName of the stack |
| Scope& top() { return *ABSL_DIE_IF_NULL(base_type::top()); } |
| const Scope& top() const { return *ABSL_DIE_IF_NULL(base_type::top()); } |
| |
| // TODO(minatoma): improve performance and memory for this function. |
| // |
| // This function uses string matching to find the definition of some |
| // variable in reverse order of the current scopes. |
| // |
| // Improvement can be replacing the string matching to comparison based on |
| // integers or enums. |
| // |
| // Search function to get the VName of a definitions of some reference. |
| // It loops over the scopes in reverse order and loops over members of every |
| // scope in reverse order to find a definition for the variable with given |
| // prefix signature. |
| // e.g |
| // { |
| // bar#module, |
| // foo#module, |
| // } |
| // { |
| // other scope, |
| // } |
| // Given bar#module it return the whole VName of that definition. |
| // And if more than one match is found the first would be returned. |
| const VName* SearchForDefinition(absl::string_view name) const; |
| }; |
| |
| // Keeps track and saves the explored scopes with a <key, value> and maps every |
| // signature to its scope. |
| // |
| // ScopeResolver saves the scopes generated while traversing the |
| // IndexingFactsTree so that they can be use to find some definition. |
| // Here the scopes are saved in a flattened manner instead of tree like |
| // hierarchy (in a structure which looks like symbol tables). |
| // e.g |
| // class m; |
| // int x; |
| // function int my_fun(); |
| // int x; |
| // return x; |
| // endfunction |
| // endclass |
| // |
| // The generated scope would be: |
| // { |
| // "m": { |
| // m#x, |
| // m#my_fun |
| // }, |
| // "my_fun": { |
| // m#my_fun#x |
| // } |
| // } |
| // this way definitions can be found using the signatures. |
| class ScopeResolver { |
| public: |
| ScopeResolver(const Signature& global_scope_signature, |
| const ScopeResolver* previous_file_scope_resolver) |
| : previous_file_scope_resolver_(previous_file_scope_resolver), |
| global_scope_signature_(global_scope_signature) {} |
| |
| ScopeResolver(const ScopeResolver&) = delete; |
| ScopeResolver(ScopeResolver&&) = delete; |
| ScopeResolver& operator=(const ScopeResolver&) = delete; |
| ScopeResolver& operator=(ScopeResolver&&) = delete; |
| |
| // Searches for the definitions of the given references' names. |
| std::vector<std::pair<const VName*, const Scope*>> SearchForDefinitions( |
| const std::vector<absl::string_view>& names) const; |
| |
| // Searches for definition of the given reference's name in the current |
| // scope (the top of scope_context). |
| const VName* SearchForDefinitionInCurrentScope(absl::string_view name) const; |
| |
| // Removes the given VName from the current scope (the top of scope_context). |
| void RemoveDefinitionFromCurrentScope(const VName& vname); |
| |
| // Adds the members of the given scope to the current scope (the top of |
| // scope_context). |
| void AppendScopeToCurrentScope(const Scope& scope); |
| |
| // Adds a ScopeMemberItem to the current scope (the top of scope_context). |
| void AddDefinitionToCurrentScope(const ScopeMemberItem& new_member); |
| |
| // Searches for a scope with the given signature in the scopes. |
| const Scope* SearchForScope(const Signature& signature) const; |
| |
| // Maps the given signature to the given scope. |
| void MapSignatureToScope(const Signature& signature, const Scope& scope); |
| |
| ScopeContext& GetMutableScopeContext() { return scope_context_; } |
| |
| private: |
| // Searches for a definition with the given name in the scope context (returns |
| // nullptr if a definitions is not found). |
| const VName* SearchForDefinitionInScopeContext(absl::string_view name) const; |
| |
| // Searches for a definition with the given name in the global scope of this |
| // ScopeResolver. |
| const VName* SearchForDefinitionInGlobalScope(absl::string_view name) const; |
| |
| // Searches for a definition with the given name in the scope with the given |
| // signature. |
| const VName* SearchForDefinitionInScope(const Signature& signature, |
| absl::string_view name) const; |
| |
| // Keeps track of scopes and definitions inside the scopes of ancestors as |
| // the visitor traverses the facts tree. |
| ScopeContext scope_context_; |
| |
| // Saves signatures alongside with their inner members (scope). |
| // This is used for resolving references to some variables after using |
| // import pkg::*. or other member access like class_Type::my_var. |
| // e.g |
| // package pkg1; |
| // function my_fun(); endfunction |
| // class my_class; endclass |
| // endpackage |
| // |
| // package pkg2; |
| // function my_fun(); endfunction |
| // class my_class; endclass |
| // endpackage |
| // |
| // Creates the following: |
| // { |
| // "pkg1": ["my_fun", "my_class"], |
| // "pkg2": ["my_fun", "my_class"] |
| // } |
| // NB: we internally work with pointers of scopes stored in the |
| // value, so we can't use flat_hash_map that moves around Scope. |
| absl::node_hash_map<Signature, Scope> scopes_; |
| |
| // Pointer to the previous file's discovered scopes (if a previous file |
| // exists). This is used for definition finding in cross-file referencing. |
| // This forms a null-terminated singly-linked list across files. |
| const ScopeResolver* previous_file_scope_resolver_; |
| |
| // The signature of the global scope of this ScopeResolver. |
| const Signature global_scope_signature_; |
| }; |
| |
| } // namespace kythe |
| } // namespace verilog |
| |
| #endif // VERIBLE_VERILOG_TOOLS_KYTHE_SCOPE_RESOLVER_H_ |