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