]> git.cworth.org Git - notmuch/blob - lib/parse-sexp.cc
2555605853f3b0803ca8b463ae4288da85244aae
[notmuch] / lib / parse-sexp.cc
1 #include "database-private.h"
2
3 #if HAVE_SFSEXP
4 #include "sexp.h"
5
6
7 /* _sexp is used for file scope symbols to avoid clashing with
8  * definitions from sexp.h */
9
10 typedef enum {
11     SEXP_FLAG_NONE      = 0,
12     SEXP_FLAG_FIELD     = 1 << 0,
13 } _sexp_flag_t;
14
15 typedef struct  {
16     const char *name;
17     Xapian::Query::op xapian_op;
18     Xapian::Query initial;
19     _sexp_flag_t flags;
20 } _sexp_prefix_t;
21
22 static _sexp_prefix_t prefixes[] =
23 {
24     { "and",            Xapian::Query::OP_AND,          Xapian::Query::MatchAll,
25       SEXP_FLAG_NONE },
26     { "not",            Xapian::Query::OP_AND_NOT,      Xapian::Query::MatchAll,
27       SEXP_FLAG_NONE },
28     { "or",             Xapian::Query::OP_OR,           Xapian::Query::MatchNothing,
29       SEXP_FLAG_NONE },
30     { "subject",        Xapian::Query::OP_AND,          Xapian::Query::MatchAll,
31       SEXP_FLAG_FIELD },
32     { }
33 };
34
35 static notmuch_status_t _sexp_to_xapian_query (notmuch_database_t *notmuch,
36                                                const _sexp_prefix_t *parent,
37                                                const sexp_t *sx,
38                                                Xapian::Query &output);
39
40 static notmuch_status_t
41 _sexp_combine_query (notmuch_database_t *notmuch,
42                      const _sexp_prefix_t *parent,
43                      Xapian::Query::op operation,
44                      Xapian::Query left,
45                      const sexp_t *sx,
46                      Xapian::Query &output)
47 {
48     Xapian::Query subquery;
49
50     notmuch_status_t status;
51
52     /* if we run out elements, return accumulator */
53
54     if (! sx) {
55         output = left;
56         return NOTMUCH_STATUS_SUCCESS;
57     }
58
59     status = _sexp_to_xapian_query (notmuch, parent, sx, subquery);
60     if (status)
61         return status;
62
63     return _sexp_combine_query (notmuch,
64                                 parent,
65                                 operation,
66                                 Xapian::Query (operation, left, subquery),
67                                 sx->next, output);
68 }
69
70 /* Here we expect the s-expression to be a proper list, with first
71  * element defining and operation, or as a special case the empty
72  * list */
73
74 static notmuch_status_t
75 _sexp_to_xapian_query (notmuch_database_t *notmuch, const _sexp_prefix_t *parent, const sexp_t *sx,
76                        Xapian::Query &output)
77 {
78
79     if (sx->ty == SEXP_VALUE) {
80         std::string term = Xapian::Unicode::tolower (sx->val);
81         Xapian::Stem stem = *(notmuch->stemmer);
82         std::string term_prefix = parent ? _find_prefix (parent->name) : "";
83         if (sx->aty == SEXP_BASIC)
84             term = "Z" + term_prefix + stem (term);
85         else
86             term = term_prefix + term;
87
88         output = Xapian::Query (term);
89         return NOTMUCH_STATUS_SUCCESS;
90     }
91
92     /* Empty list */
93     if (! sx->list) {
94         output = Xapian::Query::MatchAll;
95         return NOTMUCH_STATUS_SUCCESS;
96     }
97
98     if (sx->list->ty == SEXP_LIST) {
99         _notmuch_database_log (notmuch, "unexpected list in field/operation position\n",
100                                sx->list->val);
101         return NOTMUCH_STATUS_BAD_QUERY_SYNTAX;
102     }
103
104     for (_sexp_prefix_t *prefix = prefixes; prefix && prefix->name; prefix++) {
105         if (strcmp (prefix->name, sx->list->val) == 0) {
106             if (prefix->flags & SEXP_FLAG_FIELD) {
107                 if (parent) {
108                     _notmuch_database_log (notmuch, "nested field: '%s' inside '%s'\n",
109                                            prefix->name, parent->name);
110                     return NOTMUCH_STATUS_BAD_QUERY_SYNTAX;
111                 }
112                 parent = prefix;
113             }
114
115             return _sexp_combine_query (notmuch, parent, prefix->xapian_op, prefix->initial,
116                                         sx->list->next, output);
117         }
118     }
119
120     _notmuch_database_log (notmuch, "unknown prefix '%s'\n", sx->list->val);
121
122     return NOTMUCH_STATUS_BAD_QUERY_SYNTAX;
123 }
124
125 notmuch_status_t
126 _notmuch_sexp_string_to_xapian_query (notmuch_database_t *notmuch, const char *querystr,
127                                       Xapian::Query &output)
128 {
129     const sexp_t *sx = NULL;
130     char *buf = talloc_strdup (notmuch, querystr);
131
132     sx = parse_sexp (buf, strlen (querystr));
133     if (! sx) {
134         _notmuch_database_log (notmuch, "invalid s-expression: '%s'\n", querystr);
135         return NOTMUCH_STATUS_BAD_QUERY_SYNTAX;
136     }
137
138     return _sexp_to_xapian_query (notmuch, NULL, sx, output);
139 }
140 #endif