d01405d90b75c7c60de28f2769ab15de5c432753
[yaz-moved-to-github.git] / src / cql.y
1 /* $Id: cql.y,v 1.12 2006-12-14 08:55:52 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 TERM AND OR NOT PROX GE LE NE
62 %expect 9
63
64 %%
65
66 top: { 
67     $$.rel = cql_node_mk_sc(((CQL_parser) parm)->nmem,
68                             "cql.serverChoice", "scr", 0);
69     ((CQL_parser) parm)->top = 0;
70 } cqlQuery1 {
71     cql_node_destroy($$.rel);
72     ((CQL_parser) parm)->top = $2.cql; 
73 }
74 ;
75
76 cqlQuery1: cqlQuery
77 | cqlQuery error {
78     cql_node_destroy($1.cql);
79     $$.cql = 0;
80 }
81 ;
82
83 cqlQuery: 
84   searchClause
85 |
86   cqlQuery boolean modifiers { 
87       $$.rel = $0.rel;
88   } searchClause {
89       struct cql_node *cn = cql_node_mk_boolean(((CQL_parser) parm)->nmem,
90                                                 $2.buf);
91       
92       cn->u.boolean.modifiers = $3.cql;
93       cn->u.boolean.left = $1.cql;
94       cn->u.boolean.right = $5.cql;
95
96       $$.cql = cn;
97   }
98 ;
99
100 searchClause: 
101   '(' { 
102       $$.rel = $0.rel;
103       
104   } cqlQuery ')' {
105       $$.cql = $3.cql;
106   }
107 |
108   searchTerm {
109       struct cql_node *st = cql_node_dup (((CQL_parser) parm)->nmem, $0.rel);
110       st->u.st.term = nmem_strdup(((CQL_parser)parm)->nmem, $1.buf);
111       $$.cql = st;
112   }
113
114   index relation modifiers {
115       $$.rel = cql_node_mk_sc(((CQL_parser) parm)->nmem, $1.buf, $2.buf, 0);
116       $$.rel->u.st.modifiers = $3.cql;
117   } searchClause {
118       $$.cql = $5.cql;
119       cql_node_destroy($4.rel);
120   }
121 | '>' searchTerm '=' searchTerm {
122       $$.rel = $0.rel;
123   } cqlQuery {
124     $$.cql = cql_apply_prefix(((CQL_parser) parm)->nmem,
125                               $6.cql, $2.buf, $4.buf);
126   }
127 | '>' searchTerm {
128       $$.rel = $0.rel;
129   } cqlQuery {
130     $$.cql = cql_apply_prefix(((CQL_parser) parm)->nmem, 
131                               $4.cql, 0, $2.buf);
132    }
133 ;
134
135 /* unary NOT search TERM here .. */
136
137 boolean: 
138   AND { $$.buf = "and"; } 
139 | OR { $$.buf = "or"; } 
140 | NOT { $$.buf = "not"; } 
141 | PROX { $$.buf = "prox"; } 
142   ;
143
144 modifiers: modifiers '/' searchTerm
145
146     struct cql_node *mod = cql_node_mk_sc(((CQL_parser)parm)->nmem,
147                                           $3.buf, "=", 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 mrelation:
168   '=' 
169 | '>' 
170 | '<'
171 | GE
172 | LE
173 | NE
174 ;
175
176 relation: 
177   '=' 
178 | '>' 
179 | '<'
180 | GE
181 | LE
182 | NE
183 | TERM
184 ;
185
186 index: 
187   searchTerm;
188
189 searchTerm:
190   TERM
191 | AND
192 | OR
193 | NOT
194 | PROX
195 ;
196
197 %%
198
199 int yyerror(char *s)
200 {
201     return 0;
202 }
203
204 /**
205  * putb is a utility that puts one character to the string
206  * in current lexical token. This routine deallocates as
207  * necessary using NMEM.
208  */
209
210 static void putb(YYSTYPE *lval, CQL_parser cp, int c)
211 {
212     if (lval->len+1 >= lval->size)
213     {
214         char *nb = (char *)
215             nmem_malloc(cp->nmem, (lval->size = lval->len * 2 + 20));
216         memcpy (nb, lval->buf, lval->len);
217         lval->buf = nb;
218     }
219     if (c)
220         lval->buf[lval->len++] = c;
221     lval->buf[lval->len] = '\0';
222 }
223
224
225 /**
226  * yylex returns next token for Bison to be read. In this
227  * case one of the CQL terminals are returned.
228  */
229 int yylex(YYSTYPE *lval, void *vp)
230 {
231     CQL_parser cp = (CQL_parser) vp;
232     int c;
233     lval->cql = 0;
234     lval->rel = 0;
235     lval->len = 0;
236     lval->size = 10;
237     lval->buf = (char *) nmem_malloc(cp->nmem, lval->size);
238     lval->buf[0] = '\0';
239     do
240     {
241         c = cp->getbyte(cp->client_data);
242         if (c == 0)
243             return 0;
244         if (c == '\n')
245             return 0;
246     } while (isspace(c));
247     if (strchr("()=></", c))
248     {
249         int c1;
250         putb(lval, cp, c);
251         if (c == '>')
252         {
253             c1 = cp->getbyte(cp->client_data);
254             if (c1 == '=')
255             {
256                 putb(lval, cp, c1);
257                 return GE;
258             }
259             else
260                 cp->ungetbyte(c1, cp->client_data);
261         }
262         else if (c == '<')
263         {
264             c1 = cp->getbyte(cp->client_data);
265             if (c1 == '=')
266             {
267                 putb(lval, cp, c1);
268                 return LE;
269             }
270             else if (c1 == '>')
271             {
272                 putb(lval, cp, c1);
273                 return NE;
274             }
275             else
276                 cp->ungetbyte(c1, cp->client_data);
277         }
278         return c;
279     }
280     if (c == '"')
281     {
282         while ((c = cp->getbyte(cp->client_data)) != 0 && c != '"')
283         {
284             if (c == '\\')
285                 c = cp->getbyte(cp->client_data);
286             putb(lval, cp, c);
287         }
288         putb(lval, cp, 0);
289     }
290     else
291     {
292         while (c != 0 && !strchr(" \n()=<>/", c))
293         {
294             if (c == '\\')
295                 c = cp->getbyte(cp->client_data);
296             putb(lval, cp, c);
297             c = cp->getbyte(cp->client_data);
298         }
299 #if YYDEBUG
300         printf ("got %s\n", lval->buf);
301 #endif
302         if (c != 0)
303             cp->ungetbyte(c, cp->client_data);
304         if (!cql_strcmp(lval->buf, "and"))
305             return AND;
306         if (!cql_strcmp(lval->buf, "or"))
307             return OR;
308         if (!cql_strcmp(lval->buf, "not"))
309             return NOT;
310         if (!cql_strcmp(lval->buf, "prox"))
311             return PROX;
312     }
313     return TERM;
314 }
315
316
317 int cql_parser_stream(CQL_parser cp,
318                       int (*getbyte)(void *client_data),
319                       void (*ungetbyte)(int b, void *client_data),
320                       void *client_data)
321 {
322     nmem_reset(cp->nmem);
323     cp->getbyte = getbyte;
324     cp->ungetbyte = ungetbyte;
325     cp->client_data = client_data;
326     if (cp->top)
327         cql_node_destroy(cp->top);
328     cql_parse(cp);
329     if (cp->top)
330         return 0;
331     return -1;
332 }
333
334 CQL_parser cql_parser_create(void)
335 {
336     CQL_parser cp = (CQL_parser) xmalloc (sizeof(*cp));
337
338     cp->top = 0;
339     cp->getbyte = 0;
340     cp->ungetbyte = 0;
341     cp->client_data = 0;
342     cp->last_error = 0;
343     cp->last_pos = 0;
344     cp->nmem = nmem_create();
345     return cp;
346 }
347
348 void cql_parser_destroy(CQL_parser cp)
349 {
350     cql_node_destroy(cp->top);
351     nmem_destroy(cp->nmem);
352     xfree (cp);
353 }
354
355 struct cql_node *cql_parser_result(CQL_parser cp)
356 {
357     return cp->top;
358 }