ZOOM: diagnostics for invalid ES usage, bug #3893.
[yaz-moved-to-github.git] / src / cql.y
1 /* This file is part of the YAZ toolkit.
2  * Copyright (C) 1995-2011 Index Data
3  * See the file LICENSE for details.
4  */ 
5 /* bison parser for CQL grammar. */
6 %{
7 /** 
8  * \file cql.c
9  * \brief Implements CQL parser.
10  *
11  * This is a YACC parser, but since it must be reentrant, Bison is required.
12  * The original source file is cql.y.
13  */
14 #if HAVE_CONFIG_H
15 #include <config.h>
16 #endif
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <ctype.h>
21 #include <yaz/xmalloc.h>
22 #include <yaz/nmem.h>
23 #include <yaz/cql.h>
24
25     /** Node in the LALR parse tree. */
26     typedef struct {
27         /** Inhereted attribute: relation */
28         struct cql_node *rel;
29         /** Synthesized attribute: CQL node */
30         struct cql_node *cql;
31         /** string buffer with token */
32         char *buf;
33         /** length of token */
34         size_t len;
35         /** size of buffer (len <= size) */
36         size_t size;
37     } token;        
38
39     struct cql_parser {
40         int (*getbyte)(void *client_data);
41         void (*ungetbyte)(int b, void *client_data);
42         void *client_data;
43         int last_error;
44         int last_pos;
45         struct cql_node *top;
46         NMEM nmem;
47     };
48
49 #define YYSTYPE token
50     
51 #define YYPARSE_PARAM parm
52 #define YYLEX_PARAM parm
53     
54     int yylex(YYSTYPE *lval, void *vp);
55     int yyerror(char *s);
56 %}
57
58 %pure_parser
59 %token DOTTERM TERM AND OR NOT PROX GE LE NE EXACT SORTBY
60
61 %%
62
63 top: { 
64     $$.rel = cql_node_mk_sc(((CQL_parser) parm)->nmem,
65                             "cql.serverChoice", "=", 0);
66     ((CQL_parser) parm)->top = 0;
67 } cqlQuery1 sortby {
68     cql_node_destroy($$.rel);
69     ((CQL_parser) parm)->top = $2.cql; 
70 }
71 ;
72
73 sortby: /* empty */
74 | SORTBY sortSpec;
75
76 sortSpec: sortSpec singleSpec
77 | singleSpec; 
78
79 singleSpec: index modifiers ;
80
81 cqlQuery1: cqlQuery
82 | cqlQuery error {
83     cql_node_destroy($1.cql);
84     $$.cql = 0;
85 }
86 ;
87
88 cqlQuery:
89   scopedClause
90  |
91   '>' searchTerm '=' searchTerm {
92     $$.rel = $0.rel;
93   } cqlQuery {
94     $$.cql = cql_apply_prefix(((CQL_parser) parm)->nmem,
95                               $6.cql, $2.buf, $4.buf);
96   }
97 | '>' searchTerm {
98       $$.rel = $0.rel;
99   } cqlQuery {
100     $$.cql = cql_apply_prefix(((CQL_parser) parm)->nmem, 
101                               $4.cql, 0, $2.buf);
102    }
103 ;
104
105 scopedClause: 
106   searchClause
107 |
108   scopedClause boolean modifiers { 
109       $$.rel = $0.rel;
110   } searchClause {
111       struct cql_node *cn = cql_node_mk_boolean(((CQL_parser) parm)->nmem,
112                                                 $2.buf);
113       
114       cn->u.boolean.modifiers = $3.cql;
115       cn->u.boolean.left = $1.cql;
116       cn->u.boolean.right = $5.cql;
117
118       $$.cql = cn;
119   }
120 ;
121
122 searchClause: 
123   '(' { 
124       $$.rel = $0.rel;
125       
126   } cqlQuery ')' {
127       $$.cql = $3.cql;
128   }
129 |
130 searchTerm extraTerms {
131       struct cql_node *st = cql_node_dup(((CQL_parser) parm)->nmem, $0.rel);
132       st->u.st.extra_terms = $2.cql;
133       st->u.st.term = nmem_strdup(((CQL_parser)parm)->nmem, $1.buf);
134       $$.cql = st;
135   }
136
137   index relation modifiers {
138       $$.rel = cql_node_mk_sc(((CQL_parser) parm)->nmem, $1.buf, $2.buf, 0);
139       $$.rel->u.st.modifiers = $3.cql;
140   } searchClause {
141       $$.cql = $5.cql;
142       cql_node_destroy($4.rel);
143   }
144 ;
145
146 extraTerms:
147 extraTerms TERM {
148     struct cql_node *st = cql_node_mk_sc(((CQL_parser) parm)->nmem, 
149                                          /* index */ 0, /* rel */ 0, $2.buf);
150     st->u.st.extra_terms = $1.cql;
151     $$.cql = st;
152 }
153
154 { $$.cql = 0; }
155 ;
156
157
158 /* unary NOT search TERM here .. */
159
160 boolean: 
161   AND | OR | NOT | PROX ;
162
163 modifiers: modifiers '/' searchTerm
164
165     struct cql_node *mod = cql_node_mk_sc(((CQL_parser)parm)->nmem,
166                                           $3.buf, 0, 0);
167
168     mod->u.st.modifiers = $1.cql;
169     $$.cql = mod;
170 }
171 |
172 modifiers '/' searchTerm mrelation searchTerm
173 {
174     struct cql_node *mod = cql_node_mk_sc(((CQL_parser)parm)->nmem,
175                                           $3.buf, $4.buf, $5.buf);
176
177     mod->u.st.modifiers = $1.cql;
178     $$.cql = mod;
179 }
180 |
181
182     $$.cql = 0;
183 }
184 ;
185
186 mrelation:
187   '=' 
188 | '>' 
189 | '<'
190 | GE
191 | LE
192 | NE
193 | EXACT
194 ;
195
196 relation: 
197   '=' 
198 | '>' 
199 | '<'
200 | GE
201 | LE
202 | NE
203 | EXACT
204 | DOTTERM
205 ;
206
207 index: 
208   searchTerm;
209
210 searchTerm:
211   TERM
212 | DOTTERM
213 | AND
214 | OR
215 | NOT
216 | PROX
217 | SORTBY
218 ;
219
220 %%
221
222 int yyerror(char *s)
223 {
224     return 0;
225 }
226
227 /**
228  * putb is a utility that puts one character to the string
229  * in current lexical token. This routine deallocates as
230  * necessary using NMEM.
231  */
232
233 static void putb(YYSTYPE *lval, CQL_parser cp, int c)
234 {
235     if (lval->len+1 >= lval->size)
236     {
237         char *nb = (char *)
238             nmem_malloc(cp->nmem, (lval->size = lval->len * 2 + 20));
239         memcpy (nb, lval->buf, lval->len);
240         lval->buf = nb;
241     }
242     if (c)
243         lval->buf[lval->len++] = c;
244     lval->buf[lval->len] = '\0';
245 }
246
247
248 /**
249  * yylex returns next token for Bison to be read. In this
250  * case one of the CQL terminals are returned.
251  */
252 int yylex(YYSTYPE *lval, void *vp)
253 {
254     CQL_parser cp = (CQL_parser) vp;
255     int c;
256     lval->cql = 0;
257     lval->rel = 0;
258     lval->len = 0;
259     lval->size = 10;
260     lval->buf = (char *) nmem_malloc(cp->nmem, lval->size);
261     lval->buf[0] = '\0';
262     do
263     {
264         c = cp->getbyte(cp->client_data);
265         if (c == 0)
266             return 0;
267         if (c == '\n')
268             return 0;
269     } while (isspace(c));
270     if (strchr("()=></", c))
271     {
272         int c1;
273         putb(lval, cp, c);
274         if (c == '=')
275         {
276             c1 = cp->getbyte(cp->client_data);
277             if (c1 == '=')
278             {
279                 putb(lval, cp, c1);
280                 return EXACT;
281             }
282             else
283                 cp->ungetbyte(c1, cp->client_data);
284         }
285         else if (c == '>')
286         {
287             c1 = cp->getbyte(cp->client_data);
288             if (c1 == '=')
289             {
290                 putb(lval, cp, c1);
291                 return GE;
292             }
293             else
294                 cp->ungetbyte(c1, cp->client_data);
295         }
296         else if (c == '<')
297         {
298             c1 = cp->getbyte(cp->client_data);
299             if (c1 == '=')
300             {
301                 putb(lval, cp, c1);
302                 return LE;
303             }
304             else if (c1 == '>')
305             {
306                 putb(lval, cp, c1);
307                 return NE;
308             }
309             else
310                 cp->ungetbyte(c1, cp->client_data);
311         }
312         return c;
313     }
314     if (c == '"')
315     {
316         while ((c = cp->getbyte(cp->client_data)) != 0 && c != '"')
317         {
318             if (c == '\\')
319             {
320                 putb(lval, cp, c);
321                 c = cp->getbyte(cp->client_data);
322                 if (!c)
323                     break;
324             }
325             putb(lval, cp, c);
326         }
327         putb(lval, cp, 0);
328         return TERM;
329     }
330     else
331     {
332         int relation_like = 0;
333         while (c != 0 && !strchr(" \n()=<>/", c))
334         {
335             if (c == '.')
336                 relation_like = 1;
337             if (c == '\\')
338             {
339                 putb(lval, cp, c);
340                 c = cp->getbyte(cp->client_data);
341                 if (!c)
342                     break;
343             }
344             putb(lval, cp, c);
345             c = cp->getbyte(cp->client_data);
346         }
347         putb(lval, cp, 0);
348 #if YYDEBUG
349         printf ("got %s\n", lval->buf);
350 #endif
351         if (c != 0)
352             cp->ungetbyte(c, cp->client_data);
353         if (!cql_strcmp(lval->buf, "and"))
354         {
355             lval->buf = "and";
356             return AND;
357         }
358         if (!cql_strcmp(lval->buf, "or"))
359         {
360             lval->buf = "or";
361             return OR;
362         }
363         if (!cql_strcmp(lval->buf, "not"))
364         {
365             lval->buf = "not";
366             return NOT;
367         }
368         if (!cql_strcmp(lval->buf, "prox"))
369         {
370             lval->buf = "prox";
371             return PROX;
372         }
373         if (!cql_strcmp(lval->buf, "sortby"))
374         {
375             lval->buf = "sortby";
376             return SORTBY;
377         }
378         if (!cql_strcmp(lval->buf, "all"))
379             relation_like = 1;
380         if (!cql_strcmp(lval->buf, "any"))
381             relation_like = 1;
382         if (relation_like)
383             return DOTTERM;
384     }
385     return TERM;
386 }
387
388
389 int cql_parser_stream(CQL_parser cp,
390                       int (*getbyte)(void *client_data),
391                       void (*ungetbyte)(int b, void *client_data),
392                       void *client_data)
393 {
394     nmem_reset(cp->nmem);
395     cp->getbyte = getbyte;
396     cp->ungetbyte = ungetbyte;
397     cp->client_data = client_data;
398     if (cp->top)
399         cql_node_destroy(cp->top);
400     cql_parse(cp);
401     if (cp->top)
402         return 0;
403     return -1;
404 }
405
406 CQL_parser cql_parser_create(void)
407 {
408     CQL_parser cp = (CQL_parser) xmalloc (sizeof(*cp));
409
410     cp->top = 0;
411     cp->getbyte = 0;
412     cp->ungetbyte = 0;
413     cp->client_data = 0;
414     cp->last_error = 0;
415     cp->last_pos = 0;
416     cp->nmem = nmem_create();
417     return cp;
418 }
419
420 void cql_parser_destroy(CQL_parser cp)
421 {
422     cql_node_destroy(cp->top);
423     nmem_destroy(cp->nmem);
424     xfree (cp);
425 }
426
427 struct cql_node *cql_parser_result(CQL_parser cp)
428 {
429     return cp->top;
430 }