Fix mistake: fuzzy matching is 5=103, not 5=102
[yaz-moved-to-github.git] / src / cql.y
1 /* $Id: cql.y,v 1.9 2005-06-27 22:03:59 adam Exp $
2    Copyright (C) 2002-2004
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 | OR | NOT | PROX 
139   ;
140
141 modifiers: modifiers '/' searchTerm
142
143     struct cql_node *mod = cql_node_mk_sc(((CQL_parser)parm)->nmem,
144                                           $3.buf, "=", 0);
145
146     mod->u.st.modifiers = $1.cql;
147     $$.cql = mod;
148 }
149 |
150 modifiers '/' searchTerm mrelation searchTerm
151 {
152     struct cql_node *mod = cql_node_mk_sc(((CQL_parser)parm)->nmem,
153                                           $3.buf, $4.buf, $5.buf);
154
155     mod->u.st.modifiers = $1.cql;
156     $$.cql = mod;
157 }
158 |
159
160     $$.cql = 0;
161 }
162 ;
163
164 mrelation:
165   '=' 
166 | '>' 
167 | '<'
168 | GE
169 | LE
170 | NE
171 ;
172
173 relation: 
174   '=' 
175 | '>' 
176 | '<'
177 | GE
178 | LE
179 | NE
180 | TERM
181 ;
182
183 index: 
184   searchTerm;
185
186 searchTerm:
187   TERM
188 | AND
189 | OR
190 | NOT
191 | PROX
192 ;
193
194 %%
195
196 int yyerror(char *s)
197 {
198     return 0;
199 }
200
201 /**
202  * putb is a utility that puts one character to the string
203  * in current lexical token. This routine deallocates as
204  * necessary using NMEM.
205  */
206
207 static void putb(YYSTYPE *lval, CQL_parser cp, int c)
208 {
209     if (lval->len+1 >= lval->size)
210     {
211         char *nb = (char *)
212             nmem_malloc(cp->nmem, (lval->size = lval->len * 2 + 20));
213         memcpy (nb, lval->buf, lval->len);
214         lval->buf = nb;
215     }
216     if (c)
217         lval->buf[lval->len++] = c;
218     lval->buf[lval->len] = '\0';
219 }
220
221
222 /**
223  * yylex returns next token for Bison to be read. In this
224  * case one of the CQL terminals are returned.
225  */
226 int yylex(YYSTYPE *lval, void *vp)
227 {
228     CQL_parser cp = (CQL_parser) vp;
229     int c;
230     lval->cql = 0;
231     lval->rel = 0;
232     lval->len = 0;
233     lval->size = 10;
234     lval->buf = (char *) nmem_malloc(cp->nmem, lval->size);
235     lval->buf[0] = '\0';
236     do
237     {
238         c = cp->getbyte(cp->client_data);
239         if (c == 0)
240             return 0;
241         if (c == '\n')
242             return 0;
243     } while (isspace(c));
244     if (strchr("()=></", c))
245     {
246         int c1;
247         putb(lval, cp, c);
248         if (c == '>')
249         {
250             c1 = cp->getbyte(cp->client_data);
251             if (c1 == '=')
252             {
253                 putb(lval, cp, c1);
254                 return GE;
255             }
256             else
257                 cp->ungetbyte(c1, cp->client_data);
258         }
259         else if (c == '<')
260         {
261             c1 = cp->getbyte(cp->client_data);
262             if (c1 == '=')
263             {
264                 putb(lval, cp, c1);
265                 return LE;
266             }
267             else if (c1 == '>')
268             {
269                 putb(lval, cp, c1);
270                 return NE;
271             }
272             else
273                 cp->ungetbyte(c1, cp->client_data);
274         }
275         return c;
276     }
277     if (c == '"')
278     {
279         while ((c = cp->getbyte(cp->client_data)) != 0 && c != '"')
280         {
281             if (c == '\\')
282                 c = cp->getbyte(cp->client_data);
283             putb(lval, cp, c);
284         }
285         putb(lval, cp, 0);
286     }
287     else
288     {
289         putb(lval, cp, c);
290         while ((c = cp->getbyte(cp->client_data)) != 0 &&
291                !strchr(" \n()=<>/", c))
292         {
293             if (c == '\\')
294                 c = cp->getbyte(cp->client_data);
295             putb(lval, cp, c);
296         }
297 #if YYDEBUG
298         printf ("got %s\n", lval->buf);
299 #endif
300         if (c != 0)
301             cp->ungetbyte(c, cp->client_data);
302         if (!cql_strcmp(lval->buf, "and"))
303             return AND;
304         if (!cql_strcmp(lval->buf, "or"))
305             return OR;
306         if (!cql_strcmp(lval->buf, "not"))
307             return NOT;
308         if (!cql_strcmp(lval->buf, "prox"))
309             return PROX;
310     }
311     return TERM;
312 }
313
314
315 int cql_parser_stream(CQL_parser cp,
316                       int (*getbyte)(void *client_data),
317                       void (*ungetbyte)(int b, void *client_data),
318                       void *client_data)
319 {
320     nmem_reset(cp->nmem);
321     cp->getbyte = getbyte;
322     cp->ungetbyte = ungetbyte;
323     cp->client_data = client_data;
324     if (cp->top)
325         cql_node_destroy(cp->top);
326     cql_parse(cp);
327     if (cp->top)
328         return 0;
329     return -1;
330 }
331
332 CQL_parser cql_parser_create(void)
333 {
334     CQL_parser cp = (CQL_parser) xmalloc (sizeof(*cp));
335
336     cp->top = 0;
337     cp->getbyte = 0;
338     cp->ungetbyte = 0;
339     cp->client_data = 0;
340     cp->last_error = 0;
341     cp->last_pos = 0;
342     cp->nmem = nmem_create();
343     return cp;
344 }
345
346 void cql_parser_destroy(CQL_parser cp)
347 {
348     cql_node_destroy(cp->top);
349     nmem_destroy(cp->nmem);
350     xfree (cp);
351 }
352
353 struct cql_node *cql_parser_result(CQL_parser cp)
354 {
355     return cp->top;
356 }