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