LCOV - code coverage report
Current view: top level - queryparser - queryparser_internal.cc (source / functions) Hit Total Coverage
Test: Test Coverage for xapian-core r Lines: 142 142 100.0 %
Date: 2011-08-21 Functions: 17 17 100.0 %
Branches: 90 97 92.8 %

           Branch data     Line data    Source code
       1                 :            : /* Driver template for the LEMON parser generator.
       2                 :            : ** The author disclaims copyright to this source code.
       3                 :            : **
       4                 :            : ** Synced with upstream CVS rev 1.45:
       5                 :            : ** http://www.sqlite.org/cvstrac/fileview?f=sqlite/tool/lempar.c&v=1.45
       6                 :            : */
       7                 :            : /* First off, code is included that follows the "include" declaration
       8                 :            : ** in the input grammar file. */
       9                 :            : #line 1 "queryparser/queryparser.lemony"
      10                 :            : 
      11                 :            : /* queryparser.lemony: build a Xapian::Query object from a user query string.
      12                 :            :  *
      13                 :            :  * Copyright (C) 2004,2005,2006,2007,2008,2009,2010,2011 Olly Betts
      14                 :            :  * Copyright (C) 2007,2008,2009 Lemur Consulting Ltd
      15                 :            :  *
      16                 :            :  * This program is free software; you can redistribute it and/or
      17                 :            :  * modify it under the terms of the GNU General Public License as
      18                 :            :  * published by the Free Software Foundation; either version 2 of the
      19                 :            :  * License, or (at your option) any later version.
      20                 :            :  *
      21                 :            :  * This program is distributed in the hope that it will be useful,
      22                 :            :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      23                 :            :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      24                 :            :  * GNU General Public License for more details.
      25                 :            :  *
      26                 :            :  * You should have received a copy of the GNU General Public License
      27                 :            :  * along with this program; if not, write to the Free Software
      28                 :            :  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
      29                 :            :  * USA
      30                 :            :  */
      31                 :            : 
      32                 :            : #include <config.h>
      33                 :            : 
      34                 :            : #include "omassert.h"
      35                 :            : #include "queryparser_internal.h"
      36                 :            : #include <xapian/error.h>
      37                 :            : #include <xapian/unicode.h>
      38                 :            : #include "stringutils.h"
      39                 :            : 
      40                 :            : // Include the list of token values lemon generates.
      41                 :            : #include "queryparser_token.h"
      42                 :            : 
      43                 :            : #include "cjk/cjk-tokenizer.h"
      44                 :            : 
      45                 :            : #include <algorithm>
      46                 :            : #include <list>
      47                 :            : #include <string>
      48                 :            : 
      49                 :            : #include <string.h>
      50                 :            : 
      51                 :            : using namespace std;
      52                 :            : 
      53                 :            : using namespace Xapian;
      54                 :            : 
      55                 :            : inline bool
      56                 :            : U_isupper(unsigned ch) {
      57                 :            :     return (ch < 128 && C_isupper((unsigned char)ch));
      58                 :            : }
      59                 :            : 
      60                 :            : inline bool
      61                 :            : U_isdigit(unsigned ch) {
      62                 :            :     return (ch < 128 && C_isdigit((unsigned char)ch));
      63                 :            : }
      64                 :            : 
      65                 :            : inline bool
      66                 :            : U_isalpha(unsigned ch) {
      67                 :            :     return (ch < 128 && C_isalpha((unsigned char)ch));
      68                 :            : }
      69                 :            : 
      70                 :            : using Xapian::Unicode::is_whitespace;
      71                 :            : 
      72                 :            : inline bool
      73                 :            : is_not_whitespace(unsigned ch) {
      74                 :            :     return !is_whitespace(ch);
      75                 :            : }
      76                 :            : 
      77                 :            : using Xapian::Unicode::is_wordchar;
      78                 :            : 
      79                 :            : inline bool
      80                 :            : is_not_wordchar(unsigned ch) {
      81                 :            :     return !is_wordchar(ch);
      82                 :            : }
      83                 :            : 
      84                 :            : inline bool
      85                 :            : is_digit(unsigned ch) {
      86                 :            :     return (Unicode::get_category(ch) == Unicode::DECIMAL_DIGIT_NUMBER);
      87                 :            : }
      88                 :            : 
      89                 :            : // FIXME: we used to keep trailing "-" (e.g. Cl-) but it's of dubious utility
      90                 :            : // and there's the risk of hyphens getting stuck onto the end of terms...
      91                 :            : inline bool
      92                 :            : is_suffix(unsigned ch) {
      93                 :            :     return ch == '+' || ch == '#';
      94                 :            : }
      95                 :            : 
      96                 :            : inline bool
      97                 :            : prefix_needs_colon(const string & prefix, unsigned ch)
      98                 :            : {
      99                 :            :     if (!U_isupper(ch)) return false;
     100                 :            :     string::size_type len = prefix.length();
     101                 :            :     return (len > 1 && prefix[len - 1] != ':');
     102                 :            : }
     103                 :            : 
     104                 :            : using Unicode::is_currency;
     105                 :            : 
     106                 :            : inline bool
     107                 :            : is_positional(Xapian::Query::op op)
     108                 :            : {
     109                 :            :     return (op == Xapian::Query::OP_PHRASE || op == Xapian::Query::OP_NEAR);
     110                 :            : }
     111                 :            : 
     112                 :            : /// A structure identifying a group of filter terms or a value range.
     113                 :            : struct filter_group_id {
     114                 :            :     /** The prefix info for boolean filter terms.
     115                 :            :      *
     116                 :            :      *  This is NULL for a value range.
     117                 :            :      */
     118                 :            :     const PrefixInfo *prefix_info;
     119                 :            : 
     120                 :            :     /** The value number for a value range.
     121                 :            :      *
     122                 :            :      *  This is used for value range terms.
     123                 :            :      */
     124                 :            :     Xapian::valueno slot;
     125                 :            : 
     126                 :            :     /// Make a new filter_group_id for boolean filter terms.
     127                 :            :     explicit filter_group_id(const PrefixInfo * prefix_info_)
     128                 :            :         : prefix_info(prefix_info_), slot(Xapian::BAD_VALUENO) {}
     129                 :            : 
     130                 :            :     /// Make a new filter_group_id for value range terms.
     131                 :            :     explicit filter_group_id(Xapian::valueno slot_)
     132                 :            :         : prefix_info(NULL), slot(slot_) {}
     133                 :            : 
     134                 :            :     /// Ordering needed to allow storage in a map.
     135                 :            :     bool operator<(const filter_group_id & other) const {
     136                 :            :         // Check slot first since comparison is cheap.
     137                 :            :         if (slot != other.slot)
     138                 :            :             return slot < other.slot;
     139                 :            :         if (!prefix_info || prefix_info == other.prefix_info)
     140                 :            :             return false;
     141                 :            :         if (!other.prefix_info)
     142                 :            :             return true;
     143                 :            :         return prefix_info->prefixes < other.prefix_info->prefixes;
     144                 :            :     }
     145                 :            : };
     146                 :            : 
     147                 :            : /** Class used to pass information about a token from lexer to parser.
     148                 :            :  *
     149                 :            :  *  Generally an instance of this class carries term information, but it can be
     150                 :            :  *  used for the start or end of a value range, with some operators (e.g. the
     151                 :            :  *  distance in NEAR/3 or ADJ/3, etc).
     152                 :            :  */
     153                 :            : class Term {
     154                 :            :     State * state;
     155                 :            : 
     156                 :            :   public:
     157                 :            :     string name;
     158                 :            :     const PrefixInfo * prefix_info;
     159                 :            :     string unstemmed;
     160                 :            :     QueryParser::stem_strategy stem;
     161                 :            :     termpos pos;
     162                 :            : 
     163                 :            :     Term(const string &name_, termpos pos_) : name(name_), stem(QueryParser::STEM_NONE), pos(pos_) { }
     164                 :            :     Term(const string &name_) : name(name_), stem(QueryParser::STEM_NONE), pos(0) { }
     165                 :            :     Term(const string &name_, const PrefixInfo * prefix_info_)
     166                 :            :         : name(name_), prefix_info(prefix_info_),
     167                 :            :           stem(QueryParser::STEM_NONE), pos(0) { }
     168                 :            :     Term(termpos pos_) : stem(QueryParser::STEM_NONE), pos(pos_) { }
     169                 :            :     Term(State * state_, const string &name_, const PrefixInfo * prefix_info_,
     170                 :            :          const string &unstemmed_,
     171                 :            :          QueryParser::stem_strategy stem_ = QueryParser::STEM_NONE,
     172                 :            :          termpos pos_ = 0)
     173                 :            :         : state(state_), name(name_), prefix_info(prefix_info_),
     174                 :            :           unstemmed(unstemmed_), stem(stem_), pos(pos_) { }
     175                 :            :     // For RANGE tokens.
     176                 :            :     Term(valueno slot, const string &a, const string &b)
     177                 :            :         : name(a), unstemmed(b), pos(slot) { }
     178                 :            : 
     179                 :            :     string make_term(const string & prefix) const;
     180                 :            : 
     181                 :            :     void need_positions() {
     182                 :            :         if (stem == QueryParser::STEM_SOME) stem = QueryParser::STEM_NONE;
     183                 :            :     }
     184                 :            : 
     185                 :            :     termpos get_termpos() const { return pos; }
     186                 :            : 
     187                 :            :     filter_group_id get_filter_group_id() const {
     188                 :            :         return filter_group_id(prefix_info);
     189                 :            :     }
     190                 :            : 
     191                 :            :     Query * as_wildcarded_query(State * state) const;
     192                 :            : 
     193                 :            :     /** Build a query for a term at the very end of the query string when
     194                 :            :      *  FLAG_PARTIAL is in use.
     195                 :            :      *
     196                 :            :      *  This query should match documents containing any terms which start with
     197                 :            :      *  the characters specified, but should give a higher score to exact
     198                 :            :      *  matches (since the user might have finished typing - we simply don't
     199                 :            :      *  know).
     200                 :            :      */
     201                 :            :     Query * as_partial_query(State * state_) const;
     202                 :            : 
     203                 :            :     /** Build a query for a string of CJK characters. */
     204                 :            :     Query * as_cjk_query() const;
     205                 :            : 
     206                 :            :     /// Value range query.
     207                 :            :     Query as_value_range_query() const;
     208                 :            : 
     209                 :            :     Query get_query() const;
     210                 :            : 
     211                 :            :     Query get_query_with_synonyms() const;
     212                 :            : 
     213                 :            :     Query get_query_with_auto_synonyms() const;
     214                 :            : };
     215                 :            : 
     216                 :            : /// Parser State shared between the lexer and the parser.
     217                 :            : class State {
     218                 :            :     QueryParser::Internal * qpi;
     219                 :            : 
     220                 :            :   public:
     221                 :            :     Query query;
     222                 :            :     const char * error;
     223                 :            :     unsigned flags;
     224                 :            : 
     225                 :            :     State(QueryParser::Internal * qpi_, unsigned flags_)
     226                 :            :         : qpi(qpi_), error(NULL), flags(flags_) { }
     227                 :            : 
     228                 :            :     string stem_term(const string &term) {
     229                 :            :         return qpi->stemmer(term);
     230                 :            :     }
     231                 :            : 
     232                 :            :     void add_to_stoplist(const Term * term) {
     233                 :            :         qpi->stoplist.push_back(term->name);
     234                 :            :     }
     235                 :            : 
     236                 :            :     void add_to_unstem(const string & term, const string & unstemmed) {
     237                 :            :         qpi->unstem.insert(make_pair(term, unstemmed));
     238                 :            :     }
     239                 :            : 
     240                 :            :     Term * value_range(const string &a, const string &b) {
     241                 :            :         list<ValueRangeProcessor *>::const_iterator i;
     242                 :            :         for (i = qpi->valrangeprocs.begin(); i != qpi->valrangeprocs.end(); ++i) {
     243                 :            :             string start = a;
     244                 :            :             string end = b;
     245                 :            :             Xapian::valueno slot = (**i)(start, end);
     246                 :            :             if (slot != Xapian::BAD_VALUENO) {
     247                 :            :                 return new Term(slot, start, end);
     248                 :            :             }
     249                 :            :         }
     250                 :            :         return NULL;
     251                 :            :     }
     252                 :            : 
     253                 :            :     Query::op default_op() const { return qpi->default_op; }
     254                 :            : 
     255                 :            :     bool is_stopword(const Term *term) const {
     256                 :            :         return qpi->stopper && (*qpi->stopper)(term->name);
     257                 :            :     }
     258                 :            : 
     259                 :            :     Database get_database() const {
     260                 :            :         return qpi->db;
     261                 :            :     }
     262                 :            : 
     263                 :            :     const Stopper * get_stopper() const {
     264                 :            :         return qpi->stopper;
     265                 :            :     }
     266                 :            : 
     267                 :            :     size_t stoplist_size() const {
     268                 :            :         return qpi->stoplist.size();
     269                 :            :     }
     270                 :            : 
     271                 :            :     void stoplist_resize(size_t s) {
     272                 :            :         qpi->stoplist.resize(s);
     273                 :            :     }
     274                 :            : };
     275                 :            : 
     276                 :            : string
     277                 :            : Term::make_term(const string & prefix) const
     278                 :            : {
     279                 :            :     string term;
     280                 :            :     if (stem == QueryParser::STEM_SOME) term += 'Z';
     281                 :            :     if (!prefix.empty()) {
     282                 :            :         term += prefix;
     283                 :            :         if (prefix_needs_colon(prefix, name[0])) term += ':';
     284                 :            :     }
     285                 :            :     if (stem != QueryParser::STEM_NONE) {
     286                 :            :         term += state->stem_term(name);
     287                 :            :     } else {
     288                 :            :         term += name;
     289                 :            :     }
     290                 :            : 
     291                 :            :     if (!unstemmed.empty())
     292                 :            :         state->add_to_unstem(term, unstemmed);
     293                 :            :     return term;
     294                 :            : }
     295                 :            : 
     296                 :            : Query
     297                 :            : Term::get_query_with_synonyms() const
     298                 :            : {
     299                 :            :     Query q = get_query();
     300                 :            : 
     301                 :            :     // Handle single-word synonyms with each prefix.
     302                 :            :     const list<string> & prefixes = prefix_info->prefixes;
     303                 :            :     list<string>::const_iterator piter;
     304                 :            :     for (piter = prefixes.begin(); piter != prefixes.end(); ++piter) {
     305                 :            :         // First try the unstemmed term:
     306                 :            :         string term;
     307                 :            :         if (!piter->empty()) {
     308                 :            :             term += *piter;
     309                 :            :             if (prefix_needs_colon(*piter, name[0])) term += ':';
     310                 :            :         }
     311                 :            :         term += name;
     312                 :            : 
     313                 :            :         Xapian::Database db = state->get_database();
     314                 :            :         Xapian::TermIterator syn = db.synonyms_begin(term);
     315                 :            :         Xapian::TermIterator end = db.synonyms_end(term);
     316                 :            :         if (syn == end && stem != QueryParser::STEM_NONE) {
     317                 :            :             // If that has no synonyms, try the stemmed form:
     318                 :            :             term = 'Z';
     319                 :            :             if (!piter->empty()) {
     320                 :            :                 term += *piter;
     321                 :            :                 if (prefix_needs_colon(*piter, name[0])) term += ':';
     322                 :            :             }
     323                 :            :             term += state->stem_term(name);
     324                 :            :             syn = db.synonyms_begin(term);
     325                 :            :             end = db.synonyms_end(term);
     326                 :            :         }
     327                 :            :         while (syn != end) {
     328                 :            :             q = Query(Query::OP_SYNONYM, q, Query(*syn, 1, pos));
     329                 :            :             ++syn;
     330                 :            :         }
     331                 :            :     }
     332                 :            :     return q;
     333                 :            : }
     334                 :            : 
     335                 :            : Query
     336                 :            : Term::get_query_with_auto_synonyms() const
     337                 :            : {
     338                 :            :     if (state->flags & QueryParser::FLAG_AUTO_SYNONYMS)
     339                 :            :         return get_query_with_synonyms();
     340                 :            : 
     341                 :            :     return get_query();
     342                 :            : }
     343                 :            : 
     344                 :            : static void
     345                 :            : add_to_query(Query *& q, Query::op op, Query * term)
     346                 :            : {
     347                 :            :     Assert(term);
     348                 :            :     if (q) {
     349                 :            :         *q = Query(op, *q, *term);
     350                 :            :         delete term;
     351                 :            :     } else {
     352                 :            :         q = term;
     353                 :            :     }
     354                 :            : }
     355                 :            : 
     356                 :            : static void
     357                 :            : add_to_query(Query *& q, Query::op op, const Query & term)
     358                 :            : {
     359                 :            :     if (q) {
     360                 :            :         *q = Query(op, *q, term);
     361                 :            :     } else {
     362                 :            :         q = new Query(term);
     363                 :            :     }
     364                 :            : }
     365                 :            : 
     366                 :            : Query
     367                 :            : Term::get_query() const
     368                 :            : {
     369                 :            :     const list<string> & prefixes = prefix_info->prefixes;
     370                 :            :     Assert(prefixes.size() >= 1);
     371                 :            :     list<string>::const_iterator piter = prefixes.begin();
     372                 :            :     Query q(make_term(*piter), 1, pos);
     373                 :            :     while (++piter != prefixes.end()) {
     374                 :            :         q = Query(Query::OP_OR, q, Query(make_term(*piter), 1, pos));
     375                 :            :     }
     376                 :            :     return q;
     377                 :            : }
     378                 :            : 
     379                 :            : Query *
     380                 :            : Term::as_wildcarded_query(State * state_) const
     381                 :            : {
     382                 :            :     const Database & db = state_->get_database();
     383                 :            :     vector<Query> subqs;
     384                 :            : 
     385                 :            :     const list<string> & prefixes = prefix_info->prefixes;
     386                 :            :     list<string>::const_iterator piter;
     387                 :            :     for (piter = prefixes.begin(); piter != prefixes.end(); ++piter) {
     388                 :            :         string root = *piter;
     389                 :            :         root += name;
     390                 :            :         TermIterator t = db.allterms_begin(root);
     391                 :            :         while (t != db.allterms_end(root)) {
     392                 :            :             subqs.push_back(Query(*t, 1, pos));
     393                 :            :             ++t;
     394                 :            :         }
     395                 :            :     }
     396                 :            :     Query * q = new Query(Query::OP_SYNONYM, subqs.begin(), subqs.end());
     397                 :            :     delete this;
     398                 :            :     return q;
     399                 :            : }
     400                 :            : 
     401                 :            : Query *
     402                 :            : Term::as_partial_query(State * state_) const
     403                 :            : {
     404                 :            :     const Database & db = state_->get_database();
     405                 :            :     vector<Query> subqs_partial; // A synonym of all the partial terms.
     406                 :            :     vector<Query> subqs_full; // A synonym of all the full terms.
     407                 :            : 
     408                 :            :     const list<string> & prefixes = prefix_info->prefixes;
     409                 :            :     list<string>::const_iterator piter;
     410                 :            :     for (piter = prefixes.begin(); piter != prefixes.end(); ++piter) {
     411                 :            :         string root = *piter;
     412                 :            :         root += name;
     413                 :            :         TermIterator t = db.allterms_begin(root);
     414                 :            :         while (t != db.allterms_end(root)) {
     415                 :            :             subqs_partial.push_back(Query(*t, 1, pos));
     416                 :            :             ++t;
     417                 :            :         }
     418                 :            :         // Add the term, as it would normally be handled, as an alternative.
     419                 :            :         subqs_full.push_back(Query(make_term(*piter), 1, pos));
     420                 :            :     }
     421                 :            :     Query * q = new Query(Query::OP_OR,
     422                 :            :                           Query(Query::OP_SYNONYM,
     423                 :            :                                 subqs_partial.begin(), subqs_partial.end()),
     424                 :            :                           Query(Query::OP_SYNONYM,
     425                 :            :                                 subqs_full.begin(), subqs_full.end()));
     426                 :            :     delete this;
     427                 :            :     return q;
     428                 :            : }
     429                 :            : 
     430                 :            : Query *
     431                 :            : Term::as_cjk_query() const
     432                 :            : {
     433                 :            :     vector<Query> prefix_cjk;
     434                 :            :     const list<string> & prefixes = prefix_info->prefixes;
     435                 :            :     list<string>::const_iterator piter;
     436                 :            :     for (CJKTokenIterator tk(name); tk != CJKTokenIterator(); ++tk) {
     437                 :            :         for (piter = prefixes.begin(); piter != prefixes.end(); ++piter) {
     438                 :            :             string cjk = *piter;
     439                 :            :             cjk += *tk;
     440                 :            :             prefix_cjk.push_back(Query(cjk, 1, pos));
     441                 :            :         }
     442                 :            :     }
     443                 :            :     Query * q = new Query(Query::OP_AND, prefix_cjk.begin(), prefix_cjk.end());
     444                 :            :     delete this;
     445                 :            :     return q;
     446                 :            : }
     447                 :            : 
     448                 :            : Query
     449                 :            : Term::as_value_range_query() const
     450                 :            : {
     451                 :            :     Query q;
     452                 :            :     if (unstemmed.empty())
     453                 :            :         q = Query(Query::OP_VALUE_GE, pos, name);
     454                 :            :     else
     455                 :            :         q = Query(Query::OP_VALUE_RANGE, pos, name, unstemmed);
     456                 :            :     delete this;
     457                 :            :     return q;
     458                 :            : }
     459                 :            : 
     460                 :            : inline bool
     461                 :            : is_phrase_generator(unsigned ch)
     462                 :            : {
     463                 :            :     // These characters generate a phrase search.
     464                 :            :     // Ordered mostly by frequency of calls to this function done when
     465                 :            :     // running queryparsertest.
     466                 :            :     return (ch && ch < 128 && strchr(".-/:\\@", ch) != NULL);
     467                 :            : }
     468                 :            : 
     469                 :            : inline bool
     470                 :            : is_stem_preventer(unsigned ch)
     471                 :            : {
     472                 :            :     return (ch && ch < 128 && strchr("(/\\@<>=*[{\"", ch) != NULL);
     473                 :            : }
     474                 :            : 
     475                 :            : inline bool
     476                 :            : should_stem(const string & term)
     477                 :            : {
     478                 :            :     const unsigned int SHOULD_STEM_MASK =
     479                 :            :         (1 << Unicode::LOWERCASE_LETTER) |
     480                 :            :         (1 << Unicode::TITLECASE_LETTER) |
     481                 :            :         (1 << Unicode::MODIFIER_LETTER) |
     482                 :            :         (1 << Unicode::OTHER_LETTER);
     483                 :            :     Utf8Iterator u(term);
     484                 :            :     return ((SHOULD_STEM_MASK >> Unicode::get_category(*u)) & 1);
     485                 :            : }
     486                 :            : 
     487                 :            : /** Value representing "ignore this" when returned by check_infix() or
     488                 :            :  *  check_infix_digit().
     489                 :            :  */
     490                 :            : const unsigned UNICODE_IGNORE(-1);
     491                 :            : 
     492                 :            : inline unsigned check_infix(unsigned ch) {
     493                 :            :     if (ch == '\'' || ch == '&' || ch == 0xb7 || ch == 0x5f4 || ch == 0x2027) {
     494                 :            :         // Unicode includes all these except '&' in its word boundary rules,
     495                 :            :         // as well as 0x2019 (which we handle below) and ':' (for Swedish
     496                 :            :         // apparently, but we ignore this for now as it's problematic in
     497                 :            :         // real world cases).
     498                 :            :         return ch;
     499                 :            :     }
     500                 :            :     // 0x2019 is Unicode apostrophe and single closing quote.
     501                 :            :     // 0x201b is Unicode single opening quote with the tail rising.
     502                 :            :     if (ch == 0x2019 || ch == 0x201b) return '\'';
     503                 :            :     if (ch >= 0x200b && (ch <= 0x200d || ch == 0x2060 || ch == 0xfeff))
     504                 :            :         return UNICODE_IGNORE;
     505                 :            :     return 0;
     506                 :            : }
     507                 :            : 
     508                 :            : inline unsigned check_infix_digit(unsigned ch) {
     509                 :            :     // This list of characters comes from Unicode's word identifying algorithm.
     510                 :            :     switch (ch) {
     511                 :            :         case ',':
     512                 :            :         case '.':
     513                 :            :         case ';':
     514                 :            :         case 0x037e: // GREEK QUESTION MARK
     515                 :            :         case 0x0589: // ARMENIAN FULL STOP
     516                 :            :         case 0x060D: // ARABIC DATE SEPARATOR
     517                 :            :         case 0x07F8: // NKO COMMA
     518                 :            :         case 0x2044: // FRACTION SLASH
     519                 :            :         case 0xFE10: // PRESENTATION FORM FOR VERTICAL COMMA
     520                 :            :         case 0xFE13: // PRESENTATION FORM FOR VERTICAL COLON
     521                 :            :         case 0xFE14: // PRESENTATION FORM FOR VERTICAL SEMICOLON
     522                 :            :             return ch;
     523                 :            :     }
     524                 :            :     if (ch >= 0x200b && (ch <= 0x200d || ch == 0x2060 || ch == 0xfeff))
     525                 :            :         return UNICODE_IGNORE;
     526                 :            :     return 0;
     527                 :            : }
     528                 :            : 
     529                 :            : struct yyParser;
     530                 :            : 
     531                 :            : // Prototype the functions lemon generates.
     532                 :            : static yyParser *ParseAlloc();
     533                 :            : static void ParseFree(yyParser *);
     534                 :            : static void Parse(yyParser *, int, Term *, State *);
     535                 :            : static void yy_parse_failed(yyParser *);
     536                 :            : 
     537                 :            : void
     538                 :            : QueryParser::Internal::add_prefix(const string &field, const string &prefix,
     539                 :            :                                   filter_type type)
     540                 :            : {
     541                 :            :     map<string, PrefixInfo>::iterator p = prefixmap.find(field);
     542                 :            :     if (p == prefixmap.end()) {
     543                 :            :         prefixmap.insert(make_pair(field, PrefixInfo(type, prefix)));
     544                 :            :     } else {
     545                 :            :         // Check that this is the same type of filter as the existing one(s).
     546                 :            :         if (p->second.type != type) {
     547                 :            :             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");
     548                 :            :         }
     549                 :            :         p->second.prefixes.push_back(prefix);
     550                 :            :    }
     551                 :            : }
     552                 :            : 
     553                 :            : string
     554                 :            : QueryParser::Internal::parse_term(Utf8Iterator &it, const Utf8Iterator &end,
     555                 :            :                                   bool cjk_ngram, bool & is_cjk_term,
     556                 :            :                                   bool &was_acronym)
     557                 :            : {
     558                 :            :     string term;
     559                 :            :     // Look for initials separated by '.' (e.g. P.T.O., U.N.C.L.E).
     560                 :            :     // Don't worry if there's a trailing '.' or not.
     561                 :            :     if (U_isupper(*it)) {
     562                 :            :         string t;
     563                 :            :         Utf8Iterator p = it;
     564                 :            :         do {
     565                 :            :             Unicode::append_utf8(t, *p++);
     566                 :            :         } while (p != end && *p == '.' && ++p != end && U_isupper(*p));
     567                 :            :         // One letter does not make an acronym!  If we handled a single
     568                 :            :         // uppercase letter here, we wouldn't catch M&S below.
     569                 :            :         if (t.length() > 1) {
     570                 :            :             // Check there's not a (lower case) letter or digit
     571                 :            :             // immediately after it.
     572                 :            :             // FIXME: should I.B.M..P.T.O be a range search?
     573                 :            :             if (p == end || !is_wordchar(*p)) {
     574                 :            :                 it = p;
     575                 :            :                 swap(term, t);
     576                 :            :             }
     577                 :            :         }
     578                 :            :     }
     579                 :            :     was_acronym = !term.empty();
     580                 :            : 
     581                 :            :     if (cjk_ngram && term.empty() && CJK::codepoint_is_cjk(*it)) {
     582                 :            :         term = CJK::get_cjk(it);
     583                 :            :         is_cjk_term = true;
     584                 :            :     }
     585                 :            : 
     586                 :            :     if (term.empty()) {
     587                 :            :         unsigned prevch = *it;
     588                 :            :         Unicode::append_utf8(term, prevch);
     589                 :            :         while (++it != end) {
     590                 :            :             if (cjk_ngram && CJK::codepoint_is_cjk(*it)) break;
     591                 :            :             unsigned ch = *it;
     592                 :            :             if (!is_wordchar(ch)) {
     593                 :            :                 // Treat a single embedded '&' or "'" or similar as a word
     594                 :            :                 // character (e.g. AT&T, Fred's).  Also, normalise
     595                 :            :                 // apostrophes to ASCII apostrophe.
     596                 :            :                 Utf8Iterator p = it;
     597                 :            :                 ++p;
     598                 :            :                 if (p == end || !is_wordchar(*p)) break;
     599                 :            :                 unsigned nextch = *p;
     600                 :            :                 if (is_digit(prevch) && is_digit(nextch)) {
     601                 :            :                     ch = check_infix_digit(ch);
     602                 :            :                 } else {
     603                 :            :                     ch = check_infix(ch);
     604                 :            :                 }
     605                 :            :                 if (!ch) break;
     606                 :            :                 if (ch == UNICODE_IGNORE)
     607                 :            :                     continue;
     608                 :            :             }
     609                 :            :             Unicode::append_utf8(term, ch);
     610                 :            :             prevch = ch;
     611                 :            :         }
     612                 :            :         if (it != end && is_suffix(*it)) {
     613                 :            :             string suff_term = term;
     614                 :            :             Utf8Iterator p = it;
     615                 :            :             // Keep trailing + (e.g. C++, Na+) or # (e.g. C#).
     616                 :            :             do {
     617                 :            :                 if (suff_term.size() - term.size() == 3) {
     618                 :            :                     suff_term.resize(0);
     619                 :            :                     break;
     620                 :            :                 }
     621                 :            :                 suff_term += *p;
     622                 :            :             } while (is_suffix(*++p));
     623                 :            :             if (!suff_term.empty() && (p == end || !is_wordchar(*p))) {
     624                 :            :                 // If the suffixed term doesn't exist, check that the
     625                 :            :                 // non-suffixed term does.  This also takes care of
     626                 :            :                 // the case when QueryParser::set_database() hasn't
     627                 :            :                 // been called.
     628                 :            :                 bool use_suff_term = false;
     629                 :            :                 string lc = Unicode::tolower(suff_term);
     630                 :            :                 if (db.term_exists(lc)) {
     631                 :            :                     use_suff_term = true;
     632                 :            :                 } else {
     633                 :            :                     lc = Unicode::tolower(term);
     634                 :            :                     if (!db.term_exists(lc)) use_suff_term = true;
     635                 :            :                 }
     636                 :            :                 if (use_suff_term) {
     637                 :            :                     term = suff_term;
     638                 :            :                     it = p;
     639                 :            :                 }
     640                 :            :             }
     641                 :            :         }
     642                 :            :     }
     643                 :            :     return term;
     644                 :            : }
     645                 :            : 
     646                 :            : class ParserHandler {
     647                 :            :     yyParser * parser;
     648                 :            : 
     649                 :            :   public:
     650                 :            :     explicit ParserHandler(yyParser * parser_) : parser(parser_) { }
     651                 :            :     operator yyParser*() { return parser; }
     652                 :            :     ~ParserHandler() { ParseFree(parser); }
     653                 :            : };
     654                 :            : 
     655                 :            : Query
     656                 :            : QueryParser::Internal::parse_query(const string &qs, unsigned flags,
     657                 :            :                                    const string &default_prefix)
     658                 :            : {
     659                 :            :     bool cjk_ngram = true; // FIXME: set from flag or env var or something.
     660                 :            : 
     661                 :            :     // Set value_ranges if we may have to handle value ranges in the query.
     662                 :            :     bool value_ranges;
     663                 :            :     value_ranges = !valrangeprocs.empty() && (qs.find("..") != string::npos);
     664                 :            : 
     665                 :            :     termpos term_pos = 1;
     666                 :            :     Utf8Iterator it(qs), end;
     667                 :            : 
     668                 :            :     State state(this, flags);
     669                 :            : 
     670                 :            :     // To successfully apply more than one spelling correction to a query
     671                 :            :     // string, we must keep track of the offset due to previous corrections.
     672                 :            :     int correction_offset = 0;
     673                 :            :     corrected_query.resize(0);
     674                 :            : 
     675                 :            :     // Stack of prefixes, used for phrases and subexpressions.
     676                 :            :     list<const PrefixInfo *> prefix_stack;
     677                 :            : 
     678                 :            :     // If default_prefix is specified, use it.  Otherwise, use any list
     679                 :            :     // that has been set for the empty prefix.
     680                 :            :     const PrefixInfo def_pfx(NON_BOOLEAN, default_prefix);
     681                 :            :     {
     682                 :            :         const PrefixInfo * default_prefix_info = &def_pfx;
     683                 :            :         if (default_prefix.empty()) {
     684                 :            :             map<string, PrefixInfo>::const_iterator f = prefixmap.find("");
     685                 :            :             if (f != prefixmap.end()) default_prefix_info = &(f->second);
     686                 :            :         }
     687                 :            : 
     688                 :            :         // We always have the current prefix on the top of the stack.
     689                 :            :         prefix_stack.push_back(default_prefix_info);
     690                 :            :     }
     691                 :            : 
     692                 :            :     ParserHandler pParser(ParseAlloc());
     693                 :            : 
     694                 :            :     unsigned newprev = ' ';
     695                 :            : main_lex_loop:
     696                 :            :     enum {
     697                 :            :         DEFAULT, IN_QUOTES, IN_PREFIXED_QUOTES, IN_PHRASED_TERM, IN_GROUP,
     698                 :            :         IN_GROUP2, EXPLICIT_SYNONYM
     699                 :            :     } mode = DEFAULT;
     700                 :            :     while (it != end && !state.error) {
     701                 :            :         bool last_was_operator = false;
     702                 :            :         bool last_was_operator_needing_term = false;
     703                 :            :         if (mode == EXPLICIT_SYNONYM) mode = DEFAULT;
     704                 :            :         if (false) {
     705                 :            : just_had_operator:
     706                 :            :             if (it == end) break;
     707                 :            :             mode = DEFAULT;
     708                 :            :             last_was_operator_needing_term = false;
     709                 :            :             last_was_operator = true;
     710                 :            :         }
     711                 :            :         if (false) {
     712                 :            : just_had_operator_needing_term:
     713                 :            :             last_was_operator_needing_term = true;
     714                 :            :             last_was_operator = true;
     715                 :            :         }
     716                 :            :         if (mode == IN_PHRASED_TERM) mode = DEFAULT;
     717                 :            :         if (is_whitespace(*it)) {
     718                 :            :             newprev = ' ';
     719                 :            :             ++it;
     720                 :            :             it = find_if(it, end, is_not_whitespace);
     721                 :            :             if (it == end) break;
     722                 :            :         }
     723                 :            : 
     724                 :            :         if (value_ranges &&
     725                 :            :             (mode == DEFAULT || mode == IN_GROUP || mode == IN_GROUP2)) {
     726                 :            :             // Scan forward to see if this could be the "start of range"
     727                 :            :             // token.  Sadly this has O(n^2) tendencies, though at least
     728                 :            :             // "n" is the number of words in a query which is likely to
     729                 :            :             // remain fairly small.  FIXME: can we tokenise more elegantly?
     730                 :            :             Utf8Iterator it_initial = it;
     731                 :            :             Utf8Iterator p = it;
     732                 :            :             unsigned ch = 0;
     733                 :            :             while (p != end) {
     734                 :            :                 if (ch == '.' && *p == '.') {
     735                 :            :                     string a;
     736                 :            :                     while (it != p) {
     737                 :            :                         Unicode::append_utf8(a, *it++);
     738                 :            :                     }
     739                 :            :                     // Trim off the trailing ".".
     740                 :            :                     a.resize(a.size() - 1);
     741                 :            :                     ++p;
     742                 :            :                     // Either end of the range can be empty (for an open-ended
     743                 :            :                     // range) but both can't be empty.
     744                 :            :                     if (!a.empty() || (p != end && *p > ' ' && *p != ')')) {
     745                 :            :                         string b;
     746                 :            :                         // Allow any character except whitespace and ')' in the
     747                 :            :                         // upper bound.  Or should we be consistent with the
     748                 :            :                         // lower bound?
     749                 :            :                         while (p != end && *p > ' ' && *p != ')') {
     750                 :            :                             Unicode::append_utf8(b, *p++);
     751                 :            :                         }
     752                 :            :                         Term * range = state.value_range(a, b);
     753                 :            :                         if (!range) {
     754                 :            :                             state.error = "Unknown range operation";
     755                 :            :                             if (a.find(':', 1) == string::npos) {
     756                 :            :                                 goto done;
     757                 :            :                             }
     758                 :            :                             // Might be a boolean filter with ".." in.  Leave
     759                 :            :                             // state.error in case it isn't.
     760                 :            :                             it = it_initial;
     761                 :            :                             break;
     762                 :            :                         }
     763                 :            :                         Parse(pParser, RANGE, range, &state);
     764                 :            :                     }
     765                 :            :                     it = p;
     766                 :            :                     goto main_lex_loop;
     767                 :            :                 }
     768                 :            :                 ch = *p;
     769                 :            :                 if (!(is_wordchar(ch) || is_currency(ch) ||
     770                 :            :                       (ch < 128 && strchr("%,-./:@", ch)))) break;
     771                 :            :                 ++p;
     772                 :            :             }
     773                 :            :         }
     774                 :            : 
     775                 :            :         if (!is_wordchar(*it)) {
     776                 :            :             unsigned prev = newprev;
     777                 :            :             unsigned ch = *it++;
     778                 :            :             newprev = ch;
     779                 :            :             // Drop out of IN_GROUP mode.
     780                 :            :             if (mode == IN_GROUP || mode == IN_GROUP2)
     781                 :            :                 mode = DEFAULT;
     782                 :            :             switch (ch) {
     783                 :            :               case '"': // Quoted phrase.
     784                 :            :                 if (mode == DEFAULT) {
     785                 :            :                     // Skip whitespace.
     786                 :            :                     it = find_if(it, end, is_not_whitespace);
     787                 :            :                     if (it == end) {
     788                 :            :                         // Ignore an unmatched " at the end of the query to
     789                 :            :                         // avoid generating an empty pair of QUOTEs which will
     790                 :            :                         // cause a parse error.
     791                 :            :                         goto done;
     792                 :            :                     }
     793                 :            :                     if (*it == '"') {
     794                 :            :                         // Ignore empty "" (but only if we're not already
     795                 :            :                         // IN_QUOTES as we don't merge two adjacent quoted
     796                 :            :                         // phrases!)
     797                 :            :                         newprev = *it++;
     798                 :            :                         break;
     799                 :            :                     }
     800                 :            :                 }
     801                 :            :                 if (flags & QueryParser::FLAG_PHRASE) {
     802                 :            :                     Parse(pParser, QUOTE, NULL, &state);
     803                 :            :                     if (mode == DEFAULT) {
     804                 :            :                         mode = IN_QUOTES;
     805                 :            :                     } else {
     806                 :            :                         // Remove the prefix we pushed for this phrase.
     807                 :            :                         if (mode == IN_PREFIXED_QUOTES)
     808                 :            :                             prefix_stack.pop_back();
     809                 :            :                         mode = DEFAULT;
     810                 :            :                     }
     811                 :            :                 }
     812                 :            :                 break;
     813                 :            : 
     814                 :            :               case '+': case '-': // Loved or hated term/phrase/subexpression.
     815                 :            :                 // Ignore + or - at the end of the query string.
     816                 :            :                 if (it == end) goto done;
     817                 :            :                 if (prev > ' ' && prev != '(') {
     818                 :            :                     // Or if not after whitespace or an open bracket.
     819                 :            :                     break;
     820                 :            :                 }
     821                 :            :                 if (is_whitespace(*it) || *it == '+' || *it == '-') {
     822                 :            :                     // Ignore + or - followed by a space, or further + or -.
     823                 :            :                     // Postfix + (such as in C++ and H+) is handled as part of
     824                 :            :                     // the term lexing code in parse_term().
     825                 :            :                     newprev = *it++;
     826                 :            :                     break;
     827                 :            :                 }
     828                 :            :                 if (mode == DEFAULT && (flags & FLAG_LOVEHATE)) {
     829                 :            :                     int token;
     830                 :            :                     if (ch == '+') {
     831                 :            :                         token = LOVE;
     832                 :            :                     } else if (last_was_operator) {
     833                 :            :                         token = HATE_AFTER_AND;
     834                 :            :                     } else {
     835                 :            :                         token = HATE;
     836                 :            :                     }
     837                 :            :                     Parse(pParser, token, NULL, &state);
     838                 :            :                     goto just_had_operator_needing_term;
     839                 :            :                 }
     840                 :            :                 // Need to prevent the term after a LOVE or HATE starting a
     841                 :            :                 // term group...
     842                 :            :                 break;
     843                 :            : 
     844                 :            :               case '(': // Bracketed subexpression.
     845                 :            :                 // Skip whitespace.
     846                 :            :                 it = find_if(it, end, is_not_whitespace);
     847                 :            :                 // Ignore ( at the end of the query string.
     848                 :            :                 if (it == end) goto done;
     849                 :            :                 if (prev > ' ' && strchr("()+-", prev) == NULL) {
     850                 :            :                     // Or if not after whitespace or a bracket or '+' or '-'.
     851                 :            :                     break;
     852                 :            :                 }
     853                 :            :                 if (*it == ')') {
     854                 :            :                     // Ignore empty ().
     855                 :            :                     newprev = *it++;
     856                 :            :                     break;
     857                 :            :                 }
     858                 :            :                 if (mode == DEFAULT && (flags & FLAG_BOOLEAN)) {
     859                 :            :                     prefix_stack.push_back(prefix_stack.back());
     860                 :            :                     Parse(pParser, BRA, NULL, &state);
     861                 :            :                 }
     862                 :            :                 break;
     863                 :            : 
     864                 :            :               case ')': // End of bracketed subexpression.
     865                 :            :                 if (mode == DEFAULT && (flags & FLAG_BOOLEAN)) {
     866                 :            :                     // Remove the prefix we pushed for the corresponding BRA.
     867                 :            :                     // If brackets are unmatched, it's a syntax error, but
     868                 :            :                     // that's no excuse to SEGV!
     869                 :            :                     if (prefix_stack.size() > 1) prefix_stack.pop_back();
     870                 :            :                     Parse(pParser, KET, NULL, &state);
     871                 :            :                 }
     872                 :            :                 break;
     873                 :            : 
     874                 :            :               case '~': // Synonym expansion.
     875                 :            :                 // Ignore at the end of the query string.
     876                 :            :                 if (it == end) goto done;
     877                 :            :                 if (mode == DEFAULT && (flags & FLAG_SYNONYM)) {
     878                 :            :                     if (prev > ' ' && strchr("+-(", prev) == NULL) {
     879                 :            :                         // Or if not after whitespace, +, -, or an open bracket.
     880                 :            :                         break;
     881                 :            :                     }
     882                 :            :                     if (!is_wordchar(*it)) {
     883                 :            :                         // Ignore if not followed by a word character.
     884                 :            :                         break;
     885                 :            :                     }
     886                 :            :                     Parse(pParser, SYNONYM, NULL, &state);
     887                 :            :                     mode = EXPLICIT_SYNONYM;
     888                 :            :                     goto just_had_operator_needing_term;
     889                 :            :                 }
     890                 :            :                 break;
     891                 :            :             }
     892                 :            :             // Skip any other characters.
     893                 :            :             continue;
     894                 :            :         }
     895                 :            : 
     896                 :            :         Assert(is_wordchar(*it));
     897                 :            : 
     898                 :            :         size_t term_start_index = it.raw() - qs.data();
     899                 :            : 
     900                 :            :         newprev = 'A'; // Any letter will do...
     901                 :            : 
     902                 :            :         // A term, a prefix, or a boolean operator.
     903                 :            :         const PrefixInfo * prefix_info = NULL;
     904                 :            :         if ((mode == DEFAULT || mode == IN_GROUP || mode == IN_GROUP2 || mode == EXPLICIT_SYNONYM) &&
     905                 :            :             !prefixmap.empty()) {
     906                 :            :             // Check for a fieldname prefix (e.g. title:historical).
     907                 :            :             Utf8Iterator p = find_if(it, end, is_not_wordchar);
     908                 :            :             if (p != end && *p == ':' && ++p != end && *p > ' ' && *p != ')') {
     909                 :            :                 string field;
     910                 :            :                 p = it;
     911                 :            :                 while (*p != ':')
     912                 :            :                     Unicode::append_utf8(field, *p++);
     913                 :            :                 map<string, PrefixInfo>::const_iterator f;
     914                 :            :                 f = prefixmap.find(field);
     915                 :            :                 if (f != prefixmap.end()) {
     916                 :            :                     // Special handling for prefixed fields, depending on the
     917                 :            :                     // type of the prefix.
     918                 :            :                     unsigned ch = *++p;
     919                 :            :                     prefix_info = &(f->second);
     920                 :            : 
     921                 :            :                     if (prefix_info->type != NON_BOOLEAN) {
     922                 :            :                         // Drop out of IN_GROUP if we're in it.
     923                 :            :                         if (mode == IN_GROUP || mode == IN_GROUP2)
     924                 :            :                             mode = DEFAULT;
     925                 :            :                         it = p;
     926                 :            :                         string name;
     927                 :            :                         if (it != end && *it == '"') {
     928                 :            :                             // Quoted boolean term (can contain any character).
     929                 :            :                             ++it;
     930                 :            :                             while (it != end) {
     931                 :            :                                 if (*it == '"') {
     932                 :            :                                     // Interpret "" as an escaped ".
     933                 :            :                                     if (++it == end || *it != '"')
     934                 :            :                                         break;
     935                 :            :                                 }
     936                 :            :                                 Unicode::append_utf8(name, *it++);
     937                 :            :                             }
     938                 :            :                         } else {
     939                 :            :                             // Can't boolean filter prefix a subexpression, so
     940                 :            :                             // just use anything following the prefix until the
     941                 :            :                             // next space or ')' as part of the boolean filter
     942                 :            :                             // term.
     943                 :            :                             while (it != end && *it > ' ' && *it != ')')
     944                 :            :                                 Unicode::append_utf8(name, *it++);
     945                 :            :                         }
     946                 :            :                         // Build the unstemmed form in field.
     947                 :            :                         field += ':';
     948                 :            :                         field += name;
     949                 :            :                         // Clear any pending value range error.
     950                 :            :                         state.error = NULL;
     951                 :            :                         Term * token = new Term(&state, name, prefix_info, field);
     952                 :            :                         Parse(pParser, BOOLEAN_FILTER, token, &state);
     953                 :            :                         continue;
     954                 :            :                     }
     955                 :            : 
     956                 :            :                     if (ch == '"' && (flags & FLAG_PHRASE)) {
     957                 :            :                         // Prefixed phrase, e.g.: subject:"space flight"
     958                 :            :                         mode = IN_PREFIXED_QUOTES;
     959                 :            :                         Parse(pParser, QUOTE, NULL, &state);
     960                 :            :                         it = p;
     961                 :            :                         newprev = ch;
     962                 :            :                         ++it;
     963                 :            :                         prefix_stack.push_back(prefix_info);
     964                 :            :                         continue;
     965                 :            :                     }
     966                 :            : 
     967                 :            :                     if (ch == '(' && (flags & FLAG_BOOLEAN)) {
     968                 :            :                         // Prefixed subexpression, e.g.: title:(fast NEAR food)
     969                 :            :                         mode = DEFAULT;
     970                 :            :                         Parse(pParser, BRA, NULL, &state);
     971                 :            :                         it = p;
     972                 :            :                         newprev = ch;
     973                 :            :                         ++it;
     974                 :            :                         prefix_stack.push_back(prefix_info);
     975                 :            :                         continue;
     976                 :            :                     }
     977                 :            : 
     978                 :            :                     if (ch != ':') {
     979                 :            :                         // Allow 'path:/usr/local' but not 'foo::bar::baz'.
     980                 :            :                         while (is_phrase_generator(ch)) {
     981                 :            :                             if (++p == end)
     982                 :            :                                 goto not_prefix;
     983                 :            :                             ch = *p;
     984                 :            :                         }
     985                 :            :                     }
     986                 :            : 
     987                 :            :                     if (is_wordchar(ch)) {
     988                 :            :                         // Prefixed term.
     989                 :            :                         it = p;
     990                 :            :                     } else {
     991                 :            : not_prefix:
     992                 :            :                         // It looks like a prefix but isn't, so parse it as
     993                 :            :                         // text instead.
     994                 :            :                         prefix_info = NULL;
     995                 :            :                     }
     996                 :            :                 }
     997                 :            :             }
     998                 :            :         }
     999                 :            : 
    1000                 :            : phrased_term:
    1001                 :            :         bool was_acronym;
    1002                 :            :         bool is_cjk_term = false;
    1003                 :            :         string term = parse_term(it, end, cjk_ngram, is_cjk_term, was_acronym);
    1004                 :            : 
    1005                 :            :         // Boolean operators.
    1006                 :            :         if ((mode == DEFAULT || mode == IN_GROUP || mode == IN_GROUP2) &&
    1007                 :            :             (flags & FLAG_BOOLEAN) &&
    1008                 :            :             // Don't want to interpret A.N.D. as an AND operator.
    1009                 :            :             !was_acronym &&
    1010                 :            :             !prefix_info &&
    1011                 :            :             term.size() >= 2 && term.size() <= 4 && U_isalpha(term[0])) {
    1012                 :            : 
    1013                 :            :             string op = term;
    1014                 :            :             if (flags & FLAG_BOOLEAN_ANY_CASE) {
    1015                 :            :                 for (string::iterator i = op.begin(); i != op.end(); ++i) {
    1016                 :            :                     *i = C_toupper(*i);
    1017                 :            :                 }
    1018                 :            :             }
    1019                 :            :             if (op.size() == 3) {
    1020                 :            :                 if (op == "AND") {
    1021                 :            :                     Parse(pParser, AND, NULL, &state);
    1022                 :            :                     goto just_had_operator;
    1023                 :            :                 }
    1024                 :            :                 if (op == "NOT") {
    1025                 :            :                     Parse(pParser, NOT, NULL, &state);
    1026                 :            :                     goto just_had_operator;
    1027                 :            :                 }
    1028                 :            :                 if (op == "XOR") {
    1029                 :            :                     Parse(pParser, XOR, NULL, &state);
    1030                 :            :                     goto just_had_operator;
    1031                 :            :                 }
    1032                 :            :                 if (op == "ADJ") {
    1033                 :            :                     if (it != end && *it == '/') {
    1034                 :            :                         size_t width = 0;
    1035                 :            :                         Utf8Iterator p = it;
    1036                 :            :                         while (++p != end && U_isdigit(*p)) {
    1037                 :            :                             width = (width * 10) + (*p - '0');
    1038                 :            :                         }
    1039                 :            :                         if (width && (p == end || is_whitespace(*p))) {
    1040                 :            :                             it = p;
    1041                 :            :                             Parse(pParser, ADJ, new Term(width), &state);
    1042                 :            :                             goto just_had_operator;
    1043                 :            :                         }
    1044                 :            :                     } else {
    1045                 :            :                         Parse(pParser, ADJ, NULL, &state);
    1046                 :            :                         goto just_had_operator;
    1047                 :            :                     }
    1048                 :            :                 }
    1049                 :            :             } else if (op.size() == 2) {
    1050                 :            :                 if (op == "OR") {
    1051                 :            :                     Parse(pParser, OR, NULL, &state);
    1052                 :            :                     goto just_had_operator;
    1053                 :            :                 }
    1054                 :            :             } else if (op.size() == 4) {
    1055                 :            :                 if (op == "NEAR") {
    1056                 :            :                     if (it != end && *it == '/') {
    1057                 :            :                         size_t width = 0;
    1058                 :            :                         Utf8Iterator p = it;
    1059                 :            :                         while (++p != end && U_isdigit(*p)) {
    1060                 :            :                             width = (width * 10) + (*p - '0');
    1061                 :            :                         }
    1062                 :            :                         if (width && (p == end || is_whitespace(*p))) {
    1063                 :            :                             it = p;
    1064                 :            :                             Parse(pParser, NEAR, new Term(width), &state);
    1065                 :            :                             goto just_had_operator;
    1066                 :            :                         }
    1067                 :            :                     } else {
    1068                 :            :                         Parse(pParser, NEAR, NULL, &state);
    1069                 :            :                         goto just_had_operator;
    1070                 :            :                     }
    1071                 :            :                 }
    1072                 :            :             }
    1073                 :            :         }
    1074                 :            : 
    1075                 :            :         // If no prefix is set, use the default one.
    1076                 :            :         if (!prefix_info) prefix_info = prefix_stack.back();
    1077                 :            : 
    1078                 :            :         Assert(prefix_info->type == NON_BOOLEAN);
    1079                 :            : 
    1080                 :            :         {
    1081                 :            :             string unstemmed_term(term);
    1082                 :            :             term = Unicode::tolower(term);
    1083                 :            : 
    1084                 :            :             // Reuse stem_strategy - STEM_SOME here means "stem terms except
    1085                 :            :             // when used with positional operators".
    1086                 :            :             stem_strategy stem_term = stem_action;
    1087                 :            :             if (stem_term != STEM_NONE) {
    1088                 :            :                 if (!stemmer.internal.get()) {
    1089                 :            :                     // No stemmer is set.
    1090                 :            :                     stem_term = STEM_NONE;
    1091                 :            :                 } else if (stem_term == STEM_SOME) {
    1092                 :            :                     if (!should_stem(unstemmed_term) ||
    1093                 :            :                         (it != end && is_stem_preventer(*it))) {
    1094                 :            :                         // Don't stem this particular term.
    1095                 :            :                         stem_term = STEM_NONE;
    1096                 :            :                     }
    1097                 :            :                 }
    1098                 :            :             }
    1099                 :            : 
    1100                 :            :             Term * term_obj = new Term(&state, term, prefix_info,
    1101                 :            :                                        unstemmed_term, stem_term, term_pos++);
    1102                 :            : 
    1103                 :            :             if (is_cjk_term) {
    1104                 :            :                 Parse(pParser, CJKTERM, term_obj, &state);
    1105                 :            :                 if (it == end) break;
    1106                 :            :                 continue;
    1107                 :            :             }
    1108                 :            : 
    1109                 :            :             if (mode == DEFAULT || mode == IN_GROUP || mode == IN_GROUP2) {
    1110                 :            :                 if (it != end) {
    1111                 :            :                     if ((flags & FLAG_WILDCARD) && *it == '*') {
    1112                 :            :                         Utf8Iterator p(it);
    1113                 :            :                         ++p;
    1114                 :            :                         if (p == end || !is_wordchar(*p)) {
    1115                 :            :                             it = p;
    1116                 :            :                             if (mode == IN_GROUP || mode == IN_GROUP2) {
    1117                 :            :                                 // Drop out of IN_GROUP and flag that the group
    1118                 :            :                                 // can be empty if all members are stopwords.
    1119                 :            :                                 if (mode == IN_GROUP2)
    1120                 :            :                                     Parse(pParser, EMPTY_GROUP_OK, NULL, &state);
    1121                 :            :                                 mode = DEFAULT;
    1122                 :            :                             }
    1123                 :            :                             // Wildcard at end of term (also known as
    1124                 :            :                             // "right truncation").
    1125                 :            :                             Parse(pParser, WILD_TERM, term_obj, &state);
    1126                 :            :                             continue;
    1127                 :            :                         }
    1128                 :            :                     }
    1129                 :            :                 } else {
    1130                 :            :                     if (flags & FLAG_PARTIAL) {
    1131                 :            :                         if (mode == IN_GROUP || mode == IN_GROUP2) {
    1132                 :            :                             // Drop out of IN_GROUP and flag that the group
    1133                 :            :                             // can be empty if all members are stopwords.
    1134                 :            :                             if (mode == IN_GROUP2)
    1135                 :            :                                 Parse(pParser, EMPTY_GROUP_OK, NULL, &state);
    1136                 :            :                             mode = DEFAULT;
    1137                 :            :                         }
    1138                 :            :                         // Final term of a partial match query, with no
    1139                 :            :                         // following characters - treat as a wildcard.
    1140                 :            :                         Parse(pParser, PARTIAL_TERM, term_obj, &state);
    1141                 :            :                         continue;
    1142                 :            :                     }
    1143                 :            :                 }
    1144                 :            :             }
    1145                 :            : 
    1146                 :            :             // Check spelling, if we're a normal term, and any of the prefixes
    1147                 :            :             // are empty.
    1148                 :            :             if ((flags & FLAG_SPELLING_CORRECTION) && !was_acronym) {
    1149                 :            :                 const list<string> & pfxes = prefix_info->prefixes;
    1150                 :            :                 list<string>::const_iterator pfx_it;
    1151                 :            :                 for (pfx_it = pfxes.begin(); pfx_it != pfxes.end(); ++pfx_it) {
    1152                 :            :                     if (!pfx_it->empty())
    1153                 :            :                         continue;
    1154                 :            :                     const string & suggest = db.get_spelling_suggestion(term);
    1155                 :            :                     if (!suggest.empty()) {
    1156                 :            :                         if (corrected_query.empty()) corrected_query = qs;
    1157                 :            :                         size_t term_end_index = it.raw() - qs.data();
    1158                 :            :                         size_t n = term_end_index - term_start_index;
    1159                 :            :                         size_t pos = term_start_index + correction_offset;
    1160                 :            :                         corrected_query.replace(pos, n, suggest);
    1161                 :            :                         correction_offset += suggest.size();
    1162                 :            :                         correction_offset -= n;
    1163                 :            :                     }
    1164                 :            :                     break;
    1165                 :            :                 }
    1166                 :            :             }
    1167                 :            : 
    1168                 :            :             if (mode == IN_PHRASED_TERM) {
    1169                 :            :                 Parse(pParser, PHR_TERM, term_obj, &state);
    1170                 :            :             } else {
    1171                 :            :                 // See if the next token will be PHR_TERM - if so, this one
    1172                 :            :                 // needs to be TERM not GROUP_TERM.
    1173                 :            :                 if ((mode == IN_GROUP || mode == IN_GROUP2) &&
    1174                 :            :                     is_phrase_generator(*it)) {
    1175                 :            :                     // FIXME: can we clean this up?
    1176                 :            :                     Utf8Iterator p = it;
    1177                 :            :                     do {
    1178                 :            :                         ++p;
    1179                 :            :                     } while (p != end && is_phrase_generator(*p));
    1180                 :            :                     // Don't generate a phrase unless the phrase generators are
    1181                 :            :                     // immediately followed by another term.
    1182                 :            :                     if (p != end && is_wordchar(*p)) {
    1183                 :            :                         mode = DEFAULT;
    1184                 :            :                     }
    1185                 :            :                 }
    1186                 :            : 
    1187                 :            :                 int token = TERM;
    1188                 :            :                 if (mode == IN_GROUP || mode == IN_GROUP2) {
    1189                 :            :                     mode = IN_GROUP2;
    1190                 :            :                     token = GROUP_TERM;
    1191                 :            :                 }
    1192                 :            :                 Parse(pParser, token, term_obj, &state);
    1193                 :            :                 if (token == TERM && mode != DEFAULT)
    1194                 :            :                     continue;
    1195                 :            :             }
    1196                 :            :         }
    1197                 :            : 
    1198                 :            :         if (it == end) break;
    1199                 :            : 
    1200                 :            :         if (is_phrase_generator(*it)) {
    1201                 :            :             // Skip multiple phrase generators.
    1202                 :            :             do {
    1203                 :            :                 ++it;
    1204                 :            :             } while (it != end && is_phrase_generator(*it));
    1205                 :            :             // Don't generate a phrase unless the phrase generators are
    1206                 :            :             // immediately followed by another term.
    1207                 :            :             if (it != end && is_wordchar(*it)) {
    1208                 :            :                 mode = IN_PHRASED_TERM;
    1209                 :            :                 term_start_index = it.raw() - qs.data();
    1210                 :            :                 goto phrased_term;
    1211                 :            :             }
    1212                 :            :         } else if (mode == DEFAULT || mode == IN_GROUP || mode == IN_GROUP2) {
    1213                 :            :             int old_mode = mode;
    1214                 :            :             mode = DEFAULT;
    1215                 :            :             if (!last_was_operator_needing_term && is_whitespace(*it)) {
    1216                 :            :                 newprev = ' ';
    1217                 :            :                 // Skip multiple whitespace.
    1218                 :            :                 do {
    1219                 :            :                     ++it;
    1220                 :            :                 } while (it != end && is_whitespace(*it));
    1221                 :            :                 // Don't generate a group unless the terms are only separated
    1222                 :            :                 // by whitespace.
    1223                 :            :                 if (it != end && is_wordchar(*it)) {
    1224                 :            :                     if (old_mode == IN_GROUP || old_mode == IN_GROUP2) {
    1225                 :            :                         mode = IN_GROUP2;
    1226                 :            :                     } else {
    1227                 :            :                         mode = IN_GROUP;
    1228                 :            :                     }
    1229                 :            :                 }
    1230                 :            :             }
    1231                 :            :         }
    1232                 :            :     }
    1233                 :            : done:
    1234                 :            :     if (!state.error) {
    1235                 :            :         // Implicitly close any unclosed quotes...
    1236                 :            :         if (mode == IN_QUOTES || mode == IN_PREFIXED_QUOTES)
    1237                 :            :             Parse(pParser, QUOTE, NULL, &state);
    1238                 :            :         Parse(pParser, 0, NULL, &state);
    1239                 :            :     }
    1240                 :            : 
    1241                 :            :     errmsg = state.error;
    1242                 :            :     return state.query;
    1243                 :            : }
    1244                 :            : 
    1245                 :            : struct ProbQuery {
    1246                 :            :     Query * query;
    1247                 :            :     Query * love;
    1248                 :            :     Query * hate;
    1249                 :            :     // filter is a map from prefix to a query for that prefix.  Queries with
    1250                 :            :     // the same prefix are combined with OR, and the results of this are
    1251                 :            :     // combined with AND to get the full filter.
    1252                 :            :     map<filter_group_id, Query> filter;
    1253                 :            : 
    1254                 :            :     ProbQuery() : query(0), love(0), hate(0) { }
    1255                 :            :     ~ProbQuery() {
    1256                 :            :         delete query;
    1257                 :            :         delete love;
    1258                 :            :         delete hate;
    1259                 :            :     }
    1260                 :            : 
    1261                 :            :     void add_filter(const filter_group_id & id, const Query & q) {
    1262                 :            :         filter[id] = q;
    1263                 :            :     }
    1264                 :            : 
    1265                 :            :     void append_filter(const filter_group_id & id, const Query & qnew) {
    1266                 :            :         Query & q = filter[id];
    1267                 :            :         // We OR filters with the same prefix if they're exclusive, otherwise
    1268                 :            :         // we AND them.
    1269                 :            :         bool exclusive = (id.prefix_info->type == BOOLEAN_EXCLUSIVE);
    1270                 :            :         Query::op op = exclusive ? Query::OP_OR : Query::OP_AND;
    1271                 :            :         q = Query(op, q, qnew);
    1272                 :            :     }
    1273                 :            : 
    1274                 :            :     void add_filter_range(Xapian::valueno slot, const Query & range) {
    1275                 :            :         filter[filter_group_id(slot)] = range;
    1276                 :            :     }
    1277                 :            : 
    1278                 :            :     void append_filter_range(Xapian::valueno slot, const Query & range) {
    1279                 :            :         Query & q = filter[filter_group_id(slot)];
    1280                 :            :         q = Query(Query::OP_OR, q, range);
    1281                 :            :     }
    1282                 :            : 
    1283                 :            :     Query merge_filters() const {
    1284                 :            :         map<filter_group_id, Query>::const_iterator i = filter.begin();
    1285                 :            :         Assert(i != filter.end());
    1286                 :            :         Query q = i->second;
    1287                 :            :         while (++i != filter.end()) {
    1288                 :            :             q = Query(Query::OP_AND, q, i->second);
    1289                 :            :         }
    1290                 :            :         return q;
    1291                 :            :     }
    1292                 :            : };
    1293                 :            : 
    1294                 :            : /// A group of terms separated only by whitespace.
    1295                 :            : class TermGroup {
    1296                 :            :     vector<Term *> terms;
    1297                 :            : 
    1298                 :            :     /** Controls how to handle a group where all terms are stopwords.
    1299                 :            :      *
    1300                 :            :      *  If true, then as_group() returns NULL.  If false, then the
    1301                 :            :      *  stopword status of the terms is ignored.
    1302                 :            :      */
    1303                 :            :     bool empty_ok;
    1304                 :            : 
    1305                 :            :   public:
    1306                 :            :     TermGroup() : empty_ok(false) { }
    1307                 :            : 
    1308                 :            :     /// Add a Term object to this TermGroup object.
    1309                 :            :     void add_term(Term * term) {
    1310                 :            :         terms.push_back(term);
    1311                 :            :     }
    1312                 :            : 
    1313                 :            :     /// Set the empty_ok flag.
    1314                 :            :     void set_empty_ok() { empty_ok = true; }
    1315                 :            : 
    1316                 :            :     /// Convert to a Xapian::Query * using default_op.
    1317                 :            :     Query * as_group(State *state) const;
    1318                 :            : 
    1319                 :            :     /** Provide a way to explicitly delete an object of this class.  The
    1320                 :            :      *  destructor is protected to prevent auto-variables of this type.
    1321                 :            :      */
    1322                 :            :     void destroy() { delete this; }
    1323                 :            : 
    1324                 :            :   protected:
    1325                 :            :     /** Protected destructor, so an auto-variable of this type is a
    1326                 :            :      *  compile-time error - you must allocate this object with new.
    1327                 :            :      */
    1328                 :            :     ~TermGroup() {
    1329                 :            :         vector<Term*>::const_iterator i;
    1330                 :            :         for (i = terms.begin(); i != terms.end(); ++i) {
    1331                 :            :             delete *i;
    1332                 :            :         }
    1333                 :            :     }
    1334                 :            : };
    1335                 :            : 
    1336                 :            : Query *
    1337                 :            : TermGroup::as_group(State *state) const
    1338                 :            : {
    1339                 :            :     const Xapian::Stopper * stopper = state->get_stopper();
    1340                 :            :     size_t stoplist_size = state->stoplist_size();
    1341                 :            : reprocess:
    1342                 :            :     Query::op default_op = state->default_op();
    1343                 :            :     vector<Query> subqs;
    1344                 :            :     subqs.reserve(terms.size());
    1345                 :            :     if (state->flags & QueryParser::FLAG_AUTO_MULTIWORD_SYNONYMS) {
    1346                 :            :         // Check for multi-word synonyms.
    1347                 :            :         Database db = state->get_database();
    1348                 :            : 
    1349                 :            :         string key;
    1350                 :            :         vector<Term*>::const_iterator begin = terms.begin();
    1351                 :            :         vector<Term*>::const_iterator i = begin;
    1352                 :            :         while (i != terms.end()) {
    1353                 :            :             TermIterator synkey(db.synonym_keys_begin((*i)->name));
    1354                 :            :             TermIterator synend(db.synonym_keys_end((*i)->name));
    1355                 :            :             if (synkey == synend) {
    1356                 :            :                 // No multi-synonym matches.
    1357                 :            :                 if (stopper && (*stopper)((*i)->name)) {
    1358                 :            :                     state->add_to_stoplist(*i);
    1359                 :            :                 } else {
    1360                 :            :                     subqs.push_back((*i)->get_query_with_auto_synonyms());
    1361                 :            :                 }
    1362                 :            :                 begin = ++i;
    1363                 :            :                 continue;
    1364                 :            :             }
    1365                 :            :             key.resize(0);
    1366                 :            :             while (i != terms.end()) {
    1367                 :            :                 if (!key.empty()) key += ' ';
    1368                 :            :                 key += (*i)->name;
    1369                 :            :                 ++i;
    1370                 :            :                 synkey.skip_to(key);
    1371                 :            :                 if (synkey == synend || !startswith(*synkey, key)) break;
    1372                 :            :             }
    1373                 :            :             // Greedily try to match as many consecutive words as possible.
    1374                 :            :             TermIterator syn, end;
    1375                 :            :             while (true) {
    1376                 :            :                 syn = db.synonyms_begin(key);
    1377                 :            :                 end = db.synonyms_end(key);
    1378                 :            :                 if (syn != end) break;
    1379                 :            :                 if (--i == begin) break;
    1380                 :            :                 key.resize(key.size() - (*i)->name.size() - 1);
    1381                 :            :             }
    1382                 :            :             if (i == begin) {
    1383                 :            :                 // No multi-synonym matches.
    1384                 :            :                 if (stopper && (*stopper)((*i)->name)) {
    1385                 :            :                     state->add_to_stoplist(*i);
    1386                 :            :                 } else {
    1387                 :            :                     subqs.push_back((*i)->get_query_with_auto_synonyms());
    1388                 :            :                 }
    1389                 :            :                 begin = ++i;
    1390                 :            :                 continue;
    1391                 :            :             }
    1392                 :            : 
    1393                 :            :             vector<Query> subqs2;
    1394                 :            :             vector<Term*>::const_iterator j;
    1395                 :            :             for (j = begin; j != i; ++j) {
    1396                 :            :                 if (stopper && (*stopper)((*j)->name)) {
    1397                 :            :                     state->add_to_stoplist(*j);
    1398                 :            :                 } else {
    1399                 :            :                     subqs2.push_back((*j)->get_query());
    1400                 :            :                 }
    1401                 :            :             }
    1402                 :            :             Query q_original_terms;
    1403                 :            :             if (is_positional(default_op)) {
    1404                 :            :                 q_original_terms = Query(default_op,
    1405                 :            :                                          subqs2.begin(), subqs2.end(),
    1406                 :            :                                          subqs2.size() + 9);
    1407                 :            :             } else {
    1408                 :            :                 q_original_terms = Query(default_op,
    1409                 :            :                                          subqs2.begin(), subqs2.end());
    1410                 :            :             }
    1411                 :            :             subqs2.clear();
    1412                 :            : 
    1413                 :            :             // Use the position of the first term for the synonyms.
    1414                 :            :             Xapian::termpos pos = (*begin)->pos;
    1415                 :            :             begin = i;
    1416                 :            :             while (syn != end) {
    1417                 :            :                 subqs2.push_back(Query(*syn, 1, pos));
    1418                 :            :                 ++syn;
    1419                 :            :             }
    1420                 :            :             Query q_synonym_terms(Query::OP_SYNONYM, subqs2.begin(), subqs2.end());
    1421                 :            :             subqs2.clear();
    1422                 :            :             subqs.push_back(Query(Query::OP_SYNONYM,
    1423                 :            :                                   q_original_terms, q_synonym_terms));
    1424                 :            :         }
    1425                 :            :     } else {
    1426                 :            :         vector<Term*>::const_iterator i;
    1427                 :            :         for (i = terms.begin(); i != terms.end(); ++i) {
    1428                 :            :             if (stopper && (*stopper)((*i)->name)) {
    1429                 :            :                 state->add_to_stoplist(*i);
    1430                 :            :             } else {
    1431                 :            :                 subqs.push_back((*i)->get_query_with_auto_synonyms());
    1432                 :            :             }
    1433                 :            :         }
    1434                 :            :     }
    1435                 :            : 
    1436                 :            :     if (!empty_ok && stopper && subqs.empty() &&
    1437                 :            :         stoplist_size < state->stoplist_size()) {
    1438                 :            :         // This group is all stopwords, so roll-back, disable stopper
    1439                 :            :         // temporarily, and reprocess this group.
    1440                 :            :         state->stoplist_resize(stoplist_size);
    1441                 :            :         stopper = NULL;
    1442                 :            :         goto reprocess;
    1443                 :            :     }
    1444                 :            : 
    1445                 :            :     Query * q = NULL;
    1446                 :            :     if (!subqs.empty()) {
    1447                 :            :         if (is_positional(default_op)) {
    1448                 :            :             q = new Query(default_op, subqs.begin(), subqs.end(),
    1449                 :            :                              subqs.size() + 9);
    1450                 :            :         } else {
    1451                 :            :             q = new Query(default_op, subqs.begin(), subqs.end());
    1452                 :            :         }
    1453                 :            :     }
    1454                 :            :     delete this;
    1455                 :            :     return q;
    1456                 :            : }
    1457                 :            : 
    1458                 :            : /// Some terms which form a positional sub-query.
    1459                 :            : class Terms {
    1460                 :            :     vector<Term *> terms;
    1461                 :            :     size_t window;
    1462                 :            : 
    1463                 :            :     /** Keep track of whether the terms added all have the same list of
    1464                 :            :      *  prefixes.  If so, we'll build a set of phrases, one using each prefix.
    1465                 :            :      *  This works around the limitation that a phrase cannot have multiple
    1466                 :            :      *  components which are "OR" combinations of terms, but is also probably
    1467                 :            :      *  what users expect: i.e., if a user specifies a phrase in a field, and
    1468                 :            :      *  that field maps to multiple prefixes, the user probably wants a phrase
    1469                 :            :      *  returned with all terms having one of those prefixes, rather than a
    1470                 :            :      *  phrase comprised of terms with differing prefixes.
    1471                 :            :      */
    1472                 :            :     bool uniform_prefixes;
    1473                 :            : 
    1474                 :            :     /** The list of prefixes of the terms added.
    1475                 :            :      *  This will be NULL if the terms have different prefixes.
    1476                 :            :      */
    1477                 :            :     const list<string> * prefixes;
    1478                 :            : 
    1479                 :            :     /// Convert to a query using the given operator and window size.
    1480                 :            :     Query * as_opwindow_query(Query::op op, Xapian::termcount w_delta) const {
    1481                 :            :         Query * q = NULL;
    1482                 :            :         size_t n_terms = terms.size();
    1483                 :            :         Xapian::termcount w = w_delta + terms.size();
    1484                 :            :         if (uniform_prefixes) {
    1485                 :            :             if (prefixes) {
    1486                 :            :                 list<string>::const_iterator piter;
    1487                 :            :                 for (piter = prefixes->begin(); piter != prefixes->end(); ++piter) {
    1488                 :            :                     vector<Query> subqs;
    1489                 :            :                     subqs.reserve(n_terms);
    1490                 :            :                     vector<Term *>::const_iterator titer;
    1491                 :            :                     for (titer = terms.begin(); titer != terms.end(); ++titer) {
    1492                 :            :                         Term * t = *titer;
    1493                 :            :                         subqs.push_back(Query(t->make_term(*piter), 1, t->pos));
    1494                 :            :                     }
    1495                 :            :                     add_to_query(q, Query::OP_OR,
    1496                 :            :                                  Query(op, subqs.begin(), subqs.end(), w));
    1497                 :            :                 }
    1498                 :            :             }
    1499                 :            :         } else {
    1500                 :            :             vector<Query> subqs;
    1501                 :            :             subqs.reserve(n_terms);
    1502                 :            :             vector<Term *>::const_iterator titer;
    1503                 :            :             for (titer = terms.begin(); titer != terms.end(); ++titer) {
    1504                 :            :                 subqs.push_back((*titer)->get_query());
    1505                 :            :             }
    1506                 :            :             q = new Query(op, subqs.begin(), subqs.end(), w);
    1507                 :            :         }
    1508                 :            : 
    1509                 :            :         delete this;
    1510                 :            :         return q;
    1511                 :            :     }
    1512                 :            : 
    1513                 :            :   public:
    1514                 :            :     Terms() : window(0), uniform_prefixes(true), prefixes(NULL) { }
    1515                 :            : 
    1516                 :            :     /// Add an unstemmed Term object to this Terms object.
    1517                 :            :     void add_positional_term(Term * term) {
    1518                 :            :         const list<string> & term_prefixes = term->prefix_info->prefixes;
    1519                 :            :         if (terms.empty()) {
    1520                 :            :             prefixes = &term_prefixes;
    1521                 :            :         } else if (uniform_prefixes && prefixes != &term_prefixes) {
    1522                 :            :             if (*prefixes != term_prefixes)  {
    1523                 :            :                 prefixes = NULL;
    1524                 :            :                 uniform_prefixes = false;
    1525                 :            :             }
    1526                 :            :         }
    1527                 :            :         term->need_positions();
    1528                 :            :         terms.push_back(term);
    1529                 :            :     }
    1530                 :            : 
    1531                 :            :     void adjust_window(size_t alternative_window) {
    1532                 :            :         if (alternative_window > window) window = alternative_window;
    1533                 :            :     }
    1534                 :            : 
    1535                 :            :     /// Convert to a Xapian::Query * using adjacent OP_PHRASE.
    1536                 :            :     Query * as_phrase_query() const {
    1537                 :            :         return as_opwindow_query(Query::OP_PHRASE, 0);
    1538                 :            :     }
    1539                 :            : 
    1540                 :            :     /// Convert to a Xapian::Query * using OP_NEAR.
    1541                 :            :     Query * as_near_query() const {
    1542                 :            :         // The common meaning of 'a NEAR b' is "a within 10 terms of b", which
    1543                 :            :         // means a window size of 11.  For more than 2 terms, we just add one
    1544                 :            :         // to the window size for each extra term.
    1545                 :            :         size_t w = window;
    1546                 :            :         if (w == 0) w = 10;
    1547                 :            :         return as_opwindow_query(Query::OP_NEAR, w - 1);
    1548                 :            :     }
    1549                 :            : 
    1550                 :            :     /// Convert to a Xapian::Query * using OP_PHRASE to implement ADJ.
    1551                 :            :     Query * as_adj_query() const {
    1552                 :            :         // The common meaning of 'a ADJ b' is "a at most 10 terms before b",
    1553                 :            :         // which means a window size of 11.  For more than 2 terms, we just add
    1554                 :            :         // one to the window size for each extra term.
    1555                 :            :         size_t w = window;
    1556                 :            :         if (w == 0) w = 10;
    1557                 :            :         return as_opwindow_query(Query::OP_PHRASE, w - 1);
    1558                 :            :     }
    1559                 :            : 
    1560                 :            :     /** Provide a way to explicitly delete an object of this class.  The
    1561                 :            :      *  destructor is protected to prevent auto-variables of this type.
    1562                 :            :      */
    1563                 :            :     void destroy() { delete this; }
    1564                 :            : 
    1565                 :            :   protected:
    1566                 :            :     /** Protected destructor, so an auto-variable of this type is a
    1567                 :            :      *  compile-time error - you must allocate this object with new.
    1568                 :            :      */
    1569                 :            :     ~Terms() {
    1570                 :            :         vector<Term *>::const_iterator t;
    1571                 :            :         for (t = terms.begin(); t != terms.end(); ++t) {
    1572                 :            :             delete *t;
    1573                 :            :         }
    1574                 :            :     }
    1575                 :            : };
    1576                 :            : 
    1577                 :            : // Helper macro for converting a boolean operation into a Xapian::Query.
    1578                 :            : #define BOOL_OP_TO_QUERY(E, A, OP, B, OP_TXT) \
    1579                 :            :     do {\
    1580                 :            :         if (!A || !B) {\
    1581                 :            :             state->error = "Syntax: <expression> "OP_TXT" <expression>";\
    1582                 :            :             yy_parse_failed(yypParser);\
    1583                 :            :             return;\
    1584                 :            :         }\
    1585                 :            :         E = new Query(OP, *A, *B);\
    1586                 :            :         delete A;\
    1587                 :            :         delete B;\
    1588                 :            :     } while (0)
    1589                 :            : 
    1590                 :            : #line 1591 "queryparser/queryparser_internal.cc"
    1591                 :            : /* Next is all token values, in a form suitable for use by makeheaders.
    1592                 :            : ** This section will be null unless lemon is run with the -m switch.
    1593                 :            : */
    1594                 :            : /* 
    1595                 :            : ** These constants (all generated automatically by the parser generator)
    1596                 :            : ** specify the various kinds of tokens (terminals) that the parser
    1597                 :            : ** understands. 
    1598                 :            : **
    1599                 :            : ** Each symbol here is a terminal symbol in the grammar.
    1600                 :            : */
    1601                 :            : /* Make sure the INTERFACE macro is defined.
    1602                 :            : */
    1603                 :            : #ifndef INTERFACE
    1604                 :            : # define INTERFACE 1
    1605                 :            : #endif
    1606                 :            : /* The next thing included is series of defines which control
    1607                 :            : ** various aspects of the generated parser.
    1608                 :            : **    YYCODETYPE         is the data type used for storing terminal
    1609                 :            : **                       and nonterminal numbers.  "unsigned char" is
    1610                 :            : **                       used if there are fewer than 250 terminals
    1611                 :            : **                       and nonterminals.  "int" is used otherwise.
    1612                 :            : **    YYNOCODE           is a number of type YYCODETYPE which corresponds
    1613                 :            : **                       to no legal terminal or nonterminal number.  This
    1614                 :            : **                       number is used to fill in empty slots of the hash 
    1615                 :            : **                       table.
    1616                 :            : **    YYFALLBACK         If defined, this indicates that one or more tokens
    1617                 :            : **                       have fall-back values which should be used if the
    1618                 :            : **                       original value of the token will not parse.
    1619                 :            : **    YYACTIONTYPE       is the data type used for storing terminal
    1620                 :            : **                       and nonterminal numbers.  "unsigned char" is
    1621                 :            : **                       used if there are fewer than 250 rules and
    1622                 :            : **                       states combined.  "int" is used otherwise.
    1623                 :            : **    ParseTOKENTYPE     is the data type used for minor tokens given 
    1624                 :            : **                       directly to the parser from the tokenizer.
    1625                 :            : **    YYMINORTYPE        is the data type used for all minor tokens.
    1626                 :            : **                       This is typically a union of many types, one of
    1627                 :            : **                       which is ParseTOKENTYPE.  The entry in the union
    1628                 :            : **                       for base tokens is called "yy0".
    1629                 :            : **    YYSTACKDEPTH       is the maximum depth of the parser's stack.  If
    1630                 :            : **                       zero the stack is dynamically sized using realloc()
    1631                 :            : **    ParseARG_SDECL     A static variable declaration for the %extra_argument
    1632                 :            : **    ParseARG_PDECL     A parameter declaration for the %extra_argument
    1633                 :            : **    ParseARG_STORE     Code to store %extra_argument into yypParser
    1634                 :            : **    ParseARG_FETCH     Code to extract %extra_argument from yypParser
    1635                 :            : **    YYNSTATE           the combined number of states.
    1636                 :            : **    YYNRULE            the number of rules in the grammar
    1637                 :            : **    YYERRORSYMBOL      is the code number of the error symbol.  If not
    1638                 :            : **                       defined, then do no error processing.
    1639                 :            : */
    1640                 :            : #define YYCODETYPE unsigned char
    1641                 :            : #define YYNOCODE 40
    1642                 :            : #define YYACTIONTYPE unsigned char
    1643                 :            : #define ParseTOKENTYPE Term *
    1644                 :            : typedef union {
    1645                 :            :   int yyinit;
    1646                 :            :   ParseTOKENTYPE yy0;
    1647                 :            :   TermGroup * yy14;
    1648                 :            :   Terms * yy32;
    1649                 :            :   Query * yy39;
    1650                 :            :   ProbQuery * yy40;
    1651                 :            :   int yy46;
    1652                 :            : } YYMINORTYPE;
    1653                 :            : #ifndef YYSTACKDEPTH
    1654                 :            : #define YYSTACKDEPTH 100
    1655                 :            : #endif
    1656                 :            : #define ParseARG_SDECL State * state;
    1657                 :            : #define ParseARG_PDECL ,State * state
    1658                 :            : #define ParseARG_FETCH State * state = yypParser->state
    1659                 :            : #define ParseARG_STORE yypParser->state = state
    1660                 :            : #define YYNSTATE 75
    1661                 :            : #define YYNRULE 54
    1662                 :            : #define YY_NO_ACTION      (YYNSTATE+YYNRULE+2)
    1663                 :            : #define YY_ACCEPT_ACTION  (YYNSTATE+YYNRULE+1)
    1664                 :            : #define YY_ERROR_ACTION   (YYNSTATE+YYNRULE)
    1665                 :            : 
    1666                 :            : /* Define the yytestcase() macro to be a no-op if is not already defined
    1667                 :            : ** otherwise.
    1668                 :            : **
    1669                 :            : ** Applications can choose to define yytestcase() in the %include section
    1670                 :            : ** to a macro that can assist in verifying code coverage.  For production
    1671                 :            : ** code the yytestcase() macro should be turned off.  But it is useful
    1672                 :            : ** for testing.
    1673                 :            : */
    1674                 :            : #ifndef yytestcase
    1675                 :            : # define yytestcase(X)
    1676                 :            : #endif
    1677                 :            : 
    1678                 :            : /* Next are the tables used to determine what action to take based on the
    1679                 :            : ** current state and lookahead token.  These tables are used to implement
    1680                 :            : ** functions that take a state number and lookahead value and return an
    1681                 :            : ** action integer.  
    1682                 :            : **
    1683                 :            : ** Suppose the action integer is N.  Then the action is determined as
    1684                 :            : ** follows
    1685                 :            : **
    1686                 :            : **   0 <= N < YYNSTATE                  Shift N.  That is, push the lookahead
    1687                 :            : **                                      token onto the stack and goto state N.
    1688                 :            : **
    1689                 :            : **   YYNSTATE <= N < YYNSTATE+YYNRULE   Reduce by rule N-YYNSTATE.
    1690                 :            : **
    1691                 :            : **   N == YYNSTATE+YYNRULE              A syntax error has occurred.
    1692                 :            : **
    1693                 :            : **   N == YYNSTATE+YYNRULE+1            The parser accepts its input.
    1694                 :            : **
    1695                 :            : **   N == YYNSTATE+YYNRULE+2            No such action.  Denotes unused
    1696                 :            : **                                      slots in the yy_action[] table.
    1697                 :            : **
    1698                 :            : ** The action table is constructed as a single large table named yy_action[].
    1699                 :            : ** Given state S and lookahead X, the action is computed as
    1700                 :            : **
    1701                 :            : **      yy_action[ yy_shift_ofst[S] + X ]
    1702                 :            : **
    1703                 :            : ** If the index value yy_shift_ofst[S]+X is out of range or if the value
    1704                 :            : ** yy_lookahead[yy_shift_ofst[S]+X] is not equal to X or if yy_shift_ofst[S]
    1705                 :            : ** is equal to YY_SHIFT_USE_DFLT, it means that the action is not in the table
    1706                 :            : ** and that yy_default[S] should be used instead.  
    1707                 :            : **
    1708                 :            : ** The formula above is for computing the action when the lookahead is
    1709                 :            : ** a terminal symbol.  If the lookahead is a non-terminal (as occurs after
    1710                 :            : ** a reduce action) then the yy_reduce_ofst[] array is used in place of
    1711                 :            : ** the yy_shift_ofst[] array and YY_REDUCE_USE_DFLT is used in place of
    1712                 :            : ** YY_SHIFT_USE_DFLT.
    1713                 :            : **
    1714                 :            : ** The following are the tables generated in this section:
    1715                 :            : **
    1716                 :            : **  yy_action[]        A single table containing all actions.
    1717                 :            : **  yy_lookahead[]     A table containing the lookahead for each entry in
    1718                 :            : **                     yy_action.  Used to detect hash collisions.
    1719                 :            : **  yy_shift_ofst[]    For each state, the offset into yy_action for
    1720                 :            : **                     shifting terminals.
    1721                 :            : **  yy_reduce_ofst[]   For each state, the offset into yy_action for
    1722                 :            : **                     shifting non-terminals after a reduce.
    1723                 :            : **  yy_default[]       Default action for each state.
    1724                 :            : */
    1725                 :            : static const YYACTIONTYPE yy_action[] = {
    1726                 :            :  /*     0 */   130,   25,   35,   17,    8,   56,   19,   13,   16,   49,
    1727                 :            :  /*    10 */    28,   24,   29,   31,    6,    1,    2,   11,   12,    7,
    1728                 :            :  /*    20 */    34,   15,   23,   75,   45,   46,   72,   57,   14,    5,
    1729                 :            :  /*    30 */    76,   63,   42,    3,    4,    1,    2,   48,   11,   12,
    1730                 :            :  /*    40 */    43,   34,   15,   50,   47,   45,   46,   72,   57,   14,
    1731                 :            :  /*    50 */     5,   51,   63,   38,   35,   36,    8,   56,   19,   13,
    1732                 :            :  /*    60 */    16,   52,   28,   24,   29,   31,   38,   35,   37,    8,
    1733                 :            :  /*    70 */    56,   19,   13,   16,   30,   28,   24,   29,   31,   38,
    1734                 :            :  /*    80 */    35,   21,    8,   56,   19,   13,   16,   53,   28,   24,
    1735                 :            :  /*    90 */    29,   31,   38,   35,   22,    8,   56,   19,   13,   16,
    1736                 :            :  /*   100 */    32,   28,   24,   29,   31,   33,   35,   17,    8,   56,
    1737                 :            :  /*   110 */    19,   13,   16,   54,   28,   24,   29,   31,   38,   35,
    1738                 :            :  /*   120 */    73,    8,   56,   19,   13,   16,   62,   28,   24,   29,
    1739                 :            :  /*   130 */    31,   38,   35,   74,    8,   56,   19,   13,   16,   55,
    1740                 :            :  /*   140 */    28,   24,   29,   31,   11,   12,  131,   34,   15,  131,
    1741                 :            :  /*   150 */   131,   45,   46,   72,   57,   14,    5,  131,   63,  131,
    1742                 :            :  /*   160 */   102,  102,  131,   34,   18,  131,  131,   45,   46,  102,
    1743                 :            :  /*   170 */   102,   14,    5,  106,   63,  106,  106,  106,  106,   26,
    1744                 :            :  /*   180 */    27,   26,   27,  131,  131,  131,   41,   40,   41,   40,
    1745                 :            :  /*   190 */   131,   34,   20,  131,  106,   45,   46,   60,  131,   14,
    1746                 :            :  /*   200 */     5,  131,   63,   34,   20,  131,  131,   45,   46,   65,
    1747                 :            :  /*   210 */   131,   14,    5,  131,   63,   34,   20,  131,  131,   45,
    1748                 :            :  /*   220 */    46,   69,  131,   14,    5,  131,   63,   34,   20,  131,
    1749                 :            :  /*   230 */   131,   45,   46,   71,  131,   14,    5,  131,   63,   34,
    1750                 :            :  /*   240 */    18,  131,  131,   45,   46,  131,  131,   14,    5,  131,
    1751                 :            :  /*   250 */    63,  131,   39,   44,  131,   28,   24,   29,   31,   59,
    1752                 :            :  /*   260 */   131,  131,   61,  131,   28,   24,   29,   31,  131,   64,
    1753                 :            :  /*   270 */   131,  131,   61,  131,   28,   24,   29,   31,   68,  131,
    1754                 :            :  /*   280 */   131,   61,  131,   28,   24,   29,   31,   70,  131,  131,
    1755                 :            :  /*   290 */    61,  131,   28,   24,   29,   31,  131,   67,   44,  131,
    1756                 :            :  /*   300 */    28,   24,   29,   31,  107,  131,  107,  107,  107,  107,
    1757                 :            :  /*   310 */     9,   10,    4,    1,    2,  131,  131,  131,  131,   66,
    1758                 :            :  /*   320 */    58,  131,  131,  131,  131,  107,
    1759                 :            : };
    1760                 :            : static const YYCODETYPE yy_lookahead[] = {
    1761                 :            :  /*     0 */    25,   26,   27,   28,   29,   30,   31,   32,   33,   12,
    1762                 :            :  /*    10 */    35,   36,   37,   38,    5,    4,    5,    8,    9,   10,
    1763                 :            :  /*    20 */    11,   12,   34,    0,   15,   16,   17,   18,   19,   20,
    1764                 :            :  /*    30 */     0,   22,   12,    2,    3,    4,    5,   12,    8,    9,
    1765                 :            :  /*    40 */    12,   11,   12,   14,   19,   15,   16,   17,   18,   19,
    1766                 :            :  /*    50 */    20,   13,   22,   26,   27,   28,   29,   30,   31,   32,
    1767                 :            :  /*    60 */    33,   23,   35,   36,   37,   38,   26,   27,   28,   29,
    1768                 :            :  /*    70 */    30,   31,   32,   33,    6,   35,   36,   37,   38,   26,
    1769                 :            :  /*    80 */    27,   28,   29,   30,   31,   32,   33,   12,   35,   36,
    1770                 :            :  /*    90 */    37,   38,   26,   27,   28,   29,   30,   31,   32,   33,
    1771                 :            :  /*   100 */     7,   35,   36,   37,   38,   26,   27,   28,   29,   30,
    1772                 :            :  /*   110 */    31,   32,   33,   12,   35,   36,   37,   38,   26,   27,
    1773                 :            :  /*   120 */    28,   29,   30,   31,   32,   33,   12,   35,   36,   37,
    1774                 :            :  /*   130 */    38,   26,   27,   28,   29,   30,   31,   32,   33,   21,
    1775                 :            :  /*   140 */    35,   36,   37,   38,    8,    9,   39,   11,   12,   39,
    1776                 :            :  /*   150 */    39,   15,   16,   17,   18,   19,   20,   39,   22,   39,
    1777                 :            :  /*   160 */     8,    9,   39,   11,   12,   39,   39,   15,   16,   17,
    1778                 :            :  /*   170 */    18,   19,   20,    0,   22,    2,    3,    4,    5,    6,
    1779                 :            :  /*   180 */     7,    6,    7,   39,   39,   39,   13,   14,   13,   14,
    1780                 :            :  /*   190 */    39,   11,   12,   39,   21,   15,   16,   17,   39,   19,
    1781                 :            :  /*   200 */    20,   39,   22,   11,   12,   39,   39,   15,   16,   17,
    1782                 :            :  /*   210 */    39,   19,   20,   39,   22,   11,   12,   39,   39,   15,
    1783                 :            :  /*   220 */    16,   17,   39,   19,   20,   39,   22,   11,   12,   39,
    1784                 :            :  /*   230 */    39,   15,   16,   17,   39,   19,   20,   39,   22,   11,
    1785                 :            :  /*   240 */    12,   39,   39,   15,   16,   39,   39,   19,   20,   39,
    1786                 :            :  /*   250 */    22,   39,   32,   33,   39,   35,   36,   37,   38,   30,
    1787                 :            :  /*   260 */    39,   39,   33,   39,   35,   36,   37,   38,   39,   30,
    1788                 :            :  /*   270 */    39,   39,   33,   39,   35,   36,   37,   38,   30,   39,
    1789                 :            :  /*   280 */    39,   33,   39,   35,   36,   37,   38,   30,   39,   39,
    1790                 :            :  /*   290 */    33,   39,   35,   36,   37,   38,   39,   32,   33,   39,
    1791                 :            :  /*   300 */    35,   36,   37,   38,    0,   39,    2,    3,    4,    5,
    1792                 :            :  /*   310 */     8,    9,    3,    4,    5,   39,   39,   39,   39,   17,
    1793                 :            :  /*   320 */    18,   39,   39,   39,   39,   21,
    1794                 :            : };
    1795                 :            : #define YY_SHIFT_USE_DFLT (-4)
    1796                 :            : #define YY_SHIFT_MAX 34
    1797                 :            : static const short yy_shift_ofst[] = {
    1798                 :            :  /*     0 */    30,    9,  136,  136,  136,  136,  136,  136,  152,  180,
    1799                 :            :  /*    10 */   192,  204,  216,  228,   -3,  173,  304,   31,  175,  302,
    1800                 :            :  /*    20 */   175,  309,   11,   25,   38,   23,   20,   28,   29,   68,
    1801                 :            :  /*    30 */    75,   93,  101,  118,  114,
    1802                 :            : };
    1803                 :            : #define YY_REDUCE_USE_DFLT (-26)
    1804                 :            : #define YY_REDUCE_MAX 14
    1805                 :            : static const short yy_reduce_ofst[] = {
    1806                 :            :  /*     0 */   -25,   27,   40,   53,   66,   79,   92,  105,  220,  229,
    1807                 :            :  /*    10 */   239,  248,  257,  265,  -12,
    1808                 :            : };
    1809                 :            : static const YYACTIONTYPE yy_default[] = {
    1810                 :            :  /*     0 */    85,   85,   85,   85,   85,   85,   85,   85,   86,  129,
    1811                 :            :  /*    10 */   129,  129,  129,  103,  129,  104,  105,  129,  104,  129,
    1812                 :            :  /*    20 */   106,   82,   83,  129,  112,   84,  129,  129,  111,  113,
    1813                 :            :  /*    30 */   129,  114,  129,   84,  129,   77,   78,   79,   84,   91,
    1814                 :            :  /*    40 */   120,  122,  125,  127,  105,  108,  109,  110,  119,  118,
    1815                 :            :  /*    50 */   121,  123,  124,  126,  128,  115,   87,   88,   89,   93,
    1816                 :            :  /*    60 */   101,  107,  116,  117,   95,   97,   99,   90,   92,  100,
    1817                 :            :  /*    70 */    94,   96,   98,   80,   81,
    1818                 :            : };
    1819                 :            : #define YY_SZ_ACTTAB (int)(sizeof(yy_action)/sizeof(yy_action[0]))
    1820                 :            : 
    1821                 :            : /* The next table maps tokens into fallback tokens.  If a construct
    1822                 :            : ** like the following:
    1823                 :            : ** 
    1824                 :            : **      %fallback ID X Y Z.
    1825                 :            : **
    1826                 :            : ** appears in the grammar, then ID becomes a fallback token for X, Y,
    1827                 :            : ** and Z.  Whenever one of the tokens X, Y, or Z is input to the parser
    1828                 :            : ** but it does not parse, the type of the token is changed to ID and
    1829                 :            : ** the parse is retried before an error is thrown.
    1830                 :            : */
    1831                 :            : #ifdef YYFALLBACK
    1832                 :            : static const YYCODETYPE yyFallback[] = {
    1833                 :            : };
    1834                 :            : #endif /* YYFALLBACK */
    1835                 :            : 
    1836                 :            : /* The following structure represents a single element of the
    1837                 :            : ** parser's stack.  Information stored includes:
    1838                 :            : **
    1839                 :            : **   +  The state number for the parser at this level of the stack.
    1840                 :            : **
    1841                 :            : **   +  The value of the token stored at this level of the stack.
    1842                 :            : **      (In other words, the "major" token.)
    1843                 :            : **
    1844                 :            : **   +  The semantic value stored at this level of the stack.  This is
    1845                 :            : **      the information used by the action routines in the grammar.
    1846                 :            : **      It is sometimes called the "minor" token.
    1847                 :            : */
    1848                 :      25332 : struct yyStackEntry {
    1849                 :     151787 :   yyStackEntry() {
    1850                 :     151787 :     stateno = 0;
    1851                 :     151787 :     major = 0;
    1852                 :     151787 :   }
    1853                 :     151409 :   yyStackEntry(YYACTIONTYPE stateno_, YYCODETYPE major_, YYMINORTYPE minor_) {
    1854                 :     151409 :     stateno = stateno_;
    1855                 :     151409 :     major = major_;
    1856                 :     151409 :     minor = minor_;
    1857                 :     151409 :   }
    1858                 :            :   YYACTIONTYPE stateno;  /* The state-number */
    1859                 :            :   YYCODETYPE major;       /* The major token value.  This is the code
    1860                 :            :                           ** number for the token at this stack level */
    1861                 :            :   YYMINORTYPE minor;      /* The user-supplied minor token value.  This
    1862                 :            :                           ** is the value of the token  */
    1863                 :            : };
    1864                 :            : 
    1865                 :            : /* The state of the parser is completely contained in an instance of
    1866                 :            : ** the following structure */
    1867                 :      49960 : struct yyParser {
    1868                 :            :   int yyerrcnt;                 /* Shifts left before out of the error */
    1869                 :            :   ParseARG_SDECL                /* A place to hold %extra_argument */
    1870                 :            :   vector<yyStackEntry> yystack; /* The parser's stack */
    1871                 :            : };
    1872                 :            : typedef struct yyParser yyParser;
    1873                 :            : 
    1874                 :            : #include "omassert.h"
    1875                 :            : #include "debuglog.h"
    1876                 :            : 
    1877                 :            : #ifdef XAPIAN_DEBUG_LOG
    1878                 :            : /* For tracing shifts, the names of all terminals and nonterminals
    1879                 :            : ** are required.  The following table supplies these names */
    1880                 :            : static const char *const yyTokenName[] = {
    1881                 :            :   "$",             "ERROR",         "OR",            "XOR",         
    1882                 :            :   "AND",           "NOT",           "NEAR",          "ADJ",         
    1883                 :            :   "LOVE",          "HATE",          "HATE_AFTER_AND",  "SYNONYM",     
    1884                 :            :   "TERM",          "GROUP_TERM",    "PHR_TERM",      "WILD_TERM",   
    1885                 :            :   "PARTIAL_TERM",  "BOOLEAN_FILTER",  "RANGE",         "QUOTE",       
    1886                 :            :   "BRA",           "KET",           "CJKTERM",       "EMPTY_GROUP_OK",
    1887                 :            :   "error",         "query",         "expr",          "prob_expr",   
    1888                 :            :   "bool_arg",      "prob",          "term",          "stop_prob",   
    1889                 :            :   "stop_term",     "compound_term",  "phrase",        "phrased_term",
    1890                 :            :   "group",         "near_expr",     "adj_expr",    
    1891                 :            : };
    1892                 :            : 
    1893                 :            : /* For tracing reduce actions, the names of all rules are required.
    1894                 :            : */
    1895                 :            : static const char *const yyRuleName[] = {
    1896                 :            :  /*   0 */ "query ::= expr",
    1897                 :            :  /*   1 */ "query ::=",
    1898                 :            :  /*   2 */ "expr ::= prob_expr",
    1899                 :            :  /*   3 */ "expr ::= bool_arg AND bool_arg",
    1900                 :            :  /*   4 */ "expr ::= bool_arg NOT bool_arg",
    1901                 :            :  /*   5 */ "expr ::= bool_arg AND NOT bool_arg",
    1902                 :            :  /*   6 */ "expr ::= bool_arg AND HATE_AFTER_AND bool_arg",
    1903                 :            :  /*   7 */ "expr ::= bool_arg OR bool_arg",
    1904                 :            :  /*   8 */ "expr ::= bool_arg XOR bool_arg",
    1905                 :            :  /*   9 */ "bool_arg ::= expr",
    1906                 :            :  /*  10 */ "bool_arg ::=",
    1907                 :            :  /*  11 */ "prob_expr ::= prob",
    1908                 :            :  /*  12 */ "prob_expr ::= term",
    1909                 :            :  /*  13 */ "prob ::= RANGE",
    1910                 :            :  /*  14 */ "prob ::= stop_prob RANGE",
    1911                 :            :  /*  15 */ "prob ::= stop_term stop_term",
    1912                 :            :  /*  16 */ "prob ::= prob stop_term",
    1913                 :            :  /*  17 */ "prob ::= LOVE term",
    1914                 :            :  /*  18 */ "prob ::= stop_prob LOVE term",
    1915                 :            :  /*  19 */ "prob ::= HATE term",
    1916                 :            :  /*  20 */ "prob ::= stop_prob HATE term",
    1917                 :            :  /*  21 */ "prob ::= HATE BOOLEAN_FILTER",
    1918                 :            :  /*  22 */ "prob ::= stop_prob HATE BOOLEAN_FILTER",
    1919                 :            :  /*  23 */ "prob ::= BOOLEAN_FILTER",
    1920                 :            :  /*  24 */ "prob ::= stop_prob BOOLEAN_FILTER",
    1921                 :            :  /*  25 */ "prob ::= LOVE BOOLEAN_FILTER",
    1922                 :            :  /*  26 */ "prob ::= stop_prob LOVE BOOLEAN_FILTER",
    1923                 :            :  /*  27 */ "stop_prob ::= prob",
    1924                 :            :  /*  28 */ "stop_prob ::= stop_term",
    1925                 :            :  /*  29 */ "stop_term ::= TERM",
    1926                 :            :  /*  30 */ "stop_term ::= compound_term",
    1927                 :            :  /*  31 */ "term ::= TERM",
    1928                 :            :  /*  32 */ "term ::= compound_term",
    1929                 :            :  /*  33 */ "compound_term ::= WILD_TERM",
    1930                 :            :  /*  34 */ "compound_term ::= PARTIAL_TERM",
    1931                 :            :  /*  35 */ "compound_term ::= QUOTE phrase QUOTE",
    1932                 :            :  /*  36 */ "compound_term ::= phrased_term",
    1933                 :            :  /*  37 */ "compound_term ::= group",
    1934                 :            :  /*  38 */ "compound_term ::= near_expr",
    1935                 :            :  /*  39 */ "compound_term ::= adj_expr",
    1936                 :            :  /*  40 */ "compound_term ::= BRA expr KET",
    1937                 :            :  /*  41 */ "compound_term ::= SYNONYM TERM",
    1938                 :            :  /*  42 */ "compound_term ::= CJKTERM",
    1939                 :            :  /*  43 */ "phrase ::= TERM",
    1940                 :            :  /*  44 */ "phrase ::= phrase TERM",
    1941                 :            :  /*  45 */ "phrased_term ::= TERM PHR_TERM",
    1942                 :            :  /*  46 */ "phrased_term ::= phrased_term PHR_TERM",
    1943                 :            :  /*  47 */ "group ::= TERM GROUP_TERM",
    1944                 :            :  /*  48 */ "group ::= group GROUP_TERM",
    1945                 :            :  /*  49 */ "group ::= group EMPTY_GROUP_OK",
    1946                 :            :  /*  50 */ "near_expr ::= TERM NEAR TERM",
    1947                 :            :  /*  51 */ "near_expr ::= near_expr NEAR TERM",
    1948                 :            :  /*  52 */ "adj_expr ::= TERM ADJ TERM",
    1949                 :            :  /*  53 */ "adj_expr ::= adj_expr ADJ TERM",
    1950                 :            : };
    1951                 :            : 
    1952                 :            : /*
    1953                 :            : ** This function returns the symbolic name associated with a token
    1954                 :            : ** value.
    1955                 :            : */
    1956                 :            : static const char *ParseTokenName(int tokenType){
    1957                 :            :   if( tokenType>=0 && tokenType<(int)(sizeof(yyTokenName)/sizeof(yyTokenName[0])) ){
    1958                 :            :     return yyTokenName[tokenType];
    1959                 :            :   }
    1960                 :            :   return "Unknown";
    1961                 :            : }
    1962                 :            : 
    1963                 :            : /*
    1964                 :            : ** This function returns the symbolic name associated with a rule
    1965                 :            : ** value.
    1966                 :            : */
    1967                 :            : static const char *ParseRuleName(int ruleNum){
    1968                 :            :   if( ruleNum>=0 && ruleNum<(int)(sizeof(yyRuleName)/sizeof(yyRuleName[0])) ){
    1969                 :            :     return yyRuleName[ruleNum];
    1970                 :            :   }
    1971                 :            :   return "Unknown";
    1972                 :            : }
    1973                 :            : #endif /* XAPIAN_DEBUG_LOG */
    1974                 :            : 
    1975                 :            : /* 
    1976                 :            : ** This function allocates a new parser.
    1977                 :            : ** The only argument is a pointer to a function which works like
    1978                 :            : ** malloc.
    1979                 :            : **
    1980                 :            : ** Inputs:
    1981                 :            : ** None.
    1982                 :            : **
    1983                 :            : ** Outputs:
    1984                 :            : ** A pointer to a parser.  This pointer is used in subsequent calls
    1985                 :            : ** to Parse and ParseFree.
    1986                 :            : */
    1987                 :      24980 : static yyParser *ParseAlloc(){
    1988                 :      24980 :   return new yyParser;
    1989                 :            : }
    1990                 :            : 
    1991                 :            : /* The following function deletes the value associated with a
    1992                 :            : ** symbol.  The symbol can be either a terminal or nonterminal.
    1993                 :            : ** "yymajor" is the symbol code, and "yypminor" is a pointer to
    1994                 :            : ** the value.
    1995                 :            : */
    1996                 :      26448 : static void yy_destructor(
    1997                 :            :   yyParser *yypParser,    /* The parser */
    1998                 :            :   YYCODETYPE yymajor,     /* Type code for object to destroy */
    1999                 :            :   YYMINORTYPE *yypminor   /* The object to be destroyed */
    2000                 :            : ){
    2001                 :      26448 :   ParseARG_FETCH;
    2002 [ +  +  +  +  + :      26448 :   switch( yymajor ){
                      + ]
    2003                 :            :     /* Here is inserted the actions which take place when a
    2004                 :            :     ** terminal or non-terminal is destroyed.  This can happen
    2005                 :            :     ** when the symbol is popped from the stack during a
    2006                 :            :     ** reduce or during error processing or when a parser is 
    2007                 :            :     ** being destroyed before it is finished parsing.
    2008                 :            :     **
    2009                 :            :     ** Note: during a reduce, the only symbols destroyed are those
    2010                 :            :     ** which appear on the RHS of the rule, but which are not used
    2011                 :            :     ** inside the C code.
    2012                 :            :     */
    2013                 :            :       /* TERMINAL Destructor */
    2014                 :            :     case 1: /* ERROR */
    2015                 :            :     case 2: /* OR */
    2016                 :            :     case 3: /* XOR */
    2017                 :            :     case 4: /* AND */
    2018                 :            :     case 5: /* NOT */
    2019                 :            :     case 6: /* NEAR */
    2020                 :            :     case 7: /* ADJ */
    2021                 :            :     case 8: /* LOVE */
    2022                 :            :     case 9: /* HATE */
    2023                 :            :     case 10: /* HATE_AFTER_AND */
    2024                 :            :     case 11: /* SYNONYM */
    2025                 :            :     case 12: /* TERM */
    2026                 :            :     case 13: /* GROUP_TERM */
    2027                 :            :     case 14: /* PHR_TERM */
    2028                 :            :     case 15: /* WILD_TERM */
    2029                 :            :     case 16: /* PARTIAL_TERM */
    2030                 :            :     case 17: /* BOOLEAN_FILTER */
    2031                 :            :     case 18: /* RANGE */
    2032                 :            :     case 19: /* QUOTE */
    2033                 :            :     case 20: /* BRA */
    2034                 :            :     case 21: /* KET */
    2035                 :            :     case 22: /* CJKTERM */
    2036                 :            :     case 23: /* EMPTY_GROUP_OK */
    2037                 :            : {
    2038                 :            : #line 1584 "queryparser/queryparser.lemony"
    2039                 :            : delete (yypminor->yy0);
    2040                 :            : #line 2041 "queryparser/queryparser_internal.cc"
    2041                 :            : }
    2042                 :       1214 :       break;
    2043                 :            :     case 26: /* expr */
    2044                 :            :     case 27: /* prob_expr */
    2045                 :            :     case 28: /* bool_arg */
    2046                 :            :     case 30: /* term */
    2047                 :            :     case 32: /* stop_term */
    2048                 :            :     case 33: /* compound_term */
    2049                 :            : {
    2050                 :            : #line 1659 "queryparser/queryparser.lemony"
    2051                 :            : delete (yypminor->yy39);
    2052                 :            : #line 2053 "queryparser/queryparser_internal.cc"
    2053                 :            : }
    2054                 :        198 :       break;
    2055                 :            :     case 29: /* prob */
    2056                 :            :     case 31: /* stop_prob */
    2057                 :            : {
    2058                 :            : #line 1754 "queryparser/queryparser.lemony"
    2059                 :            : delete (yypminor->yy40);
    2060                 :            : #line 2061 "queryparser/queryparser_internal.cc"
    2061                 :            : }
    2062                 :         23 :       break;
    2063                 :            :     case 34: /* phrase */
    2064                 :            :     case 35: /* phrased_term */
    2065                 :            :     case 37: /* near_expr */
    2066                 :            :     case 38: /* adj_expr */
    2067                 :            : {
    2068                 :            : #line 1959 "queryparser/queryparser.lemony"
    2069                 :            : (yypminor->yy32)->destroy();
    2070                 :            : #line 2071 "queryparser/queryparser_internal.cc"
    2071                 :            : }
    2072                 :          1 :       break;
    2073                 :            :     case 36: /* group */
    2074                 :            : {
    2075                 :            : #line 1993 "queryparser/queryparser.lemony"
    2076                 :            : (yypminor->yy14)->destroy();
    2077                 :            : #line 2078 "queryparser/queryparser_internal.cc"
    2078                 :            : }
    2079                 :            :       break;
    2080                 :            :     default:  break;   /* If no destructor action specified: do nothing */
    2081                 :            :   }
    2082                 :      26448 :   ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */
    2083                 :      26448 : }
    2084                 :            : 
    2085                 :            : /*
    2086                 :            : ** Pop the parser's stack once.
    2087                 :            : **
    2088                 :            : ** If there is a destructor routine associated with the token which
    2089                 :            : ** is popped from the stack, then call it.
    2090                 :            : **
    2091                 :            : ** Return the major token number for the symbol popped.
    2092                 :            : */
    2093                 :      25332 : static int yy_pop_parser_stack(yyParser *pParser){
    2094                 :            :   YYCODETYPE yymajor;
    2095         [ -  + ]:      25332 :   if( pParser->yystack.empty() ) return 0;
    2096                 :      25332 :   yyStackEntry *yytos = &pParser->yystack.back();
    2097                 :            : 
    2098                 :            :   LOGLINE(QUERYPARSER, "Popping " << ParseTokenName(yytos->major));
    2099                 :      25332 :   yymajor = (YYCODETYPE)yytos->major;
    2100                 :      25332 :   yy_destructor(pParser, yymajor, &yytos->minor);
    2101                 :      25332 :   pParser->yystack.pop_back();
    2102                 :      25332 :   return yymajor;
    2103                 :            : }
    2104                 :            : 
    2105                 :            : /* 
    2106                 :            : ** Deallocate and destroy a parser.  Destructors are all called for
    2107                 :            : ** all stack elements before shutting the parser down.
    2108                 :            : **
    2109                 :            : ** Inputs:
    2110                 :            : ** A pointer to the parser.  This should be a pointer
    2111                 :            : ** obtained from ParseAlloc.
    2112                 :            : */
    2113                 :      24980 : static void ParseFree(
    2114                 :            :   yyParser *pParser           /* The parser to be deleted */
    2115                 :            : ){
    2116         [ -  + ]:      24980 :   if( pParser==0 ) return;
    2117         [ +  + ]:      25040 :   while( !pParser->yystack.empty() ) yy_pop_parser_stack(pParser);
    2118         [ +  - ]:      24980 :   delete pParser;
    2119                 :            : }
    2120                 :            : 
    2121                 :            : /*
    2122                 :            : ** Find the appropriate action for a parser given the terminal
    2123                 :            : ** look-ahead token iLookAhead.
    2124                 :            : **
    2125                 :            : ** If the look-ahead token is YYNOCODE, then check to see if the action is
    2126                 :            : ** independent of the look-ahead.  If it is, return the action, otherwise
    2127                 :            : ** return YY_NO_ACTION.
    2128                 :            : */
    2129                 :     176383 : static int yy_find_shift_action(
    2130                 :            :   yyParser *pParser,        /* The parser */
    2131                 :            :   YYCODETYPE iLookAhead     /* The look-ahead token */
    2132                 :            : ){
    2133                 :            :   int i;
    2134                 :     176383 :   int stateno = pParser->yystack.back().stateno;
    2135                 :            :  
    2136   [ +  +  -  + ]:     176383 :   if( stateno>YY_SHIFT_MAX || (i = yy_shift_ofst[stateno])==YY_SHIFT_USE_DFLT ){
                 [ +  + ]
    2137                 :      74588 :     return yy_default[stateno];
    2138                 :            :   }
    2139                 :            :   Assert( iLookAhead!=YYNOCODE );
    2140                 :     101795 :   i += iLookAhead;
    2141 [ +  - ][ +  + ]:     101795 :   if( i<0 || i>=YY_SZ_ACTTAB || yy_lookahead[i]!=iLookAhead ){
                 [ +  + ]
    2142                 :            :     if( iLookAhead>0 ){
    2143                 :            : #ifdef YYFALLBACK
    2144                 :            :       YYCODETYPE iFallback;            /* Fallback token */
    2145                 :            :       if( iLookAhead<sizeof(yyFallback)/sizeof(yyFallback[0])
    2146                 :            :              && (iFallback = yyFallback[iLookAhead])!=0 ){
    2147                 :            :         LOGLINE(QUERYPARSER,
    2148                 :            :                 "FALLBACK " << ParseTokenName(iLookAhead) << " => " <<
    2149                 :            :                 ParseTokenName(iFallback));
    2150                 :            :         return yy_find_shift_action(pParser, iFallback);
    2151                 :            :       }
    2152                 :            : #endif
    2153                 :            : #ifdef YYWILDCARD
    2154                 :            :       {
    2155                 :            :         int j = i - iLookAhead + YYWILDCARD;
    2156                 :            :         if( j>=0 && j<YY_SZ_ACTTAB && yy_lookahead[j]==YYWILDCARD ){
    2157                 :            :           LOGLINE(QUERYPARSER,
    2158                 :            :                   "WILDCARD " << ParseTokenName(iLookAhead) << " => " <<
    2159                 :            :                   ParseTokenName(YYWILDCARD));
    2160                 :            :           return yy_action[j];
    2161                 :            :         }
    2162                 :            :       }
    2163                 :            : #endif /* YYWILDCARD */
    2164                 :            :     }
    2165                 :       6822 :     return yy_default[stateno];
    2166                 :            :   }else{
    2167                 :     176383 :     return yy_action[i];
    2168                 :            :   }
    2169                 :            : }
    2170                 :            : 
    2171                 :            : /*
    2172                 :            : ** Find the appropriate action for a parser given the non-terminal
    2173                 :            : ** look-ahead token iLookAhead.
    2174                 :            : **
    2175                 :            : ** If the look-ahead token is YYNOCODE, then check to see if the action is
    2176                 :            : ** independent of the look-ahead.  If it is, return the action, otherwise
    2177                 :            : ** return YY_NO_ACTION.
    2178                 :            : */
    2179                 :     126803 : static int yy_find_reduce_action(
    2180                 :            :   int stateno,              /* Current state number */
    2181                 :            :   YYCODETYPE iLookAhead     /* The look-ahead token */
    2182                 :            : ){
    2183                 :            :   int i;
    2184                 :            : #ifdef YYERRORSYMBOL
    2185                 :            :   if( stateno>YY_REDUCE_MAX ){
    2186                 :            :     return yy_default[stateno];
    2187                 :            :   }
    2188                 :            : #else
    2189                 :            :   Assert( stateno<=YY_REDUCE_MAX );
    2190                 :            : #endif
    2191                 :     126803 :   i = yy_reduce_ofst[stateno];
    2192                 :            :   Assert( i!=YY_REDUCE_USE_DFLT );
    2193                 :            :   Assert( iLookAhead!=YYNOCODE );
    2194                 :     126803 :   i += iLookAhead;
    2195                 :            : #ifdef YYERRORSYMBOL
    2196                 :            :   if( i<0 || i>=YY_SZ_ACTTAB || yy_lookahead[i]!=iLookAhead ){
    2197                 :            :     return yy_default[stateno];
    2198                 :            :   }
    2199                 :            : #else
    2200                 :            :   Assert( i>=0 && i<YY_SZ_ACTTAB );
    2201                 :            :   Assert( yy_lookahead[i]==iLookAhead );
    2202                 :            : #endif
    2203                 :     126803 :   return yy_action[i];
    2204                 :            : }
    2205                 :            : 
    2206                 :            : /*
    2207                 :            : ** Perform a shift action.
    2208                 :            : */
    2209                 :     151409 : static void yy_shift(
    2210                 :            :   yyParser *yypParser,          /* The parser to be shifted */
    2211                 :            :   int yyNewState,               /* The new state to shift in */
    2212                 :            :   int yyMajor,                  /* The major token to shift in */
    2213                 :            :   YYMINORTYPE *yypMinor         /* Pointer to the minor token to shift in */
    2214                 :            : ){
    2215                 :            :   /* Here code is inserted which will execute if the parser
    2216                 :            :   ** stack every overflows.  We use std::vector<> for our stack
    2217                 :            :   ** so we'll never need this code.
    2218                 :            :   */
    2219                 :            : #if 0
    2220                 :            : #endif
    2221                 :            : #ifdef XAPIAN_DEBUG_LOG
    2222                 :            :   unsigned i;
    2223                 :            :   LOGLINE(QUERYPARSER, "Shift " << yyNewState);
    2224                 :            :   string stack("Stack:");
    2225                 :            :   for (i = 0; i < yypParser->yystack.size(); i++) {
    2226                 :            :     stack += ' ';
    2227                 :            :     stack += ParseTokenName(yypParser->yystack[i].major);
    2228                 :            :   }
    2229                 :            :   LOGLINE(QUERYPARSER, stack);
    2230                 :            : #endif
    2231                 :     151409 :   yypParser->yystack.push_back(yyStackEntry(yyNewState, yyMajor, *yypMinor));
    2232                 :     151409 : }
    2233                 :            : 
    2234                 :            : /* The following table contains information about every rule that
    2235                 :            : ** is used during the reduce.
    2236                 :            : */
    2237                 :            : static const struct {
    2238                 :            :   YYCODETYPE lhs;         /* Symbol on the left-hand side of the rule */
    2239                 :            :   unsigned char nrhs;     /* Number of right-hand side symbols in the rule */
    2240                 :            : } yyRuleInfo[] = {
    2241                 :            :   { 25, 1 },
    2242                 :            :   { 25, 0 },
    2243                 :            :   { 26, 1 },
    2244                 :            :   { 26, 3 },
    2245                 :            :   { 26, 3 },
    2246                 :            :   { 26, 4 },
    2247                 :            :   { 26, 4 },
    2248                 :            :   { 26, 3 },
    2249                 :            :   { 26, 3 },
    2250                 :            :   { 28, 1 },
    2251                 :            :   { 28, 0 },
    2252                 :            :   { 27, 1 },
    2253                 :            :   { 27, 1 },
    2254                 :            :   { 29, 1 },
    2255                 :            :   { 29, 2 },
    2256                 :            :   { 29, 2 },
    2257                 :            :   { 29, 2 },
    2258                 :            :   { 29, 2 },
    2259                 :            :   { 29, 3 },
    2260                 :            :   { 29, 2 },
    2261                 :            :   { 29, 3 },
    2262                 :            :   { 29, 2 },
    2263                 :            :   { 29, 3 },
    2264                 :            :   { 29, 1 },
    2265                 :            :   { 29, 2 },
    2266                 :            :   { 29, 2 },
    2267                 :            :   { 29, 3 },
    2268                 :            :   { 31, 1 },
    2269                 :            :   { 31, 1 },
    2270                 :            :   { 32, 1 },
    2271                 :            :   { 32, 1 },
    2272                 :            :   { 30, 1 },
    2273                 :            :   { 30, 1 },
    2274                 :            :   { 33, 1 },
    2275                 :            :   { 33, 1 },
    2276                 :            :   { 33, 3 },
    2277                 :            :   { 33, 1 },
    2278                 :            :   { 33, 1 },
    2279                 :            :   { 33, 1 },
    2280                 :            :   { 33, 1 },
    2281                 :            :   { 33, 3 },
    2282                 :            :   { 33, 2 },
    2283                 :            :   { 33, 1 },
    2284                 :            :   { 34, 1 },
    2285                 :            :   { 34, 2 },
    2286                 :            :   { 35, 2 },
    2287                 :            :   { 35, 2 },
    2288                 :            :   { 36, 2 },
    2289                 :            :   { 36, 2 },
    2290                 :            :   { 36, 2 },
    2291                 :            :   { 37, 3 },
    2292                 :            :   { 37, 3 },
    2293                 :            :   { 38, 3 },
    2294                 :            :   { 38, 3 },
    2295                 :            : };
    2296                 :            : 
    2297                 :            : static void yy_accept(yyParser*);  /* Forward Declaration */
    2298                 :            : 
    2299                 :            : /*
    2300                 :            : ** Perform a reduce action and the shift that must immediately
    2301                 :            : ** follow the reduce.
    2302                 :            : */
    2303                 :     126852 : static void yy_reduce(
    2304                 :            :   yyParser *yypParser,         /* The parser */
    2305                 :            :   int yyruleno                 /* Number of the rule by which to reduce */
    2306                 :            : ){
    2307                 :            :   int yygoto;                     /* The next state */
    2308                 :            :   int yyact;                      /* The next action */
    2309                 :            :   YYMINORTYPE yygotominor;        /* The LHS of the rule reduced */
    2310                 :            :   yyStackEntry *yymsp;            /* The top of the parser's stack */
    2311                 :            :   int yysize;                     /* Amount to pop the stack */
    2312                 :     126852 :   ParseARG_FETCH;
    2313                 :     126852 :   yymsp = &yypParser->yystack.back();
    2314                 :            : #ifdef XAPIAN_DEBUG_LOG
    2315                 :            :   LOGLINE(QUERYPARSER, "Reduce [" << ParseRuleName(yyruleno) << "].");
    2316                 :            : #endif
    2317                 :            : 
    2318                 :            :   /* Silence complaints from purify about yygotominor being uninitialized
    2319                 :            :   ** in some cases when it is copied into the stack after the following
    2320                 :            :   ** switch.  yygotominor is uninitialized when a rule reduces that does
    2321                 :            :   ** not set the value of its left-hand side nonterminal.  Leaving the
    2322                 :            :   ** value of the nonterminal uninitialized is utterly harmless as long
    2323                 :            :   ** as the value is never used.  So really the only thing this code
    2324                 :            :   ** accomplishes is to quieten purify.  
    2325                 :            :   **
    2326                 :            :   ** 2007-01-16:  The wireshark project (www.wireshark.org) reports that
    2327                 :            :   ** without this code, their parser segfaults.  I'm not sure what there
    2328                 :            :   ** parser is doing to make this happen.  This is the second bug report
    2329                 :            :   ** from wireshark this week.  Clearly they are stressing Lemon in ways
    2330                 :            :   ** that it has not been previously stressed...  (SQLite ticket #2172)
    2331                 :            :   */
    2332                 :            :   /* Later comments on ticket #2172 note that this is a bug in wireshark's
    2333                 :            :    * grammar which lemon fails to diagnose, so commenting out for Xapian. */
    2334                 :            :   /* memset(&yygotominor, 0, sizeof(yygotominor)); */
    2335                 :            : 
    2336                 :            : 
    2337 [ +  +  +  +  + :     126852 :   switch( yyruleno ){
          +  +  +  +  +  
          +  +  +  +  +  
          +  +  +  +  +  
          +  +  +  +  +  
          +  +  +  +  +  
          +  +  +  +  +  
          +  +  +  +  +  
          +  +  +  +  +  
             +  +  +  - ]
    2338                 :            :   /* Beginning here are the reduction cases.  A typical example
    2339                 :            :   ** follows:
    2340                 :            :   **   case 0:
    2341                 :            :   **  #line <lineno> <grammarfile>
    2342                 :            :   **     { ... }           // User supplied code
    2343                 :            :   **  #line <lineno> <thisfile>
    2344                 :            :   **     break;
    2345                 :            :   */
    2346                 :            :       case 0: /* query ::= expr */
    2347                 :            : #line 1641 "queryparser/queryparser.lemony"
    2348                 :            : {
    2349                 :            :     // Save the parsed query in the State structure so we can return it.
    2350                 :            :     if (yymsp[0].minor.yy39) {
    2351                 :            :         state->query = *yymsp[0].minor.yy39;
    2352                 :            :         delete yymsp[0].minor.yy39;
    2353                 :            :     } else {
    2354                 :            :         state->query = Query();
    2355                 :            :     }
    2356                 :            : }
    2357                 :            : #line 2358 "queryparser/queryparser_internal.cc"
    2358                 :      24784 :         break;
    2359                 :            :       case 1: /* query ::= */
    2360                 :            : #line 1651 "queryparser/queryparser.lemony"
    2361                 :            : {
    2362                 :            :     // Handle a query string with no terms in.
    2363                 :            :     state->query = Query();
    2364                 :            : }
    2365                 :            : #line 2366 "queryparser/queryparser_internal.cc"
    2366                 :          5 :         break;
    2367                 :            :       case 2: /* expr ::= prob_expr */
    2368                 :            :       case 9: /* bool_arg ::= expr */ yytestcase(yyruleno==9);
    2369                 :            : #line 1662 "queryparser/queryparser.lemony"
    2370                 :            : { yygotominor.yy39 = yymsp[0].minor.yy39; }
    2371                 :            : #line 2372 "queryparser/queryparser_internal.cc"
    2372                 :      25504 :         break;
    2373                 :            :       case 3: /* expr ::= bool_arg AND bool_arg */
    2374                 :            : #line 1665 "queryparser/queryparser.lemony"
    2375                 :            : { BOOL_OP_TO_QUERY(yygotominor.yy39, yymsp[-2].minor.yy39, Query::OP_AND, yymsp[0].minor.yy39, "AND");   yy_destructor(yypParser,4,&yymsp[-1].minor);
    2376                 :            : }
    2377                 :            : #line 2378 "queryparser/queryparser_internal.cc"
    2378                 :         34 :         break;
    2379                 :            :       case 4: /* expr ::= bool_arg NOT bool_arg */
    2380                 :            : #line 1667 "queryparser/queryparser.lemony"
    2381                 :            : {
    2382                 :            :     // 'NOT foo' -> '<alldocuments> NOT foo'
    2383                 :            :     if (!yymsp[-2].minor.yy39 && (state->flags & QueryParser::FLAG_PURE_NOT)) {
    2384                 :            :         yymsp[-2].minor.yy39 = new Query("", 1, 0);
    2385                 :            :     }
    2386                 :            :     BOOL_OP_TO_QUERY(yygotominor.yy39, yymsp[-2].minor.yy39, Query::OP_AND_NOT, yymsp[0].minor.yy39, "NOT");
    2387                 :            :   yy_destructor(yypParser,5,&yymsp[-1].minor);
    2388                 :            : }
    2389                 :            : #line 2390 "queryparser/queryparser_internal.cc"
    2390                 :         11 :         break;
    2391                 :            :       case 5: /* expr ::= bool_arg AND NOT bool_arg */
    2392                 :            : #line 1676 "queryparser/queryparser.lemony"
    2393                 :            : { BOOL_OP_TO_QUERY(yygotominor.yy39, yymsp[-3].minor.yy39, Query::OP_AND_NOT, yymsp[0].minor.yy39, "AND NOT");   yy_destructor(yypParser,4,&yymsp[-2].minor);
    2394                 :            :   yy_destructor(yypParser,5,&yymsp[-1].minor);
    2395                 :            : }
    2396                 :            : #line 2397 "queryparser/queryparser_internal.cc"
    2397                 :          6 :         break;
    2398                 :            :       case 6: /* expr ::= bool_arg AND HATE_AFTER_AND bool_arg */
    2399                 :            : #line 1679 "queryparser/queryparser.lemony"
    2400                 :            : { BOOL_OP_TO_QUERY(yygotominor.yy39, yymsp[-3].minor.yy39, Query::OP_AND_NOT, yymsp[0].minor.yy39, "AND");   yy_destructor(yypParser,4,&yymsp[-2].minor);
    2401                 :            :   yy_destructor(yypParser,10,&yymsp[-1].minor);
    2402                 :            : }
    2403                 :            : #line 2404 "queryparser/queryparser_internal.cc"
    2404                 :          5 :         break;
    2405                 :            :       case 7: /* expr ::= bool_arg OR bool_arg */
    2406                 :            : #line 1682 "queryparser/queryparser.lemony"
    2407                 :            : { BOOL_OP_TO_QUERY(yygotominor.yy39, yymsp[-2].minor.yy39, Query::OP_OR, yymsp[0].minor.yy39, "OR");   yy_destructor(yypParser,2,&yymsp[-1].minor);
    2408                 :            : }
    2409                 :            : #line 2410 "queryparser/queryparser_internal.cc"
    2410                 :         33 :         break;
    2411                 :            :       case 8: /* expr ::= bool_arg XOR bool_arg */
    2412                 :            : #line 1685 "queryparser/queryparser.lemony"
    2413                 :            : { BOOL_OP_TO_QUERY(yygotominor.yy39, yymsp[-2].minor.yy39, Query::OP_XOR, yymsp[0].minor.yy39, "XOR");   yy_destructor(yypParser,3,&yymsp[-1].minor);
    2414                 :            : }
    2415                 :            : #line 2416 "queryparser/queryparser_internal.cc"
    2416                 :          6 :         break;
    2417                 :            :       case 10: /* bool_arg ::= */
    2418                 :            : #line 1694 "queryparser/queryparser.lemony"
    2419                 :            : {
    2420                 :            :     // Set the argument to NULL, which enables the bool_arg-using rules in
    2421                 :            :     // expr above to report uses of AND, OR, etc which don't have two
    2422                 :            :     // arguments.
    2423                 :            :     yygotominor.yy39 = NULL;
    2424                 :            : }
    2425                 :            : #line 2426 "queryparser/queryparser_internal.cc"
    2426                 :         42 :         break;
    2427                 :            :       case 11: /* prob_expr ::= prob */
    2428                 :            : #line 1706 "queryparser/queryparser.lemony"
    2429                 :            : {
    2430                 :            :     yygotominor.yy39 = yymsp[0].minor.yy40->query;
    2431                 :            :     yymsp[0].minor.yy40->query = NULL;
    2432                 :            :     // Handle any "+ terms".
    2433                 :            :     if (yymsp[0].minor.yy40->love) {
    2434                 :            :         if (yymsp[0].minor.yy40->love->empty()) {
    2435                 :            :             // +<nothing>.
    2436                 :            :             delete yygotominor.yy39;
    2437                 :            :             yygotominor.yy39 = yymsp[0].minor.yy40->love;
    2438                 :            :         } else if (yygotominor.yy39) {
    2439                 :            :             swap(yygotominor.yy39, yymsp[0].minor.yy40->love);
    2440                 :            :             add_to_query(yygotominor.yy39, Query::OP_AND_MAYBE, yymsp[0].minor.yy40->love);
    2441                 :            :         } else {
    2442                 :            :             yygotominor.yy39 = yymsp[0].minor.yy40->love;
    2443                 :            :         }
    2444                 :            :         yymsp[0].minor.yy40->love = NULL;
    2445                 :            :     }
    2446                 :            :     // Handle any boolean filters.
    2447                 :            :     if (!yymsp[0].minor.yy40->filter.empty()) {
    2448                 :            :         if (yygotominor.yy39) {
    2449                 :            :             add_to_query(yygotominor.yy39, Query::OP_FILTER, yymsp[0].minor.yy40->merge_filters());
    2450                 :            :         } else {
    2451                 :            :             // Make the query a boolean one.
    2452                 :            :             yygotominor.yy39 = new Query(Query::OP_SCALE_WEIGHT, yymsp[0].minor.yy40->merge_filters(), 0.0);
    2453                 :            :         }
    2454                 :            :     }
    2455                 :            :     // Handle any "- terms".
    2456                 :            :     if (yymsp[0].minor.yy40->hate && !yymsp[0].minor.yy40->hate->empty()) {
    2457                 :            :         if (!yygotominor.yy39) {
    2458                 :            :             // Can't just hate!
    2459                 :            :             yy_parse_failed(yypParser);
    2460                 :            :             return;
    2461                 :            :         }
    2462                 :            :         *yygotominor.yy39 = Query(Query::OP_AND_NOT, *yygotominor.yy39, *yymsp[0].minor.yy40->hate);
    2463                 :            :     }
    2464                 :            :     delete yymsp[0].minor.yy40;
    2465                 :            : }
    2466                 :            : #line 2467 "queryparser/queryparser_internal.cc"
    2467                 :       4435 :         break;
    2468                 :            :       case 12: /* prob_expr ::= term */
    2469                 :            :       case 30: /* stop_term ::= compound_term */ yytestcase(yyruleno==30);
    2470                 :            :       case 32: /* term ::= compound_term */ yytestcase(yyruleno==32);
    2471                 :            : #line 1744 "queryparser/queryparser.lemony"
    2472                 :            : {
    2473                 :            :     yygotominor.yy39 = yymsp[0].minor.yy39;
    2474                 :            : }
    2475                 :            : #line 2476 "queryparser/queryparser_internal.cc"
    2476                 :      22143 :         break;
    2477                 :            :       case 13: /* prob ::= RANGE */
    2478                 :            : #line 1756 "queryparser/queryparser.lemony"
    2479                 :            : {
    2480                 :            :     valueno slot = yymsp[0].minor.yy0->pos;
    2481                 :            :     const Query & range = yymsp[0].minor.yy0->as_value_range_query();
    2482                 :            :     yygotominor.yy40 = new ProbQuery;
    2483                 :            :     yygotominor.yy40->add_filter_range(slot, range);
    2484                 :            : }
    2485                 :            : #line 2486 "queryparser/queryparser_internal.cc"
    2486                 :       3764 :         break;
    2487                 :            :       case 14: /* prob ::= stop_prob RANGE */
    2488                 :            : #line 1763 "queryparser/queryparser.lemony"
    2489                 :            : {
    2490                 :            :     valueno slot = yymsp[0].minor.yy0->pos;
    2491                 :            :     const Query & range = yymsp[0].minor.yy0->as_value_range_query();
    2492                 :            :     yygotominor.yy40 = yymsp[-1].minor.yy40;
    2493                 :            :     yygotominor.yy40->append_filter_range(slot, range);
    2494                 :            : }
    2495                 :            : #line 2496 "queryparser/queryparser_internal.cc"
    2496                 :         13 :         break;
    2497                 :            :       case 15: /* prob ::= stop_term stop_term */
    2498                 :            : #line 1770 "queryparser/queryparser.lemony"
    2499                 :            : {
    2500                 :            :     yygotominor.yy40 = new ProbQuery;
    2501                 :            :     yygotominor.yy40->query = yymsp[-1].minor.yy39;
    2502                 :            :     if (yymsp[0].minor.yy39) {
    2503                 :            :         Query::op op = state->default_op();
    2504                 :            :         if (yygotominor.yy40->query && is_positional(op)) {
    2505                 :            :             // If default_op is OP_NEAR or OP_PHRASE, set the window size to
    2506                 :            :             // 11 for the first pair of terms and it will automatically grow
    2507                 :            :             // by one for each subsequent term.
    2508                 :            :             Query * subqs[2] = { yygotominor.yy40->query, yymsp[0].minor.yy39 };
    2509                 :            :             *(yygotominor.yy40->query) = Query(op, subqs, subqs + 2, 11);
    2510                 :            :             delete yymsp[0].minor.yy39;
    2511                 :            :         } else {
    2512                 :            :             add_to_query(yygotominor.yy40->query, op, yymsp[0].minor.yy39);
    2513                 :            :         }
    2514                 :            :     }
    2515                 :            : }
    2516                 :            : #line 2517 "queryparser/queryparser_internal.cc"
    2517                 :        539 :         break;
    2518                 :            :       case 16: /* prob ::= prob stop_term */
    2519                 :            : #line 1788 "queryparser/queryparser.lemony"
    2520                 :            : {
    2521                 :            :     yygotominor.yy40 = yymsp[-1].minor.yy40;
    2522                 :            :     // If yymsp[0].minor.yy39 is a stopword, there's nothing to do here.
    2523                 :            :     if (yymsp[0].minor.yy39) add_to_query(yygotominor.yy40->query, state->default_op(), yymsp[0].minor.yy39);
    2524                 :            : }
    2525                 :            : #line 2526 "queryparser/queryparser_internal.cc"
    2526                 :        404 :         break;
    2527                 :            :       case 17: /* prob ::= LOVE term */
    2528                 :            : #line 1794 "queryparser/queryparser.lemony"
    2529                 :            : {
    2530                 :            :     yygotominor.yy40 = new ProbQuery;
    2531                 :            :     if (state->default_op() == Query::OP_AND) {
    2532                 :            :         yygotominor.yy40->query = yymsp[0].minor.yy39;
    2533                 :            :     } else {
    2534                 :            :         yygotominor.yy40->love = yymsp[0].minor.yy39;
    2535                 :            :     }
    2536                 :            :   yy_destructor(yypParser,8,&yymsp[-1].minor);
    2537                 :            : }
    2538                 :            : #line 2539 "queryparser/queryparser_internal.cc"
    2539                 :         31 :         break;
    2540                 :            :       case 18: /* prob ::= stop_prob LOVE term */
    2541                 :            : #line 1803 "queryparser/queryparser.lemony"
    2542                 :            : {
    2543                 :            :     yygotominor.yy40 = yymsp[-2].minor.yy40;
    2544                 :            :     if (state->default_op() == Query::OP_AND) {
    2545                 :            :         /* The default op is AND, so we just put loved terms into the query
    2546                 :            :          * (in this case the only effect of love is to ignore the stopword
    2547                 :            :          * list). */
    2548                 :            :         add_to_query(yygotominor.yy40->query, Query::OP_AND, yymsp[0].minor.yy39);
    2549                 :            :     } else {
    2550                 :            :         add_to_query(yygotominor.yy40->love, Query::OP_AND, yymsp[0].minor.yy39);
    2551                 :            :     }
    2552                 :            :   yy_destructor(yypParser,8,&yymsp[-1].minor);
    2553                 :            : }
    2554                 :            : #line 2555 "queryparser/queryparser_internal.cc"
    2555                 :         19 :         break;
    2556                 :            :       case 19: /* prob ::= HATE term */
    2557                 :            : #line 1815 "queryparser/queryparser.lemony"
    2558                 :            : {
    2559                 :            :     yygotominor.yy40 = new ProbQuery;
    2560                 :            :     yygotominor.yy40->hate = yymsp[0].minor.yy39;
    2561                 :            :   yy_destructor(yypParser,9,&yymsp[-1].minor);
    2562                 :            : }
    2563                 :            : #line 2564 "queryparser/queryparser_internal.cc"
    2564                 :          9 :         break;
    2565                 :            :       case 20: /* prob ::= stop_prob HATE term */
    2566                 :            : #line 1820 "queryparser/queryparser.lemony"
    2567                 :            : {
    2568                 :            :     yygotominor.yy40 = yymsp[-2].minor.yy40;
    2569                 :            :     add_to_query(yygotominor.yy40->hate, Query::OP_OR, yymsp[0].minor.yy39);
    2570                 :            :   yy_destructor(yypParser,9,&yymsp[-1].minor);
    2571                 :            : }
    2572                 :            : #line 2573 "queryparser/queryparser_internal.cc"
    2573                 :         30 :         break;
    2574                 :            :       case 21: /* prob ::= HATE BOOLEAN_FILTER */
    2575                 :            : #line 1825 "queryparser/queryparser.lemony"
    2576                 :            : {
    2577                 :            :     yygotominor.yy40 = new ProbQuery;
    2578                 :            :     yygotominor.yy40->hate = new Query(yymsp[0].minor.yy0->get_query());
    2579                 :            :     delete yymsp[0].minor.yy0;
    2580                 :            :   yy_destructor(yypParser,9,&yymsp[-1].minor);
    2581                 :            : }
    2582                 :            : #line 2583 "queryparser/queryparser_internal.cc"
    2583                 :          1 :         break;
    2584                 :            :       case 22: /* prob ::= stop_prob HATE BOOLEAN_FILTER */
    2585                 :            : #line 1831 "queryparser/queryparser.lemony"
    2586                 :            : {
    2587                 :            :     yygotominor.yy40 = yymsp[-2].minor.yy40;
    2588                 :            :     add_to_query(yygotominor.yy40->hate, Query::OP_OR, yymsp[0].minor.yy0->get_query());
    2589                 :            :     delete yymsp[0].minor.yy0;
    2590                 :            :   yy_destructor(yypParser,9,&yymsp[-1].minor);
    2591                 :            : }
    2592                 :            : #line 2593 "queryparser/queryparser_internal.cc"
    2593                 :          4 :         break;
    2594                 :            :       case 23: /* prob ::= BOOLEAN_FILTER */
    2595                 :            : #line 1837 "queryparser/queryparser.lemony"
    2596                 :            : {
    2597                 :            :     yygotominor.yy40 = new ProbQuery;
    2598                 :            :     yygotominor.yy40->add_filter(yymsp[0].minor.yy0->get_filter_group_id(), yymsp[0].minor.yy0->get_query());
    2599                 :            :     delete yymsp[0].minor.yy0;
    2600                 :            : }
    2601                 :            : #line 2602 "queryparser/queryparser_internal.cc"
    2602                 :         40 :         break;
    2603                 :            :       case 24: /* prob ::= stop_prob BOOLEAN_FILTER */
    2604                 :            : #line 1843 "queryparser/queryparser.lemony"
    2605                 :            : {
    2606                 :            :     yygotominor.yy40 = yymsp[-1].minor.yy40;
    2607                 :            :     yygotominor.yy40->append_filter(yymsp[0].minor.yy0->get_filter_group_id(), yymsp[0].minor.yy0->get_query());
    2608                 :            :     delete yymsp[0].minor.yy0;
    2609                 :            : }
    2610                 :            : #line 2611 "queryparser/queryparser_internal.cc"
    2611                 :         25 :         break;
    2612                 :            :       case 25: /* prob ::= LOVE BOOLEAN_FILTER */
    2613                 :            : #line 1849 "queryparser/queryparser.lemony"
    2614                 :            : {
    2615                 :            :     // LOVE BOOLEAN_FILTER(yymsp[0].minor.yy0) is just the same as BOOLEAN_FILTER
    2616                 :            :     yygotominor.yy40 = new ProbQuery;
    2617                 :            :     yygotominor.yy40->filter[yymsp[0].minor.yy0->get_filter_group_id()] = yymsp[0].minor.yy0->get_query();
    2618                 :            :     delete yymsp[0].minor.yy0;
    2619                 :            :   yy_destructor(yypParser,8,&yymsp[-1].minor);
    2620                 :            : }
    2621                 :            : #line 2622 "queryparser/queryparser_internal.cc"
    2622                 :          1 :         break;
    2623                 :            :       case 26: /* prob ::= stop_prob LOVE BOOLEAN_FILTER */
    2624                 :            : #line 1856 "queryparser/queryparser.lemony"
    2625                 :            : {
    2626                 :            :     // LOVE BOOLEAN_FILTER(yymsp[0].minor.yy0) is just the same as BOOLEAN_FILTER
    2627                 :            :     yygotominor.yy40 = yymsp[-2].minor.yy40;
    2628                 :            :     // We OR filters with the same prefix...
    2629                 :            :     Query & q = yygotominor.yy40->filter[yymsp[0].minor.yy0->get_filter_group_id()];
    2630                 :            :     q = Query(Query::OP_OR, q, yymsp[0].minor.yy0->get_query());
    2631                 :            :     delete yymsp[0].minor.yy0;
    2632                 :            :   yy_destructor(yypParser,8,&yymsp[-1].minor);
    2633                 :            : }
    2634                 :            : #line 2635 "queryparser/queryparser_internal.cc"
    2635                 :          3 :         break;
    2636                 :            :       case 27: /* stop_prob ::= prob */
    2637                 :            : #line 1871 "queryparser/queryparser.lemony"
    2638                 :            : { yygotominor.yy40 = yymsp[0].minor.yy40; }
    2639                 :            : #line 2640 "queryparser/queryparser_internal.cc"
    2640                 :         34 :         break;
    2641                 :            :       case 28: /* stop_prob ::= stop_term */
    2642                 :            : #line 1873 "queryparser/queryparser.lemony"
    2643                 :            : {
    2644                 :            :     yygotominor.yy40 = new ProbQuery;
    2645                 :            :     yygotominor.yy40->query = yymsp[0].minor.yy39;
    2646                 :            : }
    2647                 :            : #line 2648 "queryparser/queryparser_internal.cc"
    2648                 :         73 :         break;
    2649                 :            :       case 29: /* stop_term ::= TERM */
    2650                 :            : #line 1887 "queryparser/queryparser.lemony"
    2651                 :            : {
    2652                 :            :     if (state->is_stopword(yymsp[0].minor.yy0)) {
    2653                 :            :         yygotominor.yy39 = NULL;
    2654                 :            :         state->add_to_stoplist(yymsp[0].minor.yy0);
    2655                 :            :     } else {
    2656                 :            :         yygotominor.yy39 = new Query(yymsp[0].minor.yy0->get_query_with_auto_synonyms());
    2657                 :            :     }
    2658                 :            :     delete yymsp[0].minor.yy0;
    2659                 :            : }
    2660                 :            : #line 2661 "queryparser/queryparser_internal.cc"
    2661                 :        720 :         break;
    2662                 :            :       case 31: /* term ::= TERM */
    2663                 :            : #line 1906 "queryparser/queryparser.lemony"
    2664                 :            : {
    2665                 :            :     yygotominor.yy39 = new Query(yymsp[0].minor.yy0->get_query_with_auto_synonyms());
    2666                 :            :     delete yymsp[0].minor.yy0;
    2667                 :            : }
    2668                 :            : #line 2669 "queryparser/queryparser_internal.cc"
    2669                 :      20323 :         break;
    2670                 :            :       case 33: /* compound_term ::= WILD_TERM */
    2671                 :            : #line 1923 "queryparser/queryparser.lemony"
    2672                 :            : { yygotominor.yy39 = yymsp[0].minor.yy0->as_wildcarded_query(state); }
    2673                 :            : #line 2674 "queryparser/queryparser_internal.cc"
    2674                 :         93 :         break;
    2675                 :            :       case 34: /* compound_term ::= PARTIAL_TERM */
    2676                 :            : #line 1926 "queryparser/queryparser.lemony"
    2677                 :            : { yygotominor.yy39 = yymsp[0].minor.yy0->as_partial_query(state); }
    2678                 :            : #line 2679 "queryparser/queryparser_internal.cc"
    2679                 :         72 :         break;
    2680                 :            :       case 35: /* compound_term ::= QUOTE phrase QUOTE */
    2681                 :            : #line 1929 "queryparser/queryparser.lemony"
    2682                 :            : { yygotominor.yy39 = yymsp[-1].minor.yy32->as_phrase_query();   yy_destructor(yypParser,19,&yymsp[-2].minor);
    2683                 :            :   yy_destructor(yypParser,19,&yymsp[0].minor);
    2684                 :            : }
    2685                 :            : #line 2686 "queryparser/queryparser_internal.cc"
    2686                 :        187 :         break;
    2687                 :            :       case 36: /* compound_term ::= phrased_term */
    2688                 :            : #line 1932 "queryparser/queryparser.lemony"
    2689                 :            : { yygotominor.yy39 = yymsp[0].minor.yy32->as_phrase_query(); }
    2690                 :            : #line 2691 "queryparser/queryparser_internal.cc"
    2691                 :        247 :         break;
    2692                 :            :       case 37: /* compound_term ::= group */
    2693                 :            : #line 1935 "queryparser/queryparser.lemony"
    2694                 :            : { yygotominor.yy39 = yymsp[0].minor.yy14->as_group(state); }
    2695                 :            : #line 2696 "queryparser/queryparser_internal.cc"
    2696                 :        549 :         break;
    2697                 :            :       case 38: /* compound_term ::= near_expr */
    2698                 :            : #line 1938 "queryparser/queryparser.lemony"
    2699                 :            : { yygotominor.yy39 = yymsp[0].minor.yy32->as_near_query(); }
    2700                 :            : #line 2701 "queryparser/queryparser_internal.cc"
    2701                 :          8 :         break;
    2702                 :            :       case 39: /* compound_term ::= adj_expr */
    2703                 :            : #line 1941 "queryparser/queryparser.lemony"
    2704                 :            : { yygotominor.yy39 = yymsp[0].minor.yy32->as_adj_query(); }
    2705                 :            : #line 2706 "queryparser/queryparser_internal.cc"
    2706                 :          3 :         break;
    2707                 :            :       case 40: /* compound_term ::= BRA expr KET */
    2708                 :            : #line 1944 "queryparser/queryparser.lemony"
    2709                 :            : { yygotominor.yy39 = yymsp[-1].minor.yy39;   yy_destructor(yypParser,20,&yymsp[-2].minor);
    2710                 :            :   yy_destructor(yypParser,21,&yymsp[0].minor);
    2711                 :            : }
    2712                 :            : #line 2713 "queryparser/queryparser_internal.cc"
    2713                 :        187 :         break;
    2714                 :            :       case 41: /* compound_term ::= SYNONYM TERM */
    2715                 :            : #line 1946 "queryparser/queryparser.lemony"
    2716                 :            : {
    2717                 :            :     yygotominor.yy39 = new Query(yymsp[0].minor.yy0->get_query_with_synonyms());
    2718                 :            :     delete yymsp[0].minor.yy0;
    2719                 :            :   yy_destructor(yypParser,11,&yymsp[-1].minor);
    2720                 :            : }
    2721                 :            : #line 2722 "queryparser/queryparser_internal.cc"
    2722                 :         20 :         break;
    2723                 :            :       case 42: /* compound_term ::= CJKTERM */
    2724                 :            : #line 1951 "queryparser/queryparser.lemony"
    2725                 :            : {
    2726                 :            :     { yygotominor.yy39 = yymsp[0].minor.yy0->as_cjk_query(); }
    2727                 :            : }
    2728                 :            : #line 2729 "queryparser/queryparser_internal.cc"
    2729                 :         22 :         break;
    2730                 :            :       case 43: /* phrase ::= TERM */
    2731                 :            : #line 1961 "queryparser/queryparser.lemony"
    2732                 :            : {
    2733                 :            :     yygotominor.yy32 = new Terms;
    2734                 :            :     yygotominor.yy32->add_positional_term(yymsp[0].minor.yy0);
    2735                 :            : }
    2736                 :            : #line 2737 "queryparser/queryparser_internal.cc"
    2737                 :        187 :         break;
    2738                 :            :       case 44: /* phrase ::= phrase TERM */
    2739                 :            :       case 46: /* phrased_term ::= phrased_term PHR_TERM */ yytestcase(yyruleno==46);
    2740                 :            : #line 1966 "queryparser/queryparser.lemony"
    2741                 :            : {
    2742                 :            :     yygotominor.yy32 = yymsp[-1].minor.yy32;
    2743                 :            :     yygotominor.yy32->add_positional_term(yymsp[0].minor.yy0);
    2744                 :            : }
    2745                 :            : #line 2746 "queryparser/queryparser_internal.cc"
    2746                 :        502 :         break;
    2747                 :            :       case 45: /* phrased_term ::= TERM PHR_TERM */
    2748                 :            : #line 1978 "queryparser/queryparser.lemony"
    2749                 :            : {
    2750                 :            :     yygotominor.yy32 = new Terms;
    2751                 :            :     yygotominor.yy32->add_positional_term(yymsp[-1].minor.yy0);
    2752                 :            :     yygotominor.yy32->add_positional_term(yymsp[0].minor.yy0);
    2753                 :            : }
    2754                 :            : #line 2755 "queryparser/queryparser_internal.cc"
    2755                 :        248 :         break;
    2756                 :            :       case 47: /* group ::= TERM GROUP_TERM */
    2757                 :            : #line 1995 "queryparser/queryparser.lemony"
    2758                 :            : {
    2759                 :            :     yygotominor.yy14 = new TermGroup;
    2760                 :            :     yygotominor.yy14->add_term(yymsp[-1].minor.yy0);
    2761                 :            :     yygotominor.yy14->add_term(yymsp[0].minor.yy0);
    2762                 :            : }
    2763                 :            : #line 2764 "queryparser/queryparser_internal.cc"
    2764                 :        556 :         break;
    2765                 :            :       case 48: /* group ::= group GROUP_TERM */
    2766                 :            : #line 2001 "queryparser/queryparser.lemony"
    2767                 :            : {
    2768                 :            :     yygotominor.yy14 = yymsp[-1].minor.yy14;
    2769                 :            :     yygotominor.yy14->add_term(yymsp[0].minor.yy0);
    2770                 :            : }
    2771                 :            : #line 2772 "queryparser/queryparser_internal.cc"
    2772                 :      20859 :         break;
    2773                 :            :       case 49: /* group ::= group EMPTY_GROUP_OK */
    2774                 :            : #line 2006 "queryparser/queryparser.lemony"
    2775                 :            : {
    2776                 :            :     yygotominor.yy14 = yymsp[-1].minor.yy14;
    2777                 :            :     yygotominor.yy14->set_empty_ok();
    2778                 :            :   yy_destructor(yypParser,23,&yymsp[0].minor);
    2779                 :            : }
    2780                 :            : #line 2781 "queryparser/queryparser_internal.cc"
    2781                 :          8 :         break;
    2782                 :            :       case 50: /* near_expr ::= TERM NEAR TERM */
    2783                 :            :       case 52: /* adj_expr ::= TERM ADJ TERM */ yytestcase(yyruleno==52);
    2784                 :            : #line 2017 "queryparser/queryparser.lemony"
    2785                 :            : {
    2786                 :            :     yygotominor.yy32 = new Terms;
    2787                 :            :     yygotominor.yy32->add_positional_term(yymsp[-2].minor.yy0);
    2788                 :            :     yygotominor.yy32->add_positional_term(yymsp[0].minor.yy0);
    2789                 :            :     if (yymsp[-1].minor.yy0) {
    2790                 :            :         yygotominor.yy32->adjust_window(yymsp[-1].minor.yy0->get_termpos());
    2791                 :            :         delete yymsp[-1].minor.yy0;
    2792                 :            :     }
    2793                 :            : }
    2794                 :            : #line 2795 "queryparser/queryparser_internal.cc"
    2795                 :         11 :         break;
    2796                 :            :       case 51: /* near_expr ::= near_expr NEAR TERM */
    2797                 :            :       case 53: /* adj_expr ::= adj_expr ADJ TERM */ yytestcase(yyruleno==53);
    2798                 :            : #line 2027 "queryparser/queryparser.lemony"
    2799                 :            : {
    2800                 :            :     yygotominor.yy32 = yymsp[-2].minor.yy32;
    2801                 :            :     yygotominor.yy32->add_positional_term(yymsp[0].minor.yy0);
    2802                 :            :     if (yymsp[-1].minor.yy0) {
    2803                 :            :         yygotominor.yy32->adjust_window(yymsp[-1].minor.yy0->get_termpos());
    2804                 :            :         delete yymsp[-1].minor.yy0;
    2805                 :            :     }
    2806                 :            : }
    2807                 :            : #line 2808 "queryparser/queryparser_internal.cc"
    2808                 :            :         break;
    2809                 :            :       default:
    2810                 :            :         break;
    2811                 :            :   }
    2812                 :     126803 :   yygoto = yyRuleInfo[yyruleno].lhs;
    2813                 :     126803 :   yysize = yyRuleInfo[yyruleno].nrhs;
    2814                 :     126803 :   yypParser->yystack.resize(yypParser->yystack.size() - yysize);
    2815                 :     126803 :   yyact = yy_find_reduce_action(yypParser->yystack.back().stateno,(YYCODETYPE)yygoto);
    2816         [ +  + ]:     126803 :   if( yyact < YYNSTATE ){
    2817                 :     102014 :     yy_shift(yypParser,yyact,yygoto,&yygotominor);
    2818                 :            :   }else{
    2819                 :            :     Assert( yyact == YYNSTATE + YYNRULE + 1 );
    2820                 :     126836 :     yy_accept(yypParser);
    2821                 :            :   }
    2822                 :            : }
    2823                 :            : 
    2824                 :            : /*
    2825                 :            : ** The following code executes when the parse fails
    2826                 :            : */
    2827                 :            : #ifndef YYNOERRORRECOVERY
    2828                 :        190 : static void yy_parse_failed(
    2829                 :            :   yyParser *yypParser           /* The parser */
    2830                 :            : ){
    2831                 :        190 :   ParseARG_FETCH;
    2832                 :            :   LOGLINE(QUERYPARSER, "Fail!");
    2833         [ +  + ]:        673 :   while( !yypParser->yystack.empty() ) yy_pop_parser_stack(yypParser);
    2834                 :            :   /* Here code is inserted which will be executed whenever the
    2835                 :            :   ** parser fails */
    2836                 :            : #line 1588 "queryparser/queryparser.lemony"
    2837                 :            : 
    2838                 :            :     // If we've not already set an error message, set a default one.
    2839                 :            :     if (!state->error) state->error = "parse error";
    2840                 :            : #line 2841 "queryparser/queryparser_internal.cc"
    2841                 :        190 :   ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */
    2842                 :        190 : }
    2843                 :            : #endif /* YYNOERRORRECOVERY */
    2844                 :            : 
    2845                 :            : /*
    2846                 :            : ** The following code executes when a syntax error first occurs.
    2847                 :            : */
    2848                 :        136 : static void yy_syntax_error(
    2849                 :            :   yyParser *yypParser,           /* The parser */
    2850                 :            :   int yymajor,                   /* The major type of the error token */
    2851                 :            :   YYMINORTYPE yyminor            /* The minor type of the error token */
    2852                 :            : ){
    2853                 :        136 :   ParseARG_FETCH;
    2854                 :            :   (void)yymajor;
    2855                 :            :   (void)yyminor;
    2856                 :            : #define TOKEN (yyminor.yy0)
    2857                 :            : #line 1593 "queryparser/queryparser.lemony"
    2858                 :            : 
    2859                 :            :     yy_parse_failed(yypParser);
    2860                 :            : #line 2861 "queryparser/queryparser_internal.cc"
    2861                 :        136 :   ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */
    2862                 :        136 : }
    2863                 :            : 
    2864                 :            : /*
    2865                 :            : ** The following is executed when the parser accepts
    2866                 :            : */
    2867                 :      24789 : static void yy_accept(
    2868                 :            :   yyParser *yypParser           /* The parser */
    2869                 :            : ){
    2870                 :      24789 :   ParseARG_FETCH;
    2871                 :            :   LOGLINE(QUERYPARSER, "Accept!");
    2872         [ +  + ]:      49578 :   while( !yypParser->yystack.empty() ) yy_pop_parser_stack(yypParser);
    2873                 :            :   /* Here code is inserted which will be executed whenever the
    2874                 :            :   ** parser accepts */
    2875                 :      24789 :   ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */
    2876                 :      24789 : }
    2877                 :            : 
    2878                 :            : /* The main parser program.
    2879                 :            : ** The first argument is a pointer to a structure obtained from
    2880                 :            : ** "ParseAlloc" which describes the current state of the parser.
    2881                 :            : ** The second argument is the major token number.  The third is
    2882                 :            : ** the minor token.  The fourth optional argument is whatever the
    2883                 :            : ** user wants (and specified in the grammar) and is available for
    2884                 :            : ** use by the action routines.
    2885                 :            : **
    2886                 :            : ** Inputs:
    2887                 :            : ** <ul>
    2888                 :            : ** <li> A pointer to the parser (an opaque structure.)
    2889                 :            : ** <li> The major token number.
    2890                 :            : ** <li> The minor token number.
    2891                 :            : ** <li> An option argument of a grammar-specified type.
    2892                 :            : ** </ul>
    2893                 :            : **
    2894                 :            : ** Outputs:
    2895                 :            : ** None.
    2896                 :            : */
    2897                 :      74369 : static void Parse(
    2898                 :            :   yyParser *yypParser,         /* The parser */
    2899                 :            :   int yymajor,                 /* The major token code number */
    2900                 :            :   ParseTOKENTYPE yyminor       /* The value for the token */
    2901                 :            :   ParseARG_PDECL               /* Optional %extra_argument parameter */
    2902                 :            : ){
    2903                 :            :   YYMINORTYPE yyminorunion;
    2904                 :            :   int yyact;            /* The parser action. */
    2905                 :            :   int yyendofinput;     /* True if we are at the end of input */
    2906                 :            : #ifdef YYERRORSYMBOL
    2907                 :            :   int yyerrorhit = 0;   /* True if yymajor has invoked an error */
    2908                 :            : #endif
    2909                 :            : 
    2910                 :            :   /* (re)initialize the parser, if necessary */
    2911         [ +  + ]:      74369 :   if( yypParser->yystack.empty() ){
    2912                 :      24984 :     yypParser->yystack.push_back(yyStackEntry());
    2913                 :      24984 :     yypParser->yyerrcnt = -1;
    2914                 :            :   }
    2915                 :      74369 :   yyminorunion.yy0 = yyminor;
    2916                 :      74369 :   yyendofinput = (yymajor==0);
    2917                 :      74369 :   ParseARG_STORE;
    2918                 :            : 
    2919                 :            :   LOGLINE(QUERYPARSER, "Input " << ParseTokenName(yymajor) << " " <<
    2920                 :            :           (yyminor ? yyminor->name : "<<null>>"));
    2921                 :            : 
    2922 [ +  + ][ +  + ]:     176367 :   do{
                 [ +  + ]
    2923                 :     176383 :     yyact = yy_find_shift_action(yypParser,(YYCODETYPE)yymajor);
    2924         [ +  + ]:     176383 :     if( yyact<YYNSTATE ){
    2925                 :            :       Assert( !yyendofinput );  /* Impossible to shift the $ token */
    2926                 :      49395 :       yy_shift(yypParser,yyact,yymajor,&yyminorunion);
    2927                 :      49395 :       yypParser->yyerrcnt--;
    2928                 :      49395 :       yymajor = YYNOCODE;
    2929         [ +  + ]:     126988 :     }else if( yyact < YYNSTATE + YYNRULE ){
    2930                 :     126852 :       yy_reduce(yypParser,yyact-YYNSTATE);
    2931                 :            :     }else{
    2932                 :            :       Assert( yyact == YY_ERROR_ACTION );
    2933                 :            : #ifdef YYERRORSYMBOL
    2934                 :            :       int yymx;
    2935                 :            : #endif
    2936                 :            :       LOGLINE(QUERYPARSER, "Syntax Error!");
    2937                 :            : #ifdef YYERRORSYMBOL
    2938                 :            :       /* A syntax error has occurred.
    2939                 :            :       ** The response to an error depends upon whether or not the
    2940                 :            :       ** grammar defines an error token "ERROR".  
    2941                 :            :       **
    2942                 :            :       ** This is what we do if the grammar does define ERROR:
    2943                 :            :       **
    2944                 :            :       **  * Call the %syntax_error function.
    2945                 :            :       **
    2946                 :            :       **  * Begin popping the stack until we enter a state where
    2947                 :            :       **    it is legal to shift the error symbol, then shift
    2948                 :            :       **    the error symbol.
    2949                 :            :       **
    2950                 :            :       **  * Set the error count to three.
    2951                 :            :       **
    2952                 :            :       **  * Begin accepting and shifting new tokens.  No new error
    2953                 :            :       **    processing will occur until three tokens have been
    2954                 :            :       **    shifted successfully.
    2955                 :            :       **
    2956                 :            :       */
    2957                 :            :       if( yypParser->yyerrcnt<0 ){
    2958                 :            :         yy_syntax_error(yypParser,yymajor,yyminorunion);
    2959                 :            :       }
    2960                 :            :       yymx = yypParser->yystack.back().major;
    2961                 :            :       if( yymx==YYERRORSYMBOL || yyerrorhit ){
    2962                 :            :         LOGLINE(QUERYPARSER, "Discard input token " << ParseTokenName(yymajor));
    2963                 :            :         yy_destructor(yypParser, (YYCODETYPE)yymajor,&yyminorunion);
    2964                 :            :         yymajor = YYNOCODE;
    2965                 :            :       }else{
    2966                 :            :          while(
    2967                 :            :           !yypParser->yystack.empty() &&
    2968                 :            :           yymx != YYERRORSYMBOL &&
    2969                 :            :           (yyact = yy_find_reduce_action(
    2970                 :            :                         yypParser->yystack.back().stateno,
    2971                 :            :                         YYERRORSYMBOL)) >= YYNSTATE
    2972                 :            :         ){
    2973                 :            :           yy_pop_parser_stack(yypParser);
    2974                 :            :         }
    2975                 :            :         if( yypParser->yystack.empty() || yymajor==0 ){
    2976                 :            :           yy_destructor(yypParser, (YYCODETYPE)yymajor,&yyminorunion);
    2977                 :            :           yy_parse_failed(yypParser);
    2978                 :            :           yymajor = YYNOCODE;
    2979                 :            :         }else if( yymx!=YYERRORSYMBOL ){
    2980                 :            :           YYMINORTYPE u2;
    2981                 :            :           u2.YYERRSYMDT = 0;
    2982                 :            :           yy_shift(yypParser,yyact,YYERRORSYMBOL,&u2);
    2983                 :            :         }
    2984                 :            :       }
    2985                 :            :       yypParser->yyerrcnt = 3;
    2986                 :            :       yyerrorhit = 1;
    2987                 :            : #elif defined(YYNOERRORRECOVERY)
    2988                 :            :       /* If the YYNOERRORRECOVERY macro is defined, then do not attempt to
    2989                 :            :       ** do any kind of error recovery.  Instead, simply invoke the syntax
    2990                 :            :       ** error routine and continue going as if nothing had happened.
    2991                 :            :       **
    2992                 :            :       ** Applications can set this macro (for example inside %include) if
    2993                 :            :       ** they intend to abandon the parse upon the first syntax error seen.
    2994                 :            :       */
    2995                 :            :       yy_syntax_error(yypParser,yymajor,yyminorunion);
    2996                 :            :       yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion);
    2997                 :            :       yymajor = YYNOCODE;
    2998                 :            :       
    2999                 :            : #else  /* YYERRORSYMBOL is not defined */
    3000                 :            :       /* This is what we do if the grammar does not define ERROR:
    3001                 :            :       **
    3002                 :            :       **  * Report an error message, and throw away the input token.
    3003                 :            :       **
    3004                 :            :       **  * If the input token is $, then fail the parse.
    3005                 :            :       **
    3006                 :            :       ** As before, subsequent error messages are suppressed until
    3007                 :            :       ** three input tokens have been successfully shifted.
    3008                 :            :       */
    3009         [ +  - ]:        136 :       if( yypParser->yyerrcnt<=0 ){
    3010                 :        136 :         yy_syntax_error(yypParser,yymajor,yyminorunion);
    3011                 :            :       }
    3012                 :        136 :       yypParser->yyerrcnt = 3;
    3013                 :        136 :       yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion);
    3014         [ +  + ]:        136 :       if( yyendofinput ){
    3015                 :         21 :         yy_parse_failed(yypParser);
    3016                 :            :       }
    3017                 :        136 :       yymajor = YYNOCODE;
    3018                 :            : #endif
    3019                 :            :     }
    3020                 :            :   }while( yymajor!=YYNOCODE && !yypParser->yystack.empty() );
    3021                 :            :   return;
    3022                 :            : }
    3023                 :            : 
    3024                 :            : // Select C++ syntax highlighting in vim editor: vim: syntax=cpp

Generated by: LCOV version 1.8