1 Return-Path: <amdragon@mit.edu>
\r
2 X-Original-To: notmuch@notmuchmail.org
\r
3 Delivered-To: notmuch@notmuchmail.org
\r
4 Received: from localhost (localhost [127.0.0.1])
\r
5 by olra.theworths.org (Postfix) with ESMTP id 002E9431FD0
\r
6 for <notmuch@notmuchmail.org>; Thu, 20 Jan 2011 22:37:27 -0800 (PST)
\r
7 X-Virus-Scanned: Debian amavisd-new at olra.theworths.org
\r
11 X-Spam-Status: No, score=0 tagged_above=-999 required=5
\r
12 tests=[RCVD_IN_DNSWL_NONE=-0.0001] autolearn=disabled
\r
13 Received: from olra.theworths.org ([127.0.0.1])
\r
14 by localhost (olra.theworths.org [127.0.0.1]) (amavisd-new, port 10024)
\r
15 with ESMTP id jFYJbVpBDHqI for <notmuch@notmuchmail.org>;
\r
16 Thu, 20 Jan 2011 22:37:19 -0800 (PST)
\r
17 Received: from dmz-mailsec-scanner-8.mit.edu (DMZ-MAILSEC-SCANNER-8.MIT.EDU
\r
19 by olra.theworths.org (Postfix) with ESMTP id 5CEE7431FB6
\r
20 for <notmuch@notmuchmail.org>; Thu, 20 Jan 2011 22:37:18 -0800 (PST)
\r
21 X-AuditID: 12074425-b7c98ae000000a04-6c-4d39299c563f
\r
22 Received: from mailhub-auth-4.mit.edu ( [18.7.62.39])
\r
23 by dmz-mailsec-scanner-8.mit.edu (Symantec Brightmail Gateway) with
\r
24 SMTP id 9F.51.02564.C99293D4; Fri, 21 Jan 2011 01:37:17 -0500 (EST)
\r
25 Received: from outgoing.mit.edu (OUTGOING-AUTH.MIT.EDU [18.7.22.103])
\r
26 by mailhub-auth-4.mit.edu (8.13.8/8.9.2) with ESMTP id p0L6bGVc020511;
\r
27 Fri, 21 Jan 2011 01:37:16 -0500
\r
28 Received: from awakening.csail.mit.edu (awakening.csail.mit.edu [18.26.4.91])
\r
29 (authenticated bits=0)
\r
30 (User authenticated as amdragon@ATHENA.MIT.EDU)
\r
31 by outgoing.mit.edu (8.13.6/8.12.4) with ESMTP id p0L6bE4s004633
\r
32 (version=TLSv1/SSLv3 cipher=AES256-SHA bits=256 verify=NOT);
\r
33 Fri, 21 Jan 2011 01:37:15 -0500 (EST)
\r
34 Received: from amthrax by awakening.csail.mit.edu with local (Exim 4.72)
\r
35 (envelope-from <amdragon@mit.edu>)
\r
36 id 1PgAcU-0000yr-8j; Fri, 21 Jan 2011 01:37:14 -0500
\r
37 Date: Fri, 21 Jan 2011 01:37:14 -0500
\r
38 From: Austin Clements <amdragon@MIT.EDU>
\r
39 To: notmuch@notmuchmail.org
\r
40 Subject: [PATCH 1.5/8] Query parser testing framework and basic tests.
\r
41 Message-ID: <20110121063714.GI13226@mit.edu>
\r
42 References: <1295165458-9573-1-git-send-email-amdragon@mit.edu>
\r
43 <1295165458-9573-2-git-send-email-amdragon@mit.edu>
\r
45 Content-Type: text/plain; charset=iso-8859-1
\r
46 Content-Disposition: inline
\r
47 Content-Transfer-Encoding: 8bit
\r
48 In-Reply-To: <1295165458-9573-2-git-send-email-amdragon@mit.edu>
\r
49 User-Agent: Mutt/1.5.20 (2009-06-14)
\r
50 X-Brightmail-Tracker: AAAAAhcymoEXM1Bb
\r
51 Cc: amdragon@mit.edu
\r
52 X-BeenThere: notmuch@notmuchmail.org
\r
53 X-Mailman-Version: 2.1.13
\r
55 List-Id: "Use and development of the notmuch mail system."
\r
56 <notmuch.notmuchmail.org>
\r
57 List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,
\r
58 <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>
\r
59 List-Archive: <http://notmuchmail.org/pipermail/notmuch>
\r
60 List-Post: <mailto:notmuch@notmuchmail.org>
\r
61 List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>
\r
62 List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,
\r
63 <mailto:notmuch-request@notmuchmail.org?subject=subscribe>
\r
64 X-List-Received-Date: Fri, 21 Jan 2011 06:37:29 -0000
\r
66 The query parser test is implemented as a separate binary that calls
\r
67 directly in to the lexer, parser, and generator to make it easy to
\r
68 isolate test failures.
\r
71 Sorry for the patch ordering. This is intended to be applied after
\r
72 patch 1/8 in this series,
\r
73 id:1295165458-9573-2-git-send-email-amdragon@mit.edu
\r
75 test/Makefile.local | 7 +-
\r
77 test/notmuch-test | 2 +-
\r
78 test/qparser | 32 ++++++
\r
79 test/qparser-test.cc | 153 +++++++++++++++++++++++++++
\r
80 test/qparser.expected-output/operators | 145 +++++++++++++++++++++++++
\r
81 test/qparser.expected-output/prefixes | 33 ++++++
\r
82 test/qparser.expected-output/probs | 46 ++++++++
\r
83 test/qparser.expected-output/quoted-phrases | 29 +++++
\r
84 test/qparser.expected-output/terms | 136 ++++++++++++++++++++++++
\r
85 10 files changed, 581 insertions(+), 4 deletions(-)
\r
86 create mode 100755 test/qparser
\r
87 create mode 100644 test/qparser-test.cc
\r
88 create mode 100644 test/qparser.expected-output/operators
\r
89 create mode 100644 test/qparser.expected-output/prefixes
\r
90 create mode 100644 test/qparser.expected-output/probs
\r
91 create mode 100644 test/qparser.expected-output/quoted-phrases
\r
92 create mode 100644 test/qparser.expected-output/terms
\r
94 diff --git a/test/Makefile.local b/test/Makefile.local
\r
95 index 7b602bc..302482b 100644
\r
96 --- a/test/Makefile.local
\r
97 +++ b/test/Makefile.local
\r
98 @@ -5,10 +5,13 @@ dir := test
\r
99 $(dir)/smtp-dummy: $(dir)/smtp-dummy.c
\r
100 $(call quiet,CC) $^ -o $@
\r
102 +$(dir)/qparser-test: $(dir)/qparser-test.o notmuch-config.o query-string.o lib/libnotmuch.a
\r
103 + $(call quiet,CXX $(CXXFLAGS)) $^ $(FINAL_LIBNOTMUCH_LDFLAGS) -o $@
\r
106 -test: all $(dir)/smtp-dummy
\r
107 +test: all $(dir)/smtp-dummy $(dir)/qparser-test
\r
108 @${dir}/notmuch-test $(OPTIONS)
\r
112 -CLEAN := $(CLEAN) $(dir)/smtp-dummy
\r
113 +CLEAN := $(CLEAN) $(dir)/smtp-dummy $(dir)/qparser-test.o $(dir)/qparser-test
\r
114 diff --git a/test/basic b/test/basic
\r
115 index b4410f2..3191bcc 100755
\r
118 @@ -52,7 +52,7 @@ test_expect_code 2 'failure to clean up causes the test to fail' '
\r
119 # Ensure that all tests are being run
\r
120 test_begin_subtest 'Ensure that all available tests will be run by notmuch-test'
\r
121 tests_in_suite=$(grep TESTS= ../notmuch-test | sed -e "s/TESTS=\"\(.*\)\"/\1/" | tr " " "\n" | sort)
\r
122 -available=$(ls -1 ../ | grep -v -E "^(aggregate-results.sh|Makefile|Makefile.local|notmuch-test|README|test-lib.sh|test-results|tmp.*|valgrind|corpus*|emacs.expected-output|smtp-dummy|smtp-dummy.c|test-verbose|test.expected-output)" | sort)
\r
123 +available=$(ls -1 ../ | grep -v -E "^(aggregate-results.sh|Makefile|Makefile.local|notmuch-test|README|test-lib.sh|test-results|tmp.*|valgrind|corpus*|emacs.expected-output|smtp-dummy|smtp-dummy.c|test-verbose|test.expected-output|qparser-test.*|qparser-test|qparser.expected-output)" | sort)
\r
124 test_expect_equal "$tests_in_suite" "$available"
\r
126 EXPECTED=../test.expected-output
\r
127 diff --git a/test/notmuch-test b/test/notmuch-test
\r
128 index 4889e49..1e331b3 100755
\r
129 --- a/test/notmuch-test
\r
130 +++ b/test/notmuch-test
\r
131 @@ -16,7 +16,7 @@ fi
\r
135 -TESTS="basic new search search-output json thread-naming raw reply dump-restore uuencode thread-order author-order from-guessing long-id encoding emacs maildir-sync"
\r
136 +TESTS="basic new qparser search search-output json thread-naming raw reply dump-restore uuencode thread-order author-order from-guessing long-id encoding emacs maildir-sync"
\r
138 # Clean up any results from a previous run
\r
139 rm -r test-results >/dev/null 2>/dev/null
\r
140 diff --git a/test/qparser b/test/qparser
\r
141 new file mode 100755
\r
142 index 0000000..0e7b022
\r
147 +test_description="query parser"
\r
150 +EXPECTED=../qparser.expected-output
\r
152 +test_begin_subtest "Quoted phrases"
\r
153 +output=$(../qparser-test < $EXPECTED/quoted-phrases)
\r
154 +expected=$(cat $EXPECTED/quoted-phrases)
\r
155 +test_expect_equal "$output" "$expected"
\r
157 +test_begin_subtest "Prefixes"
\r
158 +output=$(../qparser-test < $EXPECTED/prefixes)
\r
159 +expected=$(cat $EXPECTED/prefixes)
\r
160 +test_expect_equal "$output" "$expected"
\r
162 +test_begin_subtest "Terms"
\r
163 +output=$(../qparser-test < $EXPECTED/terms)
\r
164 +expected=$(cat $EXPECTED/terms)
\r
165 +test_expect_equal "$output" "$expected"
\r
167 +test_begin_subtest "Operators"
\r
168 +output=$(../qparser-test < $EXPECTED/operators)
\r
169 +expected=$(cat $EXPECTED/operators)
\r
170 +test_expect_equal "$output" "$expected"
\r
172 +test_begin_subtest "Probs"
\r
173 +output=$(../qparser-test < $EXPECTED/probs)
\r
174 +expected=$(cat $EXPECTED/probs)
\r
175 +test_expect_equal "$output" "$expected"
\r
178 diff --git a/test/qparser-test.cc b/test/qparser-test.cc
\r
179 new file mode 100644
\r
180 index 0000000..01d6bae
\r
182 +++ b/test/qparser-test.cc
\r
184 +/* qparser-test - Display the lex, parse, and query tree for a query
\r
186 + * Copyright © 2011 Austin Clements
\r
188 + * This program is free software: you can redistribute it and/or modify
\r
189 + * it under the terms of the GNU General Public License as published by
\r
190 + * the Free Software Foundation, either version 3 of the License, or
\r
191 + * (at your option) any later version.
\r
193 + * This program is distributed in the hope that it will be useful,
\r
194 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
195 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
196 + * GNU General Public License for more details.
\r
198 + * You should have received a copy of the GNU General Public License
\r
199 + * along with this program. If not, see http://www.gnu.org/licenses/ .
\r
201 + * Authors: Austin Clements <amdragon@mit.edu>
\r
204 +/* If command-line arguments are given, they are used as the query
\r
205 + * string. Otherwise, qparser-test enters "echo mode", in which it
\r
206 + * accepts queries from stdin. In echo mode, lines beginning with '['
\r
207 + * are ignored and lines consisting of whitespace or comments are
\r
208 + * echoed back to stdout. All other lines are treated as queries and
\r
209 + * are echoed back, followed by the results of parsing the query.
\r
210 + * This allows the output of qparser-test to be fed back in as input.
\r
212 + * For each, qparser-test displays the lex list of that query, the
\r
213 + * parse tree of that query, and the generated query tree. Finally,
\r
214 + * if the generated query tree differs from that generated by Xapian's
\r
215 + * query parser, it also displays what Xapian's query parser
\r
219 +#include "../lib/notmuch-private.h"
\r
220 +#include "../lib/database-private.h"
\r
223 +/* notmuch-client.h also defines INTERNAL_ERROR */
\r
224 +#undef INTERNAL_ERROR
\r
225 +#include "../notmuch-client.h"
\r
228 +static _notmuch_qparser_t *qparser;
\r
229 +static Xapian::QueryParser xqparser;
\r
232 +query_desc (void *ctx, Xapian::Query q)
\r
234 + char *desc = talloc_strdup (ctx, q.get_description ().c_str ());
\r
235 + desc += strlen ("Xapian::Query(");
\r
236 + desc[strlen(desc) - 1] = 0;
\r
241 +test_one (void *ctx, const char *query_str)
\r
243 + void *local = talloc_new (ctx);
\r
245 + _notmuch_token_t *toks, *root;
\r
246 + char *error, *qparser_desc, *xqparser_desc;
\r
248 + toks = _notmuch_qparser_lex (local, qparser, query_str);
\r
249 + printf("[lex] %s\n", _notmuch_token_show_list (local, toks));
\r
251 + root = _notmuch_qparser_parse (local, qparser, query_str);
\r
252 + printf("[parse] %s\n", _notmuch_token_show_tree (local, root));
\r
254 + root = _notmuch_qparser_transform (qparser, root);
\r
255 + q = _notmuch_qparser_generate (local, qparser, root, &error);
\r
257 + printf("[gen] error %s\n", error);
\r
259 + qparser_desc = query_desc (local, q);
\r
260 + printf("[gen] %s\n", qparser_desc);
\r
264 + unsigned int flags = (Xapian::QueryParser::FLAG_BOOLEAN |
\r
265 + Xapian::QueryParser::FLAG_PHRASE |
\r
266 + Xapian::QueryParser::FLAG_LOVEHATE |
\r
267 + Xapian::QueryParser::FLAG_BOOLEAN_ANY_CASE |
\r
268 + Xapian::QueryParser::FLAG_WILDCARD |
\r
269 + Xapian::QueryParser::FLAG_PURE_NOT);
\r
270 + q = xqparser.parse_query (query_str, flags);
\r
271 + xqparser_desc = query_desc (local, q);
\r
272 + if (strcmp (qparser_desc, xqparser_desc) != 0)
\r
273 + printf("[xapian] %s\n", xqparser_desc);
\r
274 + } catch (const Xapian::QueryParserError & e) {
\r
275 + printf("[xapian] error %s\n", e.get_msg ().c_str ());
\r
278 + talloc_free (local);
\r
281 +static _notmuch_qparser_t *
\r
282 +create_qparser (void *ctx)
\r
284 + _notmuch_qparser_t *qparser = _notmuch_qparser_create (ctx, NULL);
\r
285 + _notmuch_qparser_add_db_prefix (qparser, "prob", "P", FALSE);
\r
286 + _notmuch_qparser_add_db_prefix (qparser, "lit", "L", TRUE);
\r
287 + _notmuch_qparser_add_db_prefix (qparser, "tag", "K", TRUE);
\r
291 +static Xapian::QueryParser
\r
292 +create_xapian_qparser (void)
\r
294 + Xapian::QueryParser xq;
\r
295 + xq.set_default_op (Xapian::Query::OP_AND);
\r
296 + xq.add_prefix ("prob", "P");
\r
297 + xq.add_boolean_prefix ("lit", "L");
\r
298 + xq.add_boolean_prefix ("tag", "K");
\r
303 +main (int argc, char **argv)
\r
307 + ctx = talloc_new (NULL);
\r
309 + qparser = create_qparser (ctx);
\r
310 + xqparser = create_xapian_qparser ();
\r
314 + query_str = query_string_from_args (ctx, argc - 1, argv + 1);
\r
315 + test_one (ctx, query_str);
\r
319 + while (fgets (line, sizeof (line), stdin)) {
\r
320 + if (line[0] == '\n' || line[0] == '#') {
\r
321 + /* Comment or whitespace. Echo it */
\r
322 + printf("%s", line);
\r
323 + } else if (line[0] == '[') {
\r
324 + /* Ignore line */
\r
327 + if (line[strlen (line) - 1] == '\n')
\r
328 + line[strlen (line) - 1] = 0;
\r
329 + printf("%s\n", line);
\r
330 + test_one (ctx, line);
\r
337 diff --git a/test/qparser.expected-output/operators b/test/qparser.expected-output/operators
\r
338 new file mode 100644
\r
339 index 0000000..788f007
\r
341 +++ b/test/qparser.expected-output/operators
\r
343 +# Boolean operators
\r
347 +[parse] (AND "x" "y")
\r
348 +[gen] (x:(pos=1) AND y:(pos=2))
\r
352 +[parse] (OR "x" "y")
\r
353 +[gen] (x:(pos=1) OR y:(pos=2))
\r
357 +[parse] (XOR "x" "y")
\r
358 +[gen] (x:(pos=1) XOR y:(pos=2))
\r
360 +x and y or x and w
\r
361 +[lex] "x" AND "y" OR "x" AND "w"
\r
362 +[parse] (OR (AND "x" "y") (AND "x" "w"))
\r
363 +[gen] ((x:(pos=1) AND y:(pos=2)) OR (x:(pos=3) AND w:(pos=4)))
\r
366 +[lex] "x" AND HATE "y"
\r
367 +[parse] (AND "x" (NOT "y"))
\r
368 +[gen] (x:(pos=1) AND_NOT y:(pos=2))
\r
371 +[lex] "x" OR NOT "y"
\r
372 +[parse] (OR "x" (NOT "y"))
\r
373 +[gen] (x:(pos=1) OR (<alldocuments> AND_NOT y:(pos=2)))
\r
375 +# The following three are Xapian-incompatible because they're syntax errors.
\r
380 +[xapian] error Syntax: <expression> AND <expression>
\r
386 +[xapian] error Syntax: <expression> AND <expression>
\r
391 +[gen] <alldocuments>
\r
392 +[xapian] error Syntax: <expression> AND <expression>
\r
398 +[parse] (AND "x" (NOT "y"))
\r
399 +[gen] (x:(pos=1) AND_NOT y:(pos=2))
\r
402 +[lex] "x" NOT "y" OR "z"
\r
403 +[parse] (OR (AND "x" (NOT "y")) "z")
\r
404 +[gen] ((x:(pos=1) AND_NOT y:(pos=2)) OR z:(pos=3))
\r
407 +[lex] "x" NOT "y" AND "z"
\r
408 +[parse] (AND (AND "x" (NOT "y")) "z")
\r
409 +[gen] ((x:(pos=1) AND_NOT y:(pos=2)) AND z:(pos=3))
\r
413 +[parse] (NOT (NOT "x"))
\r
414 +[gen] (<alldocuments> AND_NOT (<alldocuments> AND_NOT x:(pos=1)))
\r
415 +[xapian] error Syntax: <expression> NOT <expression>
\r
417 +# Empty subexpressions
\r
418 +# These are all Xapian-incompatible because they're syntax errors.
\r
421 +[lex] "x" AND BRA KET
\r
424 +[xapian] error Syntax: <expression> AND <expression>
\r
427 +[lex] BRA KET AND "x"
\r
430 +[xapian] error Syntax: <expression> AND <expression>
\r
433 +# These are all Xapian-incompatible because they're syntax errors.
\r
438 +[gen] <alldocuments>
\r
439 +[xapian] error Syntax: <expression> AND <expression>
\r
444 +[gen] <alldocuments>
\r
449 +[parse] (AND "@" "x")
\r
451 +[xapian] error Syntax: <expression> AND <expression>
\r
455 +[parse] (AND "x" "@")
\r
457 +[xapian] error Syntax: <expression> AND <expression>
\r
460 +[lex] "@" AND NOT "x"
\r
461 +[parse] (AND "@" (NOT "x"))
\r
462 +[gen] (<alldocuments> AND_NOT x:(pos=1))
\r
463 +[xapian] error Syntax: <expression> AND NOT <expression>
\r
466 +[lex] "x" AND NOT "@"
\r
467 +[parse] (AND "x" (NOT "@"))
\r
469 +[xapian] error Syntax: <expression> AND NOT <expression>
\r
474 +[gen] <alldocuments>
\r
475 +[xapian] error Syntax: <expression> NOT <expression>
\r
479 +[parse] (OR "@" "x")
\r
481 +[xapian] error Syntax: <expression> OR <expression>
\r
485 +[parse] (OR "x" "@")
\r
487 +[xapian] error Syntax: <expression> OR <expression>
\r
488 diff --git a/test/qparser.expected-output/prefixes b/test/qparser.expected-output/prefixes
\r
489 new file mode 100644
\r
490 index 0000000..04c4f90
\r
492 +++ b/test/qparser.expected-output/prefixes
\r
494 +prob:x lit:y none:z
\r
495 +[lex] PREFIX/prob "x" PREFIX/lit "y" "none:z"
\r
496 +[parse] (AND (AND (PREFIX/prob "x") "none:z") (FILTER (PREFIX/lit 'y')))
\r
497 +[gen] ((Px:(pos=1) AND (none:(pos=2) PHRASE 2 z:(pos=3))) FILTER Ly)
\r
499 +prob:"x y" lit:"x y" none:"x y"
\r
500 +[lex] PREFIX/prob "x y" PREFIX/lit "x y" "none:" "x y"
\r
501 +[parse] (AND (AND (AND (PREFIX/prob "x y") "none:") "x y") (FILTER (PREFIX/lit 'x y')))
\r
502 +[gen] (((Px:(pos=1) PHRASE 2 Py:(pos=2)) AND none:(pos=3) AND (x:(pos=4) PHRASE 2 y:(pos=5))) FILTER Lx y)
\r
504 +# Incompatible; Xapian bails and re-parses everything with no flags
\r
505 +prob:(x y) lit:(x y) none:(x y)
\r
506 +[lex] PREFIX/prob BRA "x" "y" KET PREFIX/lit "(x" "y" KET "none:" BRA "x" "y" KET
\r
507 +[parse] (AND (AND (AND (AND (PREFIX/prob (AND "x" "y")) "y") "none:") (AND "x" "y")) (FILTER (PREFIX/lit '(x')))
\r
508 +[gen] ((Px:(pos=1) AND Py:(pos=2) AND y:(pos=3) AND none:(pos=4) AND x:(pos=5) AND y:(pos=6)) FILTER L(x)
\r
509 +[xapian] ((prob:(pos=1) AND x:(pos=2) AND y:(pos=3) AND y:(pos=4) AND none:(pos=5) AND x:(pos=6) AND y:(pos=7)) FILTER L(x)
\r
511 +# This is Xapian-compatible, but seems ridiculous
\r
513 +[lex] PREFIX/lit "(x" KET
\r
514 +[parse] (FILTER (PREFIX/lit '(x'))
\r
517 +# Test characters accepted after the prefix colon
\r
519 +[lex] PREFIX/lit "#"
\r
520 +[parse] (FILTER (PREFIX/lit '#'))
\r
526 +[gen] prob:(pos=1)
\r
527 diff --git a/test/qparser.expected-output/probs b/test/qparser.expected-output/probs
\r
528 new file mode 100644
\r
529 index 0000000..3c166f7
\r
531 +++ b/test/qparser.expected-output/probs
\r
534 +[lex] BRA "x" OR "y" KET AND "z"
\r
535 +[parse] (AND (OR "x" "y") "z")
\r
536 +[gen] ((x:(pos=1) OR y:(pos=2)) AND z:(pos=3))
\r
538 +# Incompatible; Xapian bails on the syntax error, we forge ahead.
\r
540 +[lex] BRA "x" OR "y" KET KET AND "z"
\r
541 +[parse] (AND (OR "x" "y") "z")
\r
542 +[gen] ((x:(pos=1) OR y:(pos=2)) AND z:(pos=3))
\r
543 +[xapian] (x:(pos=1) AND or:(pos=2) AND y:(pos=3) AND and:(pos=4) AND z:(pos=5))
\r
545 +# Empty subexpression after prefix
\r
546 +# Incompatible; Xapian treats as a syntax error.
\r
548 +[lex] PREFIX/prob BRA KET AND "x"
\r
551 +[xapian] (prob:(pos=1) AND and:(pos=2) AND x:(pos=3))
\r
553 +# Subqueries with same boolean prefix
\r
555 +[lex] PREFIX/lit "x" PREFIX/lit "y"
\r
556 +[parse] (FILTER (OR (PREFIX/lit 'x') (PREFIX/lit 'y')))
\r
557 +[gen] 0 * (Lx OR Ly)
\r
559 +# Combining prob components
\r
561 +[lex] "x" HATE "y" PREFIX/lit "z"
\r
562 +[parse] (AND (AND "x" (FILTER (PREFIX/lit 'z'))) (NOT "y"))
\r
563 +[gen] ((x:(pos=1) FILTER Lz) AND_NOT y:(pos=2))
\r
566 +[lex] "x" PREFIX/lit "z"
\r
567 +[parse] (AND "x" (FILTER (PREFIX/lit 'z')))
\r
568 +[gen] (x:(pos=1) FILTER Lz)
\r
571 +[lex] HATE "y" PREFIX/lit "z"
\r
572 +[parse] (AND (FILTER (PREFIX/lit 'z')) (NOT "y"))
\r
573 +[gen] (0 * Lz AND_NOT y:(pos=1))
\r
576 +[lex] "x" HATE "y"
\r
577 +[parse] (AND "x" (NOT "y"))
\r
578 +[gen] (x:(pos=1) AND_NOT y:(pos=2))
\r
579 diff --git a/test/qparser.expected-output/quoted-phrases b/test/qparser.expected-output/quoted-phrases
\r
580 new file mode 100644
\r
581 index 0000000..e223366
\r
583 +++ b/test/qparser.expected-output/quoted-phrases
\r
586 +[lex] "x" "y z" "w"
\r
587 +[parse] (AND (AND "x" "y z") "w")
\r
588 +[gen] (x:(pos=1) AND (y:(pos=2) PHRASE 2 z:(pos=3)) AND w:(pos=4))
\r
592 +[parse] (AND "x" "y z")
\r
593 +[gen] (x:(pos=1) AND (y:(pos=2) PHRASE 2 z:(pos=3)))
\r
597 +[parse] (AND (AND "x" "") "y")
\r
598 +[gen] (x:(pos=1) AND y:(pos=2))
\r
602 +[parse] (AND (AND "x" " ") "y")
\r
603 +[gen] (x:(pos=1) AND y:(pos=2))
\r
606 +[lex] PREFIX/lit " x y"
\r
607 +[parse] (FILTER (PREFIX/lit ' x y'))
\r
611 +[lex] PREFIX/lit "x"y"
\r
612 +[parse] (FILTER (PREFIX/lit 'x"y'))
\r
614 diff --git a/test/qparser.expected-output/terms b/test/qparser.expected-output/terms
\r
615 new file mode 100644
\r
616 index 0000000..9316c54
\r
618 +++ b/test/qparser.expected-output/terms
\r
624 +[parse] (AND (AND "x" "y") "z")
\r
625 +[gen] (x:(pos=1) AND y:(pos=2) AND z:(pos=3))
\r
628 +[lex] "x" "y z" "w"
\r
629 +[parse] (AND (AND "x" "y z") "w")
\r
630 +[gen] (x:(pos=1) AND (y:(pos=2) PHRASE 2 z:(pos=3)) AND w:(pos=4))
\r
633 +[lex] "x" BRA "y" "z" KET "w"
\r
634 +[parse] (AND (AND "x" (AND "y" "z")) "w")
\r
635 +[gen] (x:(pos=1) AND y:(pos=2) AND z:(pos=3) AND w:(pos=4))
\r
637 +# The first query below is Xapian-compatible, while the second one
\r
638 +# isn't. We use much simpler term lexing rules than Xapian.
\r
642 +[gen] (x:(pos=1) PHRASE 2 y:(pos=2))
\r
647 +[gen] (x:(pos=1) PHRASE 2 y:(pos=2))
\r
648 +[xapian] (x:(pos=1) AND y:(pos=2))
\r
650 +# Incompatible; our simpler term parsing sees ! as a term
\r
652 +[lex] "x" HATE "!" "y"
\r
653 +[parse] (AND (AND "x" "y") (NOT "!"))
\r
654 +[gen] (x:(pos=1) AND y:(pos=2))
\r
655 +[xapian] (x:(pos=1) AND_NOT y:(pos=2))
\r
674 +# Prefixed operators get demoted to terms
\r
676 +[lex] PREFIX/prob AND
\r
677 +[parse] (PREFIX/prob "AND")
\r
678 +[gen] Pand:(pos=1)
\r
680 +# The first query below is Xapian-compatible, but the second isn't
\r
681 +# because Xapian handles hate very differently from love.
\r
689 +[parse] (NOT "AND")
\r
690 +[gen] (<alldocuments> AND_NOT and:(pos=1))
\r
691 +[xapian] and:(pos=1)
\r
693 +# Incompatible; Xapian sees this as prob:"prob:x"
\r
695 +[lex] PREFIX/prob PREFIX/prob "x"
\r
696 +[parse] (PREFIX/prob "x")
\r
698 +[xapian] (Pprob:(pos=1) PHRASE 2 Px:(pos=2))
\r
700 +# The rest are Xapian-incompatible because they're all considered
\r
705 +[gen] <alldocuments>
\r
711 +[gen] <alldocuments>
\r
717 +[gen] <alldocuments>
\r
721 +[lex] BRA "x" KET KET OR "y"
\r
722 +[parse] (OR "x" "y")
\r
723 +[gen] (x:(pos=1) OR y:(pos=2))
\r
724 +[xapian] (x:(pos=1) AND or:(pos=2) AND y:(pos=3))
\r
726 +# This one's only Xapian-compatible by chance.
\r
728 +[lex] BRA "x" KET KET
\r
736 +[parse] (AND "c++" "x")
\r
737 +[gen] (c++:(pos=1) AND x:(pos=2))
\r
739 +# Incompatible; + is not a "phrase generator" in Xapian.
\r
743 +[gen] (c:(pos=1) PHRASE 2 x:(pos=2))
\r
744 +[xapian] (c:(pos=1) AND x:(pos=2))
\r
749 +[gen] (c:(pos=1) PHRASE 2 x:(pos=2))
\r
753 +[parse] (AND "w" "x y z")
\r
754 +[gen] (w:(pos=1) AND (x:(pos=2) PHRASE 3 y:(pos=3) PHRASE 3 z:(pos=4)))
\r