Move xml_add utilities to xml_add.c source
[yaz-moved-to-github.git] / src / cql.y
1 /* This file is part of the YAZ toolkit.
2  * Copyright (C) 1995-2013 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 <yaz/yaz-iconv.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         int strict;
48     };
49
50 #define YYSTYPE token
51
52 #define YYPARSE_PARAM parm
53 #define YYLEX_PARAM parm
54
55     int yylex(YYSTYPE *lval, void *vp);
56     int yyerror(char *s);
57 %}
58
59 %pure_parser
60 %token PREFIX_NAME SIMPLE_STRING AND OR NOT PROX GE LE NE EXACT SORTBY
61
62 %%
63
64 top: {
65     $$.rel = cql_node_mk_sc(((CQL_parser) parm)->nmem,
66                             "cql.serverChoice", "=", 0);
67     ((CQL_parser) parm)->top = 0;
68 } cqlQuery1 sortby {
69     cql_node_destroy($$.rel);
70     if ($3.cql)
71     {
72         $3.cql->u.sort.search = $2.cql;
73         ((CQL_parser) parm)->top = $3.cql;
74     } else {
75         ((CQL_parser) parm)->top = $2.cql;
76     }
77 }
78 ;
79
80 sortby: /* empty */
81   { $$.cql = 0; }
82 | SORTBY sortSpec {
83     $$.cql = $2.cql;
84  };
85
86 sortSpec: sortSpec singleSpec {
87     $$.cql = $1.cql;
88     $$.cql->u.sort.next = $2.cql;
89  }
90 | singleSpec
91 {
92     $$.cql = $1.cql;
93 };
94
95 singleSpec: index modifiers {
96     $$.cql = cql_node_mk_sort(((CQL_parser) parm)->nmem, $1.buf, $2.cql);
97  }
98 ;
99
100 cqlQuery1: cqlQuery
101 | cqlQuery error {
102     cql_node_destroy($1.cql);
103     $$.cql = 0;
104 }
105 ;
106
107 cqlQuery:
108   scopedClause
109  |
110   '>' searchTerm '=' searchTerm {
111     $$.rel = $0.rel;
112   } cqlQuery {
113     $$.cql = cql_apply_prefix(((CQL_parser) parm)->nmem,
114                               $6.cql, $2.buf, $4.buf);
115   }
116 | '>' searchTerm {
117       $$.rel = $0.rel;
118   } cqlQuery {
119     $$.cql = cql_apply_prefix(((CQL_parser) parm)->nmem,
120                               $4.cql, 0, $2.buf);
121    }
122 ;
123
124 scopedClause:
125   searchClause
126 |
127   scopedClause boolean modifiers {
128       $$.rel = $0.rel;
129   } searchClause {
130       struct cql_node *cn = cql_node_mk_boolean(((CQL_parser) parm)->nmem,
131                                                 $2.buf);
132
133       cn->u.boolean.modifiers = $3.cql;
134       cn->u.boolean.left = $1.cql;
135       cn->u.boolean.right = $5.cql;
136
137       $$.cql = cn;
138   }
139 ;
140
141 searchClause:
142   '(' {
143       $$.rel = $0.rel;
144
145   } cqlQuery ')' {
146       $$.cql = $3.cql;
147   }
148 |
149 searchTerm extraTerms {
150       struct cql_node *st = cql_node_dup(((CQL_parser) parm)->nmem, $0.rel);
151       st->u.st.extra_terms = $2.cql;
152       st->u.st.term = nmem_strdup(((CQL_parser)parm)->nmem, $1.buf);
153       $$.cql = st;
154   }
155 |
156   index relation modifiers {
157       $$.rel = cql_node_mk_sc(((CQL_parser) parm)->nmem, $1.buf, $2.buf, 0);
158       $$.rel->u.st.modifiers = $3.cql;
159   } searchClause {
160       $$.cql = $5.cql;
161       cql_node_destroy($4.rel);
162   }
163 ;
164
165 extraTerms:
166 SIMPLE_STRING extraTerms {
167     struct cql_node *st = cql_node_mk_sc(((CQL_parser) parm)->nmem,
168                                          /* index */ 0, /* rel */ 0, $1.buf);
169     st->u.st.extra_terms = $2.cql;
170     $$.cql = st;
171 }
172 |
173 { $$.cql = 0; }
174 ;
175
176
177 /* unary NOT search SIMPLE_STRING here .. */
178
179 boolean:
180   AND | OR | NOT | PROX ;
181
182 modifiers: modifiers '/' searchTerm
183 {
184     struct cql_node *mod = cql_node_mk_sc(((CQL_parser)parm)->nmem,
185                                           $3.buf, 0, 0);
186
187     mod->u.st.modifiers = $1.cql;
188     $$.cql = mod;
189 }
190 |
191 modifiers '/' searchTerm relation_symbol searchTerm
192 {
193     struct cql_node *mod = cql_node_mk_sc(((CQL_parser)parm)->nmem,
194                                           $3.buf, $4.buf, $5.buf);
195
196     mod->u.st.modifiers = $1.cql;
197     $$.cql = mod;
198 }
199 |
200 {
201     $$.cql = 0;
202 }
203 ;
204
205 relation: PREFIX_NAME | relation_symbol;
206
207 relation_symbol:
208   '='
209 | '>'
210 | '<'
211 | GE
212 | LE
213 | NE
214 | EXACT
215 ;
216
217 index:
218   searchTerm;
219
220 searchTerm:
221   SIMPLE_STRING
222 | PREFIX_NAME
223 | AND
224 | OR
225 | NOT
226 | PROX
227 | SORTBY
228 ;
229
230 %%
231
232 int yyerror(char *s)
233 {
234     return 0;
235 }
236
237 /**
238  * putb is a utility that puts one character to the string
239  * in current lexical token. This routine deallocates as
240  * necessary using NMEM.
241  */
242
243 static void putb(YYSTYPE *lval, CQL_parser cp, int c)
244 {
245     if (lval->len+1 >= lval->size)
246     {
247         char *nb = (char *)
248             nmem_malloc(cp->nmem, (lval->size = lval->len * 2 + 20));
249         memcpy(nb, lval->buf, lval->len);
250         lval->buf = nb;
251     }
252     if (c)
253         lval->buf[lval->len++] = c;
254     lval->buf[lval->len] = '\0';
255 }
256
257
258 /**
259  * yylex returns next token for Bison to be read. In this
260  * case one of the CQL terminals are returned.
261  */
262 int yylex(YYSTYPE *lval, void *vp)
263 {
264     CQL_parser cp = (CQL_parser) vp;
265     int c;
266     lval->cql = 0;
267     lval->rel = 0;
268     lval->len = 0;
269     lval->size = 10;
270     lval->buf = (char *) nmem_malloc(cp->nmem, lval->size);
271     lval->buf[0] = '\0';
272     do
273     {
274         c = cp->getbyte(cp->client_data);
275         if (c == 0)
276             return 0;
277         if (c == '\n')
278             return 0;
279     } while (yaz_isspace(c));
280     if (strchr("()=></", c))
281     {
282         int c1;
283         putb(lval, cp, c);
284         if (c == '=')
285         {
286             c1 = cp->getbyte(cp->client_data);
287             if (c1 == '=')
288             {
289                 putb(lval, cp, c1);
290                 return EXACT;
291             }
292             else
293                 cp->ungetbyte(c1, cp->client_data);
294         }
295         else if (c == '>')
296         {
297             c1 = cp->getbyte(cp->client_data);
298             if (c1 == '=')
299             {
300                 putb(lval, cp, c1);
301                 return GE;
302             }
303             else
304                 cp->ungetbyte(c1, cp->client_data);
305         }
306         else if (c == '<')
307         {
308             c1 = cp->getbyte(cp->client_data);
309             if (c1 == '=')
310             {
311                 putb(lval, cp, c1);
312                 return LE;
313             }
314             else if (c1 == '>')
315             {
316                 putb(lval, cp, c1);
317                 return NE;
318             }
319             else
320                 cp->ungetbyte(c1, cp->client_data);
321         }
322         return c;
323     }
324     if (c == '"')
325     {
326         while ((c = cp->getbyte(cp->client_data)) != 0 && c != '"')
327         {
328             if (c == '\\')
329             {
330                 putb(lval, cp, c);
331                 c = cp->getbyte(cp->client_data);
332                 if (!c)
333                     break;
334             }
335             putb(lval, cp, c);
336         }
337         putb(lval, cp, 0);
338         return SIMPLE_STRING;
339     }
340     else
341     {
342         int relation_like = 0;
343         while (c != 0 && !strchr(" \n()=<>/", c))
344         {
345             if (c == '.')
346                 relation_like = 1;
347             if (c == '\\')
348             {
349                 putb(lval, cp, c);
350                 c = cp->getbyte(cp->client_data);
351                 if (!c)
352                     break;
353             }
354             putb(lval, cp, c);
355             c = cp->getbyte(cp->client_data);
356         }
357         putb(lval, cp, 0);
358 #if YYDEBUG
359         printf ("got %s\n", lval->buf);
360 #endif
361         if (c != 0)
362             cp->ungetbyte(c, cp->client_data);
363         if (!cql_strcmp(lval->buf, "and"))
364         {
365             lval->buf = "and";
366             return AND;
367         }
368         if (!cql_strcmp(lval->buf, "or"))
369         {
370             lval->buf = "or";
371             return OR;
372         }
373         if (!cql_strcmp(lval->buf, "not"))
374         {
375             lval->buf = "not";
376             return NOT;
377         }
378         if (!cql_strcmp(lval->buf, "prox"))
379         {
380             lval->buf = "prox";
381             return PROX;
382         }
383         if (!cql_strcmp(lval->buf, "sortby"))
384         {
385             lval->buf = "sortby";
386             return SORTBY;
387         }
388         if (cp->strict)
389             return PREFIX_NAME;
390         if (!cql_strcmp(lval->buf, "all"))
391             relation_like = 1;
392         if (!cql_strcmp(lval->buf, "any"))
393             relation_like = 1;
394         if (!cql_strcmp(lval->buf, "adj"))
395             relation_like = 1;
396         if (relation_like)
397             return PREFIX_NAME;
398     }
399     return SIMPLE_STRING;
400 }
401
402
403 int cql_parser_stream(CQL_parser cp,
404                       int (*getbyte)(void *client_data),
405                       void (*ungetbyte)(int b, void *client_data),
406                       void *client_data)
407 {
408     nmem_reset(cp->nmem);
409     cp->getbyte = getbyte;
410     cp->ungetbyte = ungetbyte;
411     cp->client_data = client_data;
412     cql_node_destroy(cp->top);
413     cql_parse(cp);
414     if (cp->top)
415         return 0;
416     return -1;
417 }
418
419 CQL_parser cql_parser_create(void)
420 {
421     CQL_parser cp = (CQL_parser) xmalloc(sizeof(*cp));
422
423     cp->top = 0;
424     cp->getbyte = 0;
425     cp->ungetbyte = 0;
426     cp->client_data = 0;
427     cp->last_error = 0;
428     cp->last_pos = 0;
429     cp->nmem = nmem_create();
430     cp->strict = 0;
431     return cp;
432 }
433
434 void cql_parser_destroy(CQL_parser cp)
435 {
436     cql_node_destroy(cp->top);
437     nmem_destroy(cp->nmem);
438     xfree (cp);
439 }
440
441 struct cql_node *cql_parser_result(CQL_parser cp)
442 {
443     return cp->top;
444 }
445
446 void cql_parser_strict(CQL_parser cp, int mode)
447 {
448     cp->strict = mode;
449 }
450
451 /*
452  * Local variables:
453  * c-basic-offset: 4
454  * c-file-style: "Stroustrup"
455  * indent-tabs-mode: nil
456  * End:
457  * vim: shiftwidth=4 tabstop=8 expandtab
458  */