queryparser/queryparser_internal.cc

Go to the documentation of this file.
00001 /* Driver template for the LEMON parser generator.
00002 ** The author disclaims copyright to this source code.
00003 */
00004 /* First off, code is included which follows the "include" declaration
00005 ** in the input file. */
00006 #line 1 "/data/home/olly/tmp/xapian-svn-snapshot/tags/1.0.10/xapian/xapian-core/queryparser/queryparser.lemony"
00007 
00008 /* queryparser.lemony: build a Xapian::Query object from a user query string.
00009  *
00010  * Copyright (C) 2004,2005,2006,2007,2008 Olly Betts
00011  *
00012  * This program is free software; you can redistribute it and/or
00013  * modify it under the terms of the GNU General Public License as
00014  * published by the Free Software Foundation; either version 2 of the
00015  * License, or (at your option) any later version.
00016  *
00017  * This program is distributed in the hope that it will be useful,
00018  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00019  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00020  * GNU General Public License for more details.
00021  *
00022  * You should have received a copy of the GNU General Public License
00023  * along with this program; if not, write to the Free Software
00024  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
00025  * USA
00026  */
00027 
00028 #include <config.h>
00029 
00030 #include "omassert.h"
00031 #include "queryparser_internal.h"
00032 #include <xapian/error.h>
00033 #include <xapian/unicode.h>
00034 #include "stringutils.h"
00035 
00036 // Include the list of token values lemon generates.
00037 #include "queryparser_token.h"
00038 
00039 #include <algorithm>
00040 #include <list>
00041 #include <string>
00042 
00043 #include <string.h>
00044 
00045 using namespace std;
00046 
00047 using namespace Xapian;
00048 
00049 inline bool
00050 U_isupper(unsigned ch) {
00051     return (ch < 128 && C_isupper((unsigned char)ch));
00052 }
00053 
00054 inline bool
00055 U_isdigit(unsigned ch) {
00056     return (ch < 128 && C_isdigit((unsigned char)ch));
00057 }
00058 
00059 inline bool
00060 U_isalpha(unsigned ch) {
00061     return (ch < 128 && C_isalpha((unsigned char)ch));
00062 }
00063 
00064 using Xapian::Unicode::is_whitespace;
00065 
00066 inline bool
00067 is_not_whitespace(unsigned ch) {
00068     return !is_whitespace(ch);
00069 }
00070 
00071 using Xapian::Unicode::is_wordchar;
00072 
00073 inline bool
00074 is_not_wordchar(unsigned ch) {
00075     return !is_wordchar(ch);
00076 }
00077 
00078 inline bool
00079 is_digit(unsigned ch) {
00080     return (Unicode::get_category(ch) == Unicode::DECIMAL_DIGIT_NUMBER);
00081 }
00082 
00083 // FIXME: we used to keep trailing "-" (e.g. Cl-) but it's of dubious utility
00084 // and there's the risk of hyphens getting stuck onto the end of terms...
00085 inline bool
00086 is_suffix(unsigned ch) {
00087     return ch == '+' || ch == '#';
00088 }
00089 
00090 inline bool
00091 prefix_needs_colon(const string & prefix, unsigned ch)
00092 {
00093     if (!U_isupper(ch)) return false;
00094     string::size_type len = prefix.length();
00095     return (len > 1 && prefix[len - 1] != ':');
00096 }
00097 
00098 using Unicode::is_currency;
00099 
00101 struct filter_group_id {
00105     list<string> prefixes;
00106 
00110     Xapian::valueno valno;
00111 
00113     explicit filter_group_id(const list<string> & prefixes_)
00114         : prefixes(prefixes_), valno(Xapian::BAD_VALUENO) {}
00115 
00117     explicit filter_group_id(Xapian::valueno valno_)
00118         : prefixes(), valno(valno_) {}
00119 
00121     bool operator<(const filter_group_id & other) const {
00122         if (prefixes != other.prefixes) {
00123             return prefixes < other.prefixes;
00124         }
00125         return valno < other.valno;
00126     }
00127 };
00128 
00135 class Term {
00136     State * state;
00137 
00138   public:
00139     string name;
00140     list<string> prefixes;
00141     string unstemmed;
00142     QueryParser::stem_strategy stem;
00143     termpos pos;
00144 
00145     Term(const string &name_, termpos pos_) : name(name_), stem(QueryParser::STEM_NONE), pos(pos_) { }
00146     Term(const string &name_) : name(name_), stem(QueryParser::STEM_NONE), pos(0) { }
00147     Term(const string &name_, const list<string> &prefixes_)
00148         : name(name_), prefixes(prefixes_), stem(QueryParser::STEM_NONE), pos(0) { }
00149     Term(termpos pos_) : stem(QueryParser::STEM_NONE), pos(pos_) { }
00150     Term(State * state_, const string &name_, const list<string> &prefixes_,
00151          const string &unstemmed_,
00152          QueryParser::stem_strategy stem_ = QueryParser::STEM_NONE,
00153          termpos pos_ = 0)
00154         : state(state_), name(name_), prefixes(prefixes_), unstemmed(unstemmed_),
00155           stem(stem_), pos(pos_) { }
00156 
00157     std::string make_term(const string & prefix) const;
00158 
00159     void need_positions() {
00160         if (stem == QueryParser::STEM_SOME) stem = QueryParser::STEM_NONE;
00161     }
00162 
00163     termpos get_termpos() const { return pos; }
00164 
00165     filter_group_id get_filter_group_id() const { return filter_group_id(prefixes); }
00166 
00167     Query * as_wildcarded_query(State * state) const;
00168 
00169     Query * as_partial_query(State * state_) const;
00170 
00171     Query get_query() const;
00172 
00173     Query get_query_with_synonyms() const;
00174 
00175     Query get_query_with_auto_synonyms() const;
00176 };
00177 
00179 class State {
00180     QueryParser::Internal * qpi;
00181 
00182   public:
00183     Query query;
00184     const char * error;
00185     unsigned flags;
00186 
00187     State(QueryParser::Internal * qpi_, unsigned flags_)
00188         : qpi(qpi_), error(NULL), flags(flags_) { }
00189 
00190     string stem_term(const string &term) {
00191         return qpi->stemmer(term);
00192     }
00193 
00194     void add_to_stoplist(const Term * term) {
00195         qpi->stoplist.push_back(term->name);
00196     }
00197 
00198     void add_to_unstem(const string & term, const string & unstemmed) {
00199         qpi->unstem.insert(make_pair(term, unstemmed));
00200     }
00201 
00202     valueno value_range(Query & q, Term *a, Term *b) {
00203         string start = a->name;
00204         string end = b->name;
00205         Xapian::valueno valno = Xapian::BAD_VALUENO;
00206         list<ValueRangeProcessor *>::const_iterator i;
00207         for (i = qpi->valrangeprocs.begin(); i != qpi->valrangeprocs.end(); ++i) {
00208             valno = (**i)(start, end);
00209             if (valno != Xapian::BAD_VALUENO) {
00210                 delete a;
00211                 delete b;
00212                 q = Query(Query::OP_VALUE_RANGE, valno, start, end);
00213                 return valno;
00214             }
00215         }
00216         // FIXME: Do we want to report an error for this?  If not we need
00217         // to perform the above check in the tokeniser and if none of the
00218         // ValueRangeProcessor classes like the range, we rollback to
00219         // parsing the query without treating this as a range.  Needs
00220         // more thought and probably a look at queries users actually
00221         // enter.
00222         error = "Unknown range operation";
00223         return valno;
00224     }
00225 
00226     Query::op default_op() const { return qpi->default_op; }
00227 
00228     bool is_stopword(const Term *term) const {
00229         return qpi->stopper && (*qpi->stopper)(term->name);
00230     }
00231 
00232     Database get_database() const {
00233         return qpi->db;
00234     }
00235 };
00236 
00237 string
00238 Term::make_term(const string & prefix) const
00239 {
00240     string term;
00241     if (stem == QueryParser::STEM_SOME) term += 'Z';
00242     if (!prefix.empty()) {
00243         term += prefix;
00244         if (prefix_needs_colon(prefix, name[0])) term += ':';
00245     }
00246     if (stem != QueryParser::STEM_NONE) {
00247         term += state->stem_term(name);
00248     } else {
00249         term += name;
00250     }
00251 
00252     if (!unstemmed.empty())
00253         state->add_to_unstem(term, unstemmed);
00254     return term;
00255 }
00256 
00257 Query
00258 Term::get_query_with_synonyms() const
00259 {
00260     Query q = get_query();
00261 
00262     // Handle single-word synonyms with each prefix.
00263     list<string>::const_iterator piter;
00264     for (piter = prefixes.begin(); piter != prefixes.end(); ++piter) {
00265         // First try the unstemmed term:
00266         string term;
00267         if (!piter->empty()) {
00268             term += *piter;
00269             if (prefix_needs_colon(*piter, name[0])) term += ':';
00270         }
00271         term += name;
00272 
00273         Xapian::Database db = state->get_database();
00274         Xapian::TermIterator syn = db.synonyms_begin(term);
00275         Xapian::TermIterator end = db.synonyms_end(term);
00276         if (syn == end && stem != QueryParser::STEM_NONE) {
00277             // If that has no synonyms, try the stemmed form:
00278             term = 'Z';
00279             if (!piter->empty()) {
00280                 term += *piter;
00281                 if (prefix_needs_colon(*piter, name[0])) term += ':';
00282             }
00283             term += state->stem_term(name);
00284             syn = db.synonyms_begin(term);
00285             end = db.synonyms_end(term);
00286         }
00287         while (syn != end) {
00288             q = Query(Query::OP_OR, q, Query(*syn, 1, pos));
00289             ++syn;
00290         }
00291     }
00292     return q;
00293 }
00294 
00295 Query
00296 Term::get_query_with_auto_synonyms() const
00297 {
00298     if (state->flags & QueryParser::FLAG_AUTO_SYNONYMS)
00299         return get_query_with_synonyms();
00300 
00301     return get_query();
00302 }
00303 
00304 static void
00305 add_to_query(Query *& q, Query::op op, Query * term)
00306 {
00307     Assert(term);
00308     if (q) {
00309         *q = Query(op, *q, *term);
00310         delete term;
00311     } else {
00312         q = term;
00313     }
00314 }
00315 
00316 static void
00317 add_to_query(Query *& q, Query::op op, const Query & term)
00318 {
00319     if (q) {
00320         *q = Query(op, *q, term);
00321     } else {
00322         q = new Query(term);
00323     }
00324 }
00325 
00326 Query
00327 Term::get_query() const
00328 {
00329     Assert(prefixes.size() >= 1);
00330     list<string>::const_iterator piter = prefixes.begin();
00331     Query q(make_term(*piter), 1, pos);
00332     while (++piter != prefixes.end()) {
00333         q = Query(Query::OP_OR, q, Query(make_term(*piter), 1, pos));
00334     }
00335     return q;
00336 }
00337 
00338 Query *
00339 Term::as_wildcarded_query(State * state_) const
00340 {
00341     Database db = state_->get_database();
00342     vector<Query> subqs;
00343     list<string>::const_iterator piter;
00344     for (piter = prefixes.begin(); piter != prefixes.end(); ++piter) {
00345         string root = *piter;
00346         root += name;
00347         TermIterator t = db.allterms_begin(root);
00348         while (t != db.allterms_end(root)) {
00349             subqs.push_back(Query(*t, 1, pos));
00350             ++t;
00351         }
00352     }
00353     delete this;
00354     return new Query(Query::OP_OR, subqs.begin(), subqs.end());
00355 }
00356 
00357 Query *
00358 Term::as_partial_query(State * state_) const
00359 {
00360     Database db = state_->get_database();
00361     vector<Query> subqs;
00362     list<string>::const_iterator piter;
00363     for (piter = prefixes.begin(); piter != prefixes.end(); ++piter) {
00364         string root = *piter;
00365         root += name;
00366         TermIterator t = db.allterms_begin(root);
00367         while (t != db.allterms_end(root)) {
00368             subqs.push_back(Query(*t, 1, pos));
00369             ++t;
00370         }
00371         // Add the term, as it would normally be handled, as an alternative.
00372         subqs.push_back(Query(make_term(*piter), 1, pos));
00373     }
00374     delete this;
00375     return new Query(Query::OP_OR, subqs.begin(), subqs.end());
00376 }
00377 
00378 inline bool
00379 is_phrase_generator(unsigned ch)
00380 {
00381     // These characters generate a phrase search.
00382     // Ordered mostly by frequency of calls to this function done when
00383     // running queryparsertest.
00384     return (ch && ch < 128 && strchr(".-/:\\@", ch) != NULL);
00385 }
00386 
00387 inline bool
00388 is_stem_preventer(unsigned ch)
00389 {
00390     return (ch && ch < 128 && strchr("(/\\@<>=*[{\"", ch) != NULL);
00391 }
00392 
00393 inline bool
00394 should_stem(const std::string & term)
00395 {
00396     const unsigned int SHOULD_STEM_MASK =
00397         (1 << Unicode::LOWERCASE_LETTER) |
00398         (1 << Unicode::TITLECASE_LETTER) |
00399         (1 << Unicode::MODIFIER_LETTER) |
00400         (1 << Unicode::OTHER_LETTER);
00401     Utf8Iterator u(term);
00402     return ((SHOULD_STEM_MASK >> Unicode::get_category(*u)) & 1);
00403 }
00404 
00405 inline unsigned check_infix(unsigned ch) {
00406     if (ch == '\'' || ch == '&' || ch == 0xb7 || ch == 0x5f4 || ch == 0x2027) {
00407         // Unicode includes all these except '&' in it's word boundary rules,
00408         // as well as 0x2019 (which we handle below) and ':' (for Swedish
00409         // apparently, but we ignore this for now as it's problematic in
00410         // real world cases).
00411         return ch;
00412     }
00413     // 0x2019 is Unicode apostrophe and single closing quote.
00414     // 0x201b is Unicode single opening quote with the tail rising.
00415     if (ch == 0x2019 || ch == 0x201b) return '\'';
00416     return 0;
00417 }
00418 
00419 inline unsigned check_infix_digit(unsigned ch) {
00420     // This list of characters comes from Unicode's word identifying algorithm.
00421     switch (ch) {
00422         case ',':
00423         case '.':
00424         case ';':
00425         case 0x037e: // GREEK QUESTION MARK
00426         case 0x0589: // ARMENIAN FULL STOP
00427         case 0x060D: // ARABIC DATE SEPARATOR
00428         case 0x07F8: // NKO COMMA
00429         case 0x2044: // FRACTION SLASH
00430         case 0xFE10: // PRESENTATION FORM FOR VERTICAL COMMA
00431         case 0xFE13: // PRESENTATION FORM FOR VERTICAL COLON
00432         case 0xFE14: // PRESENTATION FORM FOR VERTICAL SEMICOLON
00433             return ch;
00434     }
00435     return 0;
00436 }
00437 
00438 struct yyParser;
00439 
00440 // Prototype the functions lemon generates.
00441 static yyParser *ParseAlloc();
00442 static void ParseFree(yyParser *);
00443 static void Parse(yyParser *, int, Term *, State *);
00444 
00445 void
00446 QueryParser::Internal::add_prefix(const string &field, const string &prefix,
00447                                   bool filter)
00448 {
00449     map<string, PrefixInfo>::iterator p = prefixmap.find(field);
00450     if (p == prefixmap.end()) {
00451        prefixmap.insert(make_pair(field, PrefixInfo(filter, prefix)));
00452     } else {
00453        // Check that this is the same type of filter as the existing one(s).
00454        if (p->second.filter != filter) {
00455            throw Xapian::InvalidOperationError("Can't use add_prefix() and add_bool_prefix() on the same field name");
00456        }
00457        p->second.prefixes.push_back(prefix);
00458     }
00459 }
00460 
00461 string
00462 QueryParser::Internal::parse_term(Utf8Iterator &it, const Utf8Iterator &end,
00463                                   bool &was_acronym)
00464 {
00465     string term;
00466     // Look for initials separated by '.' (e.g. P.T.O., U.N.C.L.E).
00467     // Don't worry if there's a trailing '.' or not.
00468     if (U_isupper(*it)) {
00469         string t;
00470         Utf8Iterator p = it;
00471         do {
00472             Unicode::append_utf8(t, *p++);
00473         } while (p != end && *p == '.' && ++p != end && U_isupper(*p));
00474         // One letter does not make an acronym!  If we handled a single
00475         // uppercase letter here, we wouldn't catch M&S below.
00476         if (t.length() > 1) {
00477             // Check there's not a (lower case) letter or digit
00478             // immediately after it.
00479             // FIXME: should I.B.M..P.T.O be a range search?
00480             if (p == end || !is_wordchar(*p)) {
00481                 it = p;
00482                 swap(term, t);
00483             }
00484         }
00485     }
00486     was_acronym = !term.empty();
00487 
00488     if (term.empty()) {
00489         unsigned prevch = *it;
00490         Unicode::append_utf8(term, prevch);
00491         while (++it != end) {
00492             unsigned ch = *it;
00493             if (!is_wordchar(ch)) {
00494                 // Treat a single embedded '&' or "'" or similar as a word
00495                 // character (e.g. AT&T, Fred's).  Also, normalise
00496                 // apostrophes to ASCII apostrophe.
00497                 Utf8Iterator p = it;
00498                 ++p;
00499                 if (p == end || !is_wordchar(*p)) break;
00500                 unsigned nextch = *p;
00501                 if (is_digit(prevch) &&
00502                     is_digit(nextch)) {
00503                     ch = check_infix_digit(ch);
00504                 } else {
00505                     ch = check_infix(ch);
00506                 }
00507                 if (!ch) break;
00508             }
00509             Unicode::append_utf8(term, ch);
00510             prevch = ch;
00511         }
00512         if (it != end && is_suffix(*it)) {
00513             string suff_term = term;
00514             Utf8Iterator p = it;
00515             // Keep trailing + (e.g. C++, Na+) or # (e.g. C#).
00516             do {
00517                 if (suff_term.size() - term.size() == 3) {
00518                     suff_term.resize(0);
00519                     break;
00520                 }
00521                 suff_term += *p;
00522             } while (is_suffix(*++p));
00523             if (!suff_term.empty() && (p == end || !is_wordchar(*p))) {
00524                 // If the suffixed term doesn't exist, check that the
00525                 // non-suffixed term does.  This also takes care of
00526                 // the case when QueryParser::set_database() hasn't
00527                 // been called.
00528                 bool use_suff_term = false;
00529                 string lc = Unicode::tolower(suff_term);
00530                 if (db.term_exists(lc)) {
00531                     use_suff_term = true;
00532                 } else {
00533                     lc = Unicode::tolower(term);
00534                     if (!db.term_exists(lc)) use_suff_term = true;
00535                 }
00536                 if (use_suff_term) {
00537                     term = suff_term;
00538                     it = p;
00539                 }
00540             }
00541         }
00542     }
00543     return term;
00544 }
00545 
00546 Query
00547 QueryParser::Internal::parse_query(const string &qs, unsigned flags,
00548                                    const string &default_prefix)
00549 {
00550     yyParser * pParser = ParseAlloc();
00551 
00552     // Set value_ranges if we may have to handle value ranges in the query.
00553     bool value_ranges;
00554     value_ranges = !valrangeprocs.empty() && (qs.find("..") != string::npos);
00555 
00556     termpos term_pos = 1;
00557     Utf8Iterator it(qs), end;
00558 
00559     State state(this, flags);
00560 
00561     // To successfully apply more than one spelling correction to a query
00562     // string, we must keep track of the offset due to previous corrections.
00563     int correction_offset = 0;
00564     corrected_query.resize(0);
00565 
00566     // Stack of prefixes, used for phrases and subexpressions.
00567     list<const PrefixInfo *> prefix_stack;
00568 
00569     // If default_prefix is specified, use it.  Otherwise, use any list
00570     // that has been set for the empty prefix.
00571     const PrefixInfo def_pfx(false, default_prefix);
00572     {
00573         const PrefixInfo * default_prefixinfo = &def_pfx;
00574         if (default_prefix.empty()) {
00575             map<string, PrefixInfo>::const_iterator f = prefixmap.find("");
00576             if (f != prefixmap.end()) default_prefixinfo = &(f->second);
00577         }
00578 
00579         // We always have the current prefix on the top of the stack.
00580         prefix_stack.push_back(default_prefixinfo);
00581     }
00582 
00583     unsigned newprev = ' ';
00584 main_lex_loop:
00585     enum {
00586         DEFAULT, IN_QUOTES, IN_PREFIXED_QUOTES, IN_PHRASED_TERM, IN_GROUP
00587     } mode = DEFAULT;
00588     while (it != end && !state.error) {
00589         bool last_was_operator = false;
00590         if (false) {
00591 just_had_operator:
00592             if (it == end) break;
00593             last_was_operator = true;
00594             mode = DEFAULT;
00595         }
00596         if (mode == IN_PHRASED_TERM) mode = DEFAULT;
00597         if (is_whitespace(*it)) {
00598             newprev = ' ';
00599             ++it;
00600             it = find_if(it, end, is_not_whitespace);
00601             if (it == end) break;
00602         }
00603 
00604         if ((mode == DEFAULT || mode == IN_GROUP) && value_ranges) {
00605             // Scan forward to see if this could be the "start of range"
00606             // token.  Sadly this has O(n^2) tendencies, though at least
00607             // "n" is the number of words in a query which is likely to
00608             // remain fairly small.  FIXME: can we tokenise more elegantly?
00609             Utf8Iterator p = it;
00610             unsigned ch = 0;
00611             while (p != end) {
00612                 if (ch == '.' && *p == '.') {
00613                     ++p;
00614                     if (p == end || *p <= ' ' || *p == ')') break;
00615 
00616                     string r;
00617                     do {
00618                         Unicode::append_utf8(r, *it++);
00619                     } while (it != p);
00620                     // Trim off the trailing "..".
00621                     r.resize(r.size() - 2);
00622                     Parse(pParser, RANGE_START, new Term(r), &state);
00623                     r.resize(0);
00624                     // Allow any character except whitespace and ')' in a
00625                     // RANGE_END.  Or should we be consistent with RANGE_START?
00626                     do {
00627                         Unicode::append_utf8(r, *p++);
00628                     } while (p != end && *p > ' ' && *p != ')');
00629                     Parse(pParser, RANGE_END, new Term(r), &state);
00630                     it = p;
00631                     goto main_lex_loop;
00632                 }
00633                 ch = *p;
00634                 if (!(is_wordchar(ch) || is_currency(ch) ||
00635                       (ch < 128 && strchr("%,-./:@", ch)))) break;
00636                 ++p;
00637             }
00638         }
00639 
00640         if (!is_wordchar(*it)) {
00641             unsigned prev = newprev;
00642             unsigned ch = *it++;
00643             newprev = ch;
00644             // Drop out of IN_GROUP mode.
00645             if (mode == IN_GROUP) mode = DEFAULT;
00646             switch (ch) {
00647               case '"': // Quoted phrase.
00648                 if (mode == DEFAULT) {
00649                     // Skip whitespace.
00650                     it = find_if(it, end, is_not_whitespace);
00651                     if (it == end) {
00652                         // Ignore an unmatched " at the end of the query to
00653                         // avoid generating an empty pair of QUOTEs which will
00654                         // cause a parse error.
00655                         goto done;
00656                     }
00657                     if (*it == '"') {
00658                         // Ignore empty "" (but only if we're not already
00659                         // IN_QUOTES as we don't merge two adjacent quoted
00660                         // phrases!)
00661                         newprev = *it++;
00662                         break;
00663                     }
00664                 }
00665                 if (flags & QueryParser::FLAG_PHRASE) {
00666                     Parse(pParser, QUOTE, NULL, &state);
00667                     if (mode == DEFAULT) {
00668                         mode = IN_QUOTES;
00669                     } else {
00670                         // Remove the prefix we pushed for this phrase.
00671                         if (mode == IN_PREFIXED_QUOTES)
00672                             prefix_stack.pop_back();
00673                         mode = DEFAULT;
00674                     }
00675                 }
00676                 break;
00677 
00678               case '+': case '-': // Loved or hated term/phrase/subexpression.
00679                 // Ignore + or - at the end of the query string.
00680                 if (it == end) goto done;
00681                 if (prev > ' ' && prev != '(') {
00682                     // Or if not after whitespace or an open bracket.
00683                     break;
00684                 }
00685                 if (is_whitespace(*it) || *it == '+' || *it == '-') {
00686                     // Ignore + or - followed by a space, or further + or -.
00687                     // Postfix + (such as in C++ and H+) is handled as part of
00688                     // the term lexing code in parse_term().
00689                     newprev = *it++;
00690                     break;
00691                 }
00692                 if (mode == DEFAULT && (flags & FLAG_LOVEHATE)) {
00693                     Parse(pParser, (ch == '+' ? LOVE : HATE), NULL, &state);
00694                     goto just_had_operator;
00695                 }
00696                 // Need to prevent the term after a LOVE or HATE starting a
00697                 // term group...
00698                 break;
00699 
00700               case '(': // Bracketed subexpression.
00701                 // Skip whitespace.
00702                 it = find_if(it, end, is_not_whitespace);
00703                 // Ignore ( at the end of the query string.
00704                 if (it == end) goto done;
00705                 if (prev > ' ' && strchr("()+-", prev) == NULL) {
00706                     // Or if not after whitespace or a bracket or '+' or '-'.
00707                     break;
00708                 }
00709                 if (*it == ')') {
00710                     // Ignore empty ().
00711                     newprev = *it++;
00712                     break;
00713                 }
00714                 if (mode == DEFAULT && (flags & FLAG_BOOLEAN)) {
00715                     prefix_stack.push_back(prefix_stack.back());
00716                     Parse(pParser, BRA, NULL, &state);
00717                 }
00718                 break;
00719 
00720               case ')': // End of bracketed subexpression.
00721                 if (mode == DEFAULT && (flags & FLAG_BOOLEAN)) {
00722                     // Remove the prefix we pushed for the corresponding BRA.
00723                     // If brackets are unmatched, it's a syntax error, but
00724                     // that's no excuse to SEGV!
00725                     if (prefix_stack.size() > 1) prefix_stack.pop_back();
00726                     Parse(pParser, KET, NULL, &state);
00727                 }
00728                 break;
00729 
00730               case '~': // Synonym expansion.
00731                 // Ignore at the end of the query string.
00732                 if (it == end) goto done;
00733                 if (prev > ' ' && prev != '+' && prev != '-' && prev != '(') {
00734                     // Or if not after whitespace, +, -, or an open bracket.
00735                     break;
00736                 }
00737                 if (!is_wordchar(*it)) {
00738                     // Ignore if not followed by a word character.
00739                     break;
00740                 }
00741                 if (mode == DEFAULT && (flags & FLAG_SYNONYM)) {
00742                     Parse(pParser, SYNONYM, NULL, &state);
00743                     goto just_had_operator;
00744                 }
00745                 break;
00746             }
00747             // Skip any other characters.
00748             continue;
00749         }
00750 
00751         Assert(is_wordchar(*it));
00752 
00753         size_t term_start_index = it.raw() - qs.data();
00754 
00755         newprev = 'A'; // Any letter will do...
00756 
00757         // A term, a prefix, or a boolean operator.
00758         const PrefixInfo * prefixinfo = NULL;
00759         if ((mode == DEFAULT || mode == IN_GROUP) && !prefixmap.empty()) {
00760             // Check for a fieldname prefix (e.g. title:historical).
00761             Utf8Iterator p = find_if(it, end, is_not_wordchar);
00762             if (p != end && *p == ':' && ++p != end && *p > ' ' && *p != ')') {
00763                 string field;
00764                 p = it;
00765                 while (*p != ':')
00766                     Unicode::append_utf8(field, *p++);
00767                 map<string, PrefixInfo>::const_iterator f;
00768                 f = prefixmap.find(field);
00769                 if (f != prefixmap.end()) {
00770                     // Special handling for prefixed fields, depending on the
00771                     // type of the prefix.
00772                     unsigned ch = *++p;
00773                     prefixinfo = &(f->second);
00774 
00775                     if (prefixinfo->filter) {
00776                         // Drop out of IN_GROUP if we're in it.
00777                         mode = DEFAULT;
00778                         // Can't boolean filter prefix a subexpression or
00779                         // phrase; just use anything following the prefix
00780                         // until the next space or ')' as part of the boolean
00781                         // filter term.
00782                         it = p;
00783                         string name;
00784                         while (it != end && *it > ' ' && *it != ')')
00785                             Unicode::append_utf8(name, *it++);
00786                         // Build the unstemmed form in field.
00787                         field += ':';
00788                         field += name;
00789                         const list<string> & prefixes = prefixinfo->prefixes;
00790                         Term * token = new Term(&state, name, prefixes, field);
00791                         Parse(pParser, BOOLEAN_FILTER, token, &state);
00792                         continue;
00793                     }
00794 
00795                     if (ch == '"' && (flags & FLAG_PHRASE)) {
00796                         // Prefixed phrase, e.g.: subject:"space flight"
00797                         mode = IN_PREFIXED_QUOTES;
00798                         Parse(pParser, QUOTE, NULL, &state);
00799                         it = p;
00800                         newprev = ch;
00801                         ++it;
00802                         prefix_stack.push_back(prefixinfo);
00803                         continue;
00804                     }
00805 
00806                     if (ch == '(' && (flags & FLAG_BOOLEAN)) {
00807                         // Prefixed subexpression, e.g.: title:(fast NEAR food)
00808                         mode = DEFAULT;
00809                         Parse(pParser, BRA, NULL, &state);
00810                         it = p;
00811                         newprev = ch;
00812                         ++it;
00813                         prefix_stack.push_back(prefixinfo);
00814                         continue;
00815                     }
00816 
00817                     if (is_wordchar(ch)) {
00818                         // Prefixed term.
00819                         it = p;
00820                     } else {
00821                         // It looks like a prefix but isn't, so parse it as
00822                         // text instead.
00823                         prefixinfo = NULL;
00824                     }
00825                 }
00826             }
00827         }
00828 
00829 phrased_term:
00830         bool was_acronym;
00831         string term = parse_term(it, end, was_acronym);
00832 
00833         // Boolean operators.
00834         if ((mode == DEFAULT || mode == IN_GROUP) &&
00835             (flags & FLAG_BOOLEAN) &&
00836             // Don't want to interpret A.N.D. as an AND operator.
00837             !was_acronym &&
00838             !prefixinfo &&
00839             term.size() >= 2 && term.size() <= 4 && U_isalpha(term[0])) {
00840 
00841             string op = term;
00842             if (flags & FLAG_BOOLEAN_ANY_CASE) {
00843                 for (string::iterator i = op.begin(); i != op.end(); ++i) {
00844                     *i = C_toupper(*i);
00845                 }
00846             }
00847             if (op.size() == 3) {
00848                 if (op == "AND") {
00849                     Parse(pParser, AND, NULL, &state);
00850                     goto just_had_operator;
00851                 }
00852                 if (op == "NOT") {
00853                     Parse(pParser, NOT, NULL, &state);
00854                     goto just_had_operator;
00855                 }
00856                 if (op == "XOR") {
00857                     Parse(pParser, XOR, NULL, &state);
00858                     goto just_had_operator;
00859                 }
00860                 if (op == "ADJ") {
00861                     if (it != end && *it == '/') {
00862                         size_t width = 0;
00863                         Utf8Iterator p = it;
00864                         while (++p != end && U_isdigit(*p)) {
00865                             width = (width * 10) + (*p - '0');
00866                         }
00867                         if (width && (p == end || is_whitespace(*p))) {
00868                             it = p;
00869                             Parse(pParser, ADJ, new Term(width), &state);
00870                             goto just_had_operator;
00871                         }
00872                     }
00873 
00874                     Parse(pParser, ADJ, NULL, &state);
00875                     goto just_had_operator;
00876                 }
00877             } else if (op.size() == 2) {
00878                 if (op == "OR") {
00879                     Parse(pParser, OR, NULL, &state);
00880                     goto just_had_operator;
00881                 }
00882             } else if (op.size() == 4) {
00883                 if (op == "NEAR") {
00884                     if (it != end && *it == '/') {
00885                         size_t width = 0;
00886                         Utf8Iterator p = it;
00887                         while (++p != end && U_isdigit(*p)) {
00888                             width = (width * 10) + (*p - '0');
00889                         }
00890                         if (width && (p == end || is_whitespace(*p))) {
00891                             it = p;
00892                             Parse(pParser, NEAR, new Term(width), &state);
00893                             goto just_had_operator;
00894                         }
00895                     }
00896 
00897                     Parse(pParser, NEAR, NULL, &state);
00898                     goto just_had_operator;
00899                 }
00900             }
00901         }
00902 
00903         // If no prefix is set, use the default one.
00904         if (!prefixinfo) prefixinfo = prefix_stack.back();
00905 
00906         Assert(!prefixinfo->filter);
00907 
00908         {
00909             string unstemmed_term(term);
00910             term = Unicode::tolower(term);
00911 
00912             // Reuse stem_strategy - STEM_SOME here means "stem terms except
00913             // when used with positional operators".
00914             stem_strategy stem_term = stem_action;
00915             if (stem_term != STEM_NONE) {
00916                 if (!stemmer.internal.get()) {
00917                     // No stemmer is set.
00918                     stem_term = STEM_NONE;
00919                 } else if (stem_term == STEM_SOME) {
00920                     if (!should_stem(unstemmed_term) ||
00921                         (it != end && is_stem_preventer(*it))) {
00922                         // Don't stem this particular term.
00923                         stem_term = STEM_NONE;
00924                     }
00925                 }
00926             }
00927 
00928             Term * term_obj = new Term(&state, term, prefixinfo->prefixes,
00929                                        unstemmed_term, stem_term, term_pos++);
00930 
00931             // Check spelling, if we're a normal term, and any of the prefixes
00932             // are empty.
00933             if ((flags & FLAG_SPELLING_CORRECTION) && !was_acronym) {
00934                 list<string>::const_iterator prefixiter;
00935                 for (prefixiter = prefixinfo->prefixes.begin();
00936                      prefixiter != prefixinfo->prefixes.end();
00937                      ++prefixiter) {
00938                     if (!prefixiter->empty())
00939                         continue;
00940                     if (!db.term_exists(term)) {
00941                         string suggestion = db.get_spelling_suggestion(term);
00942                         if (!suggestion.empty()) {
00943                             if (corrected_query.empty()) corrected_query = qs;
00944                             size_t term_end_index = it.raw() - qs.data();
00945                             size_t n = term_end_index - term_start_index;
00946                             size_t pos = term_start_index + correction_offset;
00947                             corrected_query.replace(pos, n, suggestion);
00948                             correction_offset += suggestion.size();
00949                             correction_offset -= n;
00950                         }
00951                     }
00952                     break;
00953                 }
00954             }
00955 
00956             if (mode == IN_PHRASED_TERM) {
00957                 Parse(pParser, PHR_TERM, term_obj, &state);
00958             } else {
00959                 if (mode == DEFAULT || mode == IN_GROUP) {
00960                     if (it != end) {
00961                         if ((flags & FLAG_WILDCARD) && *it == '*') {
00962                             Utf8Iterator p(it);
00963                             ++p;
00964                             if (p == end || !is_wordchar(*p)) {
00965                                 it = p;
00966                                 // Wildcard at end of term (also known as
00967                                 // "right truncation").
00968                                 Parse(pParser, WILD_TERM, term_obj, &state);
00969                                 continue;
00970                             }
00971                         }
00972                     } else {
00973                         if (flags & FLAG_PARTIAL) {
00974                             // Final term of a partial match query, with no
00975                             // following characters - treat as a wildcard.
00976                             Parse(pParser, PARTIAL_TERM, term_obj, &state);
00977                             continue;
00978                         }
00979                     }
00980                 }
00981 
00982                 // See if the next token will be PHR_TERM - if so, this one
00983                 // needs to be TERM not GROUP_TERM.
00984                 if (mode == IN_GROUP && is_phrase_generator(*it)) {
00985                     // FIXME: can we clean this up?
00986                     Utf8Iterator p = it;
00987                     do {
00988                         ++p;
00989                     } while (p != end && is_phrase_generator(*p));
00990                     // Don't generate a phrase unless the phrase generators are
00991                     // immediately followed by another term.
00992                     if (p != end && is_wordchar(*p)) {
00993                         mode = DEFAULT;
00994                     }
00995                 }
00996 
00997                 Parse(pParser, (mode == IN_GROUP ? GROUP_TERM : TERM),
00998                       term_obj, &state);
00999                 if (mode != DEFAULT && mode != IN_GROUP) continue;
01000             }
01001         }
01002 
01003         if (it == end) break;
01004 
01005         if (is_phrase_generator(*it)) {
01006             // Skip multiple phrase generators.
01007             do {
01008                 ++it;
01009             } while (it != end && is_phrase_generator(*it));
01010             // Don't generate a phrase unless the phrase generators are
01011             // immediately followed by another term.
01012             if (it != end && is_wordchar(*it)) {
01013                 mode = IN_PHRASED_TERM;
01014                 term_start_index = it.raw() - qs.data();
01015                 goto phrased_term;
01016             }
01017         } else if (mode == DEFAULT || mode == IN_GROUP) {
01018             mode = DEFAULT;
01019             if (!last_was_operator && is_whitespace(*it)) {
01020                 newprev = ' ';
01021                 // Skip multiple whitespace.
01022                 do {
01023                     ++it;
01024                 } while (it != end && is_whitespace(*it));
01025                 // Don't generate a group unless the terms are only separated
01026                 // by whitespace.
01027                 if (it != end && is_wordchar(*it)) {
01028                     mode = IN_GROUP;
01029                 }
01030             }
01031         }
01032     }
01033 done:
01034     if (!state.error) {
01035         // Implicitly close any unclosed quotes...
01036         if (mode == IN_QUOTES || mode == IN_PREFIXED_QUOTES)
01037             Parse(pParser, QUOTE, NULL, &state);
01038         Parse(pParser, 0, NULL, &state);
01039     }
01040     ParseFree(pParser);
01041 
01042     errmsg = state.error;
01043     return state.query;
01044 }
01045 
01046 struct ProbQuery {
01047     Query * query;
01048     Query * love;
01049     Query * hate;
01050     // filter is a map from prefix to a query for that prefix.  Queries with
01051     // the same prefix are combined with OR, and the results of this are
01052     // combined with AND to get the full filter.
01053     map<filter_group_id, Query> filter;
01054 
01055     ProbQuery() : query(0), love(0), hate(0) { }
01056     ~ProbQuery() {
01057         delete query;
01058         delete love;
01059         delete hate;
01060     }
01061 
01062     Query merge_filters() const {
01063         map<filter_group_id, Query>::const_iterator i = filter.begin();
01064         Assert(i != filter.end());
01065         Query q = i->second;
01066         while (++i != filter.end()) {
01067             q = Query(Query::OP_AND, q, i->second);
01068         }
01069         return q;
01070     }
01071 };
01072 
01073 class TermGroup {
01074     vector<Term *> terms;
01075 
01076   public:
01077     TermGroup() { }
01078 
01080     void add_term(Term * term) {
01081         terms.push_back(term);
01082     }
01083 
01085     Query * as_group(State *state) const;
01086 
01090     void destroy() { delete this; }
01091 
01092   protected:
01096     ~TermGroup() {
01097         vector<Term*>::const_iterator i;
01098         for (i = terms.begin(); i != terms.end(); ++i) {
01099             delete *i;
01100         }
01101     }
01102 };
01103 
01104 Query *
01105 TermGroup::as_group(State *state) const
01106 {
01107     Query::op default_op = state->default_op();
01108     vector<Query> subqs;
01109     subqs.reserve(terms.size());
01110     if (state->flags & QueryParser::FLAG_AUTO_MULTIWORD_SYNONYMS) {
01111         // Check for multi-word synonyms.
01112         Database db = state->get_database();
01113 
01114         string key;
01115         vector<Term*>::const_iterator begin = terms.begin();
01116         vector<Term*>::const_iterator i = begin;
01117         while (i != terms.end()) {
01118             TermIterator synkey(db.synonym_keys_begin((*i)->name));
01119             TermIterator synend(db.synonym_keys_end((*i)->name));
01120             if (synkey == synend) {
01121                 // No multi-synonym matches.
01122                 if (state->is_stopword(*i)) {
01123                     state->add_to_stoplist(*i);
01124                 } else {
01125                     subqs.push_back((*i)->get_query_with_auto_synonyms());
01126                 }
01127                 begin = ++i;
01128                 continue;
01129             }
01130             key.resize(0);
01131             while (i != terms.end()) {
01132                 if (!key.empty()) key += ' ';
01133                 key += (*i)->name;
01134                 ++i;
01135                 synkey.skip_to(key);
01136                 if (synkey == synend || !startswith(*synkey, key)) break;
01137             }
01138             // Greedily try to match as many consecutive words as possible.
01139             TermIterator syn, end;
01140             while (true) {
01141                 syn = db.synonyms_begin(key);
01142                 end = db.synonyms_end(key);
01143                 if (syn != end) break;
01144                 if (--i == begin) break;
01145                 key.resize(key.size() - (*i)->name.size() - 1);
01146             }
01147             if (i == begin) {
01148                 // No multi-synonym matches.
01149                 if (state->is_stopword(*i)) {
01150                     state->add_to_stoplist(*i);
01151                 } else {
01152                     subqs.push_back((*i)->get_query_with_auto_synonyms());
01153                 }
01154                 begin = ++i;
01155                 continue;
01156             }
01157 
01158             vector<Query> subqs2;
01159             vector<Term*>::const_iterator j;
01160             for (j = begin; j != i; ++j) {
01161                 if (state->is_stopword(*j)) {
01162                     state->add_to_stoplist(*j);
01163                 } else {
01164                     subqs2.push_back((*j)->get_query());
01165                 }
01166             }
01167             Query q_original_terms(default_op, subqs2.begin(), subqs2.end());
01168             subqs2.clear();
01169 
01170             // Use the position of the first term for the synonyms.
01171             Xapian::termpos pos = (*begin)->pos;
01172             begin = i;
01173             while (syn != end) {
01174                 subqs2.push_back(Query(*syn, 1, pos));
01175                 ++syn;
01176             }
01177             Query q_synonym_terms(Query::OP_OR, subqs2.begin(), subqs2.end());
01178             subqs2.clear();
01179             subqs.push_back(Query(Query::OP_OR,
01180                                   q_original_terms, q_synonym_terms));
01181         }
01182     } else {
01183         vector<Term*>::const_iterator i;
01184         for (i = terms.begin(); i != terms.end(); ++i) {
01185             if (state->is_stopword(*i)) {
01186                 state->add_to_stoplist(*i);
01187             } else {
01188                 subqs.push_back((*i)->get_query_with_auto_synonyms());
01189             }
01190         }
01191     }
01192     delete this;
01193     return new Query(default_op, subqs.begin(), subqs.end());
01194 }
01195 
01196 class TermList {
01197     vector<Term *> terms;
01198     size_t window;
01199 
01209     bool uniform_prefixes;
01210 
01214     list<string> prefixes;
01215 
01216   public:
01217     TermList() : window(0), uniform_prefixes(true) { }
01218 
01220     void add_positional_term(Term * term) {
01221         if (terms.empty()) {
01222             prefixes = term->prefixes;
01223         } else if (uniform_prefixes && prefixes != term->prefixes)  {
01224             prefixes.clear();
01225             uniform_prefixes = false;
01226         }
01227         term->need_positions();
01228         terms.push_back(term);
01229     }
01230 
01231     void adjust_window(size_t alternative_window) {
01232         if (alternative_window > window) window = alternative_window;
01233     }
01234 
01236     Query * as_opwindow_query(Query::op op, Xapian::termcount w_delta) const {
01237         Query * q = NULL;
01238         size_t n_terms = terms.size();
01239         Xapian::termcount w = w_delta + terms.size();
01240         if (uniform_prefixes) {
01241             list<string>::const_iterator piter;
01242             for (piter = prefixes.begin(); piter != prefixes.end(); ++piter) {
01243                 vector<Query> subqs;
01244                 subqs.reserve(n_terms);
01245                 vector<Term *>::const_iterator titer;
01246                 for (titer = terms.begin(); titer != terms.end(); ++titer) {
01247                     Term * t = *titer;
01248                     subqs.push_back(Query(t->make_term(*piter), 1, t->pos));
01249                 }
01250                 add_to_query(q, Query::OP_OR,
01251                              Query(op, subqs.begin(), subqs.end(), w));
01252             }
01253         } else {
01254             vector<Query> subqs;
01255             subqs.reserve(n_terms);
01256             vector<Term *>::const_iterator titer;
01257             for (titer = terms.begin(); titer != terms.end(); ++titer) {
01258                 subqs.push_back((*titer)->get_query());
01259             }
01260             q = new Query(op, subqs.begin(), subqs.end(), w);
01261         }
01262 
01263         delete this;
01264         return q;
01265     }
01266 
01268     Query * as_phrase_query() const {
01269         return as_opwindow_query(Query::OP_PHRASE, 0);
01270     }
01271 
01273     Query * as_near_query() const {
01274         // The common meaning of 'a NEAR b' is "a within 10 terms of b", which
01275         // means a window size of 11.  For more than 2 terms, we just add one
01276         // to the window size for each extra term.
01277         size_t w = window;
01278         if (w == 0) w = 10;
01279         return as_opwindow_query(Query::OP_NEAR, w - 1);
01280     }
01281 
01283     Query * as_adj_query() const {
01284         // The common meaning of 'a ADJ b' is "a at most 10 terms before b",
01285         // which means a window size of 11.  For more than 2 terms, we just add
01286         // one to the window size for each extra term.
01287         size_t w = window;
01288         if (w == 0) w = 10;
01289         return as_opwindow_query(Query::OP_PHRASE, w - 1);
01290     }
01291 
01295     void destroy() { delete this; }
01296 
01297   protected:
01301     ~TermList() {
01302         vector<Term *>::const_iterator t;
01303         for (t = terms.begin(); t != terms.end(); ++t) {
01304             delete *t;
01305         }
01306     }
01307 };
01308 
01309 // Helper macro for converting a boolean operation into a Xapian::Query.
01310 #define BOOL_OP_TO_QUERY(E, A, OP, B, OP_TXT) \
01311     do {\
01312         if (!A || !B) {\
01313             state->error = "Syntax: <expression> "OP_TXT" <expression>";\
01314             yy_parse_failed(yypParser);\
01315             return;\
01316         }\
01317         E = new Query(OP, *A, *B);\
01318         delete A;\
01319         delete B;\
01320     } while (0)
01321 
01322 #line 1324 "queryparser/queryparser_internal.cc"
01323 /* Next is all token values, in a form suitable for use by makeheaders.
01324 ** This section will be null unless lemon is run with the -m switch.
01325 */
01326 /* 
01327 ** These constants (all generated automatically by the parser generator)
01328 ** specify the various kinds of tokens (terminals) that the parser
01329 ** understands. 
01330 **
01331 ** Each symbol here is a terminal symbol in the grammar.
01332 */
01333 /* Make sure the INTERFACE macro is defined.
01334 */
01335 #ifndef INTERFACE
01336 # define INTERFACE 1
01337 #endif
01338 /* The next thing included is series of defines which control
01339 ** various aspects of the generated parser.
01340 **    YYCODETYPE         is the data type used for storing terminal
01341 **                       and nonterminal numbers.  "unsigned char" is
01342 **                       used if there are fewer than 250 terminals
01343 **                       and nonterminals.  "int" is used otherwise.
01344 **    YYNOCODE           is a number of type YYCODETYPE which corresponds
01345 **                       to no legal terminal or nonterminal number.  This
01346 **                       number is used to fill in empty slots of the hash 
01347 **                       table.
01348 **    YYFALLBACK         If defined, this indicates that one or more tokens
01349 **                       have fall-back values which should be used if the
01350 **                       original value of the token will not parse.
01351 **    YYACTIONTYPE       is the data type used for storing terminal
01352 **                       and nonterminal numbers.  "unsigned char" is
01353 **                       used if there are fewer than 250 rules and
01354 **                       states combined.  "int" is used otherwise.
01355 **    ParseTOKENTYPE     is the data type used for minor tokens given 
01356 **                       directly to the parser from the tokenizer.
01357 **    YYMINORTYPE        is the data type used for all minor tokens.
01358 **                       This is typically a union of many types, one of
01359 **                       which is ParseTOKENTYPE.  The entry in the union
01360 **                       for base tokens is called "yy0".
01361 **    YYSTACKDEPTH       is the maximum depth of the parser's stack.
01362 **    ParseARG_SDECL     A static variable declaration for the %extra_argument
01363 **    ParseARG_PDECL     A parameter declaration for the %extra_argument
01364 **    ParseARG_STORE     Code to store %extra_argument into yypParser
01365 **    ParseARG_FETCH     Code to extract %extra_argument from yypParser
01366 **    YYNSTATE           the combined number of states.
01367 **    YYNRULE            the number of rules in the grammar
01368 **    YYERRORSYMBOL      is the code number of the error symbol.  If not
01369 **                       defined, then do no error processing.
01370 */
01371 #define YYCODETYPE unsigned char
01372 #define YYNOCODE 38
01373 #define YYACTIONTYPE unsigned char
01374 #define ParseTOKENTYPE Term *
01375 typedef union {
01376   ParseTOKENTYPE yy0;
01377   TermList * yy1;
01378   int yy8;
01379   ProbQuery * yy9;
01380   Query * yy13;
01381   TermGroup * yy60;
01382   int yy75;
01383 } YYMINORTYPE;
01384 #define YYSTACKDEPTH 100
01385 #define ParseARG_SDECL State * state;
01386 #define ParseARG_PDECL ,State * state
01387 #define ParseARG_FETCH State * state = yypParser->state
01388 #define ParseARG_STORE yypParser->state = state
01389 #define YYNSTATE 73
01390 #define YYNRULE 51
01391 #define YYERRORSYMBOL 22
01392 #define YYERRSYMDT yy75
01393 #define YY_NO_ACTION      (YYNSTATE+YYNRULE+2)
01394 #define YY_ACCEPT_ACTION  (YYNSTATE+YYNRULE+1)
01395 #define YY_ERROR_ACTION   (YYNSTATE+YYNRULE)
01396 
01397 /* Next are that tables used to determine what action to take based on the
01398 ** current state and lookahead token.  These tables are used to implement
01399 ** functions that take a state number and lookahead value and return an
01400 ** action integer.  
01401 **
01402 ** Suppose the action integer is N.  Then the action is determined as
01403 ** follows
01404 **
01405 **   0 <= N < YYNSTATE                  Shift N.  That is, push the lookahead
01406 **                                      token onto the stack and goto state N.
01407 **
01408 **   YYNSTATE <= N < YYNSTATE+YYNRULE   Reduce by rule N-YYNSTATE.
01409 **
01410 **   N == YYNSTATE+YYNRULE              A syntax error has occurred.
01411 **
01412 **   N == YYNSTATE+YYNRULE+1            The parser accepts its input.
01413 **
01414 **   N == YYNSTATE+YYNRULE+2            No such action.  Denotes unused
01415 **                                      slots in the yy_action[] table.
01416 **
01417 ** The action table is constructed as a single large table named yy_action[].
01418 ** Given state S and lookahead X, the action is computed as
01419 **
01420 **      yy_action[ yy_shift_ofst[S] + X ]
01421 **
01422 ** If the index value yy_shift_ofst[S]+X is out of range or if the value
01423 ** yy_lookahead[yy_shift_ofst[S]+X] is not equal to X or if yy_shift_ofst[S]
01424 ** is equal to YY_SHIFT_USE_DFLT, it means that the action is not in the table
01425 ** and that yy_default[S] should be used instead.  
01426 **
01427 ** The formula above is for computing the action when the lookahead is
01428 ** a terminal symbol.  If the lookahead is a non-terminal (as occurs after
01429 ** a reduce action) then the yy_reduce_ofst[] array is used in place of
01430 ** the yy_shift_ofst[] array and YY_REDUCE_USE_DFLT is used in place of
01431 ** YY_SHIFT_USE_DFLT.
01432 **
01433 ** The following are the tables generated in this section:
01434 **
01435 **  yy_action[]        A single table containing all actions.
01436 **  yy_lookahead[]     A table containing the lookahead for each entry in
01437 **                     yy_action.  Used to detect hash collisions.
01438 **  yy_shift_ofst[]    For each state, the offset into yy_action for
01439 **                     shifting terminals.
01440 **  yy_reduce_ofst[]   For each state, the offset into yy_action for
01441 **                     shifting non-terminals after a reduce.
01442 **  yy_default[]       Default action for each state.
01443 */
01444 static const YYACTIONTYPE yy_action[] = {
01445  /*     0 */   125,    1,    2,    3,   13,   43,   46,   60,   70,   73,
01446  /*    10 */    30,   32,   34,   37,   12,    2,    5,   13,   43,   46,
01447  /*    20 */    60,   70,   19,   30,   32,   34,   37,   12,    2,    7,
01448  /*    30 */    13,   43,   46,   60,   70,   21,   30,   32,   34,   37,
01449  /*    40 */    12,    2,    9,   13,   43,   46,   60,   70,   29,   30,
01450  /*    50 */    32,   34,   37,   12,    2,   11,   13,   43,   46,   60,
01451  /*    60 */    70,   31,   30,   32,   34,   37,   41,    2,    3,   13,
01452  /*    70 */    43,   46,   60,   70,   33,   30,   32,   34,   37,   12,
01453  /*    80 */     2,   72,   13,   43,   46,   60,   70,   74,   30,   32,
01454  /*    90 */    34,   37,   10,    4,    6,   62,   65,   54,   69,    4,
01455  /*   100 */     6,   23,   24,   68,   44,   26,   25,   40,   71,   36,
01456  /*   110 */    35,   62,   65,   54,   69,   38,   28,   23,   24,   68,
01457  /*   120 */    44,   39,   25,   40,   27,   45,   62,   65,   54,   69,
01458  /*   130 */    42,   48,   23,   24,   68,   44,   55,   25,   40,   90,
01459  /*   140 */    90,   99,   99,   54,   15,   90,   90,   23,   24,   99,
01460  /*   150 */    99,   90,   25,   40,  103,   90,  103,  103,  103,  103,
01461  /*   160 */    18,   20,    8,   10,    4,    6,   17,   16,   54,   52,
01462  /*   170 */    90,   90,   23,   24,   51,  103,   90,   25,   40,   54,
01463  /*   180 */    52,   90,   90,   23,   24,   58,   54,   52,   25,   40,
01464  /*   190 */    23,   24,   64,   90,   90,   25,   40,   54,   52,   90,
01465  /*   200 */    90,   23,   24,   67,   90,   90,   25,   40,   14,   22,
01466  /*   210 */    90,   30,   32,   34,   37,   90,   50,   90,   90,   53,
01467  /*   220 */    90,   30,   32,   34,   37,   57,   90,   90,   53,   90,
01468  /*   230 */    30,   32,   34,   37,   54,   15,   90,   90,   23,   24,
01469  /*   240 */    90,   90,   90,   25,   40,   61,   22,   90,   30,   32,
01470  /*   250 */    34,   37,   90,   63,   90,   90,   53,   90,   30,   32,
01471  /*   260 */    34,   37,   66,   90,   90,   53,   90,   30,   32,   34,
01472  /*   270 */    37,  104,   90,  104,  104,  104,  104,   90,   18,   20,
01473  /*   280 */    90,   90,   49,   56,   17,   16,   90,   90,   90,   90,
01474  /*   290 */    59,   47,  104,
01475 };
01476 static const YYCODETYPE yy_lookahead[] = {
01477  /*     0 */    23,   24,   25,   26,   27,   28,   29,   30,   31,    0,
01478  /*    10 */    33,   34,   35,   36,   24,   25,   26,   27,   28,   29,
01479  /*    20 */    30,   31,   11,   33,   34,   35,   36,   24,   25,   26,
01480  /*    30 */    27,   28,   29,   30,   31,   11,   33,   34,   35,   36,
01481  /*    40 */    24,   25,   26,   27,   28,   29,   30,   31,   11,   33,
01482  /*    50 */    34,   35,   36,   24,   25,   26,   27,   28,   29,   30,
01483  /*    60 */    31,   13,   33,   34,   35,   36,   24,   25,   26,   27,
01484  /*    70 */    28,   29,   30,   31,   12,   33,   34,   35,   36,   24,
01485  /*    80 */    25,   26,   27,   28,   29,   30,   31,    0,   33,   34,
01486  /*    90 */    35,   36,    3,    4,    5,    8,    9,   10,   11,    4,
01487  /*   100 */     5,   14,   15,   16,   17,   32,   19,   20,    5,   11,
01488  /*   110 */     6,    8,    9,   10,   11,    7,   11,   14,   15,   16,
01489  /*   120 */    17,   11,   19,   20,   19,   18,    8,    9,   10,   11,
01490  /*   130 */    21,   18,   14,   15,   16,   17,   11,   19,   20,   37,
01491  /*   140 */    37,    8,    9,   10,   11,   37,   37,   14,   15,   16,
01492  /*   150 */    17,   37,   19,   20,    0,   37,    2,    3,    4,    5,
01493  /*   160 */     6,    7,    2,    3,    4,    5,   12,   13,   10,   11,
01494  /*   170 */    37,   37,   14,   15,   16,   21,   37,   19,   20,   10,
01495  /*   180 */    11,   37,   37,   14,   15,   16,   10,   11,   19,   20,
01496  /*   190 */    14,   15,   16,   37,   37,   19,   20,   10,   11,   37,
01497  /*   200 */    37,   14,   15,   16,   37,   37,   19,   20,   30,   31,
01498  /*   210 */    37,   33,   34,   35,   36,   37,   28,   37,   37,   31,
01499  /*   220 */    37,   33,   34,   35,   36,   28,   37,   37,   31,   37,
01500  /*   230 */    33,   34,   35,   36,   10,   11,   37,   37,   14,   15,
01501  /*   240 */    37,   37,   37,   19,   20,   30,   31,   37,   33,   34,
01502  /*   250 */    35,   36,   37,   28,   37,   37,   31,   37,   33,   34,
01503  /*   260 */    35,   36,   28,   37,   37,   31,   37,   33,   34,   35,
01504  /*   270 */    36,    0,   37,    2,    3,    4,    5,   37,    6,    7,
01505  /*   280 */    37,   37,    8,    9,   12,   13,   37,   37,   37,   37,
01506  /*   290 */    16,   17,   21,
01507 };
01508 #define YY_SHIFT_USE_DFLT (-1)
01509 static const short yy_shift_ofst[] = {
01510  /*     0 */    87,    9,   -1,  160,  103,   -1,  118,   -1,  118,   89,
01511  /*    10 */   118,   95,   -1,  133,   -1,  272,   -1,   -1,   11,   -1,
01512  /*    20 */    24,   -1,   -1,   -1,   -1,   37,  105,   -1,   -1,   -1,
01513  /*    30 */    48,   -1,   62,   -1,  104,   98,   -1,  108,  110,   -1,
01514  /*    40 */   118,  109,   -1,   -1,  107,   -1,  274,  113,   -1,  158,
01515  /*    50 */    -1,   -1,  272,   -1,  125,   -1,  169,   -1,   -1,   -1,
01516  /*    60 */   224,   -1,  176,   -1,   -1,  187,   -1,   -1,   -1,  154,
01517  /*    70 */   271,  118,   -1,
01518 };
01519 #define YY_REDUCE_USE_DFLT (-24)
01520 static const short yy_reduce_ofst[] = {
01521  /*     0 */   -23,  -24,  -24,  -24,  -10,  -24,    3,  -24,   16,  -24,
01522  /*    10 */    29,  -24,  -24,  178,  -24,  -24,  -24,  -24,  -24,  -24,
01523  /*    20 */   -24,  -24,  -24,  -24,  -24,   73,  -24,  -24,  -24,  -24,
01524  /*    30 */   -24,  -24,  -24,  -24,  -24,  -24,  -24,  -24,  -24,  -24,
01525  /*    40 */    42,  -24,  -24,  -24,  -24,  -24,  -24,  -24,  -24,  188,
01526  /*    50 */   -24,  -24,  -24,  -24,  -24,  -24,  197,  -24,  -24,  -24,
01527  /*    60 */   215,  -24,  225,  -24,  -24,  234,  -24,  -24,  -24,  -24,
01528  /*    70 */   -24,   55,  -24,
01529 };
01530 static const YYACTIONTYPE yy_default[] = {
01531  /*     0 */    82,   81,   75,  124,   82,   76,   82,   77,   82,   79,
01532  /*    10 */    82,   80,   81,   83,   88,  101,  116,  118,  124,  120,
01533  /*    20 */   124,  122,  102,  105,  106,  124,  124,  107,  115,  114,
01534  /*    30 */   108,  117,  109,  119,  110,  124,  121,  111,  124,  123,
01535  /*    40 */    82,   81,  112,   84,  124,   85,  124,  124,   86,  124,
01536  /*    50 */    90,   98,  103,  104,  124,  113,  124,   92,   94,   96,
01537  /*    60 */   100,   87,  124,   89,   97,  124,   91,   93,   95,  101,
01538  /*    70 */   102,   82,   78,
01539 };
01540 #define YY_SZ_ACTTAB (int)(sizeof(yy_action)/sizeof(yy_action[0]))
01541 
01542 /* The next table maps tokens into fallback tokens.  If a construct
01543 ** like the following:
01544 ** 
01545 **      %fallback ID X Y Z.
01546 **
01547 ** appears in the grammar, then ID becomes a fallback token for X, Y,
01548 ** and Z.  Whenever one of the tokens X, Y, or Z is input to the parser
01549 ** but it does not parse, the type of the token is changed to ID and
01550 ** the parse is retried before an error is thrown.
01551 */
01552 #ifdef YYFALLBACK
01553 static const YYCODETYPE yyFallback[] = {
01554 };
01555 #endif /* YYFALLBACK */
01556 
01557 /* The following structure represents a single element of the
01558 ** parser's stack.  Information stored includes:
01559 **
01560 **   +  The state number for the parser at this level of the stack.
01561 **
01562 **   +  The value of the token stored at this level of the stack.
01563 **      (In other words, the "major" token.)
01564 **
01565 **   +  The semantic value stored at this level of the stack.  This is
01566 **      the information used by the action routines in the grammar.
01567 **      It is sometimes called the "minor" token.
01568 */
01569 struct yyStackEntry {
01570   yyStackEntry() {
01571     stateno = 0;
01572     major = 0;
01573   }
01574   yyStackEntry(int stateno_, int major_, YYMINORTYPE minor_)
01575   {
01576     stateno = stateno_;
01577     major = major_;
01578     minor = minor_;
01579   }
01580   int stateno;       /* The state-number */
01581   int major;         /* The major token value.  This is the code
01582                      ** number for the token at this stack level */
01583   YYMINORTYPE minor; /* The user-supplied minor token value.  This
01584                      ** is the value of the token  */
01585 };
01586 
01587 /* The state of the parser is completely contained in an instance of
01588 ** the following structure */
01589 struct yyParser {
01590   int yyerrcnt;                 /* Shifts left before out of the error */
01591   ParseARG_SDECL                /* A place to hold %extra_argument */
01592   vector<yyStackEntry> yystack; /* The parser's stack */
01593 };
01594 typedef struct yyParser yyParser;
01595 
01596 /* Prototype this here so we can call it from a rule action (ick). */
01597 static void yy_parse_failed(yyParser *);
01598 
01599 #include "omdebug.h"
01600 
01601 #ifdef XAPIAN_DEBUG_VERBOSE
01602 /* For tracing shifts, the names of all terminals and nonterminals
01603 ** are required.  The following table supplies these names */
01604 static const char *const yyTokenName[] = {
01605   "$",             "ERROR",         "OR",            "XOR",         
01606   "AND",           "NOT",           "NEAR",          "ADJ",         
01607   "LOVE",          "HATE",          "SYNONYM",       "TERM",        
01608   "GROUP_TERM",    "PHR_TERM",      "WILD_TERM",     "PARTIAL_TERM",
01609   "BOOLEAN_FILTER",  "RANGE_START",   "RANGE_END",     "QUOTE",       
01610   "BRA",           "KET",           "error",         "query",       
01611   "expr",          "prob_expr",     "bool_arg",      "prob",        
01612   "term",          "stop_prob",     "stop_term",     "compound_term",
01613   "phrase",        "phrased_term",  "group",         "near_expr",   
01614   "adj_expr",    
01615 };
01616 
01617 /* For tracing reduce actions, the names of all rules are required.
01618 */
01619 static const char *const yyRuleName[] = {
01620  /*   0 */ "query ::= expr",
01621  /*   1 */ "query ::=",
01622  /*   2 */ "expr ::= prob_expr",
01623  /*   3 */ "expr ::= bool_arg AND bool_arg",
01624  /*   4 */ "expr ::= bool_arg NOT bool_arg",
01625  /*   5 */ "expr ::= bool_arg AND NOT bool_arg",
01626  /*   6 */ "expr ::= bool_arg OR bool_arg",
01627  /*   7 */ "expr ::= bool_arg XOR bool_arg",
01628  /*   8 */ "bool_arg ::= expr",
01629  /*   9 */ "bool_arg ::=",
01630  /*  10 */ "prob_expr ::= prob",
01631  /*  11 */ "prob_expr ::= term",
01632  /*  12 */ "prob ::= RANGE_START RANGE_END",
01633  /*  13 */ "prob ::= stop_prob RANGE_START RANGE_END",
01634  /*  14 */ "prob ::= stop_term stop_term",
01635  /*  15 */ "prob ::= prob stop_term",
01636  /*  16 */ "prob ::= LOVE term",
01637  /*  17 */ "prob ::= stop_prob LOVE term",
01638  /*  18 */ "prob ::= HATE term",
01639  /*  19 */ "prob ::= stop_prob HATE term",
01640  /*  20 */ "prob ::= HATE BOOLEAN_FILTER",
01641  /*  21 */ "prob ::= stop_prob HATE BOOLEAN_FILTER",
01642  /*  22 */ "prob ::= BOOLEAN_FILTER",
01643  /*  23 */ "prob ::= stop_prob BOOLEAN_FILTER",
01644  /*  24 */ "prob ::= LOVE BOOLEAN_FILTER",
01645  /*  25 */ "prob ::= stop_prob LOVE BOOLEAN_FILTER",
01646  /*  26 */ "stop_prob ::= prob",
01647  /*  27 */ "stop_prob ::= stop_term",
01648  /*  28 */ "stop_term ::= TERM",
01649  /*  29 */ "stop_term ::= compound_term",
01650  /*  30 */ "term ::= TERM",
01651  /*  31 */ "term ::= compound_term",
01652  /*  32 */ "compound_term ::= WILD_TERM",
01653  /*  33 */ "compound_term ::= PARTIAL_TERM",
01654  /*  34 */ "compound_term ::= QUOTE phrase QUOTE",
01655  /*  35 */ "compound_term ::= phrased_term",
01656  /*  36 */ "compound_term ::= group",
01657  /*  37 */ "compound_term ::= near_expr",
01658  /*  38 */ "compound_term ::= adj_expr",
01659  /*  39 */ "compound_term ::= BRA expr KET",
01660  /*  40 */ "compound_term ::= SYNONYM TERM",
01661  /*  41 */ "phrase ::= TERM",
01662  /*  42 */ "phrase ::= phrase TERM",
01663  /*  43 */ "phrased_term ::= TERM PHR_TERM",
01664  /*  44 */ "phrased_term ::= phrased_term PHR_TERM",
01665  /*  45 */ "group ::= TERM GROUP_TERM",
01666  /*  46 */ "group ::= group GROUP_TERM",
01667  /*  47 */ "near_expr ::= TERM NEAR TERM",
01668  /*  48 */ "near_expr ::= near_expr NEAR TERM",
01669  /*  49 */ "adj_expr ::= TERM ADJ TERM",
01670  /*  50 */ "adj_expr ::= adj_expr ADJ TERM",
01671 };
01672 
01673 /*
01674 ** This function returns the symbolic name associated with a token
01675 ** value.
01676 */
01677 static const char *ParseTokenName(int tokenType){
01678   if( tokenType>0 && size_t(tokenType)<(sizeof(yyTokenName)/sizeof(yyTokenName[0])) ){
01679     return yyTokenName[tokenType];
01680   }
01681   return "Unknown";
01682 }
01683 
01684 /*
01685 ** This function returns the symbolic name associated with a rule
01686 ** value.
01687 */
01688 static const char *ParseRuleName(int ruleNum){
01689   if( ruleNum>0 && size_t(ruleNum)<(sizeof(yyRuleName)/sizeof(yyRuleName[0])) ){
01690     return yyRuleName[ruleNum];
01691   }
01692   return "Unknown";
01693 }
01694 #endif /* XAPIAN_DEBUG_VERBOSE */
01695 
01696 /* 
01697 ** This function allocates a new parser.
01698 ** The only argument is a pointer to a function which works like
01699 ** malloc.
01700 **
01701 ** Inputs:
01702 ** None.
01703 **
01704 ** Outputs:
01705 ** A pointer to a parser.  This pointer is used in subsequent calls
01706 ** to Parse and ParseFree.
01707 */
01708 static yyParser *ParseAlloc(){
01709   return new yyParser;
01710 }
01711 
01712 /* The following function deletes the value associated with a
01713 ** symbol.  The symbol can be either a terminal or nonterminal.
01714 ** "yymajor" is the symbol code, and "yypminor" is a pointer to
01715 ** the value.
01716 */
01717 static void yy_destructor(YYCODETYPE yymajor, YYMINORTYPE *yypminor){
01718   switch( yymajor ){
01719     /* Here is inserted the actions which take place when a
01720     ** terminal or non-terminal is destroyed.  This can happen
01721     ** when the symbol is popped from the stack during a
01722     ** reduce or during error processing or when a parser is 
01723     ** being destroyed before it is finished parsing.
01724     **
01725     ** Note: during a reduce, the only symbols destroyed are those
01726     ** which appear on the RHS of the rule, but which are not used
01727     ** inside the C code.
01728     */
01729     case 1:
01730     case 2:
01731     case 3:
01732     case 4:
01733     case 5:
01734     case 6:
01735     case 7:
01736     case 8:
01737     case 9:
01738     case 10:
01739     case 11:
01740     case 12:
01741     case 13:
01742     case 14:
01743     case 15:
01744     case 16:
01745     case 17:
01746     case 18:
01747     case 19:
01748     case 20:
01749     case 21:
01750 #line 1319 "/data/home/olly/tmp/xapian-svn-snapshot/tags/1.0.10/xapian/xapian-core/queryparser/queryparser.lemony"
01751 {delete (yypminor->yy0);}
01752 #line 1754 "queryparser/queryparser_internal.cc"
01753       break;
01754     case 24:
01755     case 25:
01756     case 26:
01757     case 28:
01758     case 30:
01759     case 31:
01760 #line 1390 "/data/home/olly/tmp/xapian-svn-snapshot/tags/1.0.10/xapian/xapian-core/queryparser/queryparser.lemony"
01761 {delete (yypminor->yy13);}
01762 #line 1764 "queryparser/queryparser_internal.cc"
01763       break;
01764     case 27:
01765     case 29:
01766 #line 1483 "/data/home/olly/tmp/xapian-svn-snapshot/tags/1.0.10/xapian/xapian-core/queryparser/queryparser.lemony"
01767 {delete (yypminor->yy9);}
01768 #line 1770 "queryparser/queryparser_internal.cc"
01769       break;
01770     case 32:
01771     case 33:
01772     case 35:
01773     case 36:
01774 #line 1684 "/data/home/olly/tmp/xapian-svn-snapshot/tags/1.0.10/xapian/xapian-core/queryparser/queryparser.lemony"
01775 {(yypminor->yy1)->destroy();}
01776 #line 1778 "queryparser/queryparser_internal.cc"
01777       break;
01778     case 34:
01779 #line 1718 "/data/home/olly/tmp/xapian-svn-snapshot/tags/1.0.10/xapian/xapian-core/queryparser/queryparser.lemony"
01780 {(yypminor->yy60)->destroy();}
01781 #line 1783 "queryparser/queryparser_internal.cc"
01782       break;
01783     default:  break;   /* If no destructor action specified: do nothing */
01784   }
01785 }
01786 
01787 /*
01788 ** Pop the parser's stack once.
01789 **
01790 ** If there is a destructor routine associated with the token which
01791 ** is popped from the stack, then call it.
01792 **
01793 ** Return the major token number for the symbol popped.
01794 */
01795 static int yy_pop_parser_stack(yyParser *pParser){
01796   YYCODETYPE yymajor;
01797   if( pParser->yystack.empty() ) return 0;
01798   yyStackEntry *yytos = &pParser->yystack.back();
01799 
01800   DEBUGLINE(QUERYPARSER, "Popping " << ParseTokenName(yytos->major));
01801   yymajor = (YYCODETYPE)yytos->major;
01802   yy_destructor( yymajor, &yytos->minor);
01803   pParser->yystack.pop_back();
01804   return yymajor;
01805 }
01806 
01807 /* 
01808 ** Deallocate and destroy a parser.  Destructors are all called for
01809 ** all stack elements before shutting the parser down.
01810 **
01811 ** Inputs:
01812 ** A pointer to the parser.  This should be a pointer
01813 ** obtained from ParseAlloc.
01814 */
01815 static void ParseFree(
01816   yyParser *pParser           /* The parser to be deleted */
01817 ){
01818   if( pParser==0 ) return;
01819   while( !pParser->yystack.empty() ) yy_pop_parser_stack(pParser);
01820   delete pParser;
01821 }
01822 
01823 /*
01824 ** Find the appropriate action for a parser given the terminal
01825 ** look-ahead token iLookAhead.
01826 **
01827 ** If the look-ahead token is YYNOCODE, then check to see if the action is
01828 ** independent of the look-ahead.  If it is, return the action, otherwise
01829 ** return YY_NO_ACTION.
01830 */
01831 static int yy_find_shift_action(
01832   yyParser *pParser,        /* The parser */
01833   int iLookAhead            /* The look-ahead token */
01834 ){
01835   int i;
01836   /* if( pParser->yystack.empty() ) return YY_NO_ACTION;  */
01837   int stateno = pParser->yystack.back().stateno;
01838  
01839   i = yy_shift_ofst[stateno];
01840   if( i==YY_SHIFT_USE_DFLT ){
01841     return yy_default[stateno];
01842   }
01843   if( iLookAhead==YYNOCODE ){
01844     return YY_NO_ACTION;
01845   }
01846   i += iLookAhead;
01847   if( i<0 || i>=YY_SZ_ACTTAB || yy_lookahead[i]!=iLookAhead ){
01848 #ifdef YYFALLBACK
01849     int iFallback;            /* Fallback token */
01850     if( iLookAhead<sizeof(yyFallback)/sizeof(yyFallback[0])
01851            && (iFallback = yyFallback[iLookAhead])!=0 ){
01852       DEBUGLINE(QUERYPARSER,
01853                 "FALLBACK " << ParseTokenName(iLookAhead) << " => " <<
01854                 ParseTokenName(iFallback));
01855       return yy_find_shift_action(pParser, iFallback);
01856     }
01857 #endif
01858     return yy_default[stateno];
01859   }else{
01860     return yy_action[i];
01861   }
01862 }
01863 
01864 /*
01865 ** Find the appropriate action for a parser given the non-terminal
01866 ** look-ahead token iLookAhead.
01867 **
01868 ** If the look-ahead token is YYNOCODE, then check to see if the action is
01869 ** independent of the look-ahead.  If it is, return the action, otherwise
01870 ** return YY_NO_ACTION.
01871 */
01872 static int yy_find_reduce_action(
01873   yyParser *pParser,        /* The parser */
01874   int iLookAhead            /* The look-ahead token */
01875 ){
01876   int i;
01877   int stateno = pParser->yystack.back().stateno;
01878  
01879   i = yy_reduce_ofst[stateno];
01880   if( i==YY_REDUCE_USE_DFLT ){
01881     return yy_default[stateno];
01882   }
01883   if( iLookAhead==YYNOCODE ){
01884     return YY_NO_ACTION;
01885   }
01886   i += iLookAhead;
01887   if( i<0 || i>=YY_SZ_ACTTAB || yy_lookahead[i]!=iLookAhead ){
01888     return yy_default[stateno];
01889   }else{
01890     return yy_action[i];
01891   }
01892 }
01893 
01894 /*
01895 ** Perform a shift action.
01896 */
01897 static void yy_shift(
01898   yyParser *yypParser,          /* The parser to be shifted */
01899   int yyNewState,               /* The new state to shift in */
01900   int yyMajor,                  /* The major token to shift in */
01901   YYMINORTYPE *yypMinor         /* Pointer to the minor token to shift in */
01902 ){
01903   /* Here code is inserted which will execute if the parser
01904   ** stack every overflows.  We use std::vector<> for our stack
01905   ** so we'll never need this code.
01906   */
01907 #if 0
01908 #endif
01909 #ifdef XAPIAN_DEBUG_VERBOSE
01910   unsigned i;
01911   DEBUGLINE(QUERYPARSER, "Shift " << yyNewState);
01912   string stack("Stack:");
01913   for (i = 0; i < yypParser->yystack.size(); i++) {
01914     stack += ' ';
01915     stack += ParseTokenName(yypParser->yystack[i].major);
01916   }
01917   DEBUGLINE(QUERYPARSER, stack);
01918 #endif
01919   yypParser->yystack.push_back(yyStackEntry(yyNewState, yyMajor, *yypMinor));
01920 }
01921 
01922 /* The following table contains information about every rule that
01923 ** is used during the reduce.
01924 */
01925 static const struct {
01926   YYCODETYPE lhs;         /* Symbol on the left-hand side of the rule */
01927   unsigned char nrhs;     /* Number of right-hand side symbols in the rule */
01928 } yyRuleInfo[] = {
01929   { 23, 1 },
01930   { 23, 0 },
01931   { 24, 1 },
01932   { 24, 3 },
01933   { 24, 3 },
01934   { 24, 4 },
01935   { 24, 3 },
01936   { 24, 3 },
01937   { 26, 1 },
01938   { 26, 0 },
01939   { 25, 1 },
01940   { 25, 1 },
01941   { 27, 2 },
01942   { 27, 3 },
01943   { 27, 2 },
01944   { 27, 2 },
01945   { 27, 2 },
01946   { 27, 3 },
01947   { 27, 2 },
01948   { 27, 3 },
01949   { 27, 2 },
01950   { 27, 3 },
01951   { 27, 1 },
01952   { 27, 2 },
01953   { 27, 2 },
01954   { 27, 3 },
01955   { 29, 1 },
01956   { 29, 1 },
01957   { 30, 1 },
01958   { 30, 1 },
01959   { 28, 1 },
01960   { 28, 1 },
01961   { 31, 1 },
01962   { 31, 1 },
01963   { 31, 3 },
01964   { 31, 1 },
01965   { 31, 1 },
01966   { 31, 1 },
01967   { 31, 1 },
01968   { 31, 3 },
01969   { 31, 2 },
01970   { 32, 1 },
01971   { 32, 2 },
01972   { 33, 2 },
01973   { 33, 2 },
01974   { 34, 2 },
01975   { 34, 2 },
01976   { 35, 3 },
01977   { 35, 3 },
01978   { 36, 3 },
01979   { 36, 3 },
01980 };
01981 
01982 static void yy_accept(yyParser*);  /* Forward Declaration */
01983 
01984 /*
01985 ** Perform a reduce action and the shift that must immediately
01986 ** follow the reduce.
01987 */
01988 static void yy_reduce(
01989   yyParser *yypParser,         /* The parser */
01990   int yyruleno                 /* Number of the rule by which to reduce */
01991 ){
01992   int yygoto;                     /* The next state */
01993   int yyact;                      /* The next action */
01994   YYMINORTYPE yygotominor;        /* The LHS of the rule reduced */
01995   yyStackEntry *yymsp;            /* The top of the parser's stack */
01996   int yysize;                     /* Amount to pop the stack */
01997   ParseARG_FETCH;
01998   yymsp = &yypParser->yystack.back();
01999 #ifdef XAPIAN_DEBUG_VERBOSE
02000   DEBUGLINE(QUERYPARSER, "Reduce [" << ParseRuleName(yyruleno) << "].");
02001 #endif
02002 
02003   switch( yyruleno ){
02004   /* Beginning here are the reduction cases.  A typical example
02005   ** follows:
02006   **   case 0:
02007   **  #line <lineno> <grammarfile>
02008   **     { ... }           // User supplied code
02009   **  #line <lineno> <thisfile>
02010   **     break;
02011   */
02012       case 0:
02013 #line 1372 "/data/home/olly/tmp/xapian-svn-snapshot/tags/1.0.10/xapian/xapian-core/queryparser/queryparser.lemony"
02014 {
02015     // Save the parsed query in the State structure so we can return it.
02016     if (yymsp[0].minor.yy13) {
02017         state->query = *yymsp[0].minor.yy13;
02018         delete yymsp[0].minor.yy13;
02019     } else {
02020         state->query = Query();
02021     }
02022 }
02023 #line 2025 "queryparser/queryparser_internal.cc"
02024         break;
02025       case 1:
02026 #line 1382 "/data/home/olly/tmp/xapian-svn-snapshot/tags/1.0.10/xapian/xapian-core/queryparser/queryparser.lemony"
02027 {
02028     // Handle a query string with no terms in.
02029     state->query = Query();
02030 }
02031 #line 2033 "queryparser/queryparser_internal.cc"
02032         break;
02033       case 2:
02034       case 8:
02035 #line 1393 "/data/home/olly/tmp/xapian-svn-snapshot/tags/1.0.10/xapian/xapian-core/queryparser/queryparser.lemony"
02036 { yygotominor.yy13 = yymsp[0].minor.yy13; }
02037 #line 2039 "queryparser/queryparser_internal.cc"
02038         break;
02039       case 3:
02040 #line 1396 "/data/home/olly/tmp/xapian-svn-snapshot/tags/1.0.10/xapian/xapian-core/queryparser/queryparser.lemony"
02041 { BOOL_OP_TO_QUERY(yygotominor.yy13, yymsp[-2].minor.yy13, Query::OP_AND, yymsp[0].minor.yy13, "AND");   yy_destructor(4,&yymsp[-1].minor);
02042 }
02043 #line 2045 "queryparser/queryparser_internal.cc"
02044         break;
02045       case 4:
02046 #line 1398 "/data/home/olly/tmp/xapian-svn-snapshot/tags/1.0.10/xapian/xapian-core/queryparser/queryparser.lemony"
02047 {
02048     // 'NOT foo' -> '<alldocuments> NOT foo'
02049     if (!yymsp[-2].minor.yy13 && (state->flags & QueryParser::FLAG_PURE_NOT)) {
02050         yymsp[-2].minor.yy13 = new Query("", 1, 0);
02051     }
02052     BOOL_OP_TO_QUERY(yygotominor.yy13, yymsp[-2].minor.yy13, Query::OP_AND_NOT, yymsp[0].minor.yy13, "NOT");
02053   yy_destructor(5,&yymsp[-1].minor);
02054 }
02055 #line 2057 "queryparser/queryparser_internal.cc"
02056         break;
02057       case 5:
02058 #line 1407 "/data/home/olly/tmp/xapian-svn-snapshot/tags/1.0.10/xapian/xapian-core/queryparser/queryparser.lemony"
02059 { BOOL_OP_TO_QUERY(yygotominor.yy13, yymsp[-3].minor.yy13, Query::OP_AND_NOT, yymsp[0].minor.yy13, "AND NOT");   yy_destructor(4,&yymsp[-2].minor);
02060   yy_destructor(5,&yymsp[-1].minor);
02061 }
02062 #line 2064 "queryparser/queryparser_internal.cc"
02063         break;
02064       case 6:
02065 #line 1410 "/data/home/olly/tmp/xapian-svn-snapshot/tags/1.0.10/xapian/xapian-core/queryparser/queryparser.lemony"
02066 { BOOL_OP_TO_QUERY(yygotominor.yy13, yymsp[-2].minor.yy13, Query::OP_OR, yymsp[0].minor.yy13, "OR");   yy_destructor(2,&yymsp[-1].minor);
02067 }
02068 #line 2070 "queryparser/queryparser_internal.cc"
02069         break;
02070       case 7:
02071 #line 1413 "/data/home/olly/tmp/xapian-svn-snapshot/tags/1.0.10/xapian/xapian-core/queryparser/queryparser.lemony"
02072 { BOOL_OP_TO_QUERY(yygotominor.yy13, yymsp[-2].minor.yy13, Query::OP_XOR, yymsp[0].minor.yy13, "XOR");   yy_destructor(3,&yymsp[-1].minor);
02073 }
02074 #line 2076 "queryparser/queryparser_internal.cc"
02075         break;
02076       case 9:
02077 #line 1422 "/data/home/olly/tmp/xapian-svn-snapshot/tags/1.0.10/xapian/xapian-core/queryparser/queryparser.lemony"
02078 {
02079     // Set the argument to NULL, which enables the bool_arg-using rules in
02080     // expr above to report uses of AND, OR, etc which don't have two
02081     // arguments.
02082     yygotominor.yy13 = NULL;
02083 }
02084 #line 2086 "queryparser/queryparser_internal.cc"
02085         break;
02086       case 10:
02087 #line 1434 "/data/home/olly/tmp/xapian-svn-snapshot/tags/1.0.10/xapian/xapian-core/queryparser/queryparser.lemony"
02088 {
02089     yygotominor.yy13 = yymsp[0].minor.yy9->query;
02090     yymsp[0].minor.yy9->query = NULL;
02091     // Handle any "+ terms".
02092     if (yymsp[0].minor.yy9->love) {
02093         if (yymsp[0].minor.yy9->love->empty()) {
02094             // +<nothing>.
02095             delete yygotominor.yy13;
02096             yygotominor.yy13 = yymsp[0].minor.yy9->love;
02097         } else if (yygotominor.yy13) {
02098             swap(yygotominor.yy13, yymsp[0].minor.yy9->love);
02099             add_to_query(yygotominor.yy13, Query::OP_AND_MAYBE, yymsp[0].minor.yy9->love);
02100         } else {
02101             yygotominor.yy13 = yymsp[0].minor.yy9->love;
02102         }
02103         yymsp[0].minor.yy9->love = NULL;
02104     }
02105     // Handle any boolean filters.
02106     if (!yymsp[0].minor.yy9->filter.empty()) {
02107         if (yygotominor.yy13) {
02108             add_to_query(yygotominor.yy13, Query::OP_FILTER, yymsp[0].minor.yy9->merge_filters());
02109         } else {
02110             // Make the query a boolean one.
02111             yygotominor.yy13 = new Query(Query::OP_SCALE_WEIGHT, yymsp[0].minor.yy9->merge_filters(), 0.0);
02112         }
02113     }
02114     // Handle any "- terms".
02115     if (yymsp[0].minor.yy9->hate && !yymsp[0].minor.yy9->hate->empty()) {
02116         if (!yygotominor.yy13) {
02117             // Can't just hate!
02118             yy_parse_failed(yypParser);
02119             return;
02120         }
02121         *yygotominor.yy13 = Query(Query::OP_AND_NOT, *yygotominor.yy13, *yymsp[0].minor.yy9->hate);
02122     }
02123     // FIXME what if yygotominor.yy13 && yygotominor.yy13->empty() (all terms are stopwords)?
02124     delete yymsp[0].minor.yy9;
02125 }
02126 #line 2128 "queryparser/queryparser_internal.cc"
02127         break;
02128       case 11:
02129       case 29:
02130       case 31:
02131 #line 1473 "/data/home/olly/tmp/xapian-svn-snapshot/tags/1.0.10/xapian/xapian-core/queryparser/queryparser.lemony"
02132 {
02133     yygotominor.yy13 = yymsp[0].minor.yy13;
02134 }
02135 #line 2137 "queryparser/queryparser_internal.cc"
02136         break;
02137       case 12:
02138 #line 1485 "/data/home/olly/tmp/xapian-svn-snapshot/tags/1.0.10/xapian/xapian-core/queryparser/queryparser.lemony"
02139 {
02140     Query range;
02141     Xapian::valueno valno = state->value_range(range, yymsp[-1].minor.yy0, yymsp[0].minor.yy0);
02142     if (valno == BAD_VALUENO) {
02143         yy_parse_failed(yypParser);
02144         return;
02145     }
02146     yygotominor.yy9 = new ProbQuery;
02147     yygotominor.yy9->filter[filter_group_id(valno)] = range;
02148 }
02149 #line 2151 "queryparser/queryparser_internal.cc"
02150         break;
02151       case 13:
02152 #line 1496 "/data/home/olly/tmp/xapian-svn-snapshot/tags/1.0.10/xapian/xapian-core/queryparser/queryparser.lemony"
02153 {
02154     Query range;
02155     Xapian::valueno valno = state->value_range(range, yymsp[-1].minor.yy0, yymsp[0].minor.yy0);
02156     if (valno == BAD_VALUENO) {
02157         yy_parse_failed(yypParser);
02158         return;
02159     }
02160     yygotominor.yy9 = yymsp[-2].minor.yy9;
02161     Query & q = yygotominor.yy9->filter[filter_group_id(valno)];
02162     q = Query(Query::OP_OR, q, range);
02163 }
02164 #line 2166 "queryparser/queryparser_internal.cc"
02165         break;
02166       case 14:
02167 #line 1508 "/data/home/olly/tmp/xapian-svn-snapshot/tags/1.0.10/xapian/xapian-core/queryparser/queryparser.lemony"
02168 {
02169     yygotominor.yy9 = new ProbQuery;
02170     yygotominor.yy9->query = yymsp[-1].minor.yy13;
02171     if (yymsp[0].minor.yy13) add_to_query(yygotominor.yy9->query, state->default_op(), yymsp[0].minor.yy13);
02172 }
02173 #line 2175 "queryparser/queryparser_internal.cc"
02174         break;
02175       case 15:
02176 #line 1514 "/data/home/olly/tmp/xapian-svn-snapshot/tags/1.0.10/xapian/xapian-core/queryparser/queryparser.lemony"
02177 {
02178     yygotominor.yy9 = yymsp[-1].minor.yy9;
02179     // If yymsp[0].minor.yy13 is a stopword, there's nothing to do here.
02180     if (yymsp[0].minor.yy13) add_to_query(yygotominor.yy9->query, state->default_op(), yymsp[0].minor.yy13);
02181 }
02182 #line 2184 "queryparser/queryparser_internal.cc"
02183         break;
02184       case 16:
02185 #line 1520 "/data/home/olly/tmp/xapian-svn-snapshot/tags/1.0.10/xapian/xapian-core/queryparser/queryparser.lemony"
02186 {
02187     yygotominor.yy9 = new ProbQuery;
02188     if (state->default_op() == Query::OP_AND) {
02189         yygotominor.yy9->query = yymsp[0].minor.yy13;
02190     } else {
02191         yygotominor.yy9->love = yymsp[0].minor.yy13;
02192     }
02193   yy_destructor(8,&yymsp[-1].minor);
02194 }
02195 #line 2197 "queryparser/queryparser_internal.cc"
02196         break;
02197       case 17:
02198 #line 1529 "/data/home/olly/tmp/xapian-svn-snapshot/tags/1.0.10/xapian/xapian-core/queryparser/queryparser.lemony"
02199 {
02200     yygotominor.yy9 = yymsp[-2].minor.yy9;
02201     if (state->default_op() == Query::OP_AND) {
02202         /* The default op is AND, so we just put loved terms into the query
02203          * (in this case the only effect of love is to ignore the stopword
02204          * list). */
02205         add_to_query(yygotominor.yy9->query, Query::OP_AND, yymsp[0].minor.yy13);
02206     } else {
02207         add_to_query(yygotominor.yy9->love, Query::OP_AND, yymsp[0].minor.yy13);
02208     }
02209   yy_destructor(8,&yymsp[-1].minor);
02210 }
02211 #line 2213 "queryparser/queryparser_internal.cc"
02212         break;
02213       case 18:
02214 #line 1541 "/data/home/olly/tmp/xapian-svn-snapshot/tags/1.0.10/xapian/xapian-core/queryparser/queryparser.lemony"
02215 {
02216     yygotominor.yy9 = new ProbQuery;
02217     yygotominor.yy9->hate = yymsp[0].minor.yy13;
02218   yy_destructor(9,&yymsp[-1].minor);
02219 }
02220 #line 2222 "queryparser/queryparser_internal.cc"
02221         break;
02222       case 19:
02223 #line 1546 "/data/home/olly/tmp/xapian-svn-snapshot/tags/1.0.10/xapian/xapian-core/queryparser/queryparser.lemony"
02224 {
02225     yygotominor.yy9 = yymsp[-2].minor.yy9;
02226     add_to_query(yygotominor.yy9->hate, Query::OP_OR, yymsp[0].minor.yy13);
02227   yy_destructor(9,&yymsp[-1].minor);
02228 }
02229 #line 2231 "queryparser/queryparser_internal.cc"
02230         break;
02231       case 20:
02232 #line 1551 "/data/home/olly/tmp/xapian-svn-snapshot/tags/1.0.10/xapian/xapian-core/queryparser/queryparser.lemony"
02233 {
02234     yygotominor.yy9 = new ProbQuery;
02235     yygotominor.yy9->hate = new Query(yymsp[0].minor.yy0->get_query());
02236     delete yymsp[0].minor.yy0;
02237   yy_destructor(9,&yymsp[-1].minor);
02238 }
02239 #line 2241 "queryparser/queryparser_internal.cc"
02240         break;
02241       case 21:
02242 #line 1557 "/data/home/olly/tmp/xapian-svn-snapshot/tags/1.0.10/xapian/xapian-core/queryparser/queryparser.lemony"
02243 {
02244     yygotominor.yy9 = yymsp[-2].minor.yy9;
02245     add_to_query(yygotominor.yy9->hate, Query::OP_OR, yymsp[0].minor.yy0->get_query());
02246     delete yymsp[0].minor.yy0;
02247   yy_destructor(9,&yymsp[-1].minor);
02248 }
02249 #line 2251 "queryparser/queryparser_internal.cc"
02250         break;
02251       case 22:
02252 #line 1563 "/data/home/olly/tmp/xapian-svn-snapshot/tags/1.0.10/xapian/xapian-core/queryparser/queryparser.lemony"
02253 {
02254     yygotominor.yy9 = new ProbQuery;
02255     yygotominor.yy9->filter[yymsp[0].minor.yy0->get_filter_group_id()] = yymsp[0].minor.yy0->get_query();
02256     delete yymsp[0].minor.yy0;
02257 }
02258 #line 2260 "queryparser/queryparser_internal.cc"
02259         break;
02260       case 23:
02261 #line 1569 "/data/home/olly/tmp/xapian-svn-snapshot/tags/1.0.10/xapian/xapian-core/queryparser/queryparser.lemony"
02262 {
02263     yygotominor.yy9 = yymsp[-1].minor.yy9;
02264     // We OR filters with the same prefix...
02265     Query & q = yygotominor.yy9->filter[yymsp[0].minor.yy0->get_filter_group_id()];
02266     q = Query(Query::OP_OR, q, yymsp[0].minor.yy0->get_query());
02267     delete yymsp[0].minor.yy0;
02268 }
02269 #line 2271 "queryparser/queryparser_internal.cc"
02270         break;
02271       case 24:
02272 #line 1577 "/data/home/olly/tmp/xapian-svn-snapshot/tags/1.0.10/xapian/xapian-core/queryparser/queryparser.lemony"
02273 {
02274     // LOVE BOOLEAN_FILTER(yymsp[0].minor.yy0) is just the same as BOOLEAN_FILTER
02275     yygotominor.yy9 = new ProbQuery;
02276     yygotominor.yy9->filter[yymsp[0].minor.yy0->get_filter_group_id()] = yymsp[0].minor.yy0->get_query();
02277     delete yymsp[0].minor.yy0;
02278   yy_destructor(8,&yymsp[-1].minor);
02279 }
02280 #line 2282 "queryparser/queryparser_internal.cc"
02281         break;
02282       case 25:
02283 #line 1584 "/data/home/olly/tmp/xapian-svn-snapshot/tags/1.0.10/xapian/xapian-core/queryparser/queryparser.lemony"
02284 {
02285     // LOVE BOOLEAN_FILTER(yymsp[0].minor.yy0) is just the same as BOOLEAN_FILTER
02286     yygotominor.yy9 = yymsp[-2].minor.yy9;
02287     // We OR filters with the same prefix...
02288     Query & q = yygotominor.yy9->filter[yymsp[0].minor.yy0->get_filter_group_id()];
02289     q = Query(Query::OP_OR, q, yymsp[0].minor.yy0->get_query());
02290     delete yymsp[0].minor.yy0;
02291   yy_destructor(8,&yymsp[-1].minor);
02292 }
02293 #line 2295 "queryparser/queryparser_internal.cc"
02294         break;
02295       case 26:
02296 #line 1599 "/data/home/olly/tmp/xapian-svn-snapshot/tags/1.0.10/xapian/xapian-core/queryparser/queryparser.lemony"
02297 { yygotominor.yy9 = yymsp[0].minor.yy9; }
02298 #line 2300 "queryparser/queryparser_internal.cc"
02299         break;
02300       case 27:
02301 #line 1601 "/data/home/olly/tmp/xapian-svn-snapshot/tags/1.0.10/xapian/xapian-core/queryparser/queryparser.lemony"
02302 {
02303     yygotominor.yy9 = new ProbQuery;
02304     yygotominor.yy9->query = yymsp[0].minor.yy13;
02305 }
02306 #line 2308 "queryparser/queryparser_internal.cc"
02307         break;
02308       case 28:
02309 #line 1615 "/data/home/olly/tmp/xapian-svn-snapshot/tags/1.0.10/xapian/xapian-core/queryparser/queryparser.lemony"
02310 {
02311     if (state->is_stopword(yymsp[0].minor.yy0)) {
02312         yygotominor.yy13 = NULL;
02313         state->add_to_stoplist(yymsp[0].minor.yy0);
02314     } else {
02315         yygotominor.yy13 = new Query(yymsp[0].minor.yy0->get_query_with_auto_synonyms());
02316     }
02317     delete yymsp[0].minor.yy0;
02318 }
02319 #line 2321 "queryparser/queryparser_internal.cc"
02320         break;
02321       case 30:
02322 #line 1634 "/data/home/olly/tmp/xapian-svn-snapshot/tags/1.0.10/xapian/xapian-core/queryparser/queryparser.lemony"
02323 {
02324     yygotominor.yy13 = new Query(yymsp[0].minor.yy0->get_query_with_auto_synonyms());
02325     delete yymsp[0].minor.yy0;
02326 }
02327 #line 2329 "queryparser/queryparser_internal.cc"
02328         break;
02329       case 32:
02330 #line 1651 "/data/home/olly/tmp/xapian-svn-snapshot/tags/1.0.10/xapian/xapian-core/queryparser/queryparser.lemony"
02331 { yygotominor.yy13 = yymsp[0].minor.yy0->as_wildcarded_query(state); }
02332 #line 2334 "queryparser/queryparser_internal.cc"
02333         break;
02334       case 33:
02335 #line 1654 "/data/home/olly/tmp/xapian-svn-snapshot/tags/1.0.10/xapian/xapian-core/queryparser/queryparser.lemony"
02336 { yygotominor.yy13 = yymsp[0].minor.yy0->as_partial_query(state); }
02337 #line 2339 "queryparser/queryparser_internal.cc"
02338         break;
02339       case 34:
02340 #line 1657 "/data/home/olly/tmp/xapian-svn-snapshot/tags/1.0.10/xapian/xapian-core/queryparser/queryparser.lemony"
02341 { yygotominor.yy13 = yymsp[-1].minor.yy1->as_phrase_query();   yy_destructor(19,&yymsp[-2].minor);
02342   yy_destructor(19,&yymsp[0].minor);
02343 }
02344 #line 2346 "queryparser/queryparser_internal.cc"
02345         break;
02346       case 35:
02347 #line 1660 "/data/home/olly/tmp/xapian-svn-snapshot/tags/1.0.10/xapian/xapian-core/queryparser/queryparser.lemony"
02348 { yygotominor.yy13 = yymsp[0].minor.yy1->as_phrase_query(); }
02349 #line 2351 "queryparser/queryparser_internal.cc"
02350         break;
02351       case 36:
02352 #line 1662 "/data/home/olly/tmp/xapian-svn-snapshot/tags/1.0.10/xapian/xapian-core/queryparser/queryparser.lemony"
02353 {
02354     yygotominor.yy13 = yymsp[0].minor.yy60->as_group(state);
02355 }
02356 #line 2358 "queryparser/queryparser_internal.cc"
02357         break;
02358       case 37:
02359 #line 1667 "/data/home/olly/tmp/xapian-svn-snapshot/tags/1.0.10/xapian/xapian-core/queryparser/queryparser.lemony"
02360 { yygotominor.yy13 = yymsp[0].minor.yy1->as_near_query(); }
02361 #line 2363 "queryparser/queryparser_internal.cc"
02362         break;
02363       case 38:
02364 #line 1670 "/data/home/olly/tmp/xapian-svn-snapshot/tags/1.0.10/xapian/xapian-core/queryparser/queryparser.lemony"
02365 { yygotominor.yy13 = yymsp[0].minor.yy1->as_adj_query(); }
02366 #line 2368 "queryparser/queryparser_internal.cc"
02367         break;
02368       case 39:
02369 #line 1673 "/data/home/olly/tmp/xapian-svn-snapshot/tags/1.0.10/xapian/xapian-core/queryparser/queryparser.lemony"
02370 { yygotominor.yy13 = yymsp[-1].minor.yy13;   yy_destructor(20,&yymsp[-2].minor);
02371   yy_destructor(21,&yymsp[0].minor);
02372 }
02373 #line 2375 "queryparser/queryparser_internal.cc"
02374         break;
02375       case 40:
02376 #line 1675 "/data/home/olly/tmp/xapian-svn-snapshot/tags/1.0.10/xapian/xapian-core/queryparser/queryparser.lemony"
02377 {
02378     yygotominor.yy13 = new Query(yymsp[0].minor.yy0->get_query_with_synonyms());
02379     delete yymsp[0].minor.yy0;
02380   yy_destructor(10,&yymsp[-1].minor);
02381 }
02382 #line 2384 "queryparser/queryparser_internal.cc"
02383         break;
02384       case 41:
02385 #line 1686 "/data/home/olly/tmp/xapian-svn-snapshot/tags/1.0.10/xapian/xapian-core/queryparser/queryparser.lemony"
02386 {
02387     yygotominor.yy1 = new TermList;
02388     yygotominor.yy1->add_positional_term(yymsp[0].minor.yy0);
02389 }
02390 #line 2392 "queryparser/queryparser_internal.cc"
02391         break;
02392       case 42:
02393       case 44:
02394 #line 1691 "/data/home/olly/tmp/xapian-svn-snapshot/tags/1.0.10/xapian/xapian-core/queryparser/queryparser.lemony"
02395 {
02396     yygotominor.yy1 = yymsp[-1].minor.yy1;
02397     yygotominor.yy1->add_positional_term(yymsp[0].minor.yy0);
02398 }
02399 #line 2401 "queryparser/queryparser_internal.cc"
02400         break;
02401       case 43:
02402 #line 1703 "/data/home/olly/tmp/xapian-svn-snapshot/tags/1.0.10/xapian/xapian-core/queryparser/queryparser.lemony"
02403 {
02404     yygotominor.yy1 = new TermList;
02405     yygotominor.yy1->add_positional_term(yymsp[-1].minor.yy0);
02406     yygotominor.yy1->add_positional_term(yymsp[0].minor.yy0);
02407 }
02408 #line 2410 "queryparser/queryparser_internal.cc"
02409         break;
02410       case 45:
02411 #line 1720 "/data/home/olly/tmp/xapian-svn-snapshot/tags/1.0.10/xapian/xapian-core/queryparser/queryparser.lemony"
02412 {
02413     yygotominor.yy60 = new TermGroup;
02414     yygotominor.yy60->add_term(yymsp[-1].minor.yy0);
02415     yygotominor.yy60->add_term(yymsp[0].minor.yy0);
02416 }
02417 #line 2419 "queryparser/queryparser_internal.cc"
02418         break;
02419       case 46:
02420 #line 1726 "/data/home/olly/tmp/xapian-svn-snapshot/tags/1.0.10/xapian/xapian-core/queryparser/queryparser.lemony"
02421 {
02422     yygotominor.yy60 = yymsp[-1].minor.yy60;
02423     yygotominor.yy60->add_term(yymsp[0].minor.yy0);
02424 }
02425 #line 2427 "queryparser/queryparser_internal.cc"
02426         break;
02427       case 47:
02428       case 49:
02429 #line 1737 "/data/home/olly/tmp/xapian-svn-snapshot/tags/1.0.10/xapian/xapian-core/queryparser/queryparser.lemony"
02430 {
02431     yygotominor.yy1 = new TermList;
02432     yygotominor.yy1->add_positional_term(yymsp[-2].minor.yy0);
02433     yygotominor.yy1->add_positional_term(yymsp[0].minor.yy0);
02434     if (yymsp[-1].minor.yy0) {
02435         yygotominor.yy1->adjust_window(yymsp[-1].minor.yy0->get_termpos());
02436         delete yymsp[-1].minor.yy0;
02437     }
02438 }
02439 #line 2441 "queryparser/queryparser_internal.cc"
02440         break;
02441       case 48:
02442       case 50:
02443 #line 1747 "/data/home/olly/tmp/xapian-svn-snapshot/tags/1.0.10/xapian/xapian-core/queryparser/queryparser.lemony"
02444 {
02445     yygotominor.yy1 = yymsp[-2].minor.yy1;
02446     yygotominor.yy1->add_positional_term(yymsp[0].minor.yy0);
02447     if (yymsp[-1].minor.yy0) {
02448         yygotominor.yy1->adjust_window(yymsp[-1].minor.yy0->get_termpos());
02449         delete yymsp[-1].minor.yy0;
02450     }
02451 }
02452 #line 2454 "queryparser/queryparser_internal.cc"
02453         break;
02454   }
02455   yygoto = yyRuleInfo[yyruleno].lhs;
02456   yysize = yyRuleInfo[yyruleno].nrhs;
02457   yypParser->yystack.resize(yypParser->yystack.size() - yysize);
02458   yyact = yy_find_reduce_action(yypParser,yygoto);
02459   if( yyact < YYNSTATE ){
02460     yy_shift(yypParser,yyact,yygoto,&yygotominor);
02461   }else if( yyact == YYNSTATE + YYNRULE + 1 ){
02462     yy_accept(yypParser);
02463   }
02464 }
02465 
02466 /*
02467 ** The following code executes when the parse fails
02468 */
02469 static void yy_parse_failed(
02470   yyParser *yypParser           /* The parser */
02471 ){
02472   ParseARG_FETCH;
02473   DEBUGLINE(QUERYPARSER, "Fail!");
02474   while( !yypParser->yystack.empty() ) yy_pop_parser_stack(yypParser);
02475   /* Here code is inserted which will be executed whenever the
02476   ** parser fails */
02477 #line 1323 "/data/home/olly/tmp/xapian-svn-snapshot/tags/1.0.10/xapian/xapian-core/queryparser/queryparser.lemony"
02478 
02479     // If we've not already set an error message, set a default one.
02480     if (!state->error) state->error = "parse error";
02481 #line 2484 "queryparser/queryparser_internal.cc"
02482   ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */
02483 }
02484 
02485 /*
02486 ** The following code executes when a syntax error first occurs.
02487 */
02488 static void yy_syntax_error(
02489   yyParser *yypParser,           /* The parser */
02490   int yymajor,                   /* The major type of the error token */
02491   YYMINORTYPE yyminor            /* The minor type of the error token */
02492 ){
02493   ParseARG_FETCH;
02494   (void)yymajor;
02495   (void)yyminor;
02496 #define TOKEN (yyminor.yy0)
02497   ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */
02498 }
02499 
02500 /*
02501 ** The following is executed when the parser accepts
02502 */
02503 static void yy_accept(
02504   yyParser *yypParser           /* The parser */
02505 ){
02506   ParseARG_FETCH;
02507   DEBUGLINE(QUERYPARSER, "Accept!");
02508   while( !yypParser->yystack.empty() ) yy_pop_parser_stack(yypParser);
02509   /* Here code is inserted which will be executed whenever the
02510   ** parser accepts */
02511   ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */
02512 }
02513 
02514 /* The main parser program.
02515 ** The first argument is a pointer to a structure obtained from
02516 ** "ParseAlloc" which describes the current state of the parser.
02517 ** The second argument is the major token number.  The third is
02518 ** the minor token.  The fourth optional argument is whatever the
02519 ** user wants (and specified in the grammar) and is available for
02520 ** use by the action routines.
02521 **
02522 ** Inputs:
02523 ** <ul>
02524 ** <li> A pointer to the parser (an opaque structure.)
02525 ** <li> The major token number.
02526 ** <li> The minor token number.
02527 ** <li> An option argument of a grammar-specified type.
02528 ** </ul>
02529 **
02530 ** Outputs:
02531 ** None.
02532 */
02533 static void Parse(
02534   yyParser *yypParser,         /* The parser */
02535   int yymajor,                 /* The major token code number */
02536   ParseTOKENTYPE yyminor       /* The value for the token */
02537   ParseARG_PDECL               /* Optional %extra_argument parameter */
02538 ){
02539   YYMINORTYPE yyminorunion;
02540   int yyact;            /* The parser action. */
02541   int yyendofinput;     /* True if we are at the end of input */
02542   int yyerrorhit = 0;   /* True if yymajor has invoked an error */
02543 
02544   /* (re)initialize the parser, if necessary */
02545   if( yypParser->yystack.empty() ){
02546     if( yymajor==0 ) return;
02547     yypParser->yystack.push_back(yyStackEntry());
02548     yypParser->yyerrcnt = -1;
02549   }
02550   yyminorunion.yy0 = yyminor;
02551   yyendofinput = (yymajor==0);
02552   ParseARG_STORE;
02553 
02554   DEBUGLINE(QUERYPARSER, "Input " << ParseTokenName(yymajor) << " " <<
02555             (yyminor ? yyminor->name : "<<null>>"));
02556 
02557   do{
02558     yyact = yy_find_shift_action(yypParser,yymajor);
02559     if( yyact<YYNSTATE ){
02560       yy_shift(yypParser,yyact,yymajor,&yyminorunion);
02561       yypParser->yyerrcnt--;
02562       if( yyendofinput && !yypParser->yystack.empty() ){
02563         yymajor = 0;
02564       }else{
02565         yymajor = YYNOCODE;
02566       }
02567     }else if( yyact < YYNSTATE + YYNRULE ){
02568       yy_reduce(yypParser,yyact-YYNSTATE);
02569     }else if( yyact == YY_ERROR_ACTION ){
02570       int yymx;
02571       DEBUGLINE(QUERYPARSER, "Syntax Error!");
02572 #ifdef YYERRORSYMBOL
02573       /* A syntax error has occurred.
02574       ** The response to an error depends upon whether or not the
02575       ** grammar defines an error token "ERROR".  
02576       **
02577       ** This is what we do if the grammar does define ERROR:
02578       **
02579       **  * Call the %syntax_error function.
02580       **
02581       **  * Begin popping the stack until we enter a state where
02582       **    it is legal to shift the error symbol, then shift
02583       **    the error symbol.
02584       **
02585       **  * Set the error count to three.
02586       **
02587       **  * Begin accepting and shifting new tokens.  No new error
02588       **    processing will occur until three tokens have been
02589       **    shifted successfully.
02590       **
02591       */
02592       if( yypParser->yyerrcnt<0 ){
02593         yy_syntax_error(yypParser,yymajor,yyminorunion);
02594       }
02595       yymx = yypParser->yystack.back().major;
02596       if( yymx==YYERRORSYMBOL || yyerrorhit ){
02597         DEBUGLINE(QUERYPARSER, "Discard input token " << ParseTokenName(yymajor));
02598         yy_destructor((YYCODETYPE)yymajor,&yyminorunion);
02599         yymajor = YYNOCODE;
02600       }else{
02601          while(
02602           !yypParser->yystack.empty() &&
02603           yymx != YYERRORSYMBOL &&
02604           (yyact = yy_find_shift_action(yypParser,YYERRORSYMBOL)) >= YYNSTATE
02605         ){
02606           yy_pop_parser_stack(yypParser);
02607         }
02608         if( yypParser->yystack.empty() || yymajor==0 ){
02609           yy_destructor((YYCODETYPE)yymajor,&yyminorunion);
02610           yy_parse_failed(yypParser);
02611           yymajor = YYNOCODE;
02612         }else if( yymx!=YYERRORSYMBOL ){
02613           YYMINORTYPE u2;
02614           u2.YYERRSYMDT = 0;
02615           yy_shift(yypParser,yyact,YYERRORSYMBOL,&u2);
02616         }
02617       }
02618       yypParser->yyerrcnt = 3;
02619       yyerrorhit = 1;
02620 #else  /* YYERRORSYMBOL is not defined */
02621       /* This is what we do if the grammar does not define ERROR:
02622       **
02623       **  * Report an error message, and throw away the input token.
02624       **
02625       **  * If the input token is $, then fail the parse.
02626       **
02627       ** As before, subsequent error messages are suppressed until
02628       ** three input tokens have been successfully shifted.
02629       */
02630       if( yypParser->yyerrcnt<=0 ){
02631         yy_syntax_error(yypParser,yymajor,yyminorunion);
02632       }
02633       yypParser->yyerrcnt = 3;
02634       yy_destructor((YYCODETYPE)yymajor,&yyminorunion);
02635       if( yyendofinput ){
02636         yy_parse_failed(yypParser);
02637       }
02638       yymajor = YYNOCODE;
02639 #endif
02640     }else{
02641       yy_accept(yypParser);
02642       yymajor = YYNOCODE;
02643     }
02644   }while( yymajor!=YYNOCODE && !yypParser->yystack.empty() );
02645   return;
02646 }
02647 
02648 // Select C++ syntax highlighting in vim editor: vim: syntax=cpp

Documentation for Xapian (version 1.0.10).
Generated on 24 Dec 2008 by Doxygen 1.5.2.