CQL v1.2 functionality except sort.
[yaz-moved-to-github.git] / src / cql.y
1 /* $Id: cql.y,v 1.16 2008-01-06 13:08:09 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 {
125       struct cql_node *st = cql_node_dup (((CQL_parser) parm)->nmem, $0.rel);
126       st->u.st.term = nmem_strdup(((CQL_parser)parm)->nmem, $1.buf);
127       $$.cql = st;
128   }
129
130   index relation modifiers {
131       $$.rel = cql_node_mk_sc(((CQL_parser) parm)->nmem, $1.buf, $2.buf, 0);
132       $$.rel->u.st.modifiers = $3.cql;
133   } searchClause {
134       $$.cql = $5.cql;
135       cql_node_destroy($4.rel);
136   }
137 ;
138
139 /* unary NOT search TERM here .. */
140
141 boolean: 
142   AND | OR | NOT | PROX ;
143
144 modifiers: modifiers '/' searchTerm
145
146     struct cql_node *mod = cql_node_mk_sc(((CQL_parser)parm)->nmem,
147                                           $3.buf, 0, 0);
148
149     mod->u.st.modifiers = $1.cql;
150     $$.cql = mod;
151 }
152 |
153 modifiers '/' searchTerm mrelation searchTerm
154 {
155     struct cql_node *mod = cql_node_mk_sc(((CQL_parser)parm)->nmem,
156                                           $3.buf, $4.buf, $5.buf);
157
158     mod->u.st.modifiers = $1.cql;
159     $$.cql = mod;
160 }
161 |
162
163     $$.cql = 0;
164 }
165 ;
166
167 /*
168 extraTerms:
169    extraTerms TERM | ;
170 */
171
172 mrelation:
173   '=' 
174 | '>' 
175 | '<'
176 | GE
177 | LE
178 | NE
179 | EXACT
180 ;
181
182 relation: 
183   '=' 
184 | '>' 
185 | '<'
186 | GE
187 | LE
188 | NE
189 | EXACT
190 | DOTTERM
191 ;
192
193 index: 
194   searchTerm;
195
196 searchTerm:
197   TERM
198 | DOTTERM
199 | AND
200 | OR
201 | NOT
202 | PROX
203 ;
204
205 %%
206
207 int yyerror(char *s)
208 {
209     return 0;
210 }
211
212 /**
213  * putb is a utility that puts one character to the string
214  * in current lexical token. This routine deallocates as
215  * necessary using NMEM.
216  */
217
218 static void putb(YYSTYPE *lval, CQL_parser cp, int c)
219 {
220     if (lval->len+1 >= lval->size)
221     {
222         char *nb = (char *)
223             nmem_malloc(cp->nmem, (lval->size = lval->len * 2 + 20));
224         memcpy (nb, lval->buf, lval->len);
225         lval->buf = nb;
226     }
227     if (c)
228         lval->buf[lval->len++] = c;
229     lval->buf[lval->len] = '\0';
230 }
231
232
233 /**
234  * yylex returns next token for Bison to be read. In this
235  * case one of the CQL terminals are returned.
236  */
237 int yylex(YYSTYPE *lval, void *vp)
238 {
239     CQL_parser cp = (CQL_parser) vp;
240     int c;
241     lval->cql = 0;
242     lval->rel = 0;
243     lval->len = 0;
244     lval->size = 10;
245     lval->buf = (char *) nmem_malloc(cp->nmem, lval->size);
246     lval->buf[0] = '\0';
247     do
248     {
249         c = cp->getbyte(cp->client_data);
250         if (c == 0)
251             return 0;
252         if (c == '\n')
253             return 0;
254     } while (isspace(c));
255     if (strchr("()=></", c))
256     {
257         int c1;
258         putb(lval, cp, c);
259         if (c == '=')
260         {
261             c1 = cp->getbyte(cp->client_data);
262             if (c1 == '=')
263             {
264                 putb(lval, cp, c1);
265                 return EXACT;
266             }
267             else
268                 cp->ungetbyte(c1, cp->client_data);
269         }
270         else if (c == '>')
271         {
272             c1 = cp->getbyte(cp->client_data);
273             if (c1 == '=')
274             {
275                 putb(lval, cp, c1);
276                 return GE;
277             }
278             else
279                 cp->ungetbyte(c1, cp->client_data);
280         }
281         else if (c == '<')
282         {
283             c1 = cp->getbyte(cp->client_data);
284             if (c1 == '=')
285             {
286                 putb(lval, cp, c1);
287                 return LE;
288             }
289             else if (c1 == '>')
290             {
291                 putb(lval, cp, c1);
292                 return NE;
293             }
294             else
295                 cp->ungetbyte(c1, cp->client_data);
296         }
297         return c;
298     }
299     if (c == '"')
300     {
301         while ((c = cp->getbyte(cp->client_data)) != 0 && c != '"')
302         {
303             if (c == '\\')
304             {
305                 putb(lval, cp, c);
306                 c = cp->getbyte(cp->client_data);
307                 if (!c)
308                     break;
309             }
310             putb(lval, cp, c);
311         }
312         putb(lval, cp, 0);
313         return TERM;
314     }
315     else
316     {
317         int relation_like = 0;
318         while (c != 0 && !strchr(" \n()=<>/", c))
319         {
320             if (c == '.')
321                 relation_like = 1;
322             if (c == '\\')
323             {
324                 putb(lval, cp, c);
325                 c = cp->getbyte(cp->client_data);
326                 if (!c)
327                     break;
328             }
329             putb(lval, cp, c);
330             c = cp->getbyte(cp->client_data);
331         }
332         putb(lval, cp, 0);
333 #if YYDEBUG
334         printf ("got %s\n", lval->buf);
335 #endif
336         if (c != 0)
337             cp->ungetbyte(c, cp->client_data);
338         if (!cql_strcmp(lval->buf, "and"))
339         {
340             lval->buf = "and";
341             return AND;
342         }
343         if (!cql_strcmp(lval->buf, "or"))
344         {
345             lval->buf = "or";
346             return OR;
347         }
348         if (!cql_strcmp(lval->buf, "not"))
349         {
350             lval->buf = "not";
351             return NOT;
352         }
353         if (!cql_strcmp(lval->buf, "prox"))
354         {
355             lval->buf = "prox";
356             return PROX;
357         }
358         if (!cql_strcmp(lval->buf, "all"))
359             relation_like = 1;
360         if (!cql_strcmp(lval->buf, "any"))
361             relation_like = 1;
362         if (relation_like)
363             return DOTTERM;
364     }
365     return TERM;
366 }
367
368
369 int cql_parser_stream(CQL_parser cp,
370                       int (*getbyte)(void *client_data),
371                       void (*ungetbyte)(int b, void *client_data),
372                       void *client_data)
373 {
374     nmem_reset(cp->nmem);
375     cp->getbyte = getbyte;
376     cp->ungetbyte = ungetbyte;
377     cp->client_data = client_data;
378     if (cp->top)
379         cql_node_destroy(cp->top);
380     cql_parse(cp);
381     if (cp->top)
382         return 0;
383     return -1;
384 }
385
386 CQL_parser cql_parser_create(void)
387 {
388     CQL_parser cp = (CQL_parser) xmalloc (sizeof(*cp));
389
390     cp->top = 0;
391     cp->getbyte = 0;
392     cp->ungetbyte = 0;
393     cp->client_data = 0;
394     cp->last_error = 0;
395     cp->last_pos = 0;
396     cp->nmem = nmem_create();
397     return cp;
398 }
399
400 void cql_parser_destroy(CQL_parser cp)
401 {
402     cql_node_destroy(cp->top);
403     nmem_destroy(cp->nmem);
404     xfree (cp);
405 }
406
407 struct cql_node *cql_parser_result(CQL_parser cp)
408 {
409     return cp->top;
410 }