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