LCOV - code coverage report
Current view: top level - queryparser - queryparser_internal.cc (source / functions) Hit Total Coverage
Test: Test Coverage for xapian-core 954b5873a738 Lines: 187 188 99.5 %
Date: 2019-06-30 05:20:33 Functions: 16 16 100.0 %
Branches: 86 93 92.5 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            : ** 2000-05-29
       3                 :            : **
       4                 :            : ** The author disclaims copyright to this source code.  In place of
       5                 :            : ** a legal notice, here is a blessing:
       6                 :            : **
       7                 :            : **    May you do good and not evil.
       8                 :            : **    May you find forgiveness for yourself and forgive others.
       9                 :            : **    May you share freely, never taking more than you give.
      10                 :            : **
      11                 :            : *************************************************************************
      12                 :            : ** Driver template for the LEMON parser generator.
      13                 :            : **
      14                 :            : ** Synced with upstream:
      15                 :            : ** https://www.sqlite.org/src/artifact/468a155e8729cfbccfe1d85bf60d064f1dab76167a51149ec5c7928a2de63953
      16                 :            : **
      17                 :            : ** The "lemon" program processes an LALR(1) input grammar file, then uses
      18                 :            : ** this template to construct a parser.  The "lemon" program inserts text
      19                 :            : ** at each "%%" line.  Also, any "P-a-r-s-e" identifier prefix (without the
      20                 :            : ** interstitial "-" characters) contained in this template is changed into
      21                 :            : ** the value of the %name directive from the grammar.  Otherwise, the content
      22                 :            : ** of this template is copied straight through into the generate parser
      23                 :            : ** source file.
      24                 :            : **
      25                 :            : ** The following is the concatenation of all %include directives from the
      26                 :            : ** input grammar file:
      27                 :            : */
      28                 :            : /************ Begin %include sections from the grammar ************************/
      29                 :            : #line 1 "queryparser/queryparser.lemony"
      30                 :            : 
      31                 :            : /* queryparser.lemony: build a Xapian::Query object from a user query string.
      32                 :            :  *
      33                 :            :  * Copyright (C) 2004,2005,2006,2007,2008,2009,2010,2011,2012,2013,2015,2016,2018,2019 Olly Betts
      34                 :            :  * Copyright (C) 2007,2008,2009 Lemur Consulting Ltd
      35                 :            :  * Copyright (C) 2010 Adam Sj√łgren
      36                 :            :  *
      37                 :            :  * This program is free software; you can redistribute it and/or
      38                 :            :  * modify it under the terms of the GNU General Public License as
      39                 :            :  * published by the Free Software Foundation; either version 2 of the
      40                 :            :  * License, or (at your option) any later version.
      41                 :            :  *
      42                 :            :  * This program is distributed in the hope that it will be useful,
      43                 :            :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      44                 :            :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      45                 :            :  * GNU General Public License for more details.
      46                 :            :  *
      47                 :            :  * You should have received a copy of the GNU General Public License
      48                 :            :  * along with this program; if not, write to the Free Software
      49                 :            :  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
      50                 :            :  * USA
      51                 :            :  */
      52                 :            : 
      53                 :            : #include <config.h>
      54                 :            : 
      55                 :            : #include "queryparser_internal.h"
      56                 :            : 
      57                 :            : #include "api/queryinternal.h"
      58                 :            : #include "omassert.h"
      59                 :            : #include "str.h"
      60                 :            : #include "stringutils.h"
      61                 :            : #include "xapian/error.h"
      62                 :            : #include "xapian/unicode.h"
      63                 :            : 
      64                 :            : // Include the list of token values lemon generates.
      65                 :            : #include "queryparser_token.h"
      66                 :            : 
      67                 :            : #include "cjk-tokenizer.h"
      68                 :            : 
      69                 :            : #include <algorithm>
      70                 :            : #include <cstring>
      71                 :            : #include <limits>
      72                 :            : #include <list>
      73                 :            : #include <string>
      74                 :            : #include <vector>
      75                 :            : 
      76                 :            : // We create the yyParser on the stack.
      77                 :            : #define Parse_ENGINEALWAYSONSTACK
      78                 :            : 
      79                 :            : using namespace std;
      80                 :            : 
      81                 :            : using namespace Xapian;
      82                 :            : 
      83                 :            : static constexpr unsigned NO_EDIT_DISTANCE = unsigned(-1);
      84                 :            : static constexpr unsigned DEFAULT_EDIT_DISTANCE = 2;
      85                 :            : 
      86                 :            : inline bool
      87                 :            : U_isupper(unsigned ch) {
      88                 :            :     return (ch < 128 && C_isupper(static_cast<unsigned char>(ch)));
      89                 :            : }
      90                 :            : 
      91                 :            : inline bool
      92                 :            : U_isdigit(unsigned ch) {
      93                 :            :     return (ch < 128 && C_isdigit(static_cast<unsigned char>(ch)));
      94                 :            : }
      95                 :            : 
      96                 :            : inline bool
      97                 :            : U_isalpha(unsigned ch) {
      98                 :            :     return (ch < 128 && C_isalpha(static_cast<unsigned char>(ch)));
      99                 :            : }
     100                 :            : 
     101                 :            : using Xapian::Unicode::is_whitespace;
     102                 :            : 
     103                 :            : inline bool
     104                 :            : is_not_whitespace(unsigned ch) {
     105                 :            :     return !is_whitespace(ch);
     106                 :            : }
     107                 :            : 
     108                 :            : using Xapian::Unicode::is_wordchar;
     109                 :            : 
     110                 :            : inline bool
     111                 :            : is_not_wordchar(unsigned ch) {
     112                 :            :     return !is_wordchar(ch);
     113                 :            : }
     114                 :            : 
     115                 :            : inline bool
     116                 :            : is_digit(unsigned ch) {
     117                 :            :     return (Unicode::get_category(ch) == Unicode::DECIMAL_DIGIT_NUMBER);
     118                 :            : }
     119                 :            : 
     120                 :            : // FIXME: we used to keep trailing "-" (e.g. Cl-) but it's of dubious utility
     121                 :            : // and there's the risk of hyphens getting stuck onto the end of terms...
     122                 :            : //
     123                 :            : // There are currently assumptions below that this only matches ASCII
     124                 :            : // characters.
     125                 :            : inline bool
     126                 :            : is_suffix(unsigned ch) {
     127                 :            :     return ch == '+' || ch == '#';
     128                 :            : }
     129                 :            : 
     130                 :            : inline bool
     131                 :            : is_double_quote(unsigned ch) {
     132                 :            :     // We simply treat all double quotes as equivalent, which is a bit crude,
     133                 :            :     // but it isn't clear that it would actually better to require them to
     134                 :            :     // match up exactly.
     135                 :            :     //
     136                 :            :     // 0x201c is Unicode opening double quote.
     137                 :            :     // 0x201d is Unicode closing double quote.
     138                 :            :     return ch == '"' || ch == 0x201c || ch == 0x201d;
     139                 :            : }
     140                 :            : 
     141                 :            : inline bool
     142                 :            : prefix_needs_colon(const string & prefix, unsigned ch)
     143                 :            : {
     144                 :            :     if (!U_isupper(ch) && ch != ':') return false;
     145                 :            :     string::size_type len = prefix.length();
     146                 :            :     return (len > 1 && prefix[len - 1] != ':');
     147                 :            : }
     148                 :            : 
     149                 :            : using Unicode::is_currency;
     150                 :            : 
     151                 :            : inline bool
     152                 :            : is_positional(Xapian::Query::op op)
     153                 :            : {
     154                 :            :     return (op == Xapian::Query::OP_PHRASE || op == Xapian::Query::OP_NEAR);
     155                 :            : }
     156                 :            : 
     157                 :            : class Terms;
     158                 :            : 
     159                 :            : /** Class used to pass information about a token from lexer to parser.
     160                 :            :  *
     161                 :            :  *  Generally an instance of this class carries term information, but it can be
     162                 :            :  *  used for a range query, and with some operators (e.g. the distance in
     163                 :            :  *  NEAR/3 or ADJ/3, etc).
     164                 :            :  */
     165                 :            : class Term {
     166                 :            :     State * state;
     167                 :            : 
     168                 :            :   public:
     169                 :            :     string name;
     170                 :            :     const FieldInfo * field_info;
     171                 :            :     string unstemmed;
     172                 :            :     QueryParser::stem_strategy stem;
     173                 :            :     termpos pos;
     174                 :            :     Query query;
     175                 :            :     unsigned edit_distance;
     176                 :            : 
     177                 :            :     Term(const string &name_, termpos pos_)
     178                 :            :         : name(name_), stem(QueryParser::STEM_NONE), pos(pos_) { }
     179                 :            :     explicit Term(const string &name_)
     180                 :            :         : name(name_), stem(QueryParser::STEM_NONE), pos(0) { }
     181                 :            :     Term(const string &name_, const FieldInfo * field_info_)
     182                 :            :         : name(name_), field_info(field_info_),
     183                 :            :           stem(QueryParser::STEM_NONE), pos(0) { }
     184                 :            :     explicit Term(termpos pos_) : stem(QueryParser::STEM_NONE), pos(pos_) { }
     185                 :            :     Term(State * state_, const string &name_, const FieldInfo * field_info_,
     186                 :            :          const string &unstemmed_,
     187                 :            :          QueryParser::stem_strategy stem_ = QueryParser::STEM_NONE,
     188                 :            :          termpos pos_ = 0,
     189                 :            :          unsigned edit_distance_ = NO_EDIT_DISTANCE)
     190                 :            :         : state(state_), name(name_), field_info(field_info_),
     191                 :            :           unstemmed(unstemmed_), stem(stem_), pos(pos_),
     192                 :            :           edit_distance(edit_distance_) { }
     193                 :            :     // For RANGE tokens.
     194                 :            :     Term(const Xapian::Query & q, const string & grouping)
     195                 :            :         : name(grouping), query(q) { }
     196                 :            : 
     197                 :            :     string make_term(const string & prefix) const;
     198                 :            : 
     199                 :            :     void need_positions() {
     200                 :            :         if (stem == QueryParser::STEM_SOME) stem = QueryParser::STEM_NONE;
     201                 :            :     }
     202                 :            : 
     203                 :            :     termpos get_termpos() const { return pos; }
     204                 :            : 
     205                 :            :     string get_grouping() const {
     206                 :            :         return field_info->grouping;
     207                 :            :     }
     208                 :            : 
     209                 :            :     Query * as_fuzzy_query(State * state) const;
     210                 :            : 
     211                 :            :     Query * as_wildcarded_query(State * state) const;
     212                 :            : 
     213                 :            :     /** Build a query for a term at the very end of the query string when
     214                 :            :      *  FLAG_PARTIAL is in use.
     215                 :            :      *
     216                 :            :      *  This query should match documents containing any terms which start with
     217                 :            :      *  the characters specified, but should give a higher score to exact
     218                 :            :      *  matches (since the user might have finished typing - we simply don't
     219                 :            :      *  know).
     220                 :            :      */
     221                 :            :     Query * as_partial_query(State * state_) const;
     222                 :            : 
     223                 :            :     /** Build a query for a string of CJK characters. */
     224                 :            :     Query * as_cjk_query() const;
     225                 :            : 
     226                 :            :     /** Handle a CJK character string in a positional context. */
     227                 :            :     void as_positional_cjk_term(Terms * terms) const;
     228                 :            : 
     229                 :            :     /// Range query.
     230                 :            :     Query as_range_query() const;
     231                 :            : 
     232                 :            :     Query get_query() const;
     233                 :            : 
     234                 :            :     Query get_query_with_synonyms() const;
     235                 :            : 
     236                 :            :     Query get_query_with_auto_synonyms() const;
     237                 :            : };
     238                 :            : 
     239                 :            : /// Parser State shared between the lexer and the parser.
     240                 :            : class State {
     241                 :            :     QueryParser::Internal * qpi;
     242                 :            : 
     243                 :            :   public:
     244                 :            :     Query query;
     245                 :            :     const char * error;
     246                 :            :     unsigned flags;
     247                 :            : 
     248                 :            :     State(QueryParser::Internal * qpi_, unsigned flags_)
     249                 :            :         : qpi(qpi_), error(NULL), flags(flags_) { }
     250                 :            : 
     251                 :            :     string stem_term(const string &term) {
     252                 :            :         return qpi->stemmer(term);
     253                 :            :     }
     254                 :            : 
     255                 :            :     void add_to_stoplist(const Term * term) {
     256                 :            :         qpi->stoplist.push_back(term->name);
     257                 :            :     }
     258                 :            : 
     259                 :            :     void add_to_unstem(const string & term, const string & unstemmed) {
     260                 :            :         qpi->unstem.insert(make_pair(term, unstemmed));
     261                 :            :     }
     262                 :            : 
     263                 :            :     Term * range(const string &a, const string &b) {
     264                 :            :         for (auto i : qpi->rangeprocs) {
     265                 :            :             Xapian::Query range_query = (i.proc)->check_range(a, b);
     266                 :            :             Xapian::Query::op op = range_query.get_type();
     267                 :            :             switch (op) {
     268                 :            :                 case Xapian::Query::OP_INVALID:
     269                 :            :                     break;
     270                 :            :                 case Xapian::Query::OP_VALUE_RANGE:
     271                 :            :                 case Xapian::Query::OP_VALUE_GE:
     272                 :            :                 case Xapian::Query::OP_VALUE_LE:
     273                 :            :                     if (i.default_grouping) {
     274                 :            :                         Xapian::Internal::QueryValueBase * base =
     275                 :            :                             static_cast<Xapian::Internal::QueryValueBase*>(
     276                 :            :                                 range_query.internal.get());
     277                 :            :                         Xapian::valueno slot = base->get_slot();
     278                 :            :                         return new Term(range_query, str(slot));
     279                 :            :                     }
     280                 :            :                     // FALLTHRU
     281                 :            :                 case Xapian::Query::LEAF_TERM:
     282                 :            :                     return new Term(range_query, i.grouping);
     283                 :            :                 default:
     284                 :            :                     return new Term(range_query, string());
     285                 :            :             }
     286                 :            :         }
     287                 :            :         return NULL;
     288                 :            :     }
     289                 :            : 
     290                 :            :     Query::op default_op() const { return qpi->default_op; }
     291                 :            : 
     292                 :            :     bool is_stopword(const Term *term) const {
     293                 :            :         return qpi->stopper.get() && (*qpi->stopper)(term->name);
     294                 :            :     }
     295                 :            : 
     296                 :            :     Database get_database() const {
     297                 :            :         return qpi->db;
     298                 :            :     }
     299                 :            : 
     300                 :            :     const Stopper * get_stopper() const {
     301                 :            :         return qpi->stopper.get();
     302                 :            :     }
     303                 :            : 
     304                 :            :     size_t stoplist_size() const {
     305                 :            :         return qpi->stoplist.size();
     306                 :            :     }
     307                 :            : 
     308                 :            :     void stoplist_resize(size_t s) {
     309                 :            :         qpi->stoplist.resize(s);
     310                 :            :     }
     311                 :            : 
     312                 :            :     Xapian::termcount get_max_wildcard_expansion() const {
     313                 :            :         return qpi->max_wildcard_expansion;
     314                 :            :     }
     315                 :            : 
     316                 :            :     int get_max_wildcard_type() const {
     317                 :            :         return qpi->max_wildcard_type;
     318                 :            :     }
     319                 :            : 
     320                 :            :     unsigned get_min_wildcard_prefix_len() const {
     321                 :            :         return qpi->min_wildcard_prefix_len;
     322                 :            :     }
     323                 :            : 
     324                 :            :     Xapian::termcount get_max_partial_expansion() const {
     325                 :            :         return qpi->max_partial_expansion;
     326                 :            :     }
     327                 :            : 
     328                 :            :     int get_max_partial_type() const {
     329                 :            :         return qpi->max_partial_type;
     330                 :            :     }
     331                 :            : 
     332                 :            :     unsigned get_min_partial_prefix_len() const {
     333                 :            :         return qpi->min_partial_prefix_len;
     334                 :            :     }
     335                 :            : 
     336                 :            :     Xapian::termcount get_max_fuzzy_expansion() const {
     337                 :            :         return qpi->max_fuzzy_expansion;
     338                 :            :     }
     339                 :            : 
     340                 :            :     int get_max_fuzzy_type() const {
     341                 :            :         return qpi->max_fuzzy_type;
     342                 :            :     }
     343                 :            : };
     344                 :            : 
     345                 :            : string
     346                 :            : Term::make_term(const string & prefix) const
     347                 :            : {
     348                 :            :     string term;
     349                 :            :     if (stem != QueryParser::STEM_NONE && stem != QueryParser::STEM_ALL)
     350                 :            :         term += 'Z';
     351                 :            :     if (!prefix.empty()) {
     352                 :            :         term += prefix;
     353                 :            :         if (prefix_needs_colon(prefix, name[0])) term += ':';
     354                 :            :     }
     355                 :            :     if (stem != QueryParser::STEM_NONE) {
     356                 :            :         term += state->stem_term(name);
     357                 :            :     } else {
     358                 :            :         term += name;
     359                 :            :     }
     360                 :            : 
     361                 :            :     if (!unstemmed.empty())
     362                 :            :         state->add_to_unstem(term, unstemmed);
     363                 :            :     return term;
     364                 :            : }
     365                 :            : 
     366                 :            : // Iterator shim to allow building a synonym query from a TermIterator pair.
     367                 :            : class SynonymIterator {
     368                 :            :     Xapian::TermIterator i;
     369                 :            : 
     370                 :            :     Xapian::termpos pos;
     371                 :            : 
     372                 :            :     const Xapian::Query * first;
     373                 :            : 
     374                 :            :   public:
     375                 :            :     SynonymIterator(const Xapian::TermIterator & i_,
     376                 :            :                     Xapian::termpos pos_ = 0,
     377                 :            :                     const Xapian::Query * first_ = NULL)
     378                 :            :         : i(i_), pos(pos_), first(first_) { }
     379                 :            : 
     380                 :            :     SynonymIterator & operator++() {
     381                 :            :         if (first)
     382                 :            :             first = NULL;
     383                 :            :         else
     384                 :            :             ++i;
     385                 :            :         return *this;
     386                 :            :     }
     387                 :            : 
     388                 :            :     const Xapian::Query operator*() const {
     389                 :            :         if (first) return *first;
     390                 :            :         return Xapian::Query(*i, 1, pos);
     391                 :            :     }
     392                 :            : 
     393                 :            :     bool operator==(const SynonymIterator & o) const {
     394                 :            :         return i == o.i && first == o.first;
     395                 :            :     }
     396                 :            : 
     397                 :            :     bool operator!=(const SynonymIterator & o) const {
     398                 :            :         return !(*this == o);
     399                 :            :     }
     400                 :            : 
     401                 :            :     typedef std::input_iterator_tag iterator_category;
     402                 :            :     typedef Xapian::Query value_type;
     403                 :            :     typedef Xapian::termcount_diff difference_type;
     404                 :            :     typedef Xapian::Query * pointer;
     405                 :            :     typedef Xapian::Query & reference;
     406                 :            : };
     407                 :            : 
     408                 :            : Query
     409                 :            : Term::get_query_with_synonyms() const
     410                 :            : {
     411                 :            :     // Handle single-word synonyms with each prefix.
     412                 :            :     const list<string> & prefixes = field_info->prefixes;
     413                 :            :     if (prefixes.empty()) {
     414                 :            :         // FIXME: handle multiple here
     415                 :            :         Assert(!field_info->procs.empty());
     416                 :            :         return (**field_info->procs.begin())(name);
     417                 :            :     }
     418                 :            : 
     419                 :            :     Query q = get_query();
     420                 :            : 
     421                 :            :     list<string>::const_iterator piter;
     422                 :            :     for (piter = prefixes.begin(); piter != prefixes.end(); ++piter) {
     423                 :            :         // First try the unstemmed term:
     424                 :            :         string term;
     425                 :            :         if (!piter->empty()) {
     426                 :            :             term += *piter;
     427                 :            :             if (prefix_needs_colon(*piter, name[0])) term += ':';
     428                 :            :         }
     429                 :            :         term += name;
     430                 :            : 
     431                 :            :         Xapian::Database db = state->get_database();
     432                 :            :         Xapian::TermIterator syn = db.synonyms_begin(term);
     433                 :            :         Xapian::TermIterator end = db.synonyms_end(term);
     434                 :            :         if (syn == end && stem != QueryParser::STEM_NONE) {
     435                 :            :             // If that has no synonyms, try the stemmed form:
     436                 :            :             term = 'Z';
     437                 :            :             if (!piter->empty()) {
     438                 :            :                 term += *piter;
     439                 :            :                 if (prefix_needs_colon(*piter, name[0])) term += ':';
     440                 :            :             }
     441                 :            :             term += state->stem_term(name);
     442                 :            :             syn = db.synonyms_begin(term);
     443                 :            :             end = db.synonyms_end(term);
     444                 :            :         }
     445                 :            :         q = Query(q.OP_SYNONYM,
     446                 :            :                   SynonymIterator(syn, pos, &q),
     447                 :            :                   SynonymIterator(end));
     448                 :            :     }
     449                 :            :     return q;
     450                 :            : }
     451                 :            : 
     452                 :            : Query
     453                 :            : Term::get_query_with_auto_synonyms() const
     454                 :            : {
     455                 :            :     const unsigned MASK_ENABLE_AUTO_SYNONYMS =
     456                 :            :         QueryParser::FLAG_AUTO_SYNONYMS |
     457                 :            :         QueryParser::FLAG_AUTO_MULTIWORD_SYNONYMS;
     458                 :            :     if (state->flags & MASK_ENABLE_AUTO_SYNONYMS)
     459                 :            :         return get_query_with_synonyms();
     460                 :            : 
     461                 :            :     return get_query();
     462                 :            : }
     463                 :            : 
     464                 :            : static void
     465                 :            : add_to_query(Query *& q, Query::op op, Query * term)
     466                 :            : {
     467                 :            :     Assert(term);
     468                 :            :     if (q) {
     469                 :            :         if (op == Query::OP_OR) {
     470                 :            :             *q |= *term;
     471                 :            :         } else if (op == Query::OP_AND) {
     472                 :            :             *q &= *term;
     473                 :            :         } else {
     474                 :            :             *q = Query(op, *q, *term);
     475                 :            :         }
     476                 :            :         delete term;
     477                 :            :     } else {
     478                 :            :         q = term;
     479                 :            :     }
     480                 :            : }
     481                 :            : 
     482                 :            : static void
     483                 :            : add_to_query(Query *& q, Query::op op, const Query & term)
     484                 :            : {
     485                 :            :     if (q) {
     486                 :            :         if (op == Query::OP_OR) {
     487                 :            :             *q |= term;
     488                 :            :         } else if (op == Query::OP_AND) {
     489                 :            :             *q &= term;
     490                 :            :         } else {
     491                 :            :             *q = Query(op, *q, term);
     492                 :            :         }
     493                 :            :     } else {
     494                 :            :         q = new Query(term);
     495                 :            :     }
     496                 :            : }
     497                 :            : 
     498                 :            : Query
     499                 :            : Term::get_query() const
     500                 :            : {
     501                 :            :     const list<string> & prefixes = field_info->prefixes;
     502                 :            :     if (prefixes.empty()) {
     503                 :            :         // FIXME: handle multiple here
     504                 :            :         Assert(!field_info->procs.empty());
     505                 :            :         return (**field_info->procs.begin())(name);
     506                 :            :     }
     507                 :            :     list<string>::const_iterator piter = prefixes.begin();
     508                 :            :     Query q(make_term(*piter), 1, pos);
     509                 :            :     while (++piter != prefixes.end()) {
     510                 :            :         q |= Query(make_term(*piter), 1, pos);
     511                 :            :     }
     512                 :            :     return q;
     513                 :            : }
     514                 :            : 
     515                 :            : Query *
     516                 :            : Term::as_fuzzy_query(State* state_) const
     517                 :            : {
     518                 :            :     const list<string>& prefixes = field_info->prefixes;
     519                 :            :     Xapian::termcount max = state_->get_max_fuzzy_expansion();
     520                 :            :     int query_flags = state_->get_max_fuzzy_type();
     521                 :            :     vector<Query> subqs;
     522                 :            :     subqs.reserve(prefixes.size());
     523                 :            :     for (auto&& prefix : prefixes) {
     524                 :            :         // Combine with OP_OR, and apply OP_SYNONYM afterwards.
     525                 :            :         subqs.emplace_back(Query::OP_EDIT_DISTANCE,
     526                 :            :                            prefix + name,
     527                 :            :                            max,
     528                 :            :                            query_flags,
     529                 :            :                            Query::OP_OR,
     530                 :            :                            edit_distance,
     531                 :            :                            prefix.size());
     532                 :            :     }
     533                 :            :     Query* q = new Query(Query::OP_SYNONYM, subqs.begin(), subqs.end());
     534                 :            :     delete this;
     535                 :            :     return q;
     536                 :            : }
     537                 :            : 
     538                 :            : Query *
     539                 :            : Term::as_wildcarded_query(State * state_) const
     540                 :            : {
     541                 :            :     const list<string> & prefixes = field_info->prefixes;
     542                 :            :     list<string>::const_iterator piter;
     543                 :            :     Xapian::termcount max = state_->get_max_wildcard_expansion();
     544                 :            :     int query_flags = state_->get_max_wildcard_type();
     545                 :            :     if (state_->flags & QueryParser::FLAG_WILDCARD_SINGLE)
     546                 :            :         query_flags |= Query::WILDCARD_PATTERN_SINGLE;
     547                 :            :     if (state_->flags & QueryParser::FLAG_WILDCARD_MULTI)
     548                 :            :         query_flags |= Query::WILDCARD_PATTERN_MULTI;
     549                 :            :     vector<Query> subqs;
     550                 :            :     subqs.reserve(prefixes.size());
     551                 :            :     for (piter = prefixes.begin(); piter != prefixes.end(); ++piter) {
     552                 :            :         string root = *piter;
     553                 :            :         root += name;
     554                 :            :         // Combine with OP_OR, and apply OP_SYNONYM afterwards.
     555                 :            :         subqs.push_back(Query(Query::OP_WILDCARD, root, max, query_flags,
     556                 :            :                               Query::OP_OR));
     557                 :            :     }
     558                 :            :     Query * q = new Query(Query::OP_SYNONYM, subqs.begin(), subqs.end());
     559                 :            :     delete this;
     560                 :            :     return q;
     561                 :            : }
     562                 :            : 
     563                 :            : Query *
     564                 :            : Term::as_partial_query(State * state_) const
     565                 :            : {
     566                 :            :     Xapian::termcount max = state_->get_max_partial_expansion();
     567                 :            :     int max_type = state_->get_max_partial_type();
     568                 :            :     vector<Query> subqs_partial; // A synonym of all the partial terms.
     569                 :            :     vector<Query> subqs_full; // A synonym of all the full terms.
     570                 :            : 
     571                 :            :     const list<string> & prefixes = field_info->prefixes;
     572                 :            :     list<string>::const_iterator piter;
     573                 :            :     for (piter = prefixes.begin(); piter != prefixes.end(); ++piter) {
     574                 :            :         string root = *piter;
     575                 :            :         root += name;
     576                 :            :         // Combine with OP_OR, and apply OP_SYNONYM afterwards.
     577                 :            :         subqs_partial.push_back(Query(Query::OP_WILDCARD, root, max, max_type,
     578                 :            :                                       Query::OP_OR));
     579                 :            :         // Add the term, as it would normally be handled, as an alternative.
     580                 :            :         subqs_full.push_back(Query(make_term(*piter), 1, pos));
     581                 :            :     }
     582                 :            :     Query * q = new Query(Query::OP_OR,
     583                 :            :                           Query(Query::OP_SYNONYM,
     584                 :            :                                 subqs_partial.begin(), subqs_partial.end()),
     585                 :            :                           Query(Query::OP_SYNONYM,
     586                 :            :                                 subqs_full.begin(), subqs_full.end()));
     587                 :            :     delete this;
     588                 :            :     return q;
     589                 :            : }
     590                 :            : 
     591                 :            : Query *
     592                 :            : Term::as_cjk_query() const
     593                 :            : {
     594                 :            :     const list<string> & prefixes = field_info->prefixes;
     595                 :            :     Query *q;
     596                 :            : 
     597                 :            : #ifdef USE_ICU
     598                 :            :     if (state->flags & QueryParser::FLAG_CJK_WORDS) {
     599                 :            :         vector<Query> prefix_cjk;
     600                 :            :         list<string>::const_iterator piter;
     601                 :            : 
     602                 :            :         for (CJKWordIterator tk(name); tk != CJKWordIterator(); ++tk) {
     603                 :            :             const string& token = *tk;
     604                 :            :             for (piter = prefixes.begin(); piter != prefixes.end(); ++piter) {
     605                 :            :                 prefix_cjk.push_back(Query(*piter + token, 1, pos));
     606                 :            :             }
     607                 :            :         }
     608                 :            : 
     609                 :            :         q = new Query(Query::OP_AND, prefix_cjk.begin(), prefix_cjk.end());
     610                 :            : 
     611                 :            :         delete this;
     612                 :            :         return q;
     613                 :            :     }
     614                 :            : #endif
     615                 :            : 
     616                 :            :     vector<Query> prefix_subqs;
     617                 :            :     vector<Query> cjk_subqs;
     618                 :            :     list<string>::const_iterator piter;
     619                 :            : 
     620                 :            :     for (piter = prefixes.begin(); piter != prefixes.end(); ++piter) {
     621                 :            :         const string& prefix = *piter;
     622                 :            : 
     623                 :            :         for (CJKNgramIterator tk(name); tk != CJKNgramIterator(); ++tk) {
     624                 :            :             cjk_subqs.push_back(Query(prefix + *tk, 1, pos));
     625                 :            :         }
     626                 :            :         prefix_subqs.push_back(Query(Query::OP_AND,
     627                 :            :                                      cjk_subqs.begin(), cjk_subqs.end()));
     628                 :            :         cjk_subqs.clear();
     629                 :            :     }
     630                 :            :     q = new Query(Query::OP_OR, prefix_subqs.begin(), prefix_subqs.end());
     631                 :            : 
     632                 :            :     delete this;
     633                 :            :     return q;
     634                 :            : }
     635                 :            : 
     636                 :            : Query
     637                 :            : Term::as_range_query() const
     638                 :            : {
     639                 :            :     Query q = query;
     640                 :            :     delete this;
     641                 :            :     return q;
     642                 :            : }
     643                 :            : 
     644                 :            : inline bool
     645                 :            : is_phrase_generator(unsigned ch)
     646                 :            : {
     647                 :            :     // These characters generate a phrase search.
     648                 :            :     // Ordered mostly by frequency of calls to this function done when
     649                 :            :     // running the testcases in api_queryparser.cc.
     650                 :            :     return (ch && ch < 128 && strchr(".-/:\\@", ch) != NULL);
     651                 :            : }
     652                 :            : 
     653                 :            : inline bool
     654                 :            : is_stem_preventer(unsigned ch)
     655                 :            : {
     656                 :            :     return (ch && ch < 128 && strchr("(/\\@<>=*[{\"", ch) != NULL);
     657                 :            : }
     658                 :            : 
     659                 :            : inline bool
     660                 :            : should_stem(const string & term)
     661                 :            : {
     662                 :            :     const unsigned int SHOULD_STEM_MASK =
     663                 :            :         (1 << Unicode::LOWERCASE_LETTER) |
     664                 :            :         (1 << Unicode::TITLECASE_LETTER) |
     665                 :            :         (1 << Unicode::MODIFIER_LETTER) |
     666                 :            :         (1 << Unicode::OTHER_LETTER);
     667                 :            :     Utf8Iterator u(term);
     668                 :            :     return ((SHOULD_STEM_MASK >> Unicode::get_category(*u)) & 1);
     669                 :            : }
     670                 :            : 
     671                 :            : /** Value representing "ignore this" when returned by check_infix() or
     672                 :            :  *  check_infix_digit().
     673                 :            :  */
     674                 :            : const unsigned UNICODE_IGNORE = numeric_limits<unsigned>::max();
     675                 :            : 
     676                 :            : inline unsigned check_infix(unsigned ch) {
     677                 :            :     if (ch == '\'' || ch == '&' || ch == 0xb7 || ch == 0x5f4 || ch == 0x2027) {
     678                 :            :         // Unicode includes all these except '&' in its word boundary rules,
     679                 :            :         // as well as 0x2019 (which we handle below) and ':' (for Swedish
     680                 :            :         // apparently, but we ignore this for now as it's problematic in
     681                 :            :         // real world cases).
     682                 :            :         return ch;
     683                 :            :     }
     684                 :            :     if (ch >= 0x200b) {
     685                 :            :         // 0x2019 is Unicode apostrophe and single closing quote.
     686                 :            :         // 0x201b is Unicode single opening quote with the tail rising.
     687                 :            :         if (ch == 0x2019 || ch == 0x201b)
     688                 :            :             return '\'';
     689                 :            :         if (ch <= 0x200d || ch == 0x2060 || ch == 0xfeff)
     690                 :            :             return UNICODE_IGNORE;
     691                 :            :     }
     692                 :            :     return 0;
     693                 :            : }
     694                 :            : 
     695                 :            : inline unsigned check_infix_digit(unsigned ch) {
     696                 :            :     // This list of characters comes from Unicode's word identifying algorithm.
     697                 :            :     switch (ch) {
     698                 :            :         case ',':
     699                 :            :         case '.':
     700                 :            :         case ';':
     701                 :            :         case 0x037e: // GREEK QUESTION MARK
     702                 :            :         case 0x0589: // ARMENIAN FULL STOP
     703                 :            :         case 0x060D: // ARABIC DATE SEPARATOR
     704                 :            :         case 0x07F8: // NKO COMMA
     705                 :            :         case 0x2044: // FRACTION SLASH
     706                 :            :         case 0xFE10: // PRESENTATION FORM FOR VERTICAL COMMA
     707                 :            :         case 0xFE13: // PRESENTATION FORM FOR VERTICAL COLON
     708                 :            :         case 0xFE14: // PRESENTATION FORM FOR VERTICAL SEMICOLON
     709                 :            :             return ch;
     710                 :            :     }
     711                 :            :     if (ch >= 0x200b && (ch <= 0x200d || ch == 0x2060 || ch == 0xfeff))
     712                 :            :         return UNICODE_IGNORE;
     713                 :            :     return 0;
     714                 :            : }
     715                 :            : 
     716                 :            : // Prototype a function lemon generates, but which we want to call before that
     717                 :            : // in the generated source code file.
     718                 :            : struct yyParser;
     719                 :            : static void yy_parse_failed(yyParser *);
     720                 :            : 
     721                 :            : void
     722                 :            : QueryParser::Internal::add_prefix(const string &field, const string &prefix)
     723                 :            : {
     724                 :            :     map<string, FieldInfo>::iterator p = field_map.find(field);
     725                 :            :     if (p == field_map.end()) {
     726                 :            :         field_map.insert(make_pair(field, FieldInfo(NON_BOOLEAN, prefix)));
     727                 :            :     } else {
     728                 :            :         // Check that this is the same type of filter as the existing one(s).
     729                 :            :         if (p->second.type != NON_BOOLEAN) {
     730                 :            :             throw Xapian::InvalidOperationError("Can't use add_prefix() and add_boolean_prefix() on the same field name, or add_boolean_prefix() with different values of the 'exclusive' parameter");
     731                 :            :         }
     732                 :            :         if (!p->second.procs.empty())
     733                 :            :             throw Xapian::FeatureUnavailableError("Mixing FieldProcessor objects and string prefixes currently not supported");
     734                 :            :         p->second.prefixes.push_back(prefix);
     735                 :            :    }
     736                 :            : }
     737                 :            : 
     738                 :            : void
     739                 :            : QueryParser::Internal::add_prefix(const string &field, FieldProcessor *proc)
     740                 :            : {
     741                 :            :     map<string, FieldInfo>::iterator p = field_map.find(field);
     742                 :            :     if (p == field_map.end()) {
     743                 :            :         field_map.insert(make_pair(field, FieldInfo(NON_BOOLEAN, proc)));
     744                 :            :     } else {
     745                 :            :         // Check that this is the same type of filter as the existing one(s).
     746                 :            :         if (p->second.type != NON_BOOLEAN) {
     747                 :            :             throw Xapian::InvalidOperationError("Can't use add_prefix() and add_boolean_prefix() on the same field name, or add_boolean_prefix() with different values of the 'exclusive' parameter");
     748                 :            :         }
     749                 :            :         if (!p->second.prefixes.empty())
     750                 :            :             throw Xapian::FeatureUnavailableError("Mixing FieldProcessor objects and string prefixes currently not supported");
     751                 :            :         throw Xapian::FeatureUnavailableError("Multiple FieldProcessor objects for the same prefix currently not supported");
     752                 :            :         // p->second.procs.push_back(proc);
     753                 :            :    }
     754                 :            : }
     755                 :            : 
     756                 :            : void
     757                 :            : QueryParser::Internal::add_boolean_prefix(const string &field,
     758                 :            :                                           const string &prefix,
     759                 :            :                                           const string* grouping)
     760                 :            : {
     761                 :            :     // Don't allow the empty prefix to be set as boolean as it doesn't
     762                 :            :     // really make sense.
     763                 :            :     if (field.empty())
     764                 :            :         throw Xapian::UnimplementedError("Can't set the empty prefix to be a boolean filter");
     765                 :            :     if (!grouping) grouping = &field;
     766                 :            :     filter_type type = grouping->empty() ? BOOLEAN : BOOLEAN_EXCLUSIVE;
     767                 :            :     map<string, FieldInfo>::iterator p = field_map.find(field);
     768                 :            :     if (p == field_map.end()) {
     769                 :            :         field_map.insert(make_pair(field, FieldInfo(type, prefix, *grouping)));
     770                 :            :     } else {
     771                 :            :         // Check that this is the same type of filter as the existing one(s).
     772                 :            :         if (p->second.type != type) {
     773                 :            :             throw Xapian::InvalidOperationError("Can't use add_prefix() and add_boolean_prefix() on the same field name, or add_boolean_prefix() with different values of the 'exclusive' parameter"); // FIXME
     774                 :            :         }
     775                 :            :         if (!p->second.procs.empty())
     776                 :            :             throw Xapian::FeatureUnavailableError("Mixing FieldProcessor objects and string prefixes currently not supported");
     777                 :            :         p->second.prefixes.push_back(prefix); // FIXME grouping
     778                 :            :    }
     779                 :            : }
     780                 :            : 
     781                 :            : void
     782                 :            : QueryParser::Internal::add_boolean_prefix(const string &field,
     783                 :            :                                           FieldProcessor *proc,
     784                 :            :                                           const string* grouping)
     785                 :            : {
     786                 :            :     // Don't allow the empty prefix to be set as boolean as it doesn't
     787                 :            :     // really make sense.
     788                 :            :     if (field.empty())
     789                 :            :         throw Xapian::UnimplementedError("Can't set the empty prefix to be a boolean filter");
     790                 :            :     if (!grouping) grouping = &field;
     791                 :            :     filter_type type = grouping->empty() ? BOOLEAN : BOOLEAN_EXCLUSIVE;
     792                 :            :     map<string, FieldInfo>::iterator p = field_map.find(field);
     793                 :            :     if (p == field_map.end()) {
     794                 :            :         field_map.insert(make_pair(field, FieldInfo(type, proc, *grouping)));
     795                 :            :     } else {
     796                 :            :         // Check that this is the same type of filter as the existing one(s).
     797                 :            :         if (p->second.type != type) {
     798                 :            :             throw Xapian::InvalidOperationError("Can't use add_prefix() and add_boolean_prefix() on the same field name, or add_boolean_prefix() with different values of the 'exclusive' parameter"); // FIXME
     799                 :            :         }
     800                 :            :         if (!p->second.prefixes.empty())
     801                 :            :             throw Xapian::FeatureUnavailableError("Mixing FieldProcessor objects and string prefixes currently not supported");
     802                 :            :         throw Xapian::FeatureUnavailableError("Multiple FieldProcessor objects for the same prefix currently not supported");
     803                 :            :         // p->second.procs.push_back(proc);
     804                 :            :    }
     805                 :            : }
     806                 :            : 
     807                 :            : inline bool
     808                 :            : is_extended_wildcard(unsigned ch, unsigned flags)
     809                 :            : {
     810                 :            :     if (ch == '*') return (flags & QueryParser::FLAG_WILDCARD_MULTI);
     811                 :            :     if (ch == '?') return (flags & QueryParser::FLAG_WILDCARD_SINGLE);
     812                 :            :     return false;
     813                 :            : }
     814                 :            : 
     815                 :            : string
     816                 :            : QueryParser::Internal::parse_term(Utf8Iterator &it, const Utf8Iterator &end,
     817                 :            :                                   bool cjk_enable, unsigned flags,
     818                 :            :                                   bool & is_cjk_term, bool &was_acronym,
     819                 :            :                                   size_t& first_wildcard,
     820                 :            :                                   size_t& char_count,
     821                 :            :                                   unsigned& edit_distance)
     822                 :            : {
     823                 :            :     string term;
     824                 :            :     char_count = 0;
     825                 :            :     // Look for initials separated by '.' (e.g. P.T.O., U.N.C.L.E).
     826                 :            :     // Don't worry if there's a trailing '.' or not.
     827                 :            :     if (U_isupper(*it)) {
     828                 :            :         string t;
     829                 :            :         Utf8Iterator p = it;
     830                 :            :         do {
     831                 :            :             Unicode::append_utf8(t, *p++);
     832                 :            :             ++char_count;
     833                 :            :         } while (p != end && *p == '.' && ++p != end && U_isupper(*p));
     834                 :            :         // One letter does not make an acronym!  If we handled a single
     835                 :            :         // uppercase letter here, we wouldn't catch M&S below.
     836                 :            :         if (t.length() > 1) {
     837                 :            :             // Check there's not a (lower case) letter or digit
     838                 :            :             // immediately after it.
     839                 :            :             // FIXME: should I.B.M..P.T.O be a range search?
     840                 :            :             if (p == end || !is_wordchar(*p)) {
     841                 :            :                 it = p;
     842                 :            :                 swap(term, t);
     843                 :            :             } else {
     844                 :            :                 char_count = 0;
     845                 :            :             }
     846                 :            :         }
     847                 :            :     }
     848                 :            :     was_acronym = !term.empty();
     849                 :            : 
     850                 :            :     if (cjk_enable && term.empty() && CJK::codepoint_is_cjk(*it)) {
     851                 :            :         const char* cjk = it.raw();
     852                 :            :         char_count = CJK::get_cjk(it);
     853                 :            :         term.assign(cjk, it.raw() - cjk);
     854                 :            :         is_cjk_term = true;
     855                 :            :     }
     856                 :            : 
     857                 :            :     if (term.empty()) {
     858                 :            :         unsigned prevch = *it;
     859                 :            :         if (first_wildcard == term.npos &&
     860                 :            :             is_extended_wildcard(prevch, flags)) {
     861                 :            :             // Leading wildcard.
     862                 :            :             first_wildcard = 0;
     863                 :            :         }
     864                 :            :         Unicode::append_utf8(term, prevch);
     865                 :            :         char_count = 1;
     866                 :            :         while (++it != end) {
     867                 :            :             if (cjk_enable && CJK::codepoint_is_cjk(*it)) break;
     868                 :            :             unsigned ch = *it;
     869                 :            :             if (is_extended_wildcard(ch, flags)) {
     870                 :            :                 if (first_wildcard == term.npos) {
     871                 :            :                     first_wildcard = char_count;
     872                 :            :                 }
     873                 :            :             } else if (!is_wordchar(ch)) {
     874                 :            :                 // Treat a single embedded '&' or "'" or similar as a word
     875                 :            :                 // character (e.g. AT&T, Fred's).  Also, normalise
     876                 :            :                 // apostrophes to ASCII apostrophe.
     877                 :            :                 Utf8Iterator p = it;
     878                 :            :                 ++p;
     879                 :            :                 if (p == end) break;
     880                 :            :                 unsigned nextch = *p;
     881                 :            :                 if (is_extended_wildcard(nextch, flags)) {
     882                 :            :                     // A wildcard follows, which could expand to a digit or a non-digit.
     883                 :            :                     unsigned ch_orig = ch;
     884                 :            :                     ch = check_infix(ch);
     885                 :            :                     if (!ch && is_digit(prevch))
     886                 :            :                         ch = check_infix_digit(ch_orig);
     887                 :            :                     if (!ch)
     888                 :            :                         break;
     889                 :            :                 } else {
     890                 :            :                     if (!is_wordchar(nextch)) break;
     891                 :            :                 }
     892                 :            :                 if (is_digit(prevch) && is_digit(nextch)) {
     893                 :            :                     ch = check_infix_digit(ch);
     894                 :            :                 } else {
     895                 :            :                     ch = check_infix(ch);
     896                 :            :                 }
     897                 :            :                 if (!ch) break;
     898                 :            :                 if (ch == UNICODE_IGNORE)
     899                 :            :                     continue;
     900                 :            :             }
     901                 :            :             Unicode::append_utf8(term, ch);
     902                 :            :             ++char_count;
     903                 :            :             prevch = ch;
     904                 :            :         }
     905                 :            :         if (it != end && is_suffix(*it)) {
     906                 :            :             string suff_term = term;
     907                 :            :             Utf8Iterator p = it;
     908                 :            :             // Keep trailing + (e.g. C++, Na+) or # (e.g. C#).
     909                 :            :             do {
     910                 :            :                 // Assumes is_suffix() only matches ASCII.
     911                 :            :                 if (suff_term.size() - term.size() == 3) {
     912                 :            :                     suff_term.resize(0);
     913                 :            :                     break;
     914                 :            :                 }
     915                 :            :                 suff_term += *p;
     916                 :            :             } while (is_suffix(*++p));
     917                 :            :             if (!suff_term.empty() && (p == end || !is_wordchar(*p))) {
     918                 :            :                 // If the suffixed term doesn't exist, check that the
     919                 :            :                 // non-suffixed term does.  This also takes care of
     920                 :            :                 // the case when QueryParser::set_database() hasn't
     921                 :            :                 // been called.
     922                 :            :                 bool use_suff_term = false;
     923                 :            :                 string lc = Unicode::tolower(suff_term);
     924                 :            :                 if (db.term_exists(lc)) {
     925                 :            :                     use_suff_term = true;
     926                 :            :                 } else {
     927                 :            :                     lc = Unicode::tolower(term);
     928                 :            :                     if (!db.term_exists(lc)) use_suff_term = true;
     929                 :            :                 }
     930                 :            :                 if (use_suff_term) {
     931                 :            :                     // Assumes is_suffix() only matches ASCII.
     932                 :            :                     char_count += (suff_term.size() - term.size());
     933                 :            :                     term = suff_term;
     934                 :            :                     it = p;
     935                 :            :                 }
     936                 :            :             }
     937                 :            :         }
     938                 :            :         if (first_wildcard == term.npos &&
     939                 :            :             (flags & QueryParser::FLAG_WILDCARD)) {
     940                 :            :             // Check for right-truncation.
     941                 :            :             if (it != end && *it == '*') {
     942                 :            :                 ++it;
     943                 :            :                 first_wildcard = char_count;
     944                 :            :             }
     945                 :            :         }
     946                 :            :         if (it != end &&
     947                 :            :             (flags & QueryParser::FLAG_FUZZY) &&
     948                 :            :             // Not a wildcard.
     949                 :            :             first_wildcard == string::npos &&
     950                 :            :             *it == '~') {
     951                 :            :             Utf8Iterator p = it;
     952                 :            :             ++p;
     953                 :            :             unsigned ch = *p;
     954                 :            :             if (p == end || is_whitespace(ch) || ch == ')') {
     955                 :            :                 it = p;
     956                 :            :                 edit_distance = DEFAULT_EDIT_DISTANCE;
     957                 :            :             } else if (U_isdigit(ch)) {
     958                 :            :                 unsigned distance = ch - '0';
     959                 :            :                 while (++p != end && U_isdigit(*p)) {
     960                 :            :                     distance = distance * 10 + (*p - '0');
     961                 :            :                 }
     962                 :            :                 if (p != end && *p == '.') {
     963                 :            :                     if (distance == 0) goto fractional;
     964                 :            :                     // Ignore the fractional part on e.g. foo~12.5
     965                 :            :                     while (++p != end && U_isdigit(*p)) { }
     966                 :            :                 }
     967                 :            :                 if (p == end || is_whitespace(ch) || ch == ')') {
     968                 :            :                     it = p;
     969                 :            :                     edit_distance = distance;
     970                 :            :                 }
     971                 :            :             } else if (ch == '.') {
     972                 :            : fractional:
     973                 :            :                 double fraction = 0.0;
     974                 :            :                 double digit = 0.1;
     975                 :            :                 while (++p != end && U_isdigit(*p)) {
     976                 :            :                     fraction += digit * (*p - '0');
     977                 :            :                     digit *= 0.1;
     978                 :            :                 }
     979                 :            :                 if (p == end || is_whitespace(ch) || ch == ')') {
     980                 :            :                     it = p;
     981                 :            :                     unsigned codepoints = 0;
     982                 :            :                     for (Utf8Iterator u8(term); u8 != Utf8Iterator(); ++u8) {
     983                 :            :                         ++codepoints;
     984                 :            :                     }
     985                 :            :                     edit_distance = unsigned(codepoints * fraction);
     986                 :            :                 }
     987                 :            :             }
     988                 :            :         }
     989                 :            :     }
     990                 :            :     return term;
     991                 :            : }
     992                 :            : 
     993                 :            : #line 1616 "queryparser/queryparser.lemony"
     994                 :            : 
     995                 :            : 
     996                 :            : struct ProbQuery {
     997                 :            :     Query* query = NULL;
     998                 :            :     Query* love = NULL;
     999                 :            :     Query* hate = NULL;
    1000                 :            :     // filter is a map from prefix to a query for that prefix.  Queries with
    1001                 :            :     // the same prefix are combined with OR, and the results of this are
    1002                 :            :     // combined with AND to get the full filter.
    1003                 :            :     map<string, Query> filter;
    1004                 :            : 
    1005                 :            :     ProbQuery() {}
    1006                 :            : 
    1007                 :            :     explicit
    1008                 :            :     ProbQuery(Query* query_) : query(query_) {}
    1009                 :            : 
    1010                 :            :     ~ProbQuery() {
    1011                 :            :         delete query;
    1012                 :            :         delete love;
    1013                 :            :         delete hate;
    1014                 :            :     }
    1015                 :            : 
    1016                 :            :     void add_filter(const string& grouping, const Query & q) {
    1017                 :            :         filter[grouping] = q;
    1018                 :            :     }
    1019                 :            : 
    1020                 :            :     void append_filter(const string& grouping, const Query & qnew) {
    1021                 :            :         auto it = filter.find(grouping);
    1022                 :            :         if (it == filter.end()) {
    1023                 :            :             filter.insert(make_pair(grouping, qnew));
    1024                 :            :         } else {
    1025                 :            :             Query & q = it->second;
    1026                 :            :             // We OR multiple filters with the same prefix if they're
    1027                 :            :             // exclusive, otherwise we AND them.
    1028                 :            :             bool exclusive = !grouping.empty();
    1029                 :            :             if (exclusive) {
    1030                 :            :                 q |= qnew;
    1031                 :            :             } else {
    1032                 :            :                 q &= qnew;
    1033                 :            :             }
    1034                 :            :         }
    1035                 :            :     }
    1036                 :            : 
    1037                 :            :     void add_filter_range(const string& grouping, const Query & range) {
    1038                 :            :         filter[grouping] = range;
    1039                 :            :     }
    1040                 :            : 
    1041                 :            :     void append_filter_range(const string& grouping, const Query & range) {
    1042                 :            :         Query & q = filter[grouping];
    1043                 :            :         q |= range;
    1044                 :            :     }
    1045                 :            : 
    1046                 :            :     Query merge_filters() const {
    1047                 :            :         auto i = filter.begin();
    1048                 :            :         Assert(i != filter.end());
    1049                 :            :         Query q = i->second;
    1050                 :            :         while (++i != filter.end()) {
    1051                 :            :             q &= i->second;
    1052                 :            :         }
    1053                 :            :         return q;
    1054                 :            :     }
    1055                 :            : };
    1056                 :            : 
    1057                 :            : /// A group of terms separated only by whitespace.
    1058                 :            : class TermGroup {
    1059                 :            :     vector<Term *> terms;
    1060                 :            : 
    1061                 :            :     /** Controls how to handle a group where all terms are stopwords.
    1062                 :            :      *
    1063                 :            :      *  If true, then as_group() returns NULL.  If false, then the
    1064                 :            :      *  stopword status of the terms is ignored.
    1065                 :            :      */
    1066                 :            :     bool empty_ok;
    1067                 :            : 
    1068                 :            :     TermGroup(Term* t1, Term* t2) : empty_ok(false) {
    1069                 :            :         add_term(t1);
    1070                 :            :         add_term(t2);
    1071                 :            :     }
    1072                 :            : 
    1073                 :            :   public:
    1074                 :            :     /// Factory function - ensures heap allocation.
    1075                 :            :     static TermGroup* create(Term* t1, Term* t2) {
    1076                 :            :         return new TermGroup(t1, t2);
    1077                 :            :     }
    1078                 :            : 
    1079                 :            :     ~TermGroup() {
    1080                 :            :         for (auto&& t : terms) {
    1081                 :            :             delete t;
    1082                 :            :         }
    1083                 :            :     }
    1084                 :            : 
    1085                 :            :     /// Add a Term object to this TermGroup object.
    1086                 :            :     void add_term(Term * term) {
    1087                 :            :         terms.push_back(term);
    1088                 :            :     }
    1089                 :            : 
    1090                 :            :     /// Set the empty_ok flag.
    1091                 :            :     void set_empty_ok() { empty_ok = true; }
    1092                 :            : 
    1093                 :            :     /// Convert to a Xapian::Query * using default_op.
    1094                 :            :     Query * as_group(State *state) const;
    1095                 :            : };
    1096                 :            : 
    1097                 :            : Query *
    1098                 :            : TermGroup::as_group(State *state) const
    1099                 :            : {
    1100                 :            :     const Xapian::Stopper * stopper = state->get_stopper();
    1101                 :            :     size_t stoplist_size = state->stoplist_size();
    1102                 :            :     bool default_op_is_positional = is_positional(state->default_op());
    1103                 :            : reprocess:
    1104                 :            :     Query::op default_op = state->default_op();
    1105                 :            :     vector<Query> subqs;
    1106                 :            :     subqs.reserve(terms.size());
    1107                 :            :     if (state->flags & QueryParser::FLAG_AUTO_MULTIWORD_SYNONYMS) {
    1108                 :            :         // Check for multi-word synonyms.
    1109                 :            :         Database db = state->get_database();
    1110                 :            : 
    1111                 :            :         string key;
    1112                 :            :         vector<Term*>::const_iterator begin = terms.begin();
    1113                 :            :         vector<Term*>::const_iterator i = begin;
    1114                 :            :         while (i != terms.end()) {
    1115                 :            :             TermIterator synkey(db.synonym_keys_begin((*i)->name));
    1116                 :            :             TermIterator synend(db.synonym_keys_end((*i)->name));
    1117                 :            :             if (synkey == synend) {
    1118                 :            :                 // No multi-synonym matches.
    1119                 :            :                 if (stopper && (*stopper)((*i)->name)) {
    1120                 :            :                     state->add_to_stoplist(*i);
    1121                 :            :                 } else {
    1122                 :            :                     if (default_op_is_positional)
    1123                 :            :                         (*i)->need_positions();
    1124                 :            :                     subqs.push_back((*i)->get_query_with_auto_synonyms());
    1125                 :            :                 }
    1126                 :            :                 begin = ++i;
    1127                 :            :                 continue;
    1128                 :            :             }
    1129                 :            :             key.resize(0);
    1130                 :            :             while (i != terms.end()) {
    1131                 :            :                 if (!key.empty()) key += ' ';
    1132                 :            :                 key += (*i)->name;
    1133                 :            :                 ++i;
    1134                 :            :                 synkey.skip_to(key);
    1135                 :            :                 if (synkey == synend || !startswith(*synkey, key)) break;
    1136                 :            :             }
    1137                 :            :             // Greedily try to match as many consecutive words as possible.
    1138                 :            :             TermIterator syn, end;
    1139                 :            :             while (true) {
    1140                 :            :                 syn = db.synonyms_begin(key);
    1141                 :            :                 end = db.synonyms_end(key);
    1142                 :            :                 if (syn != end) break;
    1143                 :            :                 if (--i == begin) break;
    1144                 :            :                 key.resize(key.size() - (*i)->name.size() - 1);
    1145                 :            :             }
    1146                 :            :             if (i == begin) {
    1147                 :            :                 // No multi-synonym matches.
    1148                 :            :                 if (stopper && (*stopper)((*i)->name)) {
    1149                 :            :                     state->add_to_stoplist(*i);
    1150                 :            :                 } else {
    1151                 :            :                     if (default_op_is_positional)
    1152                 :            :                         (*i)->need_positions();
    1153                 :            :                     subqs.push_back((*i)->get_query_with_auto_synonyms());
    1154                 :            :                 }
    1155                 :            :                 begin = ++i;
    1156                 :            :                 continue;
    1157                 :            :             }
    1158                 :            : 
    1159                 :            :             vector<Query> subqs2;
    1160                 :            :             vector<Term*>::const_iterator j;
    1161                 :            :             for (j = begin; j != i; ++j) {
    1162                 :            :                 if (stopper && (*stopper)((*j)->name)) {
    1163                 :            :                     state->add_to_stoplist(*j);
    1164                 :            :                 } else {
    1165                 :            :                     if (default_op_is_positional)
    1166                 :            :                         (*i)->need_positions();
    1167                 :            :                     subqs2.push_back((*j)->get_query());
    1168                 :            :                 }
    1169                 :            :             }
    1170                 :            :             Query q_original_terms;
    1171                 :            :             if (default_op_is_positional) {
    1172                 :            :                 q_original_terms = Query(default_op,
    1173                 :            :                                          subqs2.begin(), subqs2.end(),
    1174                 :            :                                          subqs2.size() + 9);
    1175                 :            :             } else {
    1176                 :            :                 q_original_terms = Query(default_op,
    1177                 :            :                                          subqs2.begin(), subqs2.end());
    1178                 :            :             }
    1179                 :            :             subqs2.clear();
    1180                 :            : 
    1181                 :            :             // Use the position of the first term for the synonyms.
    1182                 :            :             Query q(Query::OP_SYNONYM,
    1183                 :            :                     SynonymIterator(syn, (*begin)->pos, &q_original_terms),
    1184                 :            :                     SynonymIterator(end));
    1185                 :            :             subqs.push_back(q);
    1186                 :            : 
    1187                 :            :             begin = i;
    1188                 :            :         }
    1189                 :            :     } else {
    1190                 :            :         vector<Term*>::const_iterator i;
    1191                 :            :         for (i = terms.begin(); i != terms.end(); ++i) {
    1192                 :            :             if (stopper && (*stopper)((*i)->name)) {
    1193                 :            :                 state->add_to_stoplist(*i);
    1194                 :            :             } else {
    1195                 :            :                 if (default_op_is_positional)
    1196                 :            :                     (*i)->need_positions();
    1197                 :            :                 subqs.push_back((*i)->get_query_with_auto_synonyms());
    1198                 :            :             }
    1199                 :            :         }
    1200                 :            :     }
    1201                 :            : 
    1202                 :            :     if (!empty_ok && stopper && subqs.empty() &&
    1203                 :            :         stoplist_size < state->stoplist_size()) {
    1204                 :            :         // This group is all stopwords, so roll-back, disable stopper
    1205                 :            :         // temporarily, and reprocess this group.
    1206                 :            :         state->stoplist_resize(stoplist_size);
    1207                 :            :         stopper = NULL;
    1208                 :            :         goto reprocess;
    1209                 :            :     }
    1210                 :            : 
    1211                 :            :     Query * q = NULL;
    1212                 :            :     if (!subqs.empty()) {
    1213                 :            :         if (default_op_is_positional) {
    1214                 :            :             q = new Query(default_op, subqs.begin(), subqs.end(),
    1215                 :            :                              subqs.size() + 9);
    1216                 :            :         } else {
    1217                 :            :             q = new Query(default_op, subqs.begin(), subqs.end());
    1218                 :            :         }
    1219                 :            :     }
    1220                 :            :     delete this;
    1221                 :            :     return q;
    1222                 :            : }
    1223                 :            : 
    1224                 :            : /// Some terms which form a positional sub-query.
    1225                 :            : class Terms {
    1226                 :            :     vector<Term *> terms;
    1227                 :            :     size_t window;
    1228                 :            : 
    1229                 :            :     /** Keep track of whether the terms added all have the same list of
    1230                 :            :      *  prefixes.  If so, we'll build a set of phrases, one using each prefix.
    1231                 :            :      *  This works around the limitation that a phrase cannot have multiple
    1232                 :            :      *  components which are "OR" combinations of terms, but is also probably
    1233                 :            :      *  what users expect: i.e., if a user specifies a phrase in a field, and
    1234                 :            :      *  that field maps to multiple prefixes, the user probably wants a phrase
    1235                 :            :      *  returned with all terms having one of those prefixes, rather than a
    1236                 :            :      *  phrase comprised of terms with differing prefixes.
    1237                 :            :      */
    1238                 :            :     bool uniform_prefixes;
    1239                 :            : 
    1240                 :            :     /** The list of prefixes of the terms added.
    1241                 :            :      *  This will be NULL if the terms have different prefixes.
    1242                 :            :      */
    1243                 :            :     const list<string> * prefixes;
    1244                 :            : 
    1245                 :            :     /// Convert to a query using the given operator and window size.
    1246                 :            :     Query * as_opwindow_query(Query::op op, Xapian::termcount w_delta) const {
    1247                 :            :         Query * q = NULL;
    1248                 :            :         size_t n_terms = terms.size();
    1249                 :            :         Xapian::termcount w = w_delta + terms.size();
    1250                 :            :         if (uniform_prefixes) {
    1251                 :            :             if (prefixes) {
    1252                 :            :                 list<string>::const_iterator piter;
    1253                 :            :                 for (piter = prefixes->begin(); piter != prefixes->end(); ++piter) {
    1254                 :            :                     vector<Query> subqs;
    1255                 :            :                     subqs.reserve(n_terms);
    1256                 :            :                     vector<Term *>::const_iterator titer;
    1257                 :            :                     for (titer = terms.begin(); titer != terms.end(); ++titer) {
    1258                 :            :                         Term * t = *titer;
    1259                 :            :                         subqs.push_back(Query(t->make_term(*piter), 1, t->pos));
    1260                 :            :                     }
    1261                 :            :                     add_to_query(q, Query::OP_OR,
    1262                 :            :                                  Query(op, subqs.begin(), subqs.end(), w));
    1263                 :            :                 }
    1264                 :            :             }
    1265                 :            :         } else {
    1266                 :            :             vector<Query> subqs;
    1267                 :            :             subqs.reserve(n_terms);
    1268                 :            :             vector<Term *>::const_iterator titer;
    1269                 :            :             for (titer = terms.begin(); titer != terms.end(); ++titer) {
    1270                 :            :                 subqs.push_back((*titer)->get_query());
    1271                 :            :             }
    1272                 :            :             q = new Query(op, subqs.begin(), subqs.end(), w);
    1273                 :            :         }
    1274                 :            : 
    1275                 :            :         delete this;
    1276                 :            :         return q;
    1277                 :            :     }
    1278                 :            : 
    1279                 :            :     Terms() : window(0), uniform_prefixes(true), prefixes(NULL) { }
    1280                 :            : 
    1281                 :            :   public:
    1282                 :            :     /// Factory function - ensures heap allocation.
    1283                 :            :     static Terms* create() {
    1284                 :            :         return new Terms();
    1285                 :            :     }
    1286                 :            : 
    1287                 :            :     ~Terms() {
    1288                 :            :         for (auto&& t : terms) {
    1289                 :            :             delete t;
    1290                 :            :         }
    1291                 :            :     }
    1292                 :            : 
    1293                 :            :     /// Add an unstemmed Term object to this Terms object.
    1294                 :            :     void add_positional_term(Term * term) {
    1295                 :            :         const list<string> & term_prefixes = term->field_info->prefixes;
    1296                 :            :         if (terms.empty()) {
    1297                 :            :             prefixes = &term_prefixes;
    1298                 :            :         } else if (uniform_prefixes && prefixes != &term_prefixes) {
    1299                 :            :             if (*prefixes != term_prefixes)  {
    1300                 :            :                 prefixes = NULL;
    1301                 :            :                 uniform_prefixes = false;
    1302                 :            :             }
    1303                 :            :         }
    1304                 :            :         term->need_positions();
    1305                 :            :         terms.push_back(term);
    1306                 :            :     }
    1307                 :            : 
    1308                 :            :     void adjust_window(size_t alternative_window) {
    1309                 :            :         if (alternative_window > window) window = alternative_window;
    1310                 :            :     }
    1311                 :            : 
    1312                 :            :     /// Convert to a Xapian::Query * using adjacent OP_PHRASE.
    1313                 :            :     Query * as_phrase_query() const {
    1314                 :            :         return as_opwindow_query(Query::OP_PHRASE, 0);
    1315                 :            :     }
    1316                 :            : 
    1317                 :            :     /// Convert to a Xapian::Query * using OP_NEAR.
    1318                 :            :     Query * as_near_query() const {
    1319                 :            :         // The common meaning of 'a NEAR b' is "a within 10 terms of b", which
    1320                 :            :         // means a window size of 11.  For more than 2 terms, we just add one
    1321                 :            :         // to the window size for each extra term.
    1322                 :            :         size_t w = window;
    1323                 :            :         if (w == 0) w = 10;
    1324                 :            :         return as_opwindow_query(Query::OP_NEAR, w - 1);
    1325                 :            :     }
    1326                 :            : 
    1327                 :            :     /// Convert to a Xapian::Query * using OP_PHRASE to implement ADJ.
    1328                 :            :     Query * as_adj_query() const {
    1329                 :            :         // The common meaning of 'a ADJ b' is "a at most 10 terms before b",
    1330                 :            :         // which means a window size of 11.  For more than 2 terms, we just add
    1331                 :            :         // one to the window size for each extra term.
    1332                 :            :         size_t w = window;
    1333                 :            :         if (w == 0) w = 10;
    1334                 :            :         return as_opwindow_query(Query::OP_PHRASE, w - 1);
    1335                 :            :     }
    1336                 :            : };
    1337                 :            : 
    1338                 :            : void
    1339                 :            : Term::as_positional_cjk_term(Terms * terms) const
    1340                 :            : {
    1341                 :            : #ifdef USE_ICU
    1342                 :            :     if (state->flags & QueryParser::FLAG_CJK_WORDS) {
    1343                 :            :         for (CJKWordIterator tk(name); tk != CJKWordIterator(); ++tk) {
    1344                 :            :             const string& t = *tk;
    1345                 :            :             Term * c = new Term(state, t, field_info, unstemmed, stem, pos);
    1346                 :            :             terms->add_positional_term(c);
    1347                 :            :         }
    1348                 :            :         delete this;
    1349                 :            :         return;
    1350                 :            :     }
    1351                 :            : #endif
    1352                 :            :     // Add each individual CJK character to the phrase.
    1353                 :            :     string t;
    1354                 :            :     for (Utf8Iterator it(name); it != Utf8Iterator(); ++it) {
    1355                 :            :         Unicode::append_utf8(t, *it);
    1356                 :            :         Term * c = new Term(state, t, field_info, unstemmed, stem, pos);
    1357                 :            :         terms->add_positional_term(c);
    1358                 :            :         t.resize(0);
    1359                 :            :     }
    1360                 :            : 
    1361                 :            :     // FIXME: we want to add the n-grams as filters too for efficiency.
    1362                 :            : 
    1363                 :            :     delete this;
    1364                 :            : }
    1365                 :            : 
    1366                 :            : // Helper macro to check for missing arguments to a boolean operator.
    1367                 :            : #define VET_BOOL_ARGS(A, B, OP_TXT) \
    1368                 :            :     do {\
    1369                 :            :         if (!A || !B) {\
    1370                 :            :             state->error = "Syntax: <expression> " OP_TXT " <expression>";\
    1371                 :            :             yy_parse_failed(yypParser);\
    1372                 :            :             return;\
    1373                 :            :         }\
    1374                 :            :     } while (0)
    1375                 :            : 
    1376                 :            : #line 1377 "queryparser/queryparser_internal.cc"
    1377                 :            : /**************** End of %include directives **********************************/
    1378                 :            : /* These constants specify the various numeric values for terminal symbols
    1379                 :            : ** in a format understandable to "makeheaders".  This section is blank unless
    1380                 :            : ** "lemon" is run with the "-m" command-line option.
    1381                 :            : ***************** Begin makeheaders token definitions *************************/
    1382                 :            : /**************** End makeheaders token definitions ***************************/
    1383                 :            : 
    1384                 :            : /* The next section is a series of control #defines.
    1385                 :            : ** various aspects of the generated parser.
    1386                 :            : **    YYCODETYPE         is the data type used to store the integer codes
    1387                 :            : **                       that represent terminal and non-terminal symbols.
    1388                 :            : **                       "unsigned char" is used if there are fewer than
    1389                 :            : **                       256 symbols.  Larger types otherwise.
    1390                 :            : **    YYNOCODE           is a number of type YYCODETYPE that is not used for
    1391                 :            : **                       any terminal or nonterminal symbol.
    1392                 :            : **    YYFALLBACK         If defined, this indicates that one or more tokens
    1393                 :            : **                       (also known as: "terminal symbols") have fall-back
    1394                 :            : **                       values which should be used if the original symbol
    1395                 :            : **                       would not parse.  This permits keywords to sometimes
    1396                 :            : **                       be used as identifiers, for example.
    1397                 :            : **    YYACTIONTYPE       is the data type used for "action codes" - numbers
    1398                 :            : **                       that indicate what to do in response to the next
    1399                 :            : **                       token.
    1400                 :            : **    ParseTOKENTYPE     is the data type used for minor type for terminal
    1401                 :            : **                       symbols.  Background: A "minor type" is a semantic
    1402                 :            : **                       value associated with a terminal or non-terminal
    1403                 :            : **                       symbols.  For example, for an "ID" terminal symbol,
    1404                 :            : **                       the minor type might be the name of the identifier.
    1405                 :            : **                       Each non-terminal can have a different minor type.
    1406                 :            : **                       Terminal symbols all have the same minor type, though.
    1407                 :            : **                       This macros defines the minor type for terminal 
    1408                 :            : **                       symbols.
    1409                 :            : **    YYMINORTYPE        is the data type used for all minor types.
    1410                 :            : **                       This is typically a union of many types, one of
    1411                 :            : **                       which is ParseTOKENTYPE.  The entry in the union
    1412                 :            : **                       for terminal symbols is called "yy0".
    1413                 :            : **    YYSTACKDEPTH       is the maximum depth of the parser's stack.  If
    1414                 :            : **                       zero the stack is dynamically sized using realloc()
    1415                 :            : **    ParseARG_SDECL     A static variable declaration for the %extra_argument
    1416                 :            : **    ParseARG_PDECL     A parameter declaration for the %extra_argument
    1417                 :            : **    ParseARG_STORE     Code to store %extra_argument into yypParser
    1418                 :            : **    ParseARG_FETCH     Code to extract %extra_argument from yypParser
    1419                 :            : **    YYERRORSYMBOL      is the code number of the error symbol.  If not
    1420                 :            : **                       defined, then do no error processing.
    1421                 :            : **    YYNSTATE           the combined number of states.
    1422                 :            : **    YYNRULE            the number of rules in the grammar
    1423                 :            : **    YYNTOKEN           Number of terminal symbols
    1424                 :            : **    YY_MAX_SHIFT       Maximum value for shift actions
    1425                 :            : **    YY_MIN_SHIFTREDUCE Minimum value for shift-reduce actions
    1426                 :            : **    YY_MAX_SHIFTREDUCE Maximum value for shift-reduce actions
    1427                 :            : **    YY_ERROR_ACTION    The yy_action[] code for syntax error
    1428                 :            : **    YY_ACCEPT_ACTION   The yy_action[] code for accept
    1429                 :            : **    YY_NO_ACTION       The yy_action[] code for no-op
    1430                 :            : **    YY_MIN_REDUCE      Minimum value for reduce actions
    1431                 :            : **    YY_MAX_REDUCE      Maximum value for reduce actions
    1432                 :            : */
    1433                 :            : #ifndef INTERFACE
    1434                 :            : # define INTERFACE 1
    1435                 :            : #endif
    1436                 :            : /************* Begin control #defines *****************************************/
    1437                 :            : #define YYCODETYPE unsigned char
    1438                 :            : #define YYNOCODE 41
    1439                 :            : #define YYACTIONTYPE unsigned char
    1440                 :            : #define ParseTOKENTYPE Term *
    1441                 :            : typedef union {
    1442                 :            :   int yyinit;
    1443                 :            :   ParseTOKENTYPE yy0;
    1444                 :            :   int yy12;
    1445                 :            :   Terms * yy14;
    1446                 :            :   Query * yy45;
    1447                 :            :   TermGroup * yy60;
    1448                 :            :   ProbQuery * yy64;
    1449                 :            : } YYMINORTYPE;
    1450                 :            : #ifndef YYSTACKDEPTH
    1451                 :            : #define YYSTACKDEPTH 100
    1452                 :            : #endif
    1453                 :            : #define ParseARG_SDECL State * state;
    1454                 :            : #define ParseARG_PDECL ,State * state
    1455                 :            : #define ParseARG_FETCH State * state = yypParser->state
    1456                 :            : #define ParseARG_STORE yypParser->state = state
    1457                 :            : #define YYNSTATE             35
    1458                 :            : #define YYNRULE              57
    1459                 :            : #define YYNTOKEN             25
    1460                 :            : #define YY_MAX_SHIFT         34
    1461                 :            : #define YY_MIN_SHIFTREDUCE   78
    1462                 :            : #define YY_MAX_SHIFTREDUCE   134
    1463                 :            : #define YY_ERROR_ACTION      135
    1464                 :            : #define YY_ACCEPT_ACTION     136
    1465                 :            : #define YY_NO_ACTION         137
    1466                 :            : #define YY_MIN_REDUCE        138
    1467                 :            : #define YY_MAX_REDUCE        194
    1468                 :            : /************* End control #defines *******************************************/
    1469                 :            : 
    1470                 :            : /* Define the yytestcase() macro to be a no-op if is not already defined
    1471                 :            : ** otherwise.
    1472                 :            : **
    1473                 :            : ** Applications can choose to define yytestcase() in the %include section
    1474                 :            : ** to a macro that can assist in verifying code coverage.  For production
    1475                 :            : ** code the yytestcase() macro should be turned off.  But it is useful
    1476                 :            : ** for testing.
    1477                 :            : */
    1478                 :            : #ifndef yytestcase
    1479                 :            : # define yytestcase(X)
    1480                 :            : #endif
    1481                 :            : 
    1482                 :            : 
    1483                 :            : /* Next are the tables used to determine what action to take based on the
    1484                 :            : ** current state and lookahead token.  These tables are used to implement
    1485                 :            : ** functions that take a state number and lookahead value and return an
    1486                 :            : ** action integer.  
    1487                 :            : **
    1488                 :            : ** Suppose the action integer is N.  Then the action is determined as
    1489                 :            : ** follows
    1490                 :            : **
    1491                 :            : **   0 <= N <= YY_MAX_SHIFT             Shift N.  That is, push the lookahead
    1492                 :            : **                                      token onto the stack and goto state N.
    1493                 :            : **
    1494                 :            : **   N between YY_MIN_SHIFTREDUCE       Shift to an arbitrary state then
    1495                 :            : **     and YY_MAX_SHIFTREDUCE           reduce by rule N-YY_MIN_SHIFTREDUCE.
    1496                 :            : **
    1497                 :            : **   N == YY_ERROR_ACTION               A syntax error has occurred.
    1498                 :            : **
    1499                 :            : **   N == YY_ACCEPT_ACTION              The parser accepts its input.
    1500                 :            : **
    1501                 :            : **   N == YY_NO_ACTION                  No such action.  Denotes unused
    1502                 :            : **                                      slots in the yy_action[] table.
    1503                 :            : **
    1504                 :            : **   N between YY_MIN_REDUCE            Reduce by rule N-YY_MIN_REDUCE
    1505                 :            : **     and YY_MAX_REDUCE
    1506                 :            : **
    1507                 :            : ** The action table is constructed as a single large table named yy_action[].
    1508                 :            : ** Given state S and lookahead X, the action is computed as either:
    1509                 :            : **
    1510                 :            : **    (A)   N = yy_action[ yy_shift_ofst[S] + X ]
    1511                 :            : **    (B)   N = yy_default[S]
    1512                 :            : **
    1513                 :            : ** The (A) formula is preferred.  The B formula is used instead if
    1514                 :            : ** yy_lookahead[yy_shift_ofst[S]+X] is not equal to X.
    1515                 :            : **
    1516                 :            : ** The formulas above are for computing the action when the lookahead is
    1517                 :            : ** a terminal symbol.  If the lookahead is a non-terminal (as occurs after
    1518                 :            : ** a reduce action) then the yy_reduce_ofst[] array is used in place of
    1519                 :            : ** the yy_shift_ofst[] array.
    1520                 :            : **
    1521                 :            : ** The following are the tables generated in this section:
    1522                 :            : **
    1523                 :            : **  yy_action[]        A single table containing all actions.
    1524                 :            : **  yy_lookahead[]     A table containing the lookahead for each entry in
    1525                 :            : **                     yy_action.  Used to detect hash collisions.
    1526                 :            : **  yy_shift_ofst[]    For each state, the offset into yy_action for
    1527                 :            : **                     shifting terminals.
    1528                 :            : **  yy_reduce_ofst[]   For each state, the offset into yy_action for
    1529                 :            : **                     shifting non-terminals after a reduce.
    1530                 :            : **  yy_default[]       Default action for each state.
    1531                 :            : **
    1532                 :            : *********** Begin parsing tables **********************************************/
    1533                 :            : #define YY_ACTTAB_COUNT (344)
    1534                 :            : static const YYACTIONTYPE yy_action[] = {
    1535                 :            :  /*     0 */    21,  137,  137,  137,  137,    3,  138,   29,   10,    9,
    1536                 :            :  /*    10 */     2,   25,   15,    1,    7,  105,  106,  107,   98,   88,
    1537                 :            :  /*    20 */    14,    4,  139,  115,  114,   12,   11,    5,    1,    7,
    1538                 :            :  /*    30 */    10,    9,  113,   25,   15,   99,   89,  105,  106,  107,
    1539                 :            :  /*    40 */    98,   88,   14,    4,  116,  115,  136,   34,   34,   20,
    1540                 :            :  /*    50 */     8,   34,   18,   13,   16,  117,   31,   23,   30,   28,
    1541                 :            :  /*    60 */   128,  140,  140,  140,    8,  140,   18,   13,   16,  123,
    1542                 :            :  /*    70 */    31,   23,   30,   28,   10,    9,   27,   25,   15,  126,
    1543                 :            :  /*    80 */   124,  105,  106,  107,   98,   88,   14,    4,  121,  115,
    1544                 :            :  /*    90 */   143,  143,  143,    8,  143,   18,   13,   16,  127,   31,
    1545                 :            :  /*   100 */    23,   30,   28,  142,  142,  142,    8,  142,   18,   13,
    1546                 :            :  /*   110 */    16,  125,   31,   23,   30,   28,  137,   26,   26,   20,
    1547                 :            :  /*   120 */     8,   26,   18,   13,   16,  137,   31,   23,   30,   28,
    1548                 :            :  /*   130 */    24,   24,   24,    8,   24,   18,   13,   16,  137,   31,
    1549                 :            :  /*   140 */    23,   30,   28,   22,   22,   22,    8,   22,   18,   13,
    1550                 :            :  /*   150 */    16,  137,   31,   23,   30,   28,  141,  141,  141,    8,
    1551                 :            :  /*   160 */   141,   18,   13,   16,  137,   31,   23,   30,   28,  192,
    1552                 :            :  /*   170 */   192,  137,   25,   19,  137,  137,  105,  106,  107,  192,
    1553                 :            :  /*   180 */   192,   14,    4,  164,  115,  164,  164,  164,  164,   33,
    1554                 :            :  /*   190 */    32,   33,   32,  118,  137,  137,  122,  120,  122,  120,
    1555                 :            :  /*   200 */   137,  108,   25,   17,  119,  164,  105,  106,  107,   96,
    1556                 :            :  /*   210 */   137,   14,    4,  137,  115,   25,   17,  137,  137,  105,
    1557                 :            :  /*   220 */   106,  107,  100,  137,   14,    4,  137,  115,   25,   17,
    1558                 :            :  /*   230 */   137,  137,  105,  106,  107,   97,  137,   14,    4,  137,
    1559                 :            :  /*   240 */   115,   25,   17,  137,  137,  105,  106,  107,  101,  137,
    1560                 :            :  /*   250 */    14,    4,  137,  115,   25,   19,  137,  137,  105,  106,
    1561                 :            :  /*   260 */   107,  137,  137,   14,    4,  137,  115,  151,  151,  137,
    1562                 :            :  /*   270 */    31,   23,   30,   28,  137,  137,  137,  154,  137,  137,
    1563                 :            :  /*   280 */   154,  137,   31,   23,   30,   28,  152,  137,  137,  152,
    1564                 :            :  /*   290 */   137,   31,   23,   30,   28,  155,  137,  137,  155,  137,
    1565                 :            :  /*   300 */    31,   23,   30,   28,  153,  137,  137,  153,  137,   31,
    1566                 :            :  /*   310 */    23,   30,   28,  137,  150,  150,  137,   31,   23,   30,
    1567                 :            :  /*   320 */    28,  194,  137,  194,  194,  194,  194,    6,    5,    1,
    1568                 :            :  /*   330 */     7,  137,  137,  137,  137,  137,  137,  137,  137,  137,
    1569                 :            :  /*   340 */   137,  137,  137,  194,
    1570                 :            : };
    1571                 :            : static const YYCODETYPE yy_lookahead[] = {
    1572                 :            :  /*     0 */    35,   40,   40,   40,   40,    5,    0,    6,    8,    9,
    1573                 :            :  /*    10 */    10,   11,   12,    4,    5,   15,   16,   17,   18,   19,
    1574                 :            :  /*    20 */    20,   21,    0,   23,   12,    8,    9,    3,    4,    5,
    1575                 :            :  /*    30 */     8,    9,   22,   11,   12,   18,   19,   15,   16,   17,
    1576                 :            :  /*    40 */    18,   19,   20,   21,   12,   23,   26,   27,   28,   29,
    1577                 :            :  /*    50 */    30,   31,   32,   33,   34,   23,   36,   37,   38,   39,
    1578                 :            :  /*    60 */    12,   27,   28,   29,   30,   31,   32,   33,   34,   13,
    1579                 :            :  /*    70 */    36,   37,   38,   39,    8,    9,    7,   11,   12,   12,
    1580                 :            :  /*    80 */    24,   15,   16,   17,   18,   19,   20,   21,   14,   23,
    1581                 :            :  /*    90 */    27,   28,   29,   30,   31,   32,   33,   34,   12,   36,
    1582                 :            :  /*   100 */    37,   38,   39,   27,   28,   29,   30,   31,   32,   33,
    1583                 :            :  /*   110 */    34,   12,   36,   37,   38,   39,   40,   27,   28,   29,
    1584                 :            :  /*   120 */    30,   31,   32,   33,   34,   40,   36,   37,   38,   39,
    1585                 :            :  /*   130 */    27,   28,   29,   30,   31,   32,   33,   34,   40,   36,
    1586                 :            :  /*   140 */    37,   38,   39,   27,   28,   29,   30,   31,   32,   33,
    1587                 :            :  /*   150 */    34,   40,   36,   37,   38,   39,   27,   28,   29,   30,
    1588                 :            :  /*   160 */    31,   32,   33,   34,   40,   36,   37,   38,   39,    8,
    1589                 :            :  /*   170 */     9,   40,   11,   12,   40,   40,   15,   16,   17,   18,
    1590                 :            :  /*   180 */    19,   20,   21,    0,   23,    2,    3,    4,    5,    6,
    1591                 :            :  /*   190 */     7,    6,    7,   12,   40,   40,   13,   14,   13,   14,
    1592                 :            :  /*   200 */    40,   20,   11,   12,   23,   22,   15,   16,   17,   18,
    1593                 :            :  /*   210 */    40,   20,   21,   40,   23,   11,   12,   40,   40,   15,
    1594                 :            :  /*   220 */    16,   17,   18,   40,   20,   21,   40,   23,   11,   12,
    1595                 :            :  /*   230 */    40,   40,   15,   16,   17,   18,   40,   20,   21,   40,
    1596                 :            :  /*   240 */    23,   11,   12,   40,   40,   15,   16,   17,   18,   40,
    1597                 :            :  /*   250 */    20,   21,   40,   23,   11,   12,   40,   40,   15,   16,
    1598                 :            :  /*   260 */    17,   40,   40,   20,   21,   40,   23,   33,   34,   40,
    1599                 :            :  /*   270 */    36,   37,   38,   39,   40,   40,   40,   31,   40,   40,
    1600                 :            :  /*   280 */    34,   40,   36,   37,   38,   39,   31,   40,   40,   34,
    1601                 :            :  /*   290 */    40,   36,   37,   38,   39,   31,   40,   40,   34,   40,
    1602                 :            :  /*   300 */    36,   37,   38,   39,   31,   40,   40,   34,   40,   36,
    1603                 :            :  /*   310 */    37,   38,   39,   40,   33,   34,   40,   36,   37,   38,
    1604                 :            :  /*   320 */    39,    0,   40,    2,    3,    4,    5,    2,    3,    4,
    1605                 :            :  /*   330 */     5,   40,   40,   40,   40,   40,   40,   40,   40,   40,
    1606                 :            :  /*   340 */    40,   40,   40,   22,   40,   40,   40,   40,   40,   40,
    1607                 :            :  /*   350 */    40,   40,   40,
    1608                 :            : };
    1609                 :            : #define YY_SHIFT_COUNT    (34)
    1610                 :            : #define YY_SHIFT_MIN      (0)
    1611                 :            : #define YY_SHIFT_MAX      (325)
    1612                 :            : static const unsigned short int yy_shift_ofst[] = {
    1613                 :            :  /*     0 */    22,    0,   66,   66,   66,   66,   66,   66,  161,  191,
    1614                 :            :  /*    10 */   204,  217,  230,  243,   32,  183,  321,  185,   17,  185,
    1615                 :            :  /*    20 */   325,  181,   24,   56,    9,   12,   10,   48,   69,   67,
    1616                 :            :  /*    30 */     1,   74,   86,   99,    6,
    1617                 :            : };
    1618                 :            : #define YY_REDUCE_COUNT (14)
    1619                 :            : #define YY_REDUCE_MIN   (-35)
    1620                 :            : #define YY_REDUCE_MAX   (281)
    1621                 :            : static const short yy_reduce_ofst[] = {
    1622                 :            :  /*     0 */    20,   34,   63,   76,   90,  103,  116,  129,  234,  246,
    1623                 :            :  /*    10 */   255,  264,  273,  281,  -35,
    1624                 :            : };
    1625                 :            : static const YYACTIONTYPE yy_default[] = {
    1626                 :            :  /*     0 */   146,  146,  146,  146,  146,  146,  146,  146,  147,  135,
    1627                 :            :  /*    10 */   135,  135,  135,  162,  135,  163,  193,  164,  135,  163,
    1628                 :            :  /*    20 */   135,  135,  144,  170,  145,  135,  190,  135,  172,  135,
    1629                 :            :  /*    30 */   171,  169,  135,  135,  190,
    1630                 :            : };
    1631                 :            : /********** End of lemon-generated parsing tables *****************************/
    1632                 :            : 
    1633                 :            : /* The next table maps tokens (terminal symbols) into fallback tokens.  
    1634                 :            : ** If a construct like the following:
    1635                 :            : ** 
    1636                 :            : **      %fallback ID X Y Z.
    1637                 :            : **
    1638                 :            : ** appears in the grammar, then ID becomes a fallback token for X, Y,
    1639                 :            : ** and Z.  Whenever one of the tokens X, Y, or Z is input to the parser
    1640                 :            : ** but it does not parse, the type of the token is changed to ID and
    1641                 :            : ** the parse is retried before an error is thrown.
    1642                 :            : **
    1643                 :            : ** This feature can be used, for example, to cause some keywords in a language
    1644                 :            : ** to revert to identifiers if they keyword does not apply in the context where
    1645                 :            : ** it appears.
    1646                 :            : */
    1647                 :            : #ifdef YYFALLBACK
    1648                 :            : static const YYCODETYPE yyFallback[] = {
    1649                 :            : };
    1650                 :            : #endif /* YYFALLBACK */
    1651                 :            : 
    1652                 :            : /* The following structure represents a single element of the
    1653                 :            : ** parser's stack.  Information stored includes:
    1654                 :            : **
    1655                 :            : **   +  The state number for the parser at this level of the stack.
    1656                 :            : **
    1657                 :            : **   +  The value of the token stored at this level of the stack.
    1658                 :            : **      (In other words, the "major" token.)
    1659                 :            : **
    1660                 :            : **   +  The semantic value stored at this level of the stack.  This is
    1661                 :            : **      the information used by the action routines in the grammar.
    1662                 :            : **      It is sometimes called the "minor" token.
    1663                 :            : **
    1664                 :            : ** After the "shift" half of a SHIFTREDUCE action, the stateno field
    1665                 :            : ** actually contains the reduce action for the second half of the
    1666                 :            : ** SHIFTREDUCE.
    1667                 :            : */
    1668                 :            : struct yyStackEntry {
    1669                 :      64507 :   yyStackEntry() {
    1670                 :      64507 :     stateno = 0;
    1671                 :      64507 :     major = 0;
    1672                 :      64507 :   }
    1673                 :     109273 :   yyStackEntry(YYACTIONTYPE stateno_, YYCODETYPE major_, ParseTOKENTYPE minor_) {
    1674                 :     109273 :     stateno = stateno_;
    1675                 :     109273 :     major = major_;
    1676                 :     109273 :     minor.yy0 = minor_;
    1677                 :     109273 :   }
    1678                 :            :   YYACTIONTYPE stateno;  /* The state-number, or reduce action in SHIFTREDUCE */
    1679                 :            :   YYCODETYPE major;      /* The major token value.  This is the code
    1680                 :            :                          ** number for the token at this stack level */
    1681                 :            :   YYMINORTYPE minor;     /* The user-supplied minor token value.  This
    1682                 :            :                          ** is the value of the token  */
    1683                 :            : };
    1684                 :            : 
    1685                 :            : static void ParseInit(yyParser *pParser);
    1686                 :            : static void ParseFinalize(yyParser *pParser);
    1687                 :            : 
    1688                 :            : /* The state of the parser is completely contained in an instance of
    1689                 :            : ** the following structure */
    1690                 :            : struct yyParser {
    1691                 :            : #ifdef YYTRACKMAXSTACKDEPTH
    1692                 :            :   int yyhwm;                    /* High-water mark of the stack */
    1693                 :            : #endif
    1694                 :            : #ifndef YYNOERRORRECOVERY
    1695                 :            :   int yyerrcnt;                 /* Shifts left before out of the error */
    1696                 :            : #endif
    1697                 :            :   ParseARG_SDECL                /* A place to hold %extra_argument */
    1698                 :            :   vector<yyStackEntry> yystack; /* The parser's stack */
    1699                 :     128918 :   yyParser() {
    1700         [ +  - ]:      64459 :     ParseInit(this);
    1701                 :      64459 :   }
    1702                 :     128918 :   ~yyParser() {
    1703                 :      64459 :     ParseFinalize(this);
    1704                 :      64459 :   }
    1705                 :            : };
    1706                 :            : typedef struct yyParser yyParser;
    1707                 :            : 
    1708                 :            : #include "omassert.h"
    1709                 :            : #include "debuglog.h"
    1710                 :            : 
    1711                 :            : #if defined(YYCOVERAGE) || defined(XAPIAN_DEBUG_LOG)
    1712                 :            : /* For tracing shifts, the names of all terminals and nonterminals
    1713                 :            : ** are required.  The following table supplies these names */
    1714                 :            : static const char *const yyTokenName[] = { 
    1715                 :            :   /*    0 */ "$",
    1716                 :            :   /*    1 */ "ERROR",
    1717                 :            :   /*    2 */ "OR",
    1718                 :            :   /*    3 */ "XOR",
    1719                 :            :   /*    4 */ "AND",
    1720                 :            :   /*    5 */ "NOT",
    1721                 :            :   /*    6 */ "NEAR",
    1722                 :            :   /*    7 */ "ADJ",
    1723                 :            :   /*    8 */ "LOVE",
    1724                 :            :   /*    9 */ "HATE",
    1725                 :            :   /*   10 */ "HATE_AFTER_AND",
    1726                 :            :   /*   11 */ "SYNONYM",
    1727                 :            :   /*   12 */ "TERM",
    1728                 :            :   /*   13 */ "GROUP_TERM",
    1729                 :            :   /*   14 */ "PHR_TERM",
    1730                 :            :   /*   15 */ "EDIT_TERM",
    1731                 :            :   /*   16 */ "WILD_TERM",
    1732                 :            :   /*   17 */ "PARTIAL_TERM",
    1733                 :            :   /*   18 */ "BOOLEAN_FILTER",
    1734                 :            :   /*   19 */ "RANGE",
    1735                 :            :   /*   20 */ "QUOTE",
    1736                 :            :   /*   21 */ "BRA",
    1737                 :            :   /*   22 */ "KET",
    1738                 :            :   /*   23 */ "CJKTERM",
    1739                 :            :   /*   24 */ "EMPTY_GROUP_OK",
    1740                 :            :   /*   25 */ "error",
    1741                 :            :   /*   26 */ "query",
    1742                 :            :   /*   27 */ "expr",
    1743                 :            :   /*   28 */ "prob_expr",
    1744                 :            :   /*   29 */ "bool_arg",
    1745                 :            :   /*   30 */ "prob",
    1746                 :            :   /*   31 */ "term",
    1747                 :            :   /*   32 */ "stop_prob",
    1748                 :            :   /*   33 */ "stop_term",
    1749                 :            :   /*   34 */ "compound_term",
    1750                 :            :   /*   35 */ "phrase",
    1751                 :            :   /*   36 */ "phrased_term",
    1752                 :            :   /*   37 */ "group",
    1753                 :            :   /*   38 */ "near_expr",
    1754                 :            :   /*   39 */ "adj_expr",
    1755                 :            : };
    1756                 :            : 
    1757                 :            : /* For tracing reduce actions, the names of all rules are required.
    1758                 :            : */
    1759                 :            : static const char *const yyRuleName[] = {
    1760                 :            :  /*   0 */ "query ::= expr",
    1761                 :            :  /*   1 */ "query ::=",
    1762                 :            :  /*   2 */ "expr ::= bool_arg AND bool_arg",
    1763                 :            :  /*   3 */ "expr ::= bool_arg NOT bool_arg",
    1764                 :            :  /*   4 */ "expr ::= bool_arg AND NOT bool_arg",
    1765                 :            :  /*   5 */ "expr ::= bool_arg AND HATE_AFTER_AND bool_arg",
    1766                 :            :  /*   6 */ "expr ::= bool_arg OR bool_arg",
    1767                 :            :  /*   7 */ "expr ::= bool_arg XOR bool_arg",
    1768                 :            :  /*   8 */ "bool_arg ::=",
    1769                 :            :  /*   9 */ "prob_expr ::= prob",
    1770                 :            :  /*  10 */ "prob ::= RANGE",
    1771                 :            :  /*  11 */ "prob ::= stop_prob RANGE",
    1772                 :            :  /*  12 */ "prob ::= stop_term stop_term",
    1773                 :            :  /*  13 */ "prob ::= prob stop_term",
    1774                 :            :  /*  14 */ "prob ::= LOVE term",
    1775                 :            :  /*  15 */ "prob ::= stop_prob LOVE term",
    1776                 :            :  /*  16 */ "prob ::= HATE term",
    1777                 :            :  /*  17 */ "prob ::= stop_prob HATE term",
    1778                 :            :  /*  18 */ "prob ::= HATE BOOLEAN_FILTER",
    1779                 :            :  /*  19 */ "prob ::= stop_prob HATE BOOLEAN_FILTER",
    1780                 :            :  /*  20 */ "prob ::= BOOLEAN_FILTER",
    1781                 :            :  /*  21 */ "prob ::= stop_prob BOOLEAN_FILTER",
    1782                 :            :  /*  22 */ "prob ::= LOVE BOOLEAN_FILTER",
    1783                 :            :  /*  23 */ "prob ::= stop_prob LOVE BOOLEAN_FILTER",
    1784                 :            :  /*  24 */ "stop_prob ::= stop_term",
    1785                 :            :  /*  25 */ "stop_term ::= TERM",
    1786                 :            :  /*  26 */ "term ::= TERM",
    1787                 :            :  /*  27 */ "compound_term ::= EDIT_TERM",
    1788                 :            :  /*  28 */ "compound_term ::= WILD_TERM",
    1789                 :            :  /*  29 */ "compound_term ::= PARTIAL_TERM",
    1790                 :            :  /*  30 */ "compound_term ::= QUOTE phrase QUOTE",
    1791                 :            :  /*  31 */ "compound_term ::= phrased_term",
    1792                 :            :  /*  32 */ "compound_term ::= group",
    1793                 :            :  /*  33 */ "compound_term ::= near_expr",
    1794                 :            :  /*  34 */ "compound_term ::= adj_expr",
    1795                 :            :  /*  35 */ "compound_term ::= BRA expr KET",
    1796                 :            :  /*  36 */ "compound_term ::= SYNONYM TERM",
    1797                 :            :  /*  37 */ "compound_term ::= CJKTERM",
    1798                 :            :  /*  38 */ "phrase ::= TERM",
    1799                 :            :  /*  39 */ "phrase ::= CJKTERM",
    1800                 :            :  /*  40 */ "phrase ::= phrase TERM",
    1801                 :            :  /*  41 */ "phrase ::= phrase CJKTERM",
    1802                 :            :  /*  42 */ "phrased_term ::= TERM PHR_TERM",
    1803                 :            :  /*  43 */ "phrased_term ::= phrased_term PHR_TERM",
    1804                 :            :  /*  44 */ "group ::= TERM GROUP_TERM",
    1805                 :            :  /*  45 */ "group ::= group GROUP_TERM",
    1806                 :            :  /*  46 */ "group ::= group EMPTY_GROUP_OK",
    1807                 :            :  /*  47 */ "near_expr ::= TERM NEAR TERM",
    1808                 :            :  /*  48 */ "near_expr ::= near_expr NEAR TERM",
    1809                 :            :  /*  49 */ "adj_expr ::= TERM ADJ TERM",
    1810                 :            :  /*  50 */ "adj_expr ::= adj_expr ADJ TERM",
    1811                 :            :  /*  51 */ "expr ::= prob_expr",
    1812                 :            :  /*  52 */ "bool_arg ::= expr",
    1813                 :            :  /*  53 */ "prob_expr ::= term",
    1814                 :            :  /*  54 */ "stop_prob ::= prob",
    1815                 :            :  /*  55 */ "stop_term ::= compound_term",
    1816                 :            :  /*  56 */ "term ::= compound_term",
    1817                 :            : };
    1818                 :            : 
    1819                 :            : /*
    1820                 :            : ** This function returns the symbolic name associated with a token
    1821                 :            : ** value.
    1822                 :            : */
    1823                 :            : static const char *ParseTokenName(int tokenType){
    1824                 :            :   if( tokenType>=0 && tokenType<(int)(sizeof(yyTokenName)/sizeof(yyTokenName[0])) ){
    1825                 :            :     return yyTokenName[tokenType];
    1826                 :            :   }
    1827                 :            :   return "Unknown";
    1828                 :            : }
    1829                 :            : 
    1830                 :            : /*
    1831                 :            : ** This function returns the symbolic name associated with a rule
    1832                 :            : ** value.
    1833                 :            : */
    1834                 :            : static const char *ParseRuleName(int ruleNum){
    1835                 :            :   if( ruleNum>=0 && ruleNum<(int)(sizeof(yyRuleName)/sizeof(yyRuleName[0])) ){
    1836                 :            :     return yyRuleName[ruleNum];
    1837                 :            :   }
    1838                 :            :   return "Unknown";
    1839                 :            : }
    1840                 :            : #endif /* defined(YYCOVERAGE) || defined(XAPIAN_DEBUG_LOG) */
    1841                 :            : 
    1842                 :            : /* Datatype of the argument to the memory allocated passed as the
    1843                 :            : ** second argument to ParseAlloc() below.  This can be changed by
    1844                 :            : ** putting an appropriate #define in the %include section of the input
    1845                 :            : ** grammar.
    1846                 :            : */
    1847                 :            : #ifndef YYMALLOCARGTYPE
    1848                 :            : # define YYMALLOCARGTYPE size_t
    1849                 :            : #endif
    1850                 :            : 
    1851                 :            : /* Initialize a new parser that has already been allocated.
    1852                 :            : */
    1853                 :            : static
    1854                 :      64459 : void ParseInit(yyParser *pParser){
    1855                 :            : #ifdef YYTRACKMAXSTACKDEPTH
    1856                 :            :   pParser->yyhwm = 0;
    1857                 :            : #endif
    1858                 :            : #if 0
    1859                 :            : #if YYSTACKDEPTH<=0
    1860                 :            :   pParser->yytos = NULL;
    1861                 :            :   pParser->yystack = NULL;
    1862                 :            :   pParser->yystksz = 0;
    1863                 :            :   if( yyGrowStack(pParser) ){
    1864                 :            :     pParser->yystack = &pParser->yystk0;
    1865                 :            :     pParser->yystksz = 1;
    1866                 :            :   }
    1867                 :            : #endif
    1868                 :            : #endif
    1869                 :            : #ifndef YYNOERRORRECOVERY
    1870                 :      64459 :   pParser->yyerrcnt = -1;
    1871                 :            : #endif
    1872                 :            : #if 0
    1873                 :            :   pParser->yytos = pParser->yystack;
    1874                 :            :   pParser->yystack[0].stateno = 0;
    1875                 :            :   pParser->yystack[0].major = 0;
    1876                 :            : #if YYSTACKDEPTH>0
    1877                 :            :   pParser->yystackEnd = &pParser->yystack[YYSTACKDEPTH-1];
    1878                 :            : #endif
    1879                 :            : #else
    1880         [ +  - ]:      64459 :   pParser->yystack.push_back(yyStackEntry());
    1881                 :            : #endif
    1882                 :      64459 : }
    1883                 :            : 
    1884                 :            : #ifndef Parse_ENGINEALWAYSONSTACK
    1885                 :            : /* 
    1886                 :            : ** This function allocates a new parser.
    1887                 :            : **
    1888                 :            : ** Inputs:
    1889                 :            : ** None.
    1890                 :            : **
    1891                 :            : ** Outputs:
    1892                 :            : ** A pointer to a parser.  This pointer is used in subsequent calls
    1893                 :            : ** to Parse and ParseFree.
    1894                 :            : */
    1895                 :            : static yyParser *ParseAlloc(void){
    1896                 :            :   return new yyParser;
    1897                 :            : }
    1898                 :            : #endif /* Parse_ENGINEALWAYSONSTACK */
    1899                 :            : 
    1900                 :            : 
    1901                 :            : /* The following function deletes the "minor type" or semantic value
    1902                 :            : ** associated with a symbol.  The symbol can be either a terminal
    1903                 :            : ** or nonterminal. "yymajor" is the symbol code, and "yypminor" is
    1904                 :            : ** a pointer to the value to be deleted.  The code used to do the 
    1905                 :            : ** deletions is derived from the %destructor and/or %token_destructor
    1906                 :            : ** directives of the input grammar.
    1907                 :            : */
    1908                 :       1636 : static void yy_destructor(
    1909                 :            :   yyParser *yypParser,    /* The parser */
    1910                 :            :   YYCODETYPE yymajor,     /* Type code for object to destroy */
    1911                 :            :   YYMINORTYPE *yypminor   /* The object to be destroyed */
    1912                 :            : ){
    1913                 :       1636 :   ParseARG_FETCH;
    1914   [ +  +  +  +  :       1636 :   switch( yymajor ){
                   -  + ]
    1915                 :            :     /* Here is inserted the actions which take place when a
    1916                 :            :     ** terminal or non-terminal is destroyed.  This can happen
    1917                 :            :     ** when the symbol is popped from the stack during a
    1918                 :            :     ** reduce or during error processing or when a parser is 
    1919                 :            :     ** being destroyed before it is finished parsing.
    1920                 :            :     **
    1921                 :            :     ** Note: during a reduce, the only symbols destroyed are those
    1922                 :            :     ** which appear on the RHS of the rule, but which are *not* used
    1923                 :            :     ** inside the C code.
    1924                 :            :     */
    1925                 :            : /********* Begin destructor definitions ***************************************/
    1926                 :            :       /* TERMINAL Destructor */
    1927                 :            :     case 1: /* ERROR */
    1928                 :            :     case 2: /* OR */
    1929                 :            :     case 3: /* XOR */
    1930                 :            :     case 4: /* AND */
    1931                 :            :     case 5: /* NOT */
    1932                 :            :     case 6: /* NEAR */
    1933                 :            :     case 7: /* ADJ */
    1934                 :            :     case 8: /* LOVE */
    1935                 :            :     case 9: /* HATE */
    1936                 :            :     case 10: /* HATE_AFTER_AND */
    1937                 :            :     case 11: /* SYNONYM */
    1938                 :            :     case 12: /* TERM */
    1939                 :            :     case 13: /* GROUP_TERM */
    1940                 :            :     case 14: /* PHR_TERM */
    1941                 :            :     case 15: /* EDIT_TERM */
    1942                 :            :     case 16: /* WILD_TERM */
    1943                 :            :     case 17: /* PARTIAL_TERM */
    1944                 :            :     case 18: /* BOOLEAN_FILTER */
    1945                 :            :     case 19: /* RANGE */
    1946                 :            :     case 20: /* QUOTE */
    1947                 :            :     case 21: /* BRA */
    1948                 :            :     case 22: /* KET */
    1949                 :            :     case 23: /* CJKTERM */
    1950                 :            :     case 24: /* EMPTY_GROUP_OK */
    1951                 :            : {
    1952                 :            : #line 2001 "queryparser/queryparser.lemony"
    1953                 :            :  delete (yypminor->yy0); 
    1954                 :            : #line 1955 "queryparser/queryparser_internal.cc"
    1955                 :            : }
    1956                 :       1442 :       break;
    1957                 :            :     case 27: /* expr */
    1958                 :            :     case 28: /* prob_expr */
    1959                 :            :     case 29: /* bool_arg */
    1960                 :            :     case 31: /* term */
    1961                 :            :     case 33: /* stop_term */
    1962                 :            :     case 34: /* compound_term */
    1963                 :            : {
    1964                 :            : #line 2078 "queryparser/queryparser.lemony"
    1965                 :            :  delete (yypminor->yy45); 
    1966                 :            : #line 1967 "queryparser/queryparser_internal.cc"
    1967                 :            : }
    1968                 :        169 :       break;
    1969                 :            :     case 30: /* prob */
    1970                 :            :     case 32: /* stop_prob */
    1971                 :            : {
    1972                 :            : #line 2187 "queryparser/queryparser.lemony"
    1973                 :            :  delete (yypminor->yy64); 
    1974                 :            : #line 1975 "queryparser/queryparser_internal.cc"
    1975                 :            : }
    1976                 :         14 :       break;
    1977                 :            :     case 35: /* phrase */
    1978                 :            :     case 36: /* phrased_term */
    1979                 :            :     case 38: /* near_expr */
    1980                 :            :     case 39: /* adj_expr */
    1981                 :            : {
    1982                 :            : #line 2381 "queryparser/queryparser.lemony"
    1983                 :            :  delete (yypminor->yy14); 
    1984                 :            : #line 1985 "queryparser/queryparser_internal.cc"
    1985                 :            : }
    1986                 :          3 :       break;
    1987                 :            :     case 37: /* group */
    1988                 :            : {
    1989                 :            : #line 2422 "queryparser/queryparser.lemony"
    1990                 :            :  delete (yypminor->yy60); 
    1991                 :            : #line 1992 "queryparser/queryparser_internal.cc"
    1992                 :            : }
    1993                 :          0 :       break;
    1994                 :            : /********* End destructor definitions *****************************************/
    1995                 :          8 :     default:  break;   /* If no destructor action specified: do nothing */
    1996                 :            :   }
    1997                 :       1636 :   ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */
    1998                 :       1636 : }
    1999                 :            : 
    2000                 :            : /*
    2001                 :            : ** Pop the parser's stack once.
    2002                 :            : **
    2003                 :            : ** If there is a destructor routine associated with the token which
    2004                 :            : ** is popped from the stack, then call it.
    2005                 :            : */
    2006                 :        281 : static void yy_pop_parser_stack(yyParser *pParser){
    2007                 :            :   Assert( pParser->yystack.size() > 1 );
    2008                 :        281 :   yyStackEntry *yytos = &pParser->yystack.back();
    2009                 :            : 
    2010                 :            :   LOGLINE(QUERYPARSER, "Popping " << ParseTokenName(yytos->major));
    2011                 :        281 :   yy_destructor(pParser, yytos->major, &yytos->minor);
    2012                 :        281 :   pParser->yystack.pop_back();
    2013                 :        281 : }
    2014                 :            : 
    2015                 :            : /*
    2016                 :            : ** Clear all secondary memory allocations from the parser
    2017                 :            : */
    2018                 :            : static
    2019                 :      64459 : void ParseFinalize(yyParser *pParser){
    2020         [ +  + ]:      64471 :   while( pParser->yystack.size() > 1 ) yy_pop_parser_stack(pParser);
    2021                 :      64459 : }
    2022                 :            : 
    2023                 :            : #ifndef Parse_ENGINEALWAYSONSTACK
    2024                 :            : /* 
    2025                 :            : ** Deallocate and destroy a parser.  Destructors are called for
    2026                 :            : ** all stack elements before shutting the parser down.
    2027                 :            : **
    2028                 :            : ** If the YYPARSEFREENEVERNULL macro exists (for example because it
    2029                 :            : ** is defined in a %include section of the input grammar) then it is
    2030                 :            : ** assumed that the input pointer is never NULL.
    2031                 :            : */
    2032                 :            : static
    2033                 :            : void ParseFree(
    2034                 :            :   yyParser *pParser           /* The parser to be deleted */
    2035                 :            : ){
    2036                 :            :   delete pParser;
    2037                 :            : }
    2038                 :            : #endif /* Parse_ENGINEALWAYSONSTACK */
    2039                 :            : 
    2040                 :            : /*
    2041                 :            : ** Return the peak depth of the stack for a parser.
    2042                 :            : */
    2043                 :            : #ifdef YYTRACKMAXSTACKDEPTH
    2044                 :            : int ParseStackPeak(yyParser *pParser){
    2045                 :            :   return pParser->yyhwm;
    2046                 :            : }
    2047                 :            : #endif
    2048                 :            : 
    2049                 :            : /* This array of booleans keeps track of the parser statement
    2050                 :            : ** coverage.  The element yycoverage[X][Y] is set when the parser
    2051                 :            : ** is in state X and has a lookahead token Y.  In a well-tested
    2052                 :            : ** systems, every element of this matrix should end up being set.
    2053                 :            : */
    2054                 :            : #if defined(YYCOVERAGE)
    2055                 :            : static unsigned char yycoverage[YYNSTATE][YYNTOKEN];
    2056                 :            : #endif
    2057                 :            : 
    2058                 :            : /*
    2059                 :            : ** Write into out a description of every state/lookahead combination that
    2060                 :            : **
    2061                 :            : **   (1)  has not been used by the parser, and
    2062                 :            : **   (2)  is not a syntax error.
    2063                 :            : **
    2064                 :            : ** Return the number of missed state/lookahead combinations.
    2065                 :            : */
    2066                 :            : #if defined(YYCOVERAGE)
    2067                 :            : int ParseCoverage(FILE *out){
    2068                 :            :   int stateno, iLookAhead, i;
    2069                 :            :   int nMissed = 0;
    2070                 :            :   for(stateno=0; stateno<YYNSTATE; stateno++){
    2071                 :            :     i = yy_shift_ofst[stateno];
    2072                 :            :     for(iLookAhead=0; iLookAhead<YYNTOKEN; iLookAhead++){
    2073                 :            :       if( yy_lookahead[i+iLookAhead]!=iLookAhead ) continue;
    2074                 :            :       if( yycoverage[stateno][iLookAhead]==0 ) nMissed++;
    2075                 :            :       if( out ){
    2076                 :            :         fprintf(out,"State %d lookahead %s %s\n", stateno,
    2077                 :            :                 yyTokenName[iLookAhead],
    2078                 :            :                 yycoverage[stateno][iLookAhead] ? "ok" : "missed");
    2079                 :            :       }
    2080                 :            :     }
    2081                 :            :   }
    2082                 :            :   return nMissed;
    2083                 :            : }
    2084                 :            : #endif
    2085                 :            : 
    2086                 :            : /*
    2087                 :            : ** Find the appropriate action for a parser given the terminal
    2088                 :            : ** look-ahead token iLookAhead.
    2089                 :            : */
    2090                 :     368954 : static unsigned int yy_find_shift_action(
    2091                 :            :   yyParser *pParser,        /* The parser */
    2092                 :            :   YYCODETYPE iLookAhead     /* The look-ahead token */
    2093                 :            : ){
    2094                 :            :   int i;
    2095                 :     368954 :   int stateno = pParser->yystack.back().stateno;
    2096                 :            :  
    2097         [ +  + ]:     368954 :   if( stateno>YY_MAX_SHIFT ) return stateno;
    2098                 :            :   Assert( stateno <= YY_SHIFT_COUNT );
    2099                 :            : #if defined(YYCOVERAGE)
    2100                 :            :   yycoverage[stateno][iLookAhead] = 1;
    2101                 :            : #endif
    2102                 :            :   do{
    2103                 :     240987 :     i = yy_shift_ofst[stateno];
    2104                 :            :     Assert( i>=0 );
    2105                 :            :     Assert( i+YYNTOKEN<=(int)(sizeof(yy_lookahead)/sizeof(yy_lookahead[0])) );
    2106                 :            :     Assert( iLookAhead!=YYNOCODE );
    2107                 :            :     Assert( iLookAhead < YYNTOKEN );
    2108                 :     240987 :     i += iLookAhead;
    2109         [ +  + ]:     240987 :     if( yy_lookahead[i]!=iLookAhead ){
    2110                 :            : #ifdef YYFALLBACK
    2111                 :            :       YYCODETYPE iFallback;            /* Fallback token */
    2112                 :            :       if( iLookAhead<sizeof(yyFallback)/sizeof(yyFallback[0])
    2113                 :            :              && (iFallback = yyFallback[iLookAhead])!=0 ){
    2114                 :            :         LOGLINE(QUERYPARSER,
    2115                 :            :                 "FALLBACK " << ParseTokenName(iLookAhead) << " => " <<
    2116                 :            :                 ParseTokenName(iFallback));
    2117                 :            :         Assert( yyFallback[iFallback]==0 ); /* Fallback loop must terminate */
    2118                 :            :         iLookAhead = iFallback;
    2119                 :            :         continue;
    2120                 :            :       }
    2121                 :            : #endif
    2122                 :            : #ifdef YYWILDCARD
    2123                 :            :       {
    2124                 :            :         int j = i - iLookAhead + YYWILDCARD;
    2125                 :            :         if( 
    2126                 :            : #if YY_SHIFT_MIN+YYWILDCARD<0
    2127                 :            :           j>=0 &&
    2128                 :            : #endif
    2129                 :            : #if YY_SHIFT_MAX+YYWILDCARD>=YY_ACTTAB_COUNT
    2130                 :            :           j<YY_ACTTAB_COUNT &&
    2131                 :            : #endif
    2132                 :            :           yy_lookahead[j]==YYWILDCARD && iLookAhead>0
    2133                 :            :         ){
    2134                 :            :           LOGLINE(QUERYPARSER,
    2135                 :            :                   "WILDCARD " << ParseTokenName(iLookAhead) << " => " <<
    2136                 :            :                   ParseTokenName(YYWILDCARD));
    2137                 :            :           return yy_action[j];
    2138                 :            :         }
    2139                 :            :       }
    2140                 :            : #endif /* YYWILDCARD */
    2141                 :      22190 :       return yy_default[stateno];
    2142                 :            :     }else{
    2143                 :     218797 :       return yy_action[i];
    2144                 :            :     }
    2145                 :            :   }while(1);
    2146                 :            : }
    2147                 :            : 
    2148                 :            : /*
    2149                 :            : ** Find the appropriate action for a parser given the non-terminal
    2150                 :            : ** look-ahead token iLookAhead.
    2151                 :            : */
    2152                 :     195245 : static int yy_find_reduce_action(
    2153                 :            :   int stateno,              /* Current state number */
    2154                 :            :   YYCODETYPE iLookAhead     /* The look-ahead token */
    2155                 :            : ){
    2156                 :            :   int i;
    2157                 :            : #ifdef YYERRORSYMBOL
    2158                 :            :   if( stateno>YY_REDUCE_COUNT ){
    2159                 :            :     return yy_default[stateno];
    2160                 :            :   }
    2161                 :            : #else
    2162                 :            :   Assert( stateno<=YY_REDUCE_COUNT );
    2163                 :            : #endif
    2164                 :     195245 :   i = yy_reduce_ofst[stateno];
    2165                 :            :   Assert( iLookAhead!=YYNOCODE );
    2166                 :     195245 :   i += iLookAhead;
    2167                 :            : #ifdef YYERRORSYMBOL
    2168                 :            :   if( i<0 || i>=YY_ACTTAB_COUNT || yy_lookahead[i]!=iLookAhead ){
    2169                 :            :     return yy_default[stateno];
    2170                 :            :   }
    2171                 :            : #else
    2172                 :            :   Assert( i>=0 && i<YY_ACTTAB_COUNT );
    2173                 :            :   Assert( yy_lookahead[i]==iLookAhead );
    2174                 :            : #endif
    2175                 :     195245 :   return yy_action[i];
    2176                 :            : }
    2177                 :            : 
    2178                 :            : /*
    2179                 :            : ** The following routine is called if the stack overflows.
    2180                 :            : ** In Xapian this can never happen as we use std::vector to provide a stack
    2181                 :            : ** of indefinite size.
    2182                 :            : */
    2183                 :            : #if 0
    2184                 :            : static void yyStackOverflow(yyParser *yypParser){
    2185                 :            :    ParseARG_FETCH;
    2186                 :            :    yypParser->yyidx--;
    2187                 :            : #ifndef NDEBUG
    2188                 :            :    if( yyTraceFILE ){
    2189                 :            :      fprintf(yyTraceFILE,"%sStack Overflow!\n",yyTracePrompt);
    2190                 :            :    }
    2191                 :            : #endif
    2192                 :            :    while( yypParser->yytos>yypParser->yystack ) yy_pop_parser_stack(yypParser);
    2193                 :            :    /* Here code is inserted which will execute if the parser
    2194                 :            :    ** stack ever overflows */
    2195                 :            : /******** Begin %stack_overflow code ******************************************/
    2196                 :            : /******** End %stack_overflow code ********************************************/
    2197                 :            :    ParseARG_STORE; /* Suppress warning about unused %extra_argument var */
    2198                 :            : }
    2199                 :            : #endif
    2200                 :            : 
    2201                 :            : /*
    2202                 :            : ** Print tracing information for a SHIFT action
    2203                 :            : */
    2204                 :            : #ifdef XAPIAN_DEBUG_LOG
    2205                 :            : static void yyTraceShift(yyParser *yypParser, int yyNewState, const char *zTag){
    2206                 :            :   if( yyNewState<YYNSTATE ){
    2207                 :            :     LOGLINE(QUERYPARSER, zTag << " '" <<
    2208                 :            :                          yyTokenName[yypParser->yystack.back().major] <<
    2209                 :            :                          "', go to state " << yyNewState);
    2210                 :            :   }else{
    2211                 :            :     LOGLINE(QUERYPARSER, zTag << " '" <<
    2212                 :            :                          yyTokenName[yypParser->yystack.back().major] <<
    2213                 :            :                          "', pending reduce " << yyNewState - YY_MIN_REDUCE);
    2214                 :            :   }
    2215                 :            : }
    2216                 :            : #else
    2217                 :            : # define yyTraceShift(X,Y,Z)
    2218                 :            : #endif
    2219                 :            : 
    2220                 :            : /*
    2221                 :            : ** Perform a shift action.
    2222                 :            : */
    2223                 :     109273 : static void yy_shift(
    2224                 :            :   yyParser *yypParser,          /* The parser to be shifted */
    2225                 :            :   int yyNewState,               /* The new state to shift in */
    2226                 :            :   int yyMajor,                  /* The major token to shift in */
    2227                 :            :   ParseTOKENTYPE yyMinor        /* The minor token to shift in */
    2228                 :            : ){
    2229         [ +  + ]:     109273 :   if( yyNewState > YY_MAX_SHIFT ){
    2230                 :      62316 :     yyNewState += YY_MIN_REDUCE - YY_MIN_SHIFTREDUCE;
    2231                 :            :   }
    2232         [ +  - ]:     109273 :   yypParser->yystack.push_back(yyStackEntry(yyNewState, yyMajor, yyMinor));
    2233                 :            : #ifdef YYTRACKMAXSTACKDEPTH
    2234                 :            :   if( (int)(yypParser->yystack.size()>yypParser->yyhwm ){
    2235                 :            :     yypParser->yyhwm++;
    2236                 :            :     Assert( yypParser->yyhwm == (int)(yypParser->yystack.size() );
    2237                 :            :   }
    2238                 :            : #endif
    2239                 :            :   yyTraceShift(yypParser, yyNewState, "Shift");
    2240                 :     109273 : }
    2241                 :            : 
    2242                 :            : /* The following table contains information about every rule that
    2243                 :            : ** is used during the reduce.
    2244                 :            : */
    2245                 :            : static const struct {
    2246                 :            :   YYCODETYPE lhs;       /* Symbol on the left-hand side of the rule */
    2247                 :            :   signed char nrhs;     /* Negative of the number of RHS symbols in the rule */
    2248                 :            : } yyRuleInfo[] = {
    2249                 :            :   {   26,   -1 }, /* (0) query ::= expr */
    2250                 :            :   {   26,    0 }, /* (1) query ::= */
    2251                 :            :   {   27,   -3 }, /* (2) expr ::= bool_arg AND bool_arg */
    2252                 :            :   {   27,   -3 }, /* (3) expr ::= bool_arg NOT bool_arg */
    2253                 :            :   {   27,   -4 }, /* (4) expr ::= bool_arg AND NOT bool_arg */
    2254                 :            :   {   27,   -4 }, /* (5) expr ::= bool_arg AND HATE_AFTER_AND bool_arg */
    2255                 :            :   {   27,   -3 }, /* (6) expr ::= bool_arg OR bool_arg */
    2256                 :            :   {   27,   -3 }, /* (7) expr ::= bool_arg XOR bool_arg */
    2257                 :            :   {   29,    0 }, /* (8) bool_arg ::= */
    2258                 :            :   {   28,   -1 }, /* (9) prob_expr ::= prob */
    2259                 :            :   {   30,   -1 }, /* (10) prob ::= RANGE */
    2260                 :            :   {   30,   -2 }, /* (11) prob ::= stop_prob RANGE */
    2261                 :            :   {   30,   -2 }, /* (12) prob ::= stop_term stop_term */
    2262                 :            :   {   30,   -2 }, /* (13) prob ::= prob stop_term */
    2263                 :            :   {   30,   -2 }, /* (14) prob ::= LOVE term */
    2264                 :            :   {   30,   -3 }, /* (15) prob ::= stop_prob LOVE term */
    2265                 :            :   {   30,   -2 }, /* (16) prob ::= HATE term */
    2266                 :            :   {   30,   -3 }, /* (17) prob ::= stop_prob HATE term */
    2267                 :            :   {   30,   -2 }, /* (18) prob ::= HATE BOOLEAN_FILTER */
    2268                 :            :   {   30,   -3 }, /* (19) prob ::= stop_prob HATE BOOLEAN_FILTER */
    2269                 :            :   {   30,   -1 }, /* (20) prob ::= BOOLEAN_FILTER */
    2270                 :            :   {   30,   -2 }, /* (21) prob ::= stop_prob BOOLEAN_FILTER */
    2271                 :            :   {   30,   -2 }, /* (22) prob ::= LOVE BOOLEAN_FILTER */
    2272                 :            :   {   30,   -3 }, /* (23) prob ::= stop_prob LOVE BOOLEAN_FILTER */
    2273                 :            :   {   32,   -1 }, /* (24) stop_prob ::= stop_term */
    2274                 :            :   {   33,   -1 }, /* (25) stop_term ::= TERM */
    2275                 :            :   {   31,   -1 }, /* (26) term ::= TERM */
    2276                 :            :   {   34,   -1 }, /* (27) compound_term ::= EDIT_TERM */
    2277                 :            :   {   34,   -1 }, /* (28) compound_term ::= WILD_TERM */
    2278                 :            :   {   34,   -1 }, /* (29) compound_term ::= PARTIAL_TERM */
    2279                 :            :   {   34,   -3 }, /* (30) compound_term ::= QUOTE phrase QUOTE */
    2280                 :            :   {   34,   -1 }, /* (31) compound_term ::= phrased_term */
    2281                 :            :   {   34,   -1 }, /* (32) compound_term ::= group */
    2282                 :            :   {   34,   -1 }, /* (33) compound_term ::= near_expr */
    2283                 :            :   {   34,   -1 }, /* (34) compound_term ::= adj_expr */
    2284                 :            :   {   34,   -3 }, /* (35) compound_term ::= BRA expr KET */
    2285                 :            :   {   34,   -2 }, /* (36) compound_term ::= SYNONYM TERM */
    2286                 :            :   {   34,   -1 }, /* (37) compound_term ::= CJKTERM */
    2287                 :            :   {   35,   -1 }, /* (38) phrase ::= TERM */
    2288                 :            :   {   35,   -1 }, /* (39) phrase ::= CJKTERM */
    2289                 :            :   {   35,   -2 }, /* (40) phrase ::= phrase TERM */
    2290                 :            :   {   35,   -2 }, /* (41) phrase ::= phrase CJKTERM */
    2291                 :            :   {   36,   -2 }, /* (42) phrased_term ::= TERM PHR_TERM */
    2292                 :            :   {   36,   -2 }, /* (43) phrased_term ::= phrased_term PHR_TERM */
    2293                 :            :   {   37,   -2 }, /* (44) group ::= TERM GROUP_TERM */
    2294                 :            :   {   37,   -2 }, /* (45) group ::= group GROUP_TERM */
    2295                 :            :   {   37,   -2 }, /* (46) group ::= group EMPTY_GROUP_OK */
    2296                 :            :   {   38,   -3 }, /* (47) near_expr ::= TERM NEAR TERM */
    2297                 :            :   {   38,   -3 }, /* (48) near_expr ::= near_expr NEAR TERM */
    2298                 :            :   {   39,   -3 }, /* (49) adj_expr ::= TERM ADJ TERM */
    2299                 :            :   {   39,   -3 }, /* (50) adj_expr ::= adj_expr ADJ TERM */
    2300                 :            :   {   27,   -1 }, /* (51) expr ::= prob_expr */
    2301                 :            :   {   29,   -1 }, /* (52) bool_arg ::= expr */
    2302                 :            :   {   28,   -1 }, /* (53) prob_expr ::= term */
    2303                 :            :   {   32,   -1 }, /* (54) stop_prob ::= prob */
    2304                 :            :   {   33,   -1 }, /* (55) stop_term ::= compound_term */
    2305                 :            :   {   31,   -1 }, /* (56) term ::= compound_term */
    2306                 :            : };
    2307                 :            : 
    2308                 :            : static void yy_accept(yyParser*);  /* Forward Declaration */
    2309                 :            : 
    2310                 :            : /*
    2311                 :            : ** Perform a reduce action and the shift that must immediately
    2312                 :            : ** follow the reduce.
    2313                 :            : **
    2314                 :            : ** The yyLookahead and yyLookaheadToken parameters provide reduce actions
    2315                 :            : ** access to the lookahead token (if any).  The yyLookahead will be YYNOCODE
    2316                 :            : ** if the lookahead token has already been consumed.  As this procedure is
    2317                 :            : ** only called from one place, optimizing compilers will in-line it, which
    2318                 :            : ** means that the extra parameters have no performance impact.
    2319                 :            : */
    2320                 :     195278 : static void yy_reduce(
    2321                 :            :   yyParser *yypParser,         /* The parser */
    2322                 :            :   unsigned int yyruleno,       /* Number of the rule by which to reduce */
    2323                 :            :   int yyLookahead,             /* Lookahead token, or YYNOCODE if none */
    2324                 :            :   ParseTOKENTYPE yyLookaheadToken  /* Value of the lookahead token */
    2325                 :            : ){
    2326                 :            :   int yygoto;                     /* The next state */
    2327                 :            :   int yyact;                      /* The next action */
    2328                 :            :   yyStackEntry *yymsp;            /* The top of the parser's stack */
    2329                 :            :   int yysize;                     /* Amount to pop the stack */
    2330                 :     195278 :   ParseARG_FETCH;
    2331                 :            :   (void)yyLookahead;
    2332                 :            :   (void)yyLookaheadToken;
    2333                 :     195278 :   yymsp = &yypParser->yystack.back();
    2334                 :            : #ifdef XAPIAN_DEBUG_LOG
    2335                 :            :   if( yyruleno<(int)(sizeof(yyRuleName)/sizeof(yyRuleName[0])) ){
    2336                 :            :     yysize = yyRuleInfo[yyruleno].nrhs;
    2337                 :            :     if( yysize ){
    2338                 :            :       LOGLINE(QUERYPARSER, "Reduce " << yyruleno << " [" <<
    2339                 :            :                            ParseRuleName(yyruleno) << "], go to state " <<
    2340                 :            :                            yymsp[yysize].stateno);
    2341                 :            :     } else {
    2342                 :            :       LOGLINE(QUERYPARSER, "Reduce " << yyruleno << " [" <<
    2343                 :            :                            ParseRuleName(yyruleno) << "].");
    2344                 :            :     }
    2345                 :            :   }
    2346                 :            : #endif /* XAPIAN_DEBUG_LOG */
    2347                 :            :   /*  yygotominor = yyzerominor; */
    2348                 :            : 
    2349                 :            :   /* Check that the stack is large enough to grow by a single entry
    2350                 :            :   ** if the RHS of the rule is empty.  This ensures that there is room
    2351                 :            :   ** enough on the stack to push the LHS value without invalidating
    2352                 :            :   ** pointers into the stack. */
    2353         [ +  + ]:     195278 :   if( yyRuleInfo[yyruleno].nrhs==0 ){
    2354                 :            : #if 1
    2355                 :         48 :     yypParser->yystack.reserve(yypParser->yystack.size() + 1);
    2356                 :         48 :     yymsp = &(yypParser->yystack.back());
    2357                 :            : #else
    2358                 :            : #ifdef YYTRACKMAXSTACKDEPTH
    2359                 :            :     if( (int)(yypParser->yytos - yypParser->yystack)>yypParser->yyhwm ){
    2360                 :            :       yypParser->yyhwm++;
    2361                 :            :       Assert( yypParser->yyhwm == (int)(yypParser->yytos - yypParser->yystack));
    2362                 :            :     }
    2363                 :            : #endif
    2364                 :            : #if YYSTACKDEPTH>0 
    2365                 :            :     if( yypParser->yytos>=yypParser->yystackEnd ){
    2366                 :            :       yyStackOverflow(yypParser);
    2367                 :            :       return;
    2368                 :            :     }
    2369                 :            : #else
    2370                 :            :     if( yypParser->yytos>=&yypParser->yystack[yypParser->yystksz-1] ){
    2371                 :            :       if( yyGrowStack(yypParser) ){
    2372                 :            :         yyStackOverflow(yypParser);
    2373                 :            :         return;
    2374                 :            :       }
    2375                 :            :       yymsp = yypParser->yytos;
    2376                 :            :     }
    2377                 :            : #endif
    2378                 :            : #endif
    2379                 :            :   }
    2380                 :            : 
    2381   [ +  +  +  +  :     195278 :   switch( yyruleno ){
          +  +  +  +  +  
          +  +  +  +  +  
          +  +  +  +  +  
          +  +  +  +  +  
          +  +  +  +  +  
          +  +  +  +  +  
          +  +  +  +  +  
          +  +  +  +  +  
             +  +  +  +  
                      + ]
    2382                 :            :   /* Beginning here are the reduction cases.  A typical example
    2383                 :            :   ** follows:
    2384                 :            :   **   case 0:
    2385                 :            :   **  #line <lineno> <grammarfile>
    2386                 :            :   **     { ... }           // User supplied code
    2387                 :            :   **  #line <lineno> <thisfile>
    2388                 :            :   **     break;
    2389                 :            :   */
    2390                 :            : /********** Begin reduce actions **********************************************/
    2391                 :            :         YYMINORTYPE yylhsminor;
    2392                 :            :       case 0: /* query ::= expr */
    2393                 :            : #line 2060 "queryparser/queryparser.lemony"
    2394                 :            : {
    2395                 :            :     // Save the parsed query in the State structure so we can return it.
    2396                 :            :     if (yymsp[0].minor.yy45) {
    2397                 :            :         state->query = *yymsp[0].minor.yy45;
    2398                 :            :         delete yymsp[0].minor.yy45;
    2399                 :            :     } else {
    2400                 :            :         state->query = Query();
    2401                 :            :     }
    2402                 :            : }
    2403                 :            : #line 2404 "queryparser/queryparser_internal.cc"
    2404                 :      64282 :         break;
    2405                 :            :       case 1: /* query ::= */
    2406                 :            : #line 2070 "queryparser/queryparser.lemony"
    2407                 :            : {
    2408                 :            :     // Handle a query string with no terms in.
    2409                 :            :     state->query = Query();
    2410                 :            : }
    2411                 :            : #line 2412 "queryparser/queryparser_internal.cc"
    2412                 :          6 :         break;
    2413                 :            :       case 2: /* expr ::= bool_arg AND bool_arg */
    2414                 :            : #line 2082 "queryparser/queryparser.lemony"
    2415                 :            : {
    2416                 :            :     VET_BOOL_ARGS(yymsp[-2].minor.yy45, yymsp[0].minor.yy45, "AND");
    2417                 :            :     *yymsp[-2].minor.yy45 &= *yymsp[0].minor.yy45;
    2418                 :            :     delete yymsp[0].minor.yy45;
    2419                 :            : }
    2420                 :            : #line 2421 "queryparser/queryparser_internal.cc"
    2421                 :         35 :   yy_destructor(yypParser,4,&yymsp[-1].minor);
    2422                 :         35 :         break;
    2423                 :            :       case 3: /* expr ::= bool_arg NOT bool_arg */
    2424                 :            : #line 2088 "queryparser/queryparser.lemony"
    2425                 :            : {
    2426                 :            :     // 'NOT foo' -> '<alldocuments> NOT foo'
    2427                 :            :     if (!yymsp[-2].minor.yy45 && (state->flags & QueryParser::FLAG_PURE_NOT)) {
    2428                 :            :         yymsp[-2].minor.yy45 = new Query("", 1, 0);
    2429                 :            :     }
    2430                 :            :     VET_BOOL_ARGS(yymsp[-2].minor.yy45, yymsp[0].minor.yy45, "NOT");
    2431                 :            :     *yymsp[-2].minor.yy45 &= ~*yymsp[0].minor.yy45;
    2432                 :            :     delete yymsp[0].minor.yy45;
    2433                 :            : }
    2434                 :            : #line 2435 "queryparser/queryparser_internal.cc"
    2435                 :         12 :   yy_destructor(yypParser,5,&yymsp[-1].minor);
    2436                 :         12 :         break;
    2437                 :            :       case 4: /* expr ::= bool_arg AND NOT bool_arg */
    2438                 :            : #line 2098 "queryparser/queryparser.lemony"
    2439                 :            : {
    2440                 :            :     VET_BOOL_ARGS(yymsp[-3].minor.yy45, yymsp[0].minor.yy45, "AND NOT");
    2441                 :            :     *yymsp[-3].minor.yy45 &= ~*yymsp[0].minor.yy45;
    2442                 :            :     delete yymsp[0].minor.yy45;
    2443                 :            : }
    2444                 :            : #line 2445 "queryparser/queryparser_internal.cc"
    2445                 :          6 :   yy_destructor(yypParser,4,&yymsp[-2].minor);
    2446                 :          6 :   yy_destructor(yypParser,5,&yymsp[-1].minor);
    2447                 :          6 :         break;
    2448                 :            :       case 5: /* expr ::= bool_arg AND HATE_AFTER_AND bool_arg */
    2449                 :            : #line 2104 "queryparser/queryparser.lemony"
    2450                 :            : {
    2451                 :            :     VET_BOOL_ARGS(yymsp[-3].minor.yy45, yymsp[0].minor.yy45, "AND");
    2452                 :            :     *yymsp[-3].minor.yy45 &= ~*yymsp[0].minor.yy45;
    2453                 :            :     delete yymsp[0].minor.yy45;
    2454                 :            : }
    2455                 :            : #line 2456 "queryparser/queryparser_internal.cc"
    2456                 :          5 :   yy_destructor(yypParser,4,&yymsp[-2].minor);
    2457                 :          5 :   yy_destructor(yypParser,10,&yymsp[-1].minor);
    2458                 :          5 :         break;
    2459                 :            :       case 6: /* expr ::= bool_arg OR bool_arg */
    2460                 :            : #line 2110 "queryparser/queryparser.lemony"
    2461                 :            : {
    2462                 :            :     VET_BOOL_ARGS(yymsp[-2].minor.yy45, yymsp[0].minor.yy45, "OR");
    2463                 :            :     *yymsp[-2].minor.yy45 |= *yymsp[0].minor.yy45;
    2464                 :            :     delete yymsp[0].minor.yy45;
    2465                 :            : }
    2466                 :            : #line 2467 "queryparser/queryparser_internal.cc"
    2467                 :         37 :   yy_destructor(yypParser,2,&yymsp[-1].minor);
    2468                 :         37 :         break;
    2469                 :            :       case 7: /* expr ::= bool_arg XOR bool_arg */
    2470                 :            : #line 2116 "queryparser/queryparser.lemony"
    2471                 :            : {
    2472                 :            :     VET_BOOL_ARGS(yymsp[-2].minor.yy45, yymsp[0].minor.yy45, "XOR");
    2473                 :            :     *yymsp[-2].minor.yy45 ^= *yymsp[0].minor.yy45;
    2474                 :            :     delete yymsp[0].minor.yy45;
    2475                 :            : }
    2476                 :            : #line 2477 "queryparser/queryparser_internal.cc"
    2477                 :          9 :   yy_destructor(yypParser,3,&yymsp[-1].minor);
    2478                 :          9 :         break;
    2479                 :            :       case 8: /* bool_arg ::= */
    2480                 :            : #line 2129 "queryparser/queryparser.lemony"
    2481                 :            : {
    2482                 :            :     // Set the argument to NULL, which enables the bool_arg-using rules in
    2483                 :            :     // expr above to report uses of AND, OR, etc which don't have two
    2484                 :            :     // arguments.
    2485                 :            :     yymsp[1].minor.yy45 = NULL;
    2486                 :            : }
    2487                 :            : #line 2488 "queryparser/queryparser_internal.cc"
    2488                 :         42 :         break;
    2489                 :            :       case 9: /* prob_expr ::= prob */
    2490                 :            : #line 2141 "queryparser/queryparser.lemony"
    2491                 :            : {
    2492                 :            :     yylhsminor.yy45 = yymsp[0].minor.yy64->query;
    2493                 :            :     yymsp[0].minor.yy64->query = NULL;
    2494                 :            :     // Handle any "+ terms".
    2495                 :            :     if (yymsp[0].minor.yy64->love) {
    2496                 :            :         if (yymsp[0].minor.yy64->love->empty()) {
    2497                 :            :             // +<nothing>.
    2498                 :            :             delete yylhsminor.yy45;
    2499                 :            :             yylhsminor.yy45 = yymsp[0].minor.yy64->love;
    2500                 :            :         } else if (yylhsminor.yy45) {
    2501                 :            :             swap(yylhsminor.yy45, yymsp[0].minor.yy64->love);
    2502                 :            :             add_to_query(yylhsminor.yy45, Query::OP_AND_MAYBE, yymsp[0].minor.yy64->love);
    2503                 :            :         } else {
    2504                 :            :             yylhsminor.yy45 = yymsp[0].minor.yy64->love;
    2505                 :            :         }
    2506                 :            :         yymsp[0].minor.yy64->love = NULL;
    2507                 :            :     }
    2508                 :            :     // Handle any boolean filters.
    2509                 :            :     if (!yymsp[0].minor.yy64->filter.empty()) {
    2510                 :            :         if (yylhsminor.yy45) {
    2511                 :            :             add_to_query(yylhsminor.yy45, Query::OP_FILTER, yymsp[0].minor.yy64->merge_filters());
    2512                 :            :         } else {
    2513                 :            :             // Make the query a boolean one.
    2514                 :            :             yylhsminor.yy45 = new Query(Query::OP_SCALE_WEIGHT, yymsp[0].minor.yy64->merge_filters(), 0.0);
    2515                 :            :         }
    2516                 :            :     }
    2517                 :            :     // Handle any "- terms".
    2518                 :            :     if (yymsp[0].minor.yy64->hate && !yymsp[0].minor.yy64->hate->empty()) {
    2519                 :            :         if (!yylhsminor.yy45) {
    2520                 :            :             // Can't just hate!
    2521                 :            :             yy_parse_failed(yypParser);
    2522                 :            :             return;
    2523                 :            :         }
    2524                 :            :         *yylhsminor.yy45 = Query(Query::OP_AND_NOT, *yylhsminor.yy45, *yymsp[0].minor.yy64->hate);
    2525                 :            :     }
    2526                 :            :     delete yymsp[0].minor.yy64;
    2527                 :            : }
    2528                 :            : #line 2529 "queryparser/queryparser_internal.cc"
    2529                 :      19537 :   yymsp[0].minor.yy45 = yylhsminor.yy45;
    2530                 :      19537 :         break;
    2531                 :            :       case 10: /* prob ::= RANGE */
    2532                 :            : #line 2189 "queryparser/queryparser.lemony"
    2533                 :            : {
    2534                 :            :     string grouping = yymsp[0].minor.yy0->name;
    2535                 :            :     const Query & range = yymsp[0].minor.yy0->as_range_query();
    2536                 :            :     yymsp[0].minor.yy64 = new ProbQuery; /*P-overwrites-R*/
    2537                 :            :     yymsp[0].minor.yy64->add_filter_range(grouping, range);
    2538                 :            : }
    2539                 :            : #line 2540 "queryparser/queryparser_internal.cc"
    2540                 :      18657 :         break;
    2541                 :            :       case 11: /* prob ::= stop_prob RANGE */
    2542                 :            : #line 2196 "queryparser/queryparser.lemony"
    2543                 :            : {
    2544                 :            :     string grouping = yymsp[0].minor.yy0->name;
    2545                 :            :     const Query & range = yymsp[0].minor.yy0->as_range_query();
    2546                 :            :     yymsp[-1].minor.yy64->append_filter_range(grouping, range);
    2547                 :            : }
    2548                 :            : #line 2549 "queryparser/queryparser_internal.cc"
    2549                 :         14 :         break;
    2550                 :            :       case 12: /* prob ::= stop_term stop_term */
    2551                 :            : #line 2202 "queryparser/queryparser.lemony"
    2552                 :            : {
    2553                 :            :     yymsp[-1].minor.yy64 = new ProbQuery(yymsp[-1].minor.yy45); /*P-overwrites-T*/
    2554                 :            :     if (yymsp[0].minor.yy45) {
    2555                 :            :         Query::op op = state->default_op();
    2556                 :            :         if (yymsp[-1].minor.yy64->query && is_positional(op)) {
    2557                 :            :             // If default_op is OP_NEAR or OP_PHRASE, set the window size to
    2558                 :            :             // 11 for the first pair of terms and it will automatically grow
    2559                 :            :             // by one for each subsequent term.
    2560                 :            :             Query * subqs[2] = { yymsp[-1].minor.yy64->query, yymsp[0].minor.yy45 };
    2561                 :            :             *(yymsp[-1].minor.yy64->query) = Query(op, subqs, subqs + 2, 11);
    2562                 :            :             delete yymsp[0].minor.yy45;
    2563                 :            :         } else {
    2564                 :            :             add_to_query(yymsp[-1].minor.yy64->query, op, yymsp[0].minor.yy45);
    2565                 :            :         }
    2566                 :            :     }
    2567                 :            : }
    2568                 :            : #line 2569 "queryparser/queryparser_internal.cc"
    2569                 :        610 :         break;
    2570                 :            :       case 13: /* prob ::= prob stop_term */
    2571                 :            : #line 2219 "queryparser/queryparser.lemony"
    2572                 :            : {
    2573                 :            :     // If yymsp[0].minor.yy45 is a stopword, there's nothing to do here.
    2574                 :            :     if (yymsp[0].minor.yy45) add_to_query(yymsp[-1].minor.yy64->query, state->default_op(), yymsp[0].minor.yy45);
    2575                 :            : }
    2576                 :            : #line 2577 "queryparser/queryparser_internal.cc"
    2577                 :        447 :         break;
    2578                 :            :       case 14: /* prob ::= LOVE term */
    2579                 :         76 : {  yy_destructor(yypParser,8,&yymsp[-1].minor);
    2580                 :            : #line 2224 "queryparser/queryparser.lemony"
    2581                 :            : {
    2582                 :            :     yymsp[-1].minor.yy64 = new ProbQuery;
    2583                 :            :     if (state->default_op() == Query::OP_AND) {
    2584                 :            :         yymsp[-1].minor.yy64->query = yymsp[0].minor.yy45;
    2585                 :            :     } else {
    2586                 :            :         yymsp[-1].minor.yy64->love = yymsp[0].minor.yy45;
    2587                 :            :     }
    2588                 :            : }
    2589                 :            : #line 2590 "queryparser/queryparser_internal.cc"
    2590                 :            : }
    2591                 :         76 :         break;
    2592                 :            :       case 15: /* prob ::= stop_prob LOVE term */
    2593                 :            : #line 2233 "queryparser/queryparser.lemony"
    2594                 :            : {
    2595                 :            :     if (state->default_op() == Query::OP_AND) {
    2596                 :            :         /* The default op is AND, so we just put loved terms into the query
    2597                 :            :          * (in this case the only effect of love is to ignore the stopword
    2598                 :            :          * list). */
    2599                 :            :         add_to_query(yymsp[-2].minor.yy64->query, Query::OP_AND, yymsp[0].minor.yy45);
    2600                 :            :     } else {
    2601                 :            :         add_to_query(yymsp[-2].minor.yy64->love, Query::OP_AND, yymsp[0].minor.yy45);
    2602                 :            :     }
    2603                 :            : }
    2604                 :            : #line 2605 "queryparser/queryparser_internal.cc"
    2605                 :         66 :   yy_destructor(yypParser,8,&yymsp[-1].minor);
    2606                 :         66 :         break;
    2607                 :            :       case 16: /* prob ::= HATE term */
    2608                 :         20 : {  yy_destructor(yypParser,9,&yymsp[-1].minor);
    2609                 :            : #line 2244 "queryparser/queryparser.lemony"
    2610                 :            : {
    2611                 :            :     yymsp[-1].minor.yy64 = new ProbQuery;
    2612                 :            :     yymsp[-1].minor.yy64->hate = yymsp[0].minor.yy45;
    2613                 :            : }
    2614                 :            : #line 2615 "queryparser/queryparser_internal.cc"
    2615                 :            : }
    2616                 :         20 :         break;
    2617                 :            :       case 17: /* prob ::= stop_prob HATE term */
    2618                 :            : #line 2249 "queryparser/queryparser.lemony"
    2619                 :            : {
    2620                 :            :     add_to_query(yymsp[-2].minor.yy64->hate, Query::OP_OR, yymsp[0].minor.yy45);
    2621                 :            : }
    2622                 :            : #line 2623 "queryparser/queryparser_internal.cc"
    2623                 :         68 :   yy_destructor(yypParser,9,&yymsp[-1].minor);
    2624                 :         68 :         break;
    2625                 :            :       case 18: /* prob ::= HATE BOOLEAN_FILTER */
    2626                 :          1 : {  yy_destructor(yypParser,9,&yymsp[-1].minor);
    2627                 :            : #line 2253 "queryparser/queryparser.lemony"
    2628                 :            : {
    2629                 :            :     yymsp[-1].minor.yy64 = new ProbQuery;
    2630                 :            :     yymsp[-1].minor.yy64->hate = new Query(yymsp[0].minor.yy0->get_query());
    2631                 :            :     delete yymsp[0].minor.yy0;
    2632                 :            : }
    2633                 :            : #line 2634 "queryparser/queryparser_internal.cc"
    2634                 :            : }
    2635                 :          1 :         break;
    2636                 :            :       case 19: /* prob ::= stop_prob HATE BOOLEAN_FILTER */
    2637                 :            : #line 2259 "queryparser/queryparser.lemony"
    2638                 :            : {
    2639                 :            :     add_to_query(yymsp[-2].minor.yy64->hate, Query::OP_OR, yymsp[0].minor.yy0->get_query());
    2640                 :            :     delete yymsp[0].minor.yy0;
    2641                 :            : }
    2642                 :            : #line 2643 "queryparser/queryparser_internal.cc"
    2643                 :          4 :   yy_destructor(yypParser,9,&yymsp[-1].minor);
    2644                 :          4 :         break;
    2645                 :            :       case 20: /* prob ::= BOOLEAN_FILTER */
    2646                 :            : #line 2264 "queryparser/queryparser.lemony"
    2647                 :            : {
    2648                 :            :     yylhsminor.yy64 = new ProbQuery;
    2649                 :            :     yylhsminor.yy64->add_filter(yymsp[0].minor.yy0->get_grouping(), yymsp[0].minor.yy0->get_query());
    2650                 :            :     delete yymsp[0].minor.yy0;
    2651                 :            : }
    2652                 :            : #line 2653 "queryparser/queryparser_internal.cc"
    2653                 :         60 :   yymsp[0].minor.yy64 = yylhsminor.yy64;
    2654                 :         60 :         break;
    2655                 :            :       case 21: /* prob ::= stop_prob BOOLEAN_FILTER */
    2656                 :            : #line 2270 "queryparser/queryparser.lemony"
    2657                 :            : {
    2658                 :            :     yymsp[-1].minor.yy64->append_filter(yymsp[0].minor.yy0->get_grouping(), yymsp[0].minor.yy0->get_query());
    2659                 :            :     delete yymsp[0].minor.yy0;
    2660                 :            : }
    2661                 :            : #line 2662 "queryparser/queryparser_internal.cc"
    2662                 :         34 :         break;
    2663                 :            :       case 22: /* prob ::= LOVE BOOLEAN_FILTER */
    2664                 :          1 : {  yy_destructor(yypParser,8,&yymsp[-1].minor);
    2665                 :            : #line 2275 "queryparser/queryparser.lemony"
    2666                 :            : {
    2667                 :            :     // LOVE BOOLEAN_FILTER(yymsp[0].minor.yy0) is just the same as BOOLEAN_FILTER
    2668                 :            :     yymsp[-1].minor.yy64 = new ProbQuery;
    2669                 :            :     yymsp[-1].minor.yy64->filter[yymsp[0].minor.yy0->get_grouping()] = yymsp[0].minor.yy0->get_query();
    2670                 :            :     delete yymsp[0].minor.yy0;
    2671                 :            : }
    2672                 :            : #line 2673 "queryparser/queryparser_internal.cc"
    2673                 :            : }
    2674                 :          1 :         break;
    2675                 :            :       case 23: /* prob ::= stop_prob LOVE BOOLEAN_FILTER */
    2676                 :            : #line 2282 "queryparser/queryparser.lemony"
    2677                 :            : {
    2678                 :            :     // LOVE BOOLEAN_FILTER(yymsp[0].minor.yy0) is just the same as BOOLEAN_FILTER
    2679                 :            :     // We OR filters with the same prefix...
    2680                 :            :     Query & q = yymsp[-2].minor.yy64->filter[yymsp[0].minor.yy0->get_grouping()];
    2681                 :            :     q |= yymsp[0].minor.yy0->get_query();
    2682                 :            :     delete yymsp[0].minor.yy0;
    2683                 :            : }
    2684                 :            : #line 2685 "queryparser/queryparser_internal.cc"
    2685                 :          3 :   yy_destructor(yypParser,8,&yymsp[-1].minor);
    2686                 :          3 :         break;
    2687                 :            :       case 24: /* stop_prob ::= stop_term */
    2688                 :            : #line 2297 "queryparser/queryparser.lemony"
    2689                 :            : {
    2690                 :            :     yymsp[0].minor.yy64 = new ProbQuery(yymsp[0].minor.yy45); /*P-overwrites-T*/
    2691                 :            : }
    2692                 :            : #line 2693 "queryparser/queryparser_internal.cc"
    2693                 :        126 :         break;
    2694                 :            :       case 25: /* stop_term ::= TERM */
    2695                 :            : #line 2310 "queryparser/queryparser.lemony"
    2696                 :            : {
    2697                 :            :     if (state->is_stopword(yymsp[0].minor.yy0)) {
    2698                 :            :         yylhsminor.yy45 = NULL;
    2699                 :            :         state->add_to_stoplist(yymsp[0].minor.yy0);
    2700                 :            :     } else {
    2701                 :            :         yylhsminor.yy45 = new Query(yymsp[0].minor.yy0->get_query_with_auto_synonyms());
    2702                 :            :     }
    2703                 :            :     delete yymsp[0].minor.yy0;
    2704                 :            : }
    2705                 :            : #line 2706 "queryparser/queryparser_internal.cc"
    2706                 :        804 :   yymsp[0].minor.yy45 = yylhsminor.yy45;
    2707                 :        804 :         break;
    2708                 :            :       case 26: /* term ::= TERM */
    2709                 :            : #line 2327 "queryparser/queryparser.lemony"
    2710                 :            : {
    2711                 :            :     yylhsminor.yy45 = new Query(yymsp[0].minor.yy0->get_query_with_auto_synonyms());
    2712                 :            :     delete yymsp[0].minor.yy0;
    2713                 :            : }
    2714                 :            : #line 2715 "queryparser/queryparser_internal.cc"
    2715                 :      44410 :   yymsp[0].minor.yy45 = yylhsminor.yy45;
    2716                 :      44410 :         break;
    2717                 :            :       case 27: /* compound_term ::= EDIT_TERM */
    2718                 :            : #line 2342 "queryparser/queryparser.lemony"
    2719                 :            : { yymsp[0].minor.yy45 = yymsp[0].minor.yy0->as_fuzzy_query(state); /*T-overwrites-U*/ }
    2720                 :            : #line 2721 "queryparser/queryparser_internal.cc"
    2721                 :        103 :         break;
    2722                 :            :       case 28: /* compound_term ::= WILD_TERM */
    2723                 :            : #line 2345 "queryparser/queryparser.lemony"
    2724                 :            : { yymsp[0].minor.yy45 = yymsp[0].minor.yy0->as_wildcarded_query(state); /*T-overwrites-U*/ }
    2725                 :            : #line 2726 "queryparser/queryparser_internal.cc"
    2726                 :        376 :         break;
    2727                 :            :       case 29: /* compound_term ::= PARTIAL_TERM */
    2728                 :            : #line 2348 "queryparser/queryparser.lemony"
    2729                 :            : { yymsp[0].minor.yy45 = yymsp[0].minor.yy0->as_partial_query(state); /*T-overwrites-U*/ }
    2730                 :            : #line 2731 "queryparser/queryparser_internal.cc"
    2731                 :        202 :         break;
    2732                 :            :       case 30: /* compound_term ::= QUOTE phrase QUOTE */
    2733                 :        201 : {  yy_destructor(yypParser,20,&yymsp[-2].minor);
    2734                 :            : #line 2351 "queryparser/queryparser.lemony"
    2735                 :            : { yymsp[-2].minor.yy45 = yymsp[-1].minor.yy14->as_phrase_query(); }
    2736                 :            : #line 2737 "queryparser/queryparser_internal.cc"
    2737                 :        201 :   yy_destructor(yypParser,20,&yymsp[0].minor);
    2738                 :            : }
    2739                 :        201 :         break;
    2740                 :            :       case 31: /* compound_term ::= phrased_term */
    2741                 :            : #line 2354 "queryparser/queryparser.lemony"
    2742                 :            : { yymsp[0].minor.yy45 = yymsp[0].minor.yy14->as_phrase_query(); /*T-overwrites-P*/ }
    2743                 :            : #line 2744 "queryparser/queryparser_internal.cc"
    2744                 :        229 :         break;
    2745                 :            :       case 32: /* compound_term ::= group */
    2746                 :            : #line 2357 "queryparser/queryparser.lemony"
    2747                 :            : { yymsp[0].minor.yy45 = yymsp[0].minor.yy60->as_group(state); /*T-overwrites-P*/ }
    2748                 :            : #line 2749 "queryparser/queryparser_internal.cc"
    2749                 :        600 :         break;
    2750                 :            :       case 33: /* compound_term ::= near_expr */
    2751                 :            : #line 2360 "queryparser/queryparser.lemony"
    2752                 :            : { yymsp[0].minor.yy45 = yymsp[0].minor.yy14->as_near_query(); /*T-overwrites-P*/ }
    2753                 :            : #line 2754 "queryparser/queryparser_internal.cc"
    2754                 :          9 :         break;
    2755                 :            :       case 34: /* compound_term ::= adj_expr */
    2756                 :            : #line 2363 "queryparser/queryparser.lemony"
    2757                 :            : { yymsp[0].minor.yy45 = yymsp[0].minor.yy14->as_adj_query(); /*T-overwrites-P*/ }
    2758                 :            : #line 2759 "queryparser/queryparser_internal.cc"
    2759                 :          4 :         break;
    2760                 :            :       case 35: /* compound_term ::= BRA expr KET */
    2761                 :        207 : {  yy_destructor(yypParser,21,&yymsp[-2].minor);
    2762                 :            : #line 2366 "queryparser/queryparser.lemony"
    2763                 :            : { yymsp[-2].minor.yy45 = yymsp[-1].minor.yy45; }
    2764                 :            : #line 2765 "queryparser/queryparser_internal.cc"
    2765                 :        207 :   yy_destructor(yypParser,22,&yymsp[0].minor);
    2766                 :            : }
    2767                 :        207 :         break;
    2768                 :            :       case 36: /* compound_term ::= SYNONYM TERM */
    2769                 :         30 : {  yy_destructor(yypParser,11,&yymsp[-1].minor);
    2770                 :            : #line 2368 "queryparser/queryparser.lemony"
    2771                 :            : {
    2772                 :            :     yymsp[-1].minor.yy45 = new Query(yymsp[0].minor.yy0->get_query_with_synonyms());
    2773                 :            :     delete yymsp[0].minor.yy0;
    2774                 :            : }
    2775                 :            : #line 2776 "queryparser/queryparser_internal.cc"
    2776                 :            : }
    2777                 :         30 :         break;
    2778                 :            :       case 37: /* compound_term ::= CJKTERM */
    2779                 :            : #line 2373 "queryparser/queryparser.lemony"
    2780                 :            : {
    2781                 :            :     { yymsp[0].minor.yy45 = yymsp[0].minor.yy0->as_cjk_query(); /*T-overwrites-U*/ }
    2782                 :            : }
    2783                 :            : #line 2784 "queryparser/queryparser_internal.cc"
    2784                 :         31 :         break;
    2785                 :            :       case 38: /* phrase ::= TERM */
    2786                 :            : #line 2383 "queryparser/queryparser.lemony"
    2787                 :            : {
    2788                 :            :     yylhsminor.yy14 = Terms::create();
    2789                 :            :     yylhsminor.yy14->add_positional_term(yymsp[0].minor.yy0);
    2790                 :            : }
    2791                 :            : #line 2792 "queryparser/queryparser_internal.cc"
    2792                 :        199 :   yymsp[0].minor.yy14 = yylhsminor.yy14;
    2793                 :        199 :         break;
    2794                 :            :       case 39: /* phrase ::= CJKTERM */
    2795                 :            : #line 2388 "queryparser/queryparser.lemony"
    2796                 :            : {
    2797                 :            :     yylhsminor.yy14 = Terms::create();
    2798                 :            :     yymsp[0].minor.yy0->as_positional_cjk_term(yylhsminor.yy14);
    2799                 :            : }
    2800                 :            : #line 2801 "queryparser/queryparser_internal.cc"
    2801                 :          2 :   yymsp[0].minor.yy14 = yylhsminor.yy14;
    2802                 :          2 :         break;
    2803                 :            :       case 40: /* phrase ::= phrase TERM */
    2804                 :            :       case 43: /* phrased_term ::= phrased_term PHR_TERM */ yytestcase(yyruleno==43);
    2805                 :            : #line 2393 "queryparser/queryparser.lemony"
    2806                 :            : {
    2807                 :            :     yymsp[-1].minor.yy14->add_positional_term(yymsp[0].minor.yy0);
    2808                 :            : }
    2809                 :            : #line 2810 "queryparser/queryparser_internal.cc"
    2810                 :        505 :         break;
    2811                 :            :       case 41: /* phrase ::= phrase CJKTERM */
    2812                 :            : #line 2397 "queryparser/queryparser.lemony"
    2813                 :            : {
    2814                 :            :     yymsp[0].minor.yy0->as_positional_cjk_term(yymsp[-1].minor.yy14);
    2815                 :            : }
    2816                 :            : #line 2817 "queryparser/queryparser_internal.cc"
    2817                 :          1 :         break;
    2818                 :            :       case 42: /* phrased_term ::= TERM PHR_TERM */
    2819                 :            : #line 2408 "queryparser/queryparser.lemony"
    2820                 :            : {
    2821                 :            :     yylhsminor.yy14 = Terms::create();
    2822                 :            :     yylhsminor.yy14->add_positional_term(yymsp[-1].minor.yy0);
    2823                 :            :     yylhsminor.yy14->add_positional_term(yymsp[0].minor.yy0);
    2824                 :            : }
    2825                 :            : #line 2826 "queryparser/queryparser_internal.cc"
    2826                 :        232 :   yymsp[-1].minor.yy14 = yylhsminor.yy14;
    2827                 :        232 :         break;
    2828                 :            :       case 44: /* group ::= TERM GROUP_TERM */
    2829                 :            : #line 2424 "queryparser/queryparser.lemony"
    2830                 :            : {
    2831                 :            :     yymsp[-1].minor.yy60 = TermGroup::create(yymsp[-1].minor.yy0, yymsp[0].minor.yy0); /*P-overwrites-T*/
    2832                 :            : }
    2833                 :            : #line 2834 "queryparser/queryparser_internal.cc"
    2834                 :        600 :         break;
    2835                 :            :       case 45: /* group ::= group GROUP_TERM */
    2836                 :            : #line 2428 "queryparser/queryparser.lemony"
    2837                 :            : {
    2838                 :            :     yymsp[-1].minor.yy60->add_term(yymsp[0].minor.yy0);
    2839                 :            : }
    2840                 :            : #line 2841 "queryparser/queryparser_internal.cc"
    2841                 :      40792 :         break;
    2842                 :            :       case 46: /* group ::= group EMPTY_GROUP_OK */
    2843                 :            : #line 2432 "queryparser/queryparser.lemony"
    2844                 :            : {
    2845                 :            :     yymsp[-1].minor.yy60->set_empty_ok();
    2846                 :            : }
    2847                 :            : #line 2848 "queryparser/queryparser_internal.cc"
    2848                 :         40 :   yy_destructor(yypParser,24,&yymsp[0].minor);
    2849                 :         40 :         break;
    2850                 :            :       case 47: /* near_expr ::= TERM NEAR TERM */
    2851                 :            :       case 49: /* adj_expr ::= TERM ADJ TERM */ yytestcase(yyruleno==49);
    2852                 :            : #line 2442 "queryparser/queryparser.lemony"
    2853                 :            : {
    2854                 :            :     yylhsminor.yy14 = Terms::create();
    2855                 :            :     yylhsminor.yy14->add_positional_term(yymsp[-2].minor.yy0);
    2856                 :            :     yylhsminor.yy14->add_positional_term(yymsp[0].minor.yy0);
    2857                 :            :     if (yymsp[-1].minor.yy0) {
    2858                 :            :         yylhsminor.yy14->adjust_window(yymsp[-1].minor.yy0->get_termpos());
    2859                 :            :         delete yymsp[-1].minor.yy0;
    2860                 :            :     }
    2861                 :            : }
    2862                 :            : #line 2863 "queryparser/queryparser_internal.cc"
    2863                 :         13 :   yymsp[-2].minor.yy14 = yylhsminor.yy14;
    2864                 :         13 :         break;
    2865                 :            :       case 48: /* near_expr ::= near_expr NEAR TERM */
    2866                 :            :       case 50: /* adj_expr ::= adj_expr ADJ TERM */ yytestcase(yyruleno==50);
    2867                 :            : #line 2452 "queryparser/queryparser.lemony"
    2868                 :            : {
    2869                 :            :     yymsp[-2].minor.yy14->add_positional_term(yymsp[0].minor.yy0);
    2870                 :            :     if (yymsp[-1].minor.yy0) {
    2871                 :            :         yymsp[-2].minor.yy14->adjust_window(yymsp[-1].minor.yy0->get_termpos());
    2872                 :            :         delete yymsp[-1].minor.yy0;
    2873                 :            :     }
    2874                 :            : }
    2875                 :            : #line 2876 "queryparser/queryparser_internal.cc"
    2876                 :          3 :         break;
    2877                 :            :       default:
    2878                 :            :       /* (51) expr ::= prob_expr (OPTIMIZED OUT) */ Assert(yyruleno!=51);
    2879                 :            :       /* (52) bool_arg ::= expr */ yytestcase(yyruleno==52);
    2880                 :            :       /* (53) prob_expr ::= term (OPTIMIZED OUT) */ Assert(yyruleno!=53);
    2881                 :            :       /* (54) stop_prob ::= prob */ yytestcase(yyruleno==54);
    2882                 :            :       /* (55) stop_term ::= compound_term */ yytestcase(yyruleno==55);
    2883                 :            :       /* (56) term ::= compound_term */ yytestcase(yyruleno==56);
    2884                 :       1494 :         break;
    2885                 :            : /********** End reduce actions ************************************************/
    2886                 :            :   }
    2887                 :            :   Assert( yyruleno<sizeof(yyRuleInfo)/sizeof(yyRuleInfo[0]) );
    2888                 :     195245 :   yygoto = yyRuleInfo[yyruleno].lhs;
    2889                 :     195245 :   yysize = yyRuleInfo[yyruleno].nrhs;
    2890                 :     195245 :   yyact = yy_find_reduce_action(yymsp[yysize].stateno,static_cast<YYCODETYPE>(yygoto));
    2891                 :            : 
    2892                 :            :   /* There are no SHIFTREDUCE actions on nonterminals because the table
    2893                 :            :   ** generator has simplified them to pure REDUCE actions. */
    2894                 :            :   Assert( !(yyact>YY_MAX_SHIFT && yyact<=YY_MAX_SHIFTREDUCE) );
    2895                 :            : 
    2896                 :            :   /* It is not possible for a REDUCE to be followed by an error */
    2897                 :            :   Assert( yyact!=YY_ERROR_ACTION );
    2898                 :            : 
    2899                 :     195245 :   yymsp += yysize+1;
    2900                 :     195245 :   yypParser->yystack.resize(yypParser->yystack.size() + yysize+1);
    2901                 :            :   //yymsp = &yypParser->yystack.back();
    2902                 :     195245 :   yymsp->stateno = static_cast<YYACTIONTYPE>(yyact);
    2903                 :     195245 :   yymsp->major = static_cast<YYCODETYPE>(yygoto);
    2904                 :            :   yyTraceShift(yypParser, yyact, "... then shift");
    2905                 :            : }
    2906                 :            : 
    2907                 :            : /*
    2908                 :            : ** The following code executes when the parse fails
    2909                 :            : */
    2910                 :            : #ifndef YYNOERRORRECOVERY
    2911                 :        156 : static void yy_parse_failed(
    2912                 :            :   yyParser *yypParser           /* The parser */
    2913                 :            : ){
    2914                 :        156 :   ParseARG_FETCH;
    2915                 :            :   LOGLINE(QUERYPARSER, "Fail!");
    2916         [ +  + ]:        425 :   while( yypParser->yystack.size() > 1 ) yy_pop_parser_stack(yypParser);
    2917                 :            :   /* Here code is inserted which will be executed whenever the
    2918                 :            :   ** parser fails */
    2919                 :            : /************ Begin %parse_failure code ***************************************/
    2920                 :            : #line 2005 "queryparser/queryparser.lemony"
    2921                 :            : 
    2922                 :            :     // If we've not already set an error message, set a default one.
    2923                 :            :     if (!state->error) state->error = "parse error";
    2924                 :            : #line 2925 "queryparser/queryparser_internal.cc"
    2925                 :            : /************ End %parse_failure code *****************************************/
    2926                 :        156 :   ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */
    2927                 :        156 : }
    2928                 :            : #endif /* YYNOERRORRECOVERY */
    2929                 :            : 
    2930                 :            : /*
    2931                 :            : ** The following code executes when a syntax error first occurs.
    2932                 :            : */
    2933                 :        115 : static void yy_syntax_error(
    2934                 :            :   yyParser *yypParser,           /* The parser */
    2935                 :            :   int yymajor,                   /* The major type of the error token */
    2936                 :            :   ParseTOKENTYPE yyminor         /* The minor type of the error token */
    2937                 :            : ){
    2938                 :        115 :   ParseARG_FETCH;
    2939                 :            :   (void)yymajor;
    2940                 :            :   (void)yyminor;
    2941                 :            : #define TOKEN yyminor
    2942                 :            : /************ Begin %syntax_error code ****************************************/
    2943                 :            : #line 2010 "queryparser/queryparser.lemony"
    2944                 :            : 
    2945                 :            :     yy_parse_failed(yypParser);
    2946                 :            : #line 2947 "queryparser/queryparser_internal.cc"
    2947                 :            : /************ End %syntax_error code ******************************************/
    2948                 :        115 :   ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */
    2949                 :        115 : }
    2950                 :            : 
    2951                 :            : /*
    2952                 :            : ** The following is executed when the parser accepts
    2953                 :            : */
    2954                 :      64288 : static void yy_accept(
    2955                 :            :   yyParser *yypParser           /* The parser */
    2956                 :            : ){
    2957                 :      64288 :   ParseARG_FETCH;
    2958                 :            :   LOGLINE(QUERYPARSER, "Accept!");
    2959                 :            : #ifndef YYNOERRORRECOVERY
    2960                 :      64288 :   yypParser->yyerrcnt = -1;
    2961                 :            : #endif
    2962                 :            :   AssertEq( yypParser->yystack.size(), 1 );
    2963                 :            :   /* Here code is inserted which will be executed whenever the
    2964                 :            :   ** parser accepts */
    2965                 :            : /*********** Begin %parse_accept code *****************************************/
    2966                 :            : /*********** End %parse_accept code *******************************************/
    2967                 :      64288 :   ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */
    2968                 :      64288 : }
    2969                 :            : 
    2970                 :            : /* The main parser program.
    2971                 :            : ** The first argument is a pointer to a structure obtained from
    2972                 :            : ** "ParseAlloc" which describes the current state of the parser.
    2973                 :            : ** The second argument is the major token number.  The third is
    2974                 :            : ** the minor token.  The fourth optional argument is whatever the
    2975                 :            : ** user wants (and specified in the grammar) and is available for
    2976                 :            : ** use by the action routines.
    2977                 :            : **
    2978                 :            : ** Inputs:
    2979                 :            : ** <ul>
    2980                 :            : ** <li> A pointer to the parser (an opaque structure.)
    2981                 :            : ** <li> The major token number.
    2982                 :            : ** <li> The minor token number.
    2983                 :            : ** <li> An option argument of a grammar-specified type.
    2984                 :            : ** </ul>
    2985                 :            : **
    2986                 :            : ** Outputs:
    2987                 :            : ** None.
    2988                 :            : */
    2989                 :            : static
    2990                 :     173709 : void Parse(
    2991                 :            :   yyParser *yypParser,         /* The parser */
    2992                 :            :   int yymajor,                 /* The major token code number */
    2993                 :            :   ParseTOKENTYPE yyminor       /* The value for the token */
    2994                 :            :   ParseARG_PDECL               /* Optional %extra_argument parameter */
    2995                 :            : ){
    2996                 :            :   YYMINORTYPE yyminorunion;
    2997                 :            :   unsigned int yyact;   /* The parser action. */
    2998                 :            : #if !defined(YYERRORSYMBOL) && !defined(YYNOERRORRECOVERY)
    2999                 :            :   int yyendofinput;     /* True if we are at the end of input */
    3000                 :            : #endif
    3001                 :            : #ifdef YYERRORSYMBOL
    3002                 :            :   int yyerrorhit = 0;   /* True if yymajor has invoked an error */
    3003                 :            : #endif
    3004                 :            : 
    3005                 :            : #if !defined(YYERRORSYMBOL) && !defined(YYNOERRORRECOVERY)
    3006                 :     173709 :   yyendofinput = (yymajor==0);
    3007                 :            : #endif
    3008                 :     173709 :   ParseARG_STORE;
    3009                 :            : 
    3010                 :            : #ifdef XAPIAN_DEBUG_LOG
    3011                 :            :   {
    3012                 :            :     int stateno = yypParser->yystack.back().stateno;
    3013                 :            :     if( stateno < YY_MIN_REDUCE ){
    3014                 :            :       LOGLINE(QUERYPARSER, "Input '" << ParseTokenName(yymajor) <<
    3015                 :            :                            "'," << (yyminor ? yyminor->name : "<<null>>") <<
    3016                 :            :                            "in state " << stateno);
    3017                 :            :     }else{
    3018                 :            :       LOGLINE(QUERYPARSER, "Input '" << ParseTokenName(yymajor) <<
    3019                 :            :                            "'," << (yyminor ? yyminor->name : "<<null>>") <<
    3020                 :            :                            "with pending reduce " << stateno-YY_MIN_REDUCE);
    3021                 :            :     }
    3022                 :            :   }
    3023                 :            : #endif
    3024                 :            : 
    3025         [ +  + ]:     304666 :   do{
    3026                 :     368954 :     yyact = yy_find_shift_action(yypParser,static_cast<YYCODETYPE>(yymajor));
    3027         [ +  + ]:     368954 :     if( yyact >= YY_MIN_REDUCE ){
    3028         [ +  - ]:     195278 :       yy_reduce(yypParser,yyact-YY_MIN_REDUCE,yymajor,yyminor);
    3029         [ +  + ]:     173676 :     }else if( yyact <= YY_MAX_SHIFTREDUCE ){
    3030         [ +  - ]:     109273 :       yy_shift(yypParser,yyact,yymajor,yyminor);
    3031                 :            : #ifndef YYNOERRORRECOVERY
    3032                 :     109273 :       yypParser->yyerrcnt--;
    3033                 :            : #endif
    3034                 :     109273 :       yymajor = YYNOCODE;
    3035         [ +  + ]:      64403 :     }else if( yyact==YY_ACCEPT_ACTION ){
    3036                 :      64288 :       yypParser->yystack.pop_back();
    3037                 :      64288 :       yy_accept(yypParser);
    3038                 :      64288 :       return;
    3039                 :            :     }else{
    3040                 :            :       Assert( yyact == YY_ERROR_ACTION );
    3041                 :        115 :       yyminorunion.yy0 = yyminor;
    3042                 :            : #ifdef YYERRORSYMBOL
    3043                 :            :       int yymx;
    3044                 :            : #endif
    3045                 :            :       LOGLINE(QUERYPARSER, "Syntax Error!");
    3046                 :            : #ifdef YYERRORSYMBOL
    3047                 :            :       /* A syntax error has occurred.
    3048                 :            :       ** The response to an error depends upon whether or not the
    3049                 :            :       ** grammar defines an error token "ERROR".  
    3050                 :            :       **
    3051                 :            :       ** This is what we do if the grammar does define ERROR:
    3052                 :            :       **
    3053                 :            :       **  * Call the %syntax_error function.
    3054                 :            :       **
    3055                 :            :       **  * Begin popping the stack until we enter a state where
    3056                 :            :       **    it is legal to shift the error symbol, then shift
    3057                 :            :       **    the error symbol.
    3058                 :            :       **
    3059                 :            :       **  * Set the error count to three.
    3060                 :            :       **
    3061                 :            :       **  * Begin accepting and shifting new tokens.  No new error
    3062                 :            :       **    processing will occur until three tokens have been
    3063                 :            :       **    shifted successfully.
    3064                 :            :       **
    3065                 :            :       */
    3066                 :            :       if( yypParser->yyerrcnt<0 ){
    3067                 :            :         yy_syntax_error(yypParser,yymajor,yyminor);
    3068                 :            :       }
    3069                 :            :       yymx = yypParser->yystack.back().major;
    3070                 :            :       if( yymx==YYERRORSYMBOL || yyerrorhit ){
    3071                 :            :         LOGLINE(QUERYPARSER, "Discard input token " << ParseTokenName(yymajor));
    3072                 :            :         yy_destructor(yypParser, static_cast<YYCODETYPE>(yymajor), &yyminorunion);
    3073                 :            :         yymajor = YYNOCODE;
    3074                 :            :       }else{
    3075                 :            :         while( !yypParser->yystack.empty()
    3076                 :            :             && yymx != YYERRORSYMBOL
    3077                 :            :             && (yyact = yy_find_reduce_action(
    3078                 :            :                         yypParser->yystack.back().stateno,
    3079                 :            :                         YYERRORSYMBOL)) >= YY_MIN_REDUCE
    3080                 :            :         ){
    3081                 :            :           yy_pop_parser_stack(yypParser);
    3082                 :            :         }
    3083                 :            :         if( yypParser->yystack.empty() || yymajor==0 ){
    3084                 :            :           yy_destructor(yypParser,static_cast<YYCODETYPE>(yymajor),&yyminorunion);
    3085                 :            :           yy_parse_failed(yypParser);
    3086                 :            : #ifndef YYNOERRORRECOVERY
    3087                 :            :           yypParser->yyerrcnt = -1;
    3088                 :            : #endif
    3089                 :            :           yymajor = YYNOCODE;
    3090                 :            :         }else if( yymx!=YYERRORSYMBOL ){
    3091                 :            :           yy_shift(yypParser,yyact,YYERRORSYMBOL,yyminor);
    3092                 :            :         }
    3093                 :            :       }
    3094                 :            :       yypParser->yyerrcnt = 3;
    3095                 :            :       yyerrorhit = 1;
    3096                 :            : #elif defined(YYNOERRORRECOVERY)
    3097                 :            :       /* If the YYNOERRORRECOVERY macro is defined, then do not attempt to
    3098                 :            :       ** do any kind of error recovery.  Instead, simply invoke the syntax
    3099                 :            :       ** error routine and continue going as if nothing had happened.
    3100                 :            :       **
    3101                 :            :       ** Applications can set this macro (for example inside %include) if
    3102                 :            :       ** they intend to abandon the parse upon the first syntax error seen.
    3103                 :            :       */
    3104                 :            :       yy_syntax_error(yypParser,yymajor, yyminor);
    3105                 :            :       yy_destructor(yypParser,static_cast<YYCODETYPE>(yymajor),&yyminorunion);
    3106                 :            :       yymajor = YYNOCODE;
    3107                 :            :       
    3108                 :            : #else  /* YYERRORSYMBOL is not defined */
    3109                 :            :       /* This is what we do if the grammar does not define ERROR:
    3110                 :            :       **
    3111                 :            :       **  * Report an error message, and throw away the input token.
    3112                 :            :       **
    3113                 :            :       **  * If the input token is $, then fail the parse.
    3114                 :            :       **
    3115                 :            :       ** As before, subsequent error messages are suppressed until
    3116                 :            :       ** three input tokens have been successfully shifted.
    3117                 :            :       */
    3118         [ +  - ]:        115 :       if( yypParser->yyerrcnt<=0 ){
    3119                 :        115 :         yy_syntax_error(yypParser,yymajor, yyminor);
    3120                 :            :       }
    3121                 :        115 :       yypParser->yyerrcnt = 3;
    3122                 :        115 :       yy_destructor(yypParser,static_cast<YYCODETYPE>(yymajor),&yyminorunion);
    3123         [ +  + ]:        115 :       if( yyendofinput ){
    3124                 :          8 :         yy_parse_failed(yypParser);
    3125                 :            : #ifndef YYNOERRORRECOVERY
    3126                 :          8 :         yypParser->yyerrcnt = -1;
    3127                 :            : #endif
    3128                 :            :       }
    3129                 :        115 :       yymajor = YYNOCODE;
    3130                 :            : #endif
    3131                 :            :     }
    3132 [ +  + ][ +  + ]:     304666 :   }while( yymajor!=YYNOCODE && yypParser->yystack.size() > 1 );
    3133                 :            : #ifdef XAPIAN_DEBUG_LOG
    3134                 :            :   {
    3135                 :            :     int i;
    3136                 :            :     LOGLINE(QUERYPARSER, "Return. Stack=");
    3137                 :            :     for(i=1; i<=(int)yypParser->yystack.size(); i++)
    3138                 :            :       LOGLINE(QUERYPARSER, yyTokenName[yypParser->yystack[i].major]);
    3139                 :            :   }
    3140                 :            : #endif
    3141                 :     173709 :   return;
    3142                 :            : }
    3143                 :            : 
    3144                 :            : // Select C++ syntax highlighting in vim editor: vim: syntax=cpp
    3145                 :            : #line 967 "queryparser/queryparser.lemony"
    3146                 :            : 
    3147                 :            : 
    3148                 :            : Query
    3149                 :            : QueryParser::Internal::parse_query(const string &qs, unsigned flags,
    3150                 :            :                                    const string &default_prefix)
    3151                 :            : {
    3152                 :            : #ifndef USE_ICU
    3153                 :            :     // Overall it seems best to check for this up front - otherwise we create
    3154                 :            :     // the unhelpful situation where a failure to enable ICU in the build could
    3155                 :            :     // be missed because non-CJK queries still work fine.
    3156                 :            :     if (flags & FLAG_CJK_WORDS) {
    3157                 :            :         throw Xapian::FeatureUnavailableError("FLAG_CJK_WORDS requires "
    3158                 :            :                                               "building Xapian to use ICU");
    3159                 :            :     }
    3160                 :            : #endif
    3161                 :            :     bool cjk_enable =
    3162                 :            :         (flags & (FLAG_CJK_NGRAM|FLAG_CJK_WORDS)) || CJK::is_cjk_enabled();
    3163                 :            : 
    3164                 :            :     // Set ranges if we may have to handle ranges in the query.
    3165                 :            :     bool ranges = !rangeprocs.empty() && (qs.find("..") != string::npos);
    3166                 :            : 
    3167                 :            :     termpos term_pos = 1;
    3168                 :            :     Utf8Iterator it(qs), end;
    3169                 :            : 
    3170                 :            :     State state(this, flags);
    3171                 :            : 
    3172                 :            :     // To successfully apply more than one spelling correction to a query
    3173                 :            :     // string, we must keep track of the offset due to previous corrections.
    3174                 :            :     int correction_offset = 0;
    3175                 :            :     corrected_query.resize(0);
    3176                 :            : 
    3177                 :            :     // Stack of prefixes, used for phrases and subexpressions.
    3178                 :            :     list<const FieldInfo *> prefix_stack;
    3179                 :            : 
    3180                 :            :     // If default_prefix is specified, use it.  Otherwise, use any list
    3181                 :            :     // that has been set for the empty prefix.
    3182                 :            :     const FieldInfo def_pfx(NON_BOOLEAN, default_prefix);
    3183                 :            :     {
    3184                 :            :         const FieldInfo * default_field_info = &def_pfx;
    3185                 :            :         if (default_prefix.empty()) {
    3186                 :            :             auto f = field_map.find(string());
    3187                 :            :             if (f != field_map.end()) default_field_info = &(f->second);
    3188                 :            :         }
    3189                 :            : 
    3190                 :            :         // We always have the current prefix on the top of the stack.
    3191                 :            :         prefix_stack.push_back(default_field_info);
    3192                 :            :     }
    3193                 :            : 
    3194                 :            :     yyParser parser;
    3195                 :            : 
    3196                 :            :     unsigned newprev = ' ';
    3197                 :            : main_lex_loop:
    3198                 :            :     enum {
    3199                 :            :         DEFAULT, IN_QUOTES, IN_PREFIXED_QUOTES, IN_PHRASED_TERM, IN_GROUP,
    3200                 :            :         IN_GROUP2, EXPLICIT_SYNONYM
    3201                 :            :     } mode = DEFAULT;
    3202                 :            :     while (it != end && !state.error) {
    3203                 :            :         bool last_was_operator = false;
    3204                 :            :         bool last_was_operator_needing_term = false;
    3205                 :            :         if (mode == EXPLICIT_SYNONYM) mode = DEFAULT;
    3206                 :            :         if (false) {
    3207                 :            : just_had_operator:
    3208                 :            :             if (it == end) break;
    3209                 :            :             mode = DEFAULT;
    3210                 :            :             last_was_operator_needing_term = false;
    3211                 :            :             last_was_operator = true;
    3212                 :            :         }
    3213                 :            :         if (false) {
    3214                 :            : just_had_operator_needing_term:
    3215                 :            :             last_was_operator_needing_term = true;
    3216                 :            :             last_was_operator = true;
    3217                 :            :         }
    3218                 :            :         if (mode == IN_PHRASED_TERM) mode = DEFAULT;
    3219                 :            :         if (is_whitespace(*it)) {
    3220                 :            :             newprev = ' ';
    3221                 :            :             ++it;
    3222                 :            :             it = find_if(it, end, is_not_whitespace);
    3223                 :            :             if (it == end) break;
    3224                 :            :         }
    3225                 :            : 
    3226                 :            :         if (ranges &&
    3227                 :            :             (mode == DEFAULT || mode == IN_GROUP || mode == IN_GROUP2)) {
    3228                 :            :             // Scan forward to see if this could be the "start of range"
    3229                 :            :             // token.  Sadly this has O(n^2) tendencies, though at least
    3230                 :            :             // "n" is the number of words in a query which is likely to
    3231                 :            :             // remain fairly small.  FIXME: can we tokenise more elegantly?
    3232                 :            :             Utf8Iterator it_initial = it;
    3233                 :            :             Utf8Iterator p = it;
    3234                 :            :             unsigned ch = 0;
    3235                 :            :             while (p != end) {
    3236                 :            :                 if (ch == '.' && *p == '.') {
    3237                 :            :                     string a;
    3238                 :            :                     while (it != p) {
    3239                 :            :                         Unicode::append_utf8(a, *it++);
    3240                 :            :                     }
    3241                 :            :                     // Trim off the trailing ".".
    3242                 :            :                     a.resize(a.size() - 1);
    3243                 :            :                     ++p;
    3244                 :            :                     // Either end of the range can be empty (for an open-ended
    3245                 :            :                     // range) but both can't be empty.
    3246                 :            :                     if (!a.empty() || (p != end && *p > ' ' && *p != ')')) {
    3247                 :            :                         string b;
    3248                 :            :                         // Allow any character except whitespace and ')' in the
    3249                 :            :                         // upper bound.
    3250                 :            :                         while (p != end && *p > ' ' && *p != ')') {
    3251                 :            :                             Unicode::append_utf8(b, *p++);
    3252                 :            :                         }
    3253                 :            :                         Term * range = state.range(a, b);
    3254                 :            :                         if (!range) {
    3255                 :            :                             state.error = "Unknown range operation";
    3256                 :            :                             if (a.find(':', 1) == string::npos) {
    3257                 :            :                                 goto done;
    3258                 :            :                             }
    3259                 :            :                             // Might be a boolean filter with ".." in.  Leave
    3260                 :            :                             // state.error in case it isn't.
    3261                 :            :                             it = it_initial;
    3262                 :            :                             break;
    3263                 :            :                         }
    3264                 :            :                         Parse(&parser, RANGE, range, &state);
    3265                 :            :                     }
    3266                 :            :                     it = p;
    3267                 :            :                     goto main_lex_loop;
    3268                 :            :                 }
    3269                 :            :                 ch = *p;
    3270                 :            :                 // Allow any character except whitespace and '(' in the lower
    3271                 :            :                 // bound.
    3272                 :            :                 if (ch <= ' ' || ch == '(') break;
    3273                 :            :                 ++p;
    3274                 :            :             }
    3275                 :            :         }
    3276                 :            : 
    3277                 :            :         if (!is_wordchar(*it)) {
    3278                 :            :             unsigned prev = newprev;
    3279                 :            :             Utf8Iterator p = it;
    3280                 :            :             unsigned ch = *it++;
    3281                 :            :             newprev = ch;
    3282                 :            :             // Drop out of IN_GROUP mode.
    3283                 :            :             if (mode == IN_GROUP || mode == IN_GROUP2)
    3284                 :            :                 mode = DEFAULT;
    3285                 :            :             switch (ch) {
    3286                 :            :               case '"':
    3287                 :            :               case 0x201c: // Left curly double quote.
    3288                 :            :               case 0x201d: // Right curly double quote.
    3289                 :            :                 // Quoted phrase.
    3290                 :            :                 if (mode == DEFAULT) {
    3291                 :            :                     // Skip whitespace.
    3292                 :            :                     it = find_if(it, end, is_not_whitespace);
    3293                 :            :                     if (it == end) {
    3294                 :            :                         // Ignore an unmatched " at the end of the query to
    3295                 :            :                         // avoid generating an empty pair of QUOTEs which will
    3296                 :            :                         // cause a parse error.
    3297                 :            :                         goto done;
    3298                 :            :                     }
    3299                 :            :                     if (is_double_quote(*it)) {
    3300                 :            :                         // Ignore empty "" (but only if we're not already
    3301                 :            :                         // IN_QUOTES as we don't merge two adjacent quoted
    3302                 :            :                         // phrases!)
    3303                 :            :                         newprev = *it++;
    3304                 :            :                         break;
    3305                 :            :                     }
    3306                 :            :                 }
    3307                 :            :                 if (flags & QueryParser::FLAG_PHRASE) {
    3308                 :            :                     Parse(&parser, QUOTE, NULL, &state);
    3309                 :            :                     if (mode == DEFAULT) {
    3310                 :            :                         mode = IN_QUOTES;
    3311                 :            :                     } else {
    3312                 :            :                         // Remove the prefix we pushed for this phrase.
    3313                 :            :                         if (mode == IN_PREFIXED_QUOTES)
    3314                 :            :                             prefix_stack.pop_back();
    3315                 :            :                         mode = DEFAULT;
    3316                 :            :                     }
    3317                 :            :                 }
    3318                 :            :                 break;
    3319                 :            : 
    3320                 :            :               case '+': case '-': // Loved or hated term/phrase/subexpression.
    3321                 :            :                 // Ignore + or - at the end of the query string.
    3322                 :            :                 if (it == end) goto done;
    3323                 :            :                 if (prev > ' ' && prev != '(') {
    3324                 :            :                     // Or if not after whitespace or an open bracket.
    3325                 :            :                     break;
    3326                 :            :                 }
    3327                 :            :                 if (is_whitespace(*it) || *it == '+' || *it == '-') {
    3328                 :            :                     // Ignore + or - followed by a space, or further + or -.
    3329                 :            :                     // Postfix + (such as in C++ and H+) is handled as part of
    3330                 :            :                     // the term lexing code in parse_term().
    3331                 :            :                     newprev = *it++;
    3332                 :            :                     break;
    3333                 :            :                 }
    3334                 :            :                 if (mode == DEFAULT && (flags & FLAG_LOVEHATE)) {
    3335                 :            :                     int token;
    3336                 :            :                     if (ch == '+') {
    3337                 :            :                         token = LOVE;
    3338                 :            :                     } else if (last_was_operator) {
    3339                 :            :                         token = HATE_AFTER_AND;
    3340                 :            :                     } else {
    3341                 :            :                         token = HATE;
    3342                 :            :                     }
    3343                 :            :                     Parse(&parser, token, NULL, &state);
    3344                 :            :                     goto just_had_operator_needing_term;
    3345                 :            :                 }
    3346                 :            :                 // Need to prevent the term after a LOVE or HATE starting a
    3347                 :            :                 // term group...
    3348                 :            :                 break;
    3349                 :            : 
    3350                 :            :               case '(': // Bracketed subexpression.
    3351                 :            :                 // Skip whitespace.
    3352                 :            :                 it = find_if(it, end, is_not_whitespace);
    3353                 :            :                 // Ignore ( at the end of the query string.
    3354                 :            :                 if (it == end) goto done;
    3355                 :            :                 if (prev > ' ' && strchr("()+-", prev) == NULL) {
    3356                 :            :                     // Or if not after whitespace or a bracket or '+' or '-'.
    3357                 :            :                     break;
    3358                 :            :                 }
    3359                 :            :                 if (*it == ')') {
    3360                 :            :                     // Ignore empty ().
    3361                 :            :                     newprev = *it++;
    3362                 :            :                     break;
    3363                 :            :                 }
    3364                 :            :                 if (mode == DEFAULT && (flags & FLAG_BOOLEAN)) {
    3365                 :            :                     prefix_stack.push_back(prefix_stack.back());
    3366                 :            :                     Parse(&parser, BRA, NULL, &state);
    3367                 :            :                 }
    3368                 :            :                 break;
    3369                 :            : 
    3370                 :            :               case ')': // End of bracketed subexpression.
    3371                 :            :                 if (mode == DEFAULT && (flags & FLAG_BOOLEAN)) {
    3372                 :            :                     // Remove the prefix we pushed for the corresponding BRA.
    3373                 :            :                     // If brackets are unmatched, it's a syntax error, but
    3374                 :            :                     // that's no excuse to SEGV!
    3375                 :            :                     if (prefix_stack.size() > 1) prefix_stack.pop_back();
    3376                 :            :                     Parse(&parser, KET, NULL, &state);
    3377                 :            :                 }
    3378                 :            :                 break;
    3379                 :            : 
    3380                 :            :               case '~': // Synonym expansion.
    3381                 :            :                 // Ignore at the end of the query string.
    3382                 :            :                 if (it == end) goto done;
    3383                 :            :                 if (mode == DEFAULT && (flags & FLAG_SYNONYM)) {
    3384                 :            :                     if (prev > ' ' && strchr("+-(", prev) == NULL) {
    3385                 :            :                         // Or if not after whitespace, +, -, or an open bracket.
    3386                 :            :                         break;
    3387                 :            :                     }
    3388                 :            :                     if (!is_wordchar(*it)) {
    3389                 :            :                         // Ignore if not followed by a word character.
    3390                 :            :                         break;
    3391                 :            :                     }
    3392                 :            :                     Parse(&parser, SYNONYM, NULL, &state);
    3393                 :            :                     mode = EXPLICIT_SYNONYM;
    3394                 :            :                     goto just_had_operator_needing_term;
    3395                 :            :                 }
    3396                 :            :                 break;
    3397                 :            :               case '*':
    3398                 :            :                 if (flags & FLAG_WILDCARD_MULTI) {
    3399                 :            :                     it = p;
    3400                 :            :                     goto leading_wildcard;
    3401                 :            :                 }
    3402                 :            :                 break;
    3403                 :            :               case '?':
    3404                 :            :                 if (flags & FLAG_WILDCARD_SINGLE) {
    3405                 :            :                     it = p;
    3406                 :            :                     goto leading_wildcard;
    3407                 :            :                 }
    3408                 :            :                 break;
    3409                 :            :             }
    3410                 :            :             // Skip any other characters.
    3411                 :            :             continue;
    3412                 :            :         }
    3413                 :            : 
    3414                 :            :         Assert(is_wordchar(*it));
    3415                 :            : 
    3416                 :            : leading_wildcard:
    3417                 :            :         size_t term_start_index = it.raw() - qs.data();
    3418                 :            : 
    3419                 :            :         newprev = 'A'; // Any letter will do...
    3420                 :            : 
    3421                 :            :         // A term, a prefix, or a boolean operator.
    3422                 :            :         const FieldInfo * field_info = NULL;
    3423                 :            :         if ((mode == DEFAULT || mode == IN_GROUP || mode == IN_GROUP2 || mode == EXPLICIT_SYNONYM) &&
    3424                 :            :             !field_map.empty()) {
    3425                 :            :             // Check for a fieldname prefix (e.g. title:historical).
    3426                 :            :             Utf8Iterator p = find_if(it, end, is_not_wordchar);
    3427                 :            :             if (p != end && *p == ':' && ++p != end && *p > ' ' && *p != ')') {
    3428                 :            :                 string field;
    3429                 :            :                 p = it;
    3430                 :            :                 while (*p != ':')
    3431                 :            :                     Unicode::append_utf8(field, *p++);
    3432                 :            :                 map<string, FieldInfo>::const_iterator f;
    3433                 :            :                 f = field_map.find(field);
    3434                 :            :                 if (f != field_map.end()) {
    3435                 :            :                     // Special handling for prefixed fields, depending on the
    3436                 :            :                     // type of the prefix.
    3437                 :            :                     unsigned ch = *++p;
    3438                 :            :                     field_info = &(f->second);
    3439                 :            : 
    3440                 :            :                     if (field_info->type != NON_BOOLEAN) {
    3441                 :            :                         // Drop out of IN_GROUP if we're in it.
    3442                 :            :                         if (mode == IN_GROUP || mode == IN_GROUP2)
    3443                 :            :                             mode = DEFAULT;
    3444                 :            :                         it = p;
    3445                 :            :                         string name;
    3446                 :            :                         if (it != end && is_double_quote(*it)) {
    3447                 :            :                             // Quoted boolean term (can contain any character).
    3448                 :            :                             bool fancy = (*it != '"');
    3449                 :            :                             ++it;
    3450                 :            :                             while (it != end) {
    3451                 :            :                                 if (*it == '"') {
    3452                 :            :                                     // Interpret "" as an escaped ".
    3453                 :            :                                     if (++it == end || *it != '"')
    3454                 :            :                                         break;
    3455                 :            :                                 } else if (fancy && is_double_quote(*it)) {
    3456                 :            :                                     // If the opening quote was ASCII, then the
    3457                 :            :                                     // closing one must be too - otherwise
    3458                 :            :                                     // the user can't protect non-ASCII double
    3459                 :            :                                     // quote characters by quoting or escaping.
    3460                 :            :                                     ++it;
    3461                 :            :                                     break;
    3462                 :            :                                 }
    3463                 :            :                                 Unicode::append_utf8(name, *it++);
    3464                 :            :                             }
    3465                 :            :                         } else {
    3466                 :            :                             // Can't boolean filter prefix a subexpression, so
    3467                 :            :                             // just use anything following the prefix until the
    3468                 :            :                             // next space or ')' as part of the boolean filter
    3469                 :            :                             // term.
    3470                 :            :                             while (it != end && *it > ' ' && *it != ')')
    3471                 :            :                                 Unicode::append_utf8(name, *it++);
    3472                 :            :                         }
    3473                 :            :                         // Build the unstemmed form in field.
    3474                 :            :                         field += ':';
    3475                 :            :                         field += name;
    3476                 :            :                         // Clear any pending range error.
    3477                 :            :                         state.error = NULL;
    3478                 :            :                         Term * token = new Term(&state, name, field_info, field);
    3479                 :            :                         Parse(&parser, BOOLEAN_FILTER, token, &state);
    3480                 :            :                         continue;
    3481                 :            :                     }
    3482                 :            : 
    3483                 :            :                     if ((flags & FLAG_PHRASE) && is_double_quote(ch)) {
    3484                 :            :                         // Prefixed phrase, e.g.: subject:"space flight"
    3485                 :            :                         mode = IN_PREFIXED_QUOTES;
    3486                 :            :                         Parse(&parser, QUOTE, NULL, &state);
    3487                 :            :                         it = p;
    3488                 :            :                         newprev = ch;
    3489                 :            :                         ++it;
    3490                 :            :                         prefix_stack.push_back(field_info);
    3491                 :            :                         continue;
    3492                 :            :                     }
    3493                 :            : 
    3494                 :            :                     if (ch == '(' && (flags & FLAG_BOOLEAN)) {
    3495                 :            :                         // Prefixed subexpression, e.g.: title:(fast NEAR food)
    3496                 :            :                         mode = DEFAULT;
    3497                 :            :                         Parse(&parser, BRA, NULL, &state);
    3498                 :            :                         it = p;
    3499                 :            :                         newprev = ch;
    3500                 :            :                         ++it;
    3501                 :            :                         prefix_stack.push_back(field_info);
    3502                 :            :                         continue;
    3503                 :            :                     }
    3504                 :            : 
    3505                 :            :                     if (ch != ':') {
    3506                 :            :                         // Allow 'path:/usr/local' but not 'foo::bar::baz'.
    3507                 :            :                         while (is_phrase_generator(ch)) {
    3508                 :            :                             if (++p == end)
    3509                 :            :                                 goto not_prefix;
    3510                 :            :                             ch = *p;
    3511                 :            :                         }
    3512                 :            :                     }
    3513                 :            : 
    3514                 :            :                     if (is_wordchar(ch)) {
    3515                 :            :                         // Prefixed term.
    3516                 :            :                         it = p;
    3517                 :            :                     } else {
    3518                 :            : not_prefix:
    3519                 :            :                         // It looks like a prefix but isn't, so parse it as
    3520                 :            :                         // text instead.
    3521                 :            :                         field_info = NULL;
    3522                 :            :                     }
    3523                 :            :                 }
    3524                 :            :             }
    3525                 :            :         }
    3526                 :            : 
    3527                 :            : phrased_term:
    3528                 :            :         bool was_acronym;
    3529                 :            :         bool is_cjk_term = false;
    3530                 :            :         size_t first_wildcard = string::npos;
    3531                 :            :         size_t term_char_count;
    3532                 :            :         unsigned edit_distance = NO_EDIT_DISTANCE;
    3533                 :            :         string term = parse_term(it, end, cjk_enable, flags,
    3534                 :            :                                  is_cjk_term, was_acronym, first_wildcard,
    3535                 :            :                                  term_char_count, edit_distance);
    3536                 :            : 
    3537                 :            :         if (first_wildcard == string::npos &&
    3538                 :            :             edit_distance == NO_EDIT_DISTANCE &&
    3539                 :            :             (mode == DEFAULT || mode == IN_GROUP || mode == IN_GROUP2) &&
    3540                 :            :             (flags & FLAG_BOOLEAN) &&
    3541                 :            :             // Don't want to interpret A.N.D. as an AND operator.
    3542                 :            :             !was_acronym &&
    3543                 :            :             !field_info &&
    3544                 :            :             term.size() >= 2 && term.size() <= 4 && U_isalpha(term[0])) {
    3545                 :            :             // Boolean operators.
    3546                 :            :             string op = term;
    3547                 :            :             if (flags & FLAG_BOOLEAN_ANY_CASE) {
    3548                 :            :                 for (string::iterator i = op.begin(); i != op.end(); ++i) {
    3549                 :            :                     *i = C_toupper(*i);
    3550                 :            :                 }
    3551                 :            :             }
    3552                 :            :             if (op.size() == 3) {
    3553                 :            :                 if (op == "AND") {
    3554                 :            :                     Parse(&parser, AND, NULL, &state);
    3555                 :            :                     goto just_had_operator;
    3556                 :            :                 }
    3557                 :            :                 if (op == "NOT") {
    3558                 :            :                     Parse(&parser, NOT, NULL, &state);
    3559                 :            :                     goto just_had_operator;
    3560                 :            :                 }
    3561                 :            :                 if (op == "XOR") {
    3562                 :            :                     Parse(&parser, XOR, NULL, &state);
    3563                 :            :                     goto just_had_operator;
    3564                 :            :                 }
    3565                 :            :                 if (op == "ADJ") {
    3566                 :            :                     if (it != end && *it == '/') {
    3567                 :            :                         size_t width = 0;
    3568                 :            :                         Utf8Iterator p = it;
    3569                 :            :                         while (++p != end && U_isdigit(*p)) {
    3570                 :            :                             width = (width * 10) + (*p - '0');
    3571                 :            :                         }
    3572                 :            :                         if (width && (p == end || is_whitespace(*p))) {
    3573                 :            :                             it = p;
    3574                 :            :                             Parse(&parser, ADJ, new Term(width), &state);
    3575                 :            :                             goto just_had_operator;
    3576                 :            :                         }
    3577                 :            :                     } else {
    3578                 :            :                         Parse(&parser, ADJ, NULL, &state);
    3579                 :            :                         goto just_had_operator;
    3580                 :            :                     }
    3581                 :            :                 }
    3582                 :            :             } else if (op.size() == 2) {
    3583                 :            :                 if (op == "OR") {
    3584                 :            :                     Parse(&parser, OR, NULL, &state);
    3585                 :            :                     goto just_had_operator;
    3586                 :            :                 }
    3587                 :            :             } else if (op.size() == 4) {
    3588                 :            :                 if (op == "NEAR") {
    3589                 :            :                     if (it != end && *it == '/') {
    3590                 :            :                         size_t width = 0;
    3591                 :            :                         Utf8Iterator p = it;
    3592                 :            :                         while (++p != end && U_isdigit(*p)) {
    3593                 :            :                             width = (width * 10) + (*p - '0');
    3594                 :            :                         }
    3595                 :            :                         if (width && (p == end || is_whitespace(*p))) {
    3596                 :            :                             it = p;
    3597                 :            :                             Parse(&parser, NEAR, new Term(width), &state);
    3598                 :            :                             goto just_had_operator;
    3599                 :            :                         }
    3600                 :            :                     } else {
    3601                 :            :                         Parse(&parser, NEAR, NULL, &state);
    3602                 :            :                         goto just_had_operator;
    3603                 :            :                     }
    3604                 :            :                 }
    3605                 :            :             }
    3606                 :            :         }
    3607                 :            : 
    3608                 :            :         // If no prefix is set, use the default one.
    3609                 :            :         if (!field_info) field_info = prefix_stack.back();
    3610                 :            : 
    3611                 :            :         Assert(field_info->type == NON_BOOLEAN);
    3612                 :            : 
    3613                 :            :         {
    3614                 :            :             string unstemmed_term(term);
    3615                 :            :             term = Unicode::tolower(term);
    3616                 :            : 
    3617                 :            :             // Reuse stem_strategy - STEM_SOME here means "stem terms except
    3618                 :            :             // when used with positional operators".
    3619                 :            :             stem_strategy stem_term = stem_action;
    3620                 :            :             if (stem_term != STEM_NONE) {
    3621                 :            :                 if (!stemmer.internal.get()) {
    3622                 :            :                     // No stemmer is set.
    3623                 :            :                     stem_term = STEM_NONE;
    3624                 :            :                 } else if (first_wildcard != string::npos ||
    3625                 :            :                            edit_distance != NO_EDIT_DISTANCE) {
    3626                 :            :                     stem_term = STEM_NONE;
    3627                 :            :                 } else if (stem_term == STEM_SOME ||
    3628                 :            :                            stem_term == STEM_SOME_FULL_POS) {
    3629                 :            :                     if (!should_stem(unstemmed_term) ||
    3630                 :            :                         (it != end && is_stem_preventer(*it))) {
    3631                 :            :                         // Don't stem this particular term.
    3632                 :            :                         stem_term = STEM_NONE;
    3633                 :            :                     }
    3634                 :            :                 }
    3635                 :            :             }
    3636                 :            : 
    3637                 :            :             if (first_wildcard != string::npos) {
    3638                 :            :                 if (first_wildcard < state.get_min_wildcard_prefix_len()) {
    3639                 :            :                     errmsg = "Too few characters before wildcard";
    3640                 :            :                     return state.query;
    3641                 :            :                 }
    3642                 :            :             }
    3643                 :            : 
    3644                 :            :             Term * term_obj = new Term(&state, term, field_info,
    3645                 :            :                                        unstemmed_term, stem_term, term_pos++,
    3646                 :            :                                        edit_distance);
    3647                 :            : 
    3648                 :            :             if (first_wildcard != string::npos ||
    3649                 :            :                 edit_distance != NO_EDIT_DISTANCE) {
    3650                 :            :                 if (mode == IN_GROUP || mode == IN_GROUP2) {
    3651                 :            :                     // Drop out of IN_GROUP and flag that the group
    3652                 :            :                     // can be empty if all members are stopwords.
    3653                 :            :                     if (mode == IN_GROUP2)
    3654                 :            :                         Parse(&parser, EMPTY_GROUP_OK, NULL, &state);
    3655                 :            :                     mode = DEFAULT;
    3656                 :            :                 }
    3657                 :            :                 Parse(&parser,
    3658                 :            :                       first_wildcard != string::npos ? WILD_TERM : EDIT_TERM,
    3659                 :            :                       term_obj,
    3660                 :            :                       &state);
    3661                 :            :                 continue;
    3662                 :            :             }
    3663                 :            : 
    3664                 :            :             if (is_cjk_term) {
    3665                 :            :                 Parse(&parser, CJKTERM, term_obj, &state);
    3666                 :            :                 if (it == end) break;
    3667                 :            :                 continue;
    3668                 :            :             }
    3669                 :            : 
    3670                 :            :             if (mode == DEFAULT || mode == IN_GROUP || mode == IN_GROUP2) {
    3671                 :            :                 if (it == end && (flags & FLAG_PARTIAL)) {
    3672                 :            :                     auto min_len = state.get_min_partial_prefix_len();
    3673                 :            :                     if (term_char_count >= min_len) {
    3674                 :            :                         if (mode == IN_GROUP || mode == IN_GROUP2) {
    3675                 :            :                             // Drop out of IN_GROUP and flag that the group
    3676                 :            :                             // can be empty if all members are stopwords.
    3677                 :            :                             if (mode == IN_GROUP2)
    3678                 :            :                                 Parse(&parser, EMPTY_GROUP_OK, NULL, &state);
    3679                 :            :                             mode = DEFAULT;
    3680                 :            :                         }
    3681                 :            :                         // Final term of a partial match query, with no
    3682                 :            :                         // following characters - treat as a wildcard.
    3683                 :            :                         Parse(&parser, PARTIAL_TERM, term_obj, &state);
    3684                 :            :                         continue;
    3685                 :            :                     }
    3686                 :            :                 }
    3687                 :            :             }
    3688                 :            : 
    3689                 :            :             // Check spelling, if we're a normal term, and any of the prefixes
    3690                 :            :             // are empty.
    3691                 :            :             if ((flags & FLAG_SPELLING_CORRECTION) && !was_acronym) {
    3692                 :            :                 const list<string> & pfxes = field_info->prefixes;
    3693                 :            :                 list<string>::const_iterator pfx_it;
    3694                 :            :                 for (pfx_it = pfxes.begin(); pfx_it != pfxes.end(); ++pfx_it) {
    3695                 :            :                     if (!pfx_it->empty())
    3696                 :            :                         continue;
    3697                 :            :                     const string & suggest = db.get_spelling_suggestion(term);
    3698                 :            :                     if (!suggest.empty()) {
    3699                 :            :                         if (corrected_query.empty()) corrected_query = qs;
    3700                 :            :                         size_t term_end_index = it.raw() - qs.data();
    3701                 :            :                         size_t n = term_end_index - term_start_index;
    3702                 :            :                         size_t pos = term_start_index + correction_offset;
    3703                 :            :                         corrected_query.replace(pos, n, suggest);
    3704                 :            :                         correction_offset += suggest.size();
    3705                 :            :                         correction_offset -= n;
    3706                 :            :                     }
    3707                 :            :                     break;
    3708                 :            :                 }
    3709                 :            :             }
    3710                 :            : 
    3711                 :            :             if (mode == IN_PHRASED_TERM) {
    3712                 :            :                 Parse(&parser, PHR_TERM, term_obj, &state);
    3713                 :            :             } else {
    3714                 :            :                 // See if the next token will be PHR_TERM - if so, this one
    3715                 :            :                 // needs to be TERM not GROUP_TERM.
    3716                 :            :                 if ((mode == IN_GROUP || mode == IN_GROUP2) &&
    3717                 :            :                     is_phrase_generator(*it)) {
    3718                 :            :                     // FIXME: can we clean this up?
    3719                 :            :                     Utf8Iterator p = it;
    3720                 :            :                     do {
    3721                 :            :                         ++p;
    3722                 :            :                     } while (p != end && is_phrase_generator(*p));
    3723                 :            :                     // Don't generate a phrase unless the phrase generators are
    3724                 :            :                     // immediately followed by another term.
    3725                 :            :                     if (p != end && is_wordchar(*p)) {
    3726                 :            :                         mode = DEFAULT;
    3727                 :            :                     }
    3728                 :            :                 }
    3729                 :            : 
    3730                 :            :                 int token = TERM;
    3731                 :            :                 if (mode == IN_GROUP || mode == IN_GROUP2) {
    3732                 :            :                     mode = IN_GROUP2;
    3733                 :            :                     token = GROUP_TERM;
    3734                 :            :                 }
    3735                 :            :                 Parse(&parser, token, term_obj, &state);
    3736                 :            :                 if (token == TERM && mode != DEFAULT)
    3737                 :            :                     continue;
    3738                 :            :             }
    3739                 :            :         }
    3740                 :            : 
    3741                 :            :         if (it == end) break;
    3742                 :            : 
    3743                 :            :         if (is_phrase_generator(*it)) {
    3744                 :            :             // Skip multiple phrase generators.
    3745                 :            :             do {
    3746                 :            :                 ++it;
    3747                 :            :             } while (it != end && is_phrase_generator(*it));
    3748                 :            :             // Don't generate a phrase unless the phrase generators are
    3749                 :            :             // immediately followed by another term.
    3750                 :            :             if (it != end && is_wordchar(*it)) {
    3751                 :            :                 mode = IN_PHRASED_TERM;
    3752                 :            :                 term_start_index = it.raw() - qs.data();
    3753                 :            :                 goto phrased_term;
    3754                 :            :             }
    3755                 :            :         } else if (mode == DEFAULT || mode == IN_GROUP || mode == IN_GROUP2) {
    3756                 :            :             int old_mode = mode;
    3757                 :            :             mode = DEFAULT;
    3758                 :            :             if (!last_was_operator_needing_term && is_whitespace(*it)) {
    3759                 :            :                 newprev = ' ';
    3760                 :            :                 // Skip multiple whitespace.
    3761                 :            :                 do {
    3762                 :            :                     ++it;
    3763                 :            :                 } while (it != end && is_whitespace(*it));
    3764                 :            :                 // Don't generate a group unless the terms are only separated
    3765                 :            :                 // by whitespace.
    3766                 :            :                 if (it != end && is_wordchar(*it)) {
    3767                 :            :                     if (old_mode == IN_GROUP || old_mode == IN_GROUP2) {
    3768                 :            :                         mode = IN_GROUP2;
    3769                 :            :                     } else {
    3770                 :            :                         mode = IN_GROUP;
    3771                 :            :                     }
    3772                 :            :                 }
    3773                 :            :             }
    3774                 :            :         }
    3775                 :            :     }
    3776                 :            : done:
    3777                 :            :     if (!state.error) {
    3778                 :            :         // Implicitly close any unclosed quotes.
    3779                 :            :         if (mode == IN_QUOTES || mode == IN_PREFIXED_QUOTES)
    3780                 :            :             Parse(&parser, QUOTE, NULL, &state);
    3781                 :            : 
    3782                 :            :         // Implicitly close all unclosed brackets.
    3783                 :            :         while (prefix_stack.size() > 1) {
    3784                 :            :             Parse(&parser, KET, NULL, &state);
    3785                 :            :             prefix_stack.pop_back();
    3786                 :            :         }
    3787                 :            :         Parse(&parser, 0, NULL, &state);
    3788                 :            :     }
    3789                 :            : 
    3790                 :            :     errmsg = state.error;
    3791                 :            :     return state.query;
    3792                 :            : }
    3793                 :            : 
    3794                 :            : #line 3795 "queryparser/queryparser_internal.cc"

Generated by: LCOV version 1.11