/* qparser-test - Display the lex, parse, and query tree for a query * * Copyright © 2011 Austin Clements * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see http://www.gnu.org/licenses/ . * * Authors: Austin Clements */ /* If command-line arguments are given, they are used as the query * string. Otherwise, qparser-test enters "echo mode", in which it * accepts queries from stdin. In echo mode, lines beginning with '[' * are ignored and lines consisting of whitespace or comments are * echoed back to stdout. All other lines are treated as queries and * are echoed back, followed by the results of parsing the query. * This allows the output of qparser-test to be fed back in as input. * * For each, qparser-test displays the lex list of that query, the * parse tree of that query, and the generated query tree. Finally, * if the generated query tree differs from that generated by Xapian's * query parser, it also displays what Xapian's query parser * generated. */ #include "../lib/notmuch-private.h" #include "../lib/database-private.h" extern "C" { /* notmuch-client.h also defines INTERNAL_ERROR */ #undef INTERNAL_ERROR #include "../notmuch-client.h" } static notmuch_database_t *notmuch; static _notmuch_qparser_t *qparser; static Xapian::QueryParser xqparser; static char * query_desc (void *ctx, Xapian::Query q) { char *desc = talloc_strdup (ctx, q.get_description ().c_str ()); desc += strlen ("Xapian::Query("); desc[strlen(desc) - 1] = 0; return desc; } static void test_one (void *ctx, const char *query_str) { void *local = talloc_new (ctx); Xapian::Query q; _notmuch_token_t *toks, *root; char *error, *qparser_desc, *xqparser_desc; toks = _notmuch_qparser_lex (local, qparser, query_str); printf("[lex] %s\n", _notmuch_token_show_list (local, toks)); root = _notmuch_qparser_parse (local, qparser, query_str); printf("[parse] %s\n", _notmuch_token_show_tree (local, root)); root = _notmuch_qparser_transform (qparser, root); q = _notmuch_qparser_generate (local, qparser, root, &error); if (error) printf("[gen] error %s\n", error); else { qparser_desc = query_desc (local, q); printf("[gen] %s\n", qparser_desc); } try { unsigned int flags = (Xapian::QueryParser::FLAG_BOOLEAN | Xapian::QueryParser::FLAG_PHRASE | Xapian::QueryParser::FLAG_LOVEHATE | Xapian::QueryParser::FLAG_BOOLEAN_ANY_CASE | Xapian::QueryParser::FLAG_WILDCARD | Xapian::QueryParser::FLAG_PURE_NOT); q = xqparser.parse_query (query_str, flags); xqparser_desc = query_desc (local, q); if (strcmp (qparser_desc, xqparser_desc) != 0) printf("[xapian] %s\n", xqparser_desc); } catch (const Xapian::QueryParserError & e) { printf("[xapian] error %s\n", e.get_msg ().c_str ()); } talloc_free (local); } static _notmuch_qparser_t * create_qparser (void *ctx) { _notmuch_qparser_t *qparser = _notmuch_qparser_create (ctx, notmuch); _notmuch_qparser_add_db_prefix (qparser, "prob", "P", FALSE, FALSE); _notmuch_qparser_add_db_prefix (qparser, "lit", "L", TRUE, FALSE); _notmuch_qparser_add_db_prefix (qparser, "tag", "K", TRUE, TRUE); return qparser; } static Xapian::QueryParser create_xapian_qparser (void) { Xapian::QueryParser xq; if (notmuch) xq.set_database (*notmuch->xapian_db); xq.set_default_op (Xapian::Query::OP_AND); xq.add_prefix ("prob", "P"); xq.add_boolean_prefix ("lit", "L"); xq.add_boolean_prefix ("tag", "K"); return xq; } int main (int argc, char **argv) { void *ctx; notmuch_config_t *config; ctx = talloc_new (NULL); if (argc > 1 && strcmp(argv[1], "-d") == 0) { argc--; argv++; /* Open the database */ config = notmuch_config_open (ctx, NULL, NULL); if (config == NULL) return 1; notmuch = notmuch_database_open (notmuch_config_get_database_path (config), NOTMUCH_DATABASE_MODE_READ_ONLY); if (notmuch == NULL) return 1; } else { notmuch = NULL; } qparser = create_qparser (ctx); xqparser = create_xapian_qparser (); if (argc > 1) { char *query_str; query_str = query_string_from_args (ctx, argc - 1, argv + 1); test_one (ctx, query_str); } else { /* Echo mode */ char line[512]; while (fgets (line, sizeof (line), stdin)) { if (line[0] == '\n' || line[0] == '#') { /* Comment or whitespace. Echo it */ printf("%s", line); } else if (line[0] == '[') { /* Ignore line */ } else { /* Query */ if (line[strlen (line) - 1] == '\n') line[strlen (line) - 1] = 0; printf("%s\n", line); test_one (ctx, line); } } } if (notmuch) notmuch_database_close (notmuch); return 0; }