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