From d108a4c2cc9768641fc9ea27457a4deeb40d7e22 Mon Sep 17 00:00:00 2001 From: Yuri Gorshenin Date: Wed, 8 Nov 2017 17:04:39 +0300 Subject: [PATCH] [search] Tracer. --- search/CMakeLists.txt | 2 + search/geocoder.cpp | 47 +++++++----- search/geocoder.hpp | 6 +- search/geocoder_context.cpp | 74 +++++++++++++++++-- search/geocoder_context.hpp | 27 ++++++- search/processor.cpp | 1 + search/search.pro | 2 + .../search_integration_tests/CMakeLists.txt | 1 + search/search_integration_tests/helpers.hpp | 26 +++++++ .../interactive_search_test.cpp | 20 ----- .../search_integration_tests.pro | 1 + .../search_integration_tests/tracer_tests.cpp | 62 ++++++++++++++++ search/search_params.hpp | 4 + search/search_tests/locality_scorer_test.cpp | 2 +- search/streets_matcher.cpp | 4 +- search/tracer.cpp | 65 ++++++++++++++++ search/tracer.hpp | 42 +++++++++++ 17 files changed, 335 insertions(+), 51 deletions(-) create mode 100644 search/search_integration_tests/tracer_tests.cpp create mode 100644 search/tracer.cpp create mode 100644 search/tracer.hpp diff --git a/search/CMakeLists.txt b/search/CMakeLists.txt index 0078413f2b..d23798e536 100644 --- a/search/CMakeLists.txt +++ b/search/CMakeLists.txt @@ -132,6 +132,8 @@ set( token_range.hpp token_slice.cpp token_slice.hpp + tracer.cpp + tracer.hpp types_skipper.cpp types_skipper.hpp utils.cpp diff --git a/search/geocoder.cpp b/search/geocoder.cpp index 07755475df..0d4255bf96 100644 --- a/search/geocoder.cpp +++ b/search/geocoder.cpp @@ -10,6 +10,7 @@ #include "search/processor.hpp" #include "search/retrieval.hpp" #include "search/token_slice.hpp" +#include "search/tracer.hpp" #include "search/utils.hpp" #include "indexer/classificator.hpp" @@ -78,33 +79,38 @@ UniString const kUniSpace(MakeUniString(" ")); struct ScopedMarkTokens { - ScopedMarkTokens(vector & usedTokens, TokenRange const & range) - : m_usedTokens(usedTokens), m_range(range) + static BaseContext::TokenType constexpr kUnused = BaseContext::TOKEN_TYPE_COUNT; + + ScopedMarkTokens(vector & tokens, BaseContext::TokenType type, + TokenRange const & range) + : m_tokens(tokens), m_type(type), m_range(range) { ASSERT(m_range.IsValid(), ()); - ASSERT_LESS_OR_EQUAL(m_range.End(), m_usedTokens.size(), ()); + ASSERT_LESS_OR_EQUAL(m_range.End(), m_tokens.size(), ()); #if defined(DEBUG) for (size_t i : m_range) - ASSERT(!m_usedTokens[i], (i)); + ASSERT_EQUAL(m_tokens[i], kUnused, (i)); #endif - fill(m_usedTokens.begin() + m_range.Begin(), m_usedTokens.begin() + m_range.End(), - true /* used */); + fill(m_tokens.begin() + m_range.Begin(), m_tokens.begin() + m_range.End(), m_type); } ~ScopedMarkTokens() { #if defined(DEBUG) for (size_t i : m_range) - ASSERT(m_usedTokens[i], (i)); + ASSERT_EQUAL(m_tokens[i], m_type, (i)); #endif - fill(m_usedTokens.begin() + m_range.Begin(), m_usedTokens.begin() + m_range.End(), - false /* used */); + fill(m_tokens.begin() + m_range.Begin(), m_tokens.begin() + m_range.End(), kUnused); } - vector & m_usedTokens; + vector & m_tokens; + search::BaseContext::TokenType const m_type; TokenRange const m_range; }; +// static +BaseContext::TokenType constexpr ScopedMarkTokens::kUnused; + class LazyRankTable : public RankTable { public: @@ -573,7 +579,7 @@ void Geocoder::InitBaseContext(BaseContext & ctx) { Retrieval retrieval(*m_context, m_cancellable); - ctx.m_usedTokens.assign(m_params.GetNumTokens(), false); + ctx.m_tokens.assign(m_params.GetNumTokens(), BaseContext::TOKEN_TYPE_COUNT); ctx.m_numTokens = m_params.GetNumTokens(); ctx.m_features.resize(ctx.m_numTokens); for (size_t i = 0; i < ctx.m_features.size(); ++i) @@ -849,7 +855,7 @@ void Geocoder::MatchRegions(BaseContext & ctx, Region::Type type) ctx.m_regions.push_back(®ion); MY_SCOPE_GUARD(cleanup, [&ctx]() { ctx.m_regions.pop_back(); }); - ScopedMarkTokens mark(ctx.m_usedTokens, tokenRange); + ScopedMarkTokens mark(ctx.m_tokens, BaseContext::FromRegionType(type), tokenRange); if (ctx.AllTokensUsed()) { // Region matches to search query, we need to emit it as is. @@ -888,7 +894,7 @@ void Geocoder::MatchCities(BaseContext & ctx) continue; } - ScopedMarkTokens mark(ctx.m_usedTokens, tokenRange); + ScopedMarkTokens mark(ctx.m_tokens, BaseContext::TOKEN_TYPE_CITY, tokenRange); ctx.m_city = &city; MY_SCOPE_GUARD(cleanup, [&ctx]() { ctx.m_city = nullptr; }); @@ -954,7 +960,7 @@ void Geocoder::WithPostcodes(BaseContext & ctx, TFn && fn) size_t endToken = startToken; for (size_t n = 1; startToken + n <= ctx.m_numTokens && n <= maxPostcodeTokens; ++n) { - if (ctx.m_usedTokens[startToken + n - 1]) + if (ctx.IsTokenUsed(startToken + n - 1)) break; TokenSlice slice(m_params, TokenRange(startToken, startToken + n)); @@ -972,7 +978,7 @@ void Geocoder::WithPostcodes(BaseContext & ctx, TFn && fn) if (!postcodes.IsEmpty()) { - ScopedMarkTokens mark(ctx.m_usedTokens, tokenRange); + ScopedMarkTokens mark(ctx.m_tokens, BaseContext::TOKEN_TYPE_POSTCODE, tokenRange); m_postcodes.Clear(); m_postcodes.m_tokenRange = tokenRange; @@ -1011,7 +1017,7 @@ void Geocoder::CreateStreetsLayerAndMatchLowerLayers(BaseContext & ctx, }); layer.m_sortedFeatures = &sortedFeatures; - ScopedMarkTokens mark(ctx.m_usedTokens, prediction.m_tokenRange); + ScopedMarkTokens mark(ctx.m_tokens, BaseContext::TOKEN_TYPE_STREET, prediction.m_tokenRange); MatchPOIsAndBuildings(ctx, 0 /* curToken */); } @@ -1114,7 +1120,7 @@ void Geocoder::MatchPOIsAndBuildings(BaseContext & ctx, size_t curToken) features.SetFull(); // Try to consume [curToken, m_numTokens) tokens range. - for (size_t n = 1; curToken + n <= ctx.m_numTokens && !ctx.m_usedTokens[curToken + n - 1]; ++n) + for (size_t n = 1; curToken + n <= ctx.m_numTokens && !ctx.IsTokenUsed(curToken + n - 1); ++n) { // At this point |features| is the intersection of // m_addressFeatures[curToken], m_addressFeatures[curToken + 1], @@ -1194,6 +1200,8 @@ void Geocoder::MatchPOIsAndBuildings(BaseContext & ctx, size_t curToken) } layer.m_type = static_cast(i); + ScopedMarkTokens mark(ctx.m_tokens, BaseContext::FromModelType(layer.m_type), + TokenRange(curToken, curToken + n)); if (IsLayerSequenceSane(layers)) MatchPOIsAndBuildings(ctx, curToken + n); } @@ -1283,6 +1291,9 @@ void Geocoder::EmitResult(BaseContext const & ctx, MwmSet::MwmId const & mwmId, if (m_params.m_cianMode && type != Model::TYPE_BUILDING) return; + if (m_params.m_tracer) + m_params.m_tracer->EmitParse(ctx.m_tokens); + // Distance and rank will be filled at the end, for all results at once. // // TODO (@y, @m): need to skip zero rank features that are too @@ -1343,7 +1354,7 @@ void Geocoder::MatchUnclassified(BaseContext & ctx, size_t curToken) auto startToken = curToken; for (curToken = ctx.SkipUsedTokens(curToken); - curToken < ctx.m_numTokens && !ctx.m_usedTokens[curToken]; ++curToken) + curToken < ctx.m_numTokens && !ctx.IsTokenUsed(curToken); ++curToken) { allFeatures = allFeatures.Intersect(ctx.m_features[curToken]); } diff --git a/search/geocoder.hpp b/search/geocoder.hpp index 2ff77e0556..fdd1f19b67 100644 --- a/search/geocoder.hpp +++ b/search/geocoder.hpp @@ -37,6 +37,7 @@ #include "std/limits.hpp" #include "std/set.hpp" +#include "std/shared_ptr.hpp" #include "std/string.hpp" #include "std/unique_ptr.hpp" #include "std/unordered_map.hpp" @@ -52,11 +53,11 @@ class CountryInfoGetter; namespace search { -class PreRanker; - class FeaturesFilter; class FeaturesLayerMatcher; +class PreRanker; class TokenSlice; +class Tracer; // This class is used to retrieve all features corresponding to a // search query. Search query is represented as a sequence of tokens @@ -83,6 +84,7 @@ public: shared_ptr m_hotelsFilter; bool m_cianMode = false; set m_preferredTypes; + shared_ptr m_tracer; }; Geocoder(Index const & index, storage::CountryInfoGetter const & infoGetter, diff --git a/search/geocoder_context.cpp b/search/geocoder_context.cpp index 088d91eb5c..50bc7025f6 100644 --- a/search/geocoder_context.cpp +++ b/search/geocoder_context.cpp @@ -5,37 +5,97 @@ #include "base/assert.hpp" #include "base/stl_add.hpp" -#include +using namespace std; namespace search { +// static +BaseContext::TokenType BaseContext::FromModelType(Model::Type type) +{ + switch (type) + { + case Model::TYPE_POI: return TOKEN_TYPE_POI; + case Model::TYPE_BUILDING: return TOKEN_TYPE_BUILDING; + case Model::TYPE_STREET: return TOKEN_TYPE_STREET; + case Model::TYPE_UNCLASSIFIED: return TOKEN_TYPE_UNCLASSIFIED; + case Model::TYPE_VILLAGE: return TOKEN_TYPE_VILLAGE; + case Model::TYPE_CITY: return TOKEN_TYPE_CITY; + case Model::TYPE_STATE: return TOKEN_TYPE_STATE; + case Model::TYPE_COUNTRY: return TOKEN_TYPE_COUNTRY; + case Model::TYPE_COUNT: return TOKEN_TYPE_COUNT; + } +} + +// static +BaseContext::TokenType BaseContext::FromRegionType(Region::Type type) +{ + switch (type) + { + case Region::TYPE_STATE: return TOKEN_TYPE_STATE; + case Region::TYPE_COUNTRY: return TOKEN_TYPE_COUNTRY; + case Region::TYPE_COUNT: return TOKEN_TYPE_COUNT; + } +} + size_t BaseContext::SkipUsedTokens(size_t curToken) const { - while (curToken != m_usedTokens.size() && m_usedTokens[curToken]) + while (curToken != m_tokens.size() && IsTokenUsed(curToken)) ++curToken; return curToken; } +bool BaseContext::IsTokenUsed(size_t token) const +{ + ASSERT_LESS(token, m_tokens.size(), ()); + return m_tokens[token] != TOKEN_TYPE_COUNT; +} + bool BaseContext::AllTokensUsed() const { - return std::all_of(m_usedTokens.begin(), m_usedTokens.end(), IdFunctor()); + for (size_t i = 0; i < m_tokens.size(); ++i) + { + if (!IsTokenUsed(i)) + return false; + } + return true; } bool BaseContext::HasUsedTokensInRange(TokenRange const & range) const { ASSERT(range.IsValid(), (range)); - return std::any_of(m_usedTokens.begin() + range.Begin(), m_usedTokens.begin() + range.End(), - IdFunctor()); + for (size_t i = range.Begin(); i < range.End(); ++i) + { + if (IsTokenUsed(i)) + return true; + } + return false; } size_t BaseContext::NumUnusedTokenGroups() const { size_t numGroups = 0; - for (size_t i = 0; i < m_usedTokens.size(); ++i) + for (size_t i = 0; i < m_tokens.size(); ++i) { - if (!m_usedTokens[i] && (i == 0 || m_usedTokens[i - 1])) + if (!IsTokenUsed(i) && (i == 0 || IsTokenUsed(i - 1))) ++numGroups; } return numGroups; } + +string DebugPrint(BaseContext::TokenType type) +{ + switch (type) + { + case BaseContext::TOKEN_TYPE_POI: return "POI"; + case BaseContext::TOKEN_TYPE_BUILDING: return "BUILDING"; + case BaseContext::TOKEN_TYPE_STREET: return "STREET"; + case BaseContext::TOKEN_TYPE_UNCLASSIFIED: return "UNCLASSIFIED"; + case BaseContext::TOKEN_TYPE_VILLAGE: return "VILLAGE"; + case BaseContext::TOKEN_TYPE_CITY: return "CITY"; + case BaseContext::TOKEN_TYPE_STATE: return "STATE"; + case BaseContext::TOKEN_TYPE_COUNTRY: return "COUNTRY"; + case BaseContext::TOKEN_TYPE_POSTCODE: return "POSTCODE"; + case BaseContext::TOKEN_TYPE_COUNT: return "COUNT"; + } +} } // namespace search diff --git a/search/geocoder_context.hpp b/search/geocoder_context.hpp index d00f7e9f65..3478625ba5 100644 --- a/search/geocoder_context.hpp +++ b/search/geocoder_context.hpp @@ -4,9 +4,11 @@ #include "search/features_layer.hpp" #include "search/geocoder_locality.hpp" #include "search/hotels_filter.hpp" +#include "search/model.hpp" #include #include +#include #include namespace search @@ -16,10 +18,31 @@ class TokenRange; struct BaseContext { + enum TokenType + { + TOKEN_TYPE_POI, + TOKEN_TYPE_BUILDING, + TOKEN_TYPE_STREET, + TOKEN_TYPE_UNCLASSIFIED, + TOKEN_TYPE_VILLAGE, + TOKEN_TYPE_CITY, + TOKEN_TYPE_STATE, + TOKEN_TYPE_COUNTRY, + TOKEN_TYPE_POSTCODE, + + TOKEN_TYPE_COUNT + }; + + static TokenType FromModelType(Model::Type type); + static TokenType FromRegionType(Region::Type type); + // Advances |curToken| to the nearest unused token, or to the end of // |m_usedTokens| if there are no unused tokens. size_t SkipUsedTokens(size_t curToken) const; + // Returns true if |token| is marked as used. + bool IsTokenUsed(size_t token) const; + // Returns true iff all tokens are used. bool AllTokensUsed() const; @@ -45,11 +68,13 @@ struct BaseContext // This vector is used to indicate what tokens were already matched // and can't be re-used during the geocoding process. - std::vector m_usedTokens; + std::vector m_tokens; // Number of tokens in the query. size_t m_numTokens = 0; std::unique_ptr m_hotelsFilter; }; + +std::string DebugPrint(BaseContext::TokenType type); } // namespace search diff --git a/search/processor.cpp b/search/processor.cpp index fb7e488ecc..7eeeea52d4 100644 --- a/search/processor.cpp +++ b/search/processor.cpp @@ -491,6 +491,7 @@ void Processor::InitGeocoder(Geocoder::Params & geocoderParams, SearchParams con geocoderParams.m_hotelsFilter = searchParams.m_hotelsFilter; geocoderParams.m_cianMode = searchParams.m_cianMode; geocoderParams.m_preferredTypes = m_preferredTypes; + geocoderParams.m_tracer = searchParams.m_tracer; m_geocoder.SetParams(geocoderParams); } diff --git a/search/search.pro b/search/search.pro index 4fa1b53cf9..04a4cf3593 100644 --- a/search/search.pro +++ b/search/search.pro @@ -81,6 +81,7 @@ HEADERS += \ suggest.hpp \ token_range.hpp \ token_slice.hpp \ + tracer.hpp \ types_skipper.hpp \ utils.hpp \ viewport_search_callback.hpp \ @@ -145,6 +146,7 @@ SOURCES += \ streets_matcher.cpp \ suggest.cpp \ token_slice.cpp \ + tracer.cpp \ types_skipper.cpp \ utils.cpp \ viewport_search_callback.cpp \ diff --git a/search/search_integration_tests/CMakeLists.txt b/search/search_integration_tests/CMakeLists.txt index 4e8a202ecf..af4feff138 100644 --- a/search/search_integration_tests/CMakeLists.txt +++ b/search/search_integration_tests/CMakeLists.txt @@ -12,6 +12,7 @@ set( ranker_test.cpp search_edited_features_test.cpp smoke_test.cpp + tracer_tests.cpp ) omim_add_test(${PROJECT_NAME} ${SRC}) diff --git a/search/search_integration_tests/helpers.hpp b/search/search_integration_tests/helpers.hpp index 615b13c47d..47d930067b 100644 --- a/search/search_integration_tests/helpers.hpp +++ b/search/search_integration_tests/helpers.hpp @@ -5,6 +5,8 @@ #include "search/search_tests_support/test_search_request.hpp" #include "search/search_tests_support/test_with_custom_mwms.hpp" +#include "generator/generator_tests_support/test_feature.hpp" + #include "indexer/indexer_tests_support/helpers.hpp" #include "geometry/rect2d.hpp" @@ -57,4 +59,28 @@ protected: m2::RectD m_viewport; }; + +class TestCafe : public generator::tests_support::TestPOI +{ +public: + TestCafe(m2::PointD const & center, std::string const & name, std::string const & lang) + : TestPOI(center, name, lang) + { + SetTypes({{"amenity", "cafe"}}); + } + + explicit TestCafe(m2::PointD const & center) : TestCafe(center, "cafe", "en") {} +}; + +class TestHotel : public generator::tests_support::TestPOI +{ +public: + TestHotel(m2::PointD const & center, std::string const & name, std::string const & lang) + : TestPOI(center, name, lang) + { + SetTypes({{"tourism", "hotel"}}); + } + + explicit TestHotel(m2::PointD const & center) : TestHotel(center, "hotel", "en") {} +}; } // namespace search diff --git a/search/search_integration_tests/interactive_search_test.cpp b/search/search_integration_tests/interactive_search_test.cpp index 8324ac5cf7..f47c679475 100644 --- a/search/search_integration_tests/interactive_search_test.cpp +++ b/search/search_integration_tests/interactive_search_test.cpp @@ -1,7 +1,5 @@ #include "testing/testing.hpp" -#include "generator/generator_tests_support/test_feature.hpp" - #include "search/viewport_search_callback.hpp" #include "search/mode.hpp" #include "search/search_integration_tests/helpers.hpp" @@ -17,24 +15,6 @@ namespace search { namespace { -class TestCafe : public TestPOI -{ -public: - TestCafe(m2::PointD const & center) : TestPOI(center, "cafe", "en") - { - SetTypes({{"amenity", "cafe"}}); - } -}; - -class TestHotel : public TestPOI -{ -public: - TestHotel(m2::PointD const & center) : TestPOI(center, "hotel", "en") - { - SetTypes({{"tourism", "hotel"}}); - } -}; - class TestDelegate : public ViewportSearchCallback::Delegate { public: diff --git a/search/search_integration_tests/search_integration_tests.pro b/search/search_integration_tests/search_integration_tests.pro index 663297ad05..426a91c1f2 100644 --- a/search/search_integration_tests/search_integration_tests.pro +++ b/search/search_integration_tests/search_integration_tests.pro @@ -33,6 +33,7 @@ SOURCES += \ processor_test.cpp \ search_edited_features_test.cpp \ smoke_test.cpp \ + tracer_tests.cpp \ HEADERS += \ helpers.hpp \ diff --git a/search/search_integration_tests/tracer_tests.cpp b/search/search_integration_tests/tracer_tests.cpp new file mode 100644 index 0000000000..c2df6857b8 --- /dev/null +++ b/search/search_integration_tests/tracer_tests.cpp @@ -0,0 +1,62 @@ +#include "testing/testing.hpp" + +#include "search/geocoder_context.hpp" +#include "search/search_integration_tests/helpers.hpp" +#include "search/search_tests_support/test_results_matching.hpp" +#include "search/tracer.hpp" + +#include "generator/generator_tests_support/test_feature.hpp" + +#include +#include + +using namespace generator::tests_support; +using namespace search::tests_support; +using namespace search; +using namespace std; + +namespace +{ +class TracerTest : public SearchTest +{ +}; + +UNIT_CLASS_TEST(TracerTest, Smoke) +{ + using TokenType = BaseContext::TokenType; + + TestCity moscow(m2::PointD(0, 0), "Moscow", "en", 100 /* rank */); + TestCafe regularCafe(m2::PointD(0, 0)); + TestCafe moscowCafe(m2::PointD(0, 0), "Moscow", "en"); + + BuildWorld([&](TestMwmBuilder & builder) { builder.Add(moscow); }); + + auto const id = BuildCountry("Wonderland", [&](TestMwmBuilder & builder) { + builder.Add(regularCafe); + builder.Add(moscowCafe); + }); + + auto tracer = make_shared(); + + SearchParams params; + params.m_query = "moscow cafe"; + params.m_inputLocale = "en"; + params.m_viewport = m2::RectD(-1, -1, 1, 1); + params.m_mode = Mode::Everywhere; + params.m_tracer = tracer; + + TestSearchRequest request(m_engine, params); + request.Run(); + TRules rules = {ExactMatch(id, regularCafe), ExactMatch(id, moscowCafe)}; + TEST(ResultsMatch(request.Results(), rules), ()); + + auto const actual = tracer->GetUniqueParses(); + + vector const expected{ + Tracer::Parse{{{TokenType::TOKEN_TYPE_POI, TokenRange(0, 2)}}}, + Tracer::Parse{{{TokenType::TOKEN_TYPE_CITY, TokenRange(0, 1)}, + {TokenType::TOKEN_TYPE_POI, TokenRange(1, 2)}}}}; + + TEST_EQUAL(expected, actual, ()); +} +} // namespace diff --git a/search/search_params.hpp b/search/search_params.hpp index b7d9a0f828..10724a88c1 100644 --- a/search/search_params.hpp +++ b/search/search_params.hpp @@ -16,6 +16,7 @@ namespace search { class Results; +class Tracer; struct SearchParams { @@ -56,7 +57,10 @@ struct SearchParams bool m_needHighlighting = false; std::shared_ptr m_hotelsFilter; + bool m_cianMode = false; + + std::shared_ptr m_tracer; }; std::string DebugPrint(SearchParams const & params); diff --git a/search/search_tests/locality_scorer_test.cpp b/search/search_tests/locality_scorer_test.cpp index 2b96945193..302420899c 100644 --- a/search/search_tests/locality_scorer_test.cpp +++ b/search/search_tests/locality_scorer_test.cpp @@ -64,7 +64,7 @@ public: void GetTopLocalities(size_t limit) { BaseContext ctx; - ctx.m_usedTokens.assign(m_params.GetNumTokens(), false); + ctx.m_tokens.assign(m_params.GetNumTokens(), BaseContext::TOKEN_TYPE_COUNT); ctx.m_numTokens = m_params.GetNumTokens(); for (size_t i = 0; i < m_params.GetNumTokens(); ++i) diff --git a/search/streets_matcher.cpp b/search/streets_matcher.cpp index 72e39f8071..f852b1c742 100644 --- a/search/streets_matcher.cpp +++ b/search/streets_matcher.cpp @@ -59,7 +59,7 @@ void StreetsMatcher::FindStreets(BaseContext const & ctx, FeaturesFilter const & { for (size_t startToken = 0; startToken < ctx.m_numTokens; ++startToken) { - if (ctx.m_usedTokens[startToken]) + if (ctx.IsTokenUsed(startToken)) continue; // Here we try to match as many tokens as possible while @@ -150,7 +150,7 @@ void StreetsMatcher::FindStreets(BaseContext const & ctx, FeaturesFilter const & incomplete = false; }); - for (; curToken < ctx.m_numTokens && !ctx.m_usedTokens[curToken] && !streets.IsEmpty(); + for (; curToken < ctx.m_numTokens && !ctx.IsTokenUsed(curToken) && !streets.IsEmpty(); ++curToken) { auto const & token = params.GetToken(curToken).m_original; diff --git a/search/tracer.cpp b/search/tracer.cpp new file mode 100644 index 0000000000..a773a371ba --- /dev/null +++ b/search/tracer.cpp @@ -0,0 +1,65 @@ +#include "search/tracer.hpp" + +#include "base/stl_helpers.hpp" + +#include +#include + +using namespace std; + +namespace search +{ +// Tracer::Parse ----------------------------------------------------------------------------------- +Tracer::Parse::Parse(std::vector const & types) +{ + size_t i = 0; + while (i != types.size()) + { + auto const type = types[i]; + auto j = i + 1; + while (j != types.size() && types[j] == type) + ++j; + m_ranges[type] = TokenRange(i, j); + i = j; + } +} + +Tracer::Parse::Parse(vector> const & ranges) +{ + for (auto const & kv : ranges) + m_ranges[kv.first] = kv.second; +} + +std::string DebugPrint(Tracer::Parse const & parse) +{ + using TokenType = Tracer::Parse::TokenType; + + ostringstream os; + os << "Parse ["; + + bool first = true; + for (size_t i = 0; i < TokenType::TOKEN_TYPE_COUNT; ++i) + { + auto const & range = parse.m_ranges[i]; + if (range.Begin() == range.End()) + continue; + + if (!first) + os << ", "; + + os << DebugPrint(static_cast(i)) << ": " << DebugPrint(range); + first = false; + } + + os << "]"; + return os.str(); +} + +// Tracer ------------------------------------------------------------------------------------------ +vector Tracer::GetUniqueParses() const +{ + auto parses = m_parses; + my::SortUnique(parses); + return parses; +} +} // namespace search diff --git a/search/tracer.hpp b/search/tracer.hpp new file mode 100644 index 0000000000..887422014a --- /dev/null +++ b/search/tracer.hpp @@ -0,0 +1,42 @@ +#pragma once + +#include "search/geocoder_context.hpp" +#include "search/token_range.hpp" + +#include +#include +#include +#include + +namespace search +{ +class Tracer +{ +public: + struct Parse + { + using TokenType = BaseContext::TokenType; + + explicit Parse(std::vector const & types); + explicit Parse(std::vector> const & ranges); + + bool operator==(Parse const & rhs) const { return m_ranges == rhs.m_ranges; } + bool operator<(Parse const & rhs) const { return m_ranges < rhs.m_ranges; } + + std::array m_ranges; + }; + + template + void EmitParse(Args &&... args) + { + m_parses.emplace_back(std::forward(args)...); + } + + std::vector GetUniqueParses() const; + +private: + std::vector m_parses; +}; + +std::string DebugPrint(Tracer::Parse const & parse); +} // namespace search