| // Copyright 2019 The Fuchsia Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #ifndef SRC_DEVELOPER_DEBUG_ZXDB_EXPR_IDENTIFIER_GLOB_H_ | |
| #define SRC_DEVELOPER_DEBUG_ZXDB_EXPR_IDENTIFIER_GLOB_H_ | |
| #include <optional> | |
| #include "src/developer/debug/zxdb/common/err.h" | |
| #include "src/developer/debug/zxdb/expr/parsed_identifier.h" | |
| namespace zxdb { | |
| // Provides a simple very-restricted Glob-like syntax for matching template types. | |
| // | |
| // For the requirements of the pretty-printing system, we want to be able to match different | |
| // template types but in a type-aware manner. | |
| // | |
| // For example, say we were to write a pretty-printer matching the glob "MyClass<*>" with a normal | |
| // string-based matcher. It would match "MyClass<int>" as desired. But it would also match things | |
| // like nested templates such as "MyClass<int>::Ref<Foo>" which is not desirable. | |
| // | |
| // So this class provides a way to match "*" for template type parameters ONLY in a manner that's | |
| // aware of the syntax of template definitions. Since type matching doesn't need to match things | |
| // like "all type names tarting with the letter 'a'", "*" never matches anything other than template | |
| // parameters. | |
| // | |
| // Syntax | |
| // ------ | |
| // | |
| // There is only one special character: * | |
| // | |
| // - All non-template parameters must match exactly (case sensitive). | |
| // | |
| // - A '*' normally matches EXACTLY ONE template parameter. | |
| // | |
| // - If the LAST template parameter in a glob is a "*", it will match ALL REMAINING template | |
| // parameters. | |
| // | |
| // - The "*" must occur by itself as a template parameter to match. So "Foo<*>" a glob matching | |
| // any type, but "Foo<int*>" is a literal. This is important because "*" occurs in many type | |
| // definitions but never by itself in a language we support. | |
| // | |
| // - It does not work recursively so wile "Foo<*>" is "Foo<Bar<*>>" is currently a literal. This | |
| // could be changed in the future if needed. | |
| // | |
| // - Global qualifications "::Foo" are ignored. Everything is assumed to be fully-qualified. | |
| // | |
| // Scoring glob matching | |
| // --------------------- | |
| // | |
| // Say we have three globs: | |
| // [1] MyClass<float> | |
| // [2] MyClass<*> | |
| // [3] MyClass<*, *> | |
| // | |
| // We have the following requirements: | |
| // - The type "MyClass<float>" should preferentially match [1], and secondarily match [2] | |
| // - The type "MyClass<int>" will match only [2]. | |
| // - The type "MyClass<int, float>" and "MyClass<int, float, char>" will preferentially match [3] | |
| // and secondarily match [2]. | |
| // | |
| // Note that the type "MyClass<>" will match none of the globs. If you wanted to match something | |
| // with this name (which is not a valid type name in C++, but is valid in some contexts) you will | |
| // need to supply a separate glob with an exact match. | |
| // | |
| // To measure match priority, Matches() computes the number of template parameters the last | |
| // encountered wildcard (if any) matches. Better matches have lower scores. | |
| // - An exact string match with no wildcard will have a score of 0. | |
| // - A single wildcare matching a single template parameter will score 1. | |
| // - Two wildcards matching two template parameters will score 1. | |
| // - One wildcard matching two template parameters will score 2. | |
| // - The glob "Foo<*, Bar>" matching "Foo<int, Bar>" will score 1 (last * matched 1 param). | |
| // | |
| // It's possible to have multiple levels of templates, say with a glob: | |
| // MyClass<*, *>::Something<*> | |
| // In this case we return the largest number of matches of the last "*" across all components. So | |
| // in this example | |
| // - "MyClass<int, int>::Something<int>" will score 1. | |
| // - "MyClass<int, int, int>::Something<int>" will score 2. | |
| // - "MyClass<int, int>::Something<int, int>" will also score 2 (not clear which is better). | |
| class IdentifierGlob { | |
| public: | |
| // Call Init() to initialize with a parsed identifier. | |
| IdentifierGlob() = default; | |
| // Specify a pre-parsed identifier. This also allows expressing some patterns that won't parse as | |
| // normal identifiers (they may be expressed in DWARF). | |
| explicit IdentifierGlob(ParsedIdentifier input) : parsed_(std::move(input)) {} | |
| // An error is returned if the glob could not be parsed. It must be syntactially valid. | |
| Err Init(const std::string& glob); | |
| // When the glob matches the given type, the match score will be returned. Lower scores are | |
| // better matches (see above). | |
| // | |
| // When there is no match, a nullopt will be returned. | |
| std::optional<int> Matches(const ParsedIdentifier& type) const; | |
| private: | |
| ParsedIdentifier parsed_; | |
| }; | |
| } // namespace zxdb | |
| #endif // SRC_DEVELOPER_DEBUG_ZXDB_EXPR_IDENTIFIER_GLOB_H_ |