configure.ac: refer to a better URL for peg
[iwhd.git] / query.leg
bloba7096e0d5a3aebccc59a0f1a8005593488a5fd43
1 %{
2 #include <ctype.h>
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <time.h>
6 #include <error.h>
7 #include "query.h"
9 char *arg_buf;
10 int arg_off;
11 int arg_len;
12 value_t **cur_expr;
14 static void
15 xalloc_die (void)
17   error (EXIT_FAILURE, 0, "%s", "memory exhausted");
19   /* The `noreturn' cannot be given to error, since it may return if
20      its first argument is 0.  To help compilers understand the
21      xalloc_die does not return, call abort.  Also, the abort is a
22      safety feature if exit_failure is 0 (which shouldn't happen).  */
23   abort ();
26 /* Allocate N bytes of memory dynamically, with error checking.  */
27 static void *
28 xmalloc (size_t n)
30   void *p = malloc (n);
31   if (!p && n != 0)
32     xalloc_die ();
33   return p;
36 /* Change the size of an allocated block of memory P to N bytes,
37    with error checking.  */
38 static void *
39 xrealloc (void *p, size_t n)
41   p = realloc (p, n);
42   if (!p && n != 0)
43     xalloc_die ();
44   return p;
47 /* Clone an object P of size S, with error checking.  There's no need
48    for xnmemdup (P, N, S), since xmemdup (P, N * S) works without any
49    need for an arithmetic overflow check.  */
50 static void *
51 xmemdup (void const *p, size_t s)
53   return memcpy (xmalloc (s), p, s);
56 /* Clone STRING.  */
57 static char *
58 xstrdup (char const *string)
60   return xmemdup (string, strlen (string) + 1);
63 /* TBD: use separate function to parse dates differently */
64 value_t *
65 make_number (char *text)
67         value_t *tmp = malloc(sizeof(*tmp));
69         if (tmp) {
70                 tmp->type = T_NUMBER;
71                 tmp->as_num = strtoll(text,NULL,10);
72         }
74         return tmp;
77 value_t *
78 make_string (char *text, type_t t)
80         value_t *tmp = malloc(sizeof(*tmp));
82         if (tmp) {
83                 tmp->type = t;
84                 tmp->as_str = xstrdup(text);
85         }
87         return tmp;
90 value_t *
91 make_tree (type_t t, value_t *left, value_t *right)
93         value_t *tmp = malloc(sizeof(*tmp));
95         if (tmp) {
96                 tmp->type = t;
97                 tmp->as_tree.left = left;
98                 tmp->as_tree.right = right;
99         }
101         return tmp;
104 value_t *
105 make_comp (comp_t c, value_t *left, value_t *right)
107         value_t *tmp = make_tree(T_COMP,left,right);
109         if (tmp) {
110                 tmp->as_tree.op = c;
111         }
113         return tmp;
116 #if defined(UNIT_TEST)
117 struct { char *name; char *value; } hacked_obj_fields[] = {
118         { "a", "2" }, { "b", "7" }, { "c", "11" },
119         { NULL }
122 char *
123 unit_oget_func (void * notused, char *text)
125         int i;
127         for (i = 0; hacked_obj_fields[i].name; ++i) {
128                 if (!strcmp(hacked_obj_fields[i].name,text)) {
129                         return hacked_obj_fields[i].value;
130                 }
131         }
133         return NULL;
135 getter_t unit_oget = { unit_oget_func };
137 char *
138 unit_sget_func (void * notused, char *text)
140         return "never";
142 getter_t unit_sget = { unit_sget_func };
143 #endif
145 char *
146 string_value (value_t *v, getter_t *oget, getter_t *sget)
148         switch (v->type) {
149         case T_STRING:
150                 return v->as_str;
151         case T_OFIELD:
152                 return oget ? CALL_GETTER(oget,v->as_str) : "";
153         case T_SFIELD:
154                 return sget ? CALL_GETTER(sget,v->as_str) : "";
155         default:
156                 return NULL;
157         }
161 is_ok_number (char *a_str)
163         char    *p;
165         if (!a_str) {
166                 return 0;
167         }
169         for (p = a_str; *p; ++p) {
170                 if (!isdigit(*p)) {
171                         return 0;
172                 }
173         }
175         return 1;
179 compare (value_t *left, comp_t op, value_t *right,
180          getter_t *oget, getter_t *sget)
182         char    *lstr;
183         char    *rstr;
184         int      lval = 0; // solely to placate gcc
185         int      rval;
186         int      num_ok = 1;
188         lstr = string_value(left,oget,sget);
189         rstr = string_value(right,oget,sget);
191         if (left->type == T_NUMBER) {
192                 lval = left->as_num;
193         }
194         else if (lstr) {
195                 if (is_ok_number(lstr)) {
196                         lval = strtoll(lstr,NULL,0);
197                 }
198                 else {
199                         num_ok = 0;
200                 }
201         }
202         else {
203                 lval = eval(left,oget,sget);
204                 if (lval < 0) {
205                         return lval;
206                 }
207         }
209         if (right->type == T_NUMBER) {
210                 rval = right->as_num;
211         }
212         else if (rstr) {
213                 if (is_ok_number(rstr)) {
214                         rval = strtoll(rstr,NULL,0);
215                 }
216                 else {
217                         num_ok = 0;
218                 }
219         }
220         else {
221                 rval = eval(right,oget,sget);
222                 if (rval < 0) {
223                         return rval;
224                 }
225         }
227         if (!num_ok) {
228                 if (!lstr || !rstr) {
229                         return -1;
230                 }
231                 lval = strcmp(lstr,rstr);
232                 rval = 0;
233         }
235         switch (op) {
236         case C_LESSTHAN:        return (lval < rval);
237         case C_LESSOREQ:        return (lval <= rval);
238         case C_EQUAL:           return (lval == rval);
239         case C_DIFFERENT:       return (lval != rval);
240         case C_GREATEROREQ:     return (lval >= rval);
241         case C_GREATERTHAN:     return (lval > rval);
242         default:
243                 return -1;
244         }
247 void
248 _print_value (value_t *v, int level)
250         if (!v) {
251                 printf("%*sNULL\n",level,"");
252                 return;
253         }
255         switch (v->type) {
256         case T_NUMBER:
257                 printf("%*sNUMBER %lld\n",level,"",v->as_num);
258                 break;
259         case T_STRING:
260                 printf("%*sSTRING %s\n",level,"",v->as_str);
261                 break;
262         case T_OFIELD:
263 #if defined(UNIT_TEST)
264                 printf("%*sOBJECT FIELD %s (%s)\n",level,"",v->as_str,
265                         unit_oget_func(NULL,v->as_str));
266 #else
267                 printf("%*sOBJECT FIELD %s\n",level,"",v->as_str);
268 #endif
269                 break;
270         case T_SFIELD:
271 #if defined(UNIT_TEST)
272                 printf("%*sSERVER FIELD %s (%s)\n",level,"",v->as_str,
273                         unit_sget_func(NULL,v->as_str));
274 #else
275                 printf("%*sSERVER FIELD %s\n",level,"",v->as_str);
276 #endif
277                 break;
278         case T_COMP:
279                 printf("%*sCOMPARISON\n",level,"");
280                 _print_value(v->as_tree.left,level+2);
281                 _print_value(v->as_tree.right,level+2);
282                 break;
283         case T_NOT:
284                 printf("%*sNOT\n",level,"");
285                 _print_value(v->as_tree.left,level+2);
286                 break;
287         case T_AND:
288                 printf("%*sAND\n",level,"");
289                 _print_value(v->as_tree.left,level+2);
290                 _print_value(v->as_tree.right,level+2);
291                 break;
292         case T_OR:
293                 printf("%*sOR\n",level,"");
294                 _print_value(v->as_tree.left,level+2);
295                 _print_value(v->as_tree.right,level+2);
296                 break;
297         default:
298                 printf("%*sUNKNOWN %d\n",level,"",v->type);
299         }
302 void
303 print_value (value_t *v)
305         _print_value(v,0);
308 void
309 free_value (value_t *v)
311         if (!v) {
312                 return;
313         }
315         switch (v->type) {
316         case T_STRING:
317         case T_OFIELD:
318         case T_SFIELD:
319                 if (v->as_str) {
320                         free(v->as_str);
321                 }
322                 break;
323         case T_COMP:
324         case T_AND:
325         case T_OR:
326                 free_value(v->as_tree.right);
327                 /* Fall through. */
328         case T_NOT:
329                 free_value(v->as_tree.left);
330                 /* Fall through. */
331         default:
332                 free(v);
333         }
337 eval (value_t *v, getter_t *oget, getter_t *sget)
339         int     res;
341         switch (v->type) {
342         case T_NUMBER:
343                 return v->as_num != 0;
344         case T_STRING:
345         case T_OFIELD:
346         case T_SFIELD:
347                 return -1;
348         case T_COMP:
349                 return compare(v->as_tree.left,(comp_t)v->as_tree.op,
350                         v->as_tree.right, oget, sget);
351         case T_NOT:
352                 res = eval(v->as_tree.left,oget,sget);
353                 return (res >= 0) ? !res : res;
354         case T_AND:
355                 res = eval(v->as_tree.left,oget,sget);
356                 if (res > 0) {
357                         res = eval(v->as_tree.right,oget,sget);
358                 }
359                 return res;
360         case T_OR:
361                 res = eval(v->as_tree.left,oget,sget);
362                 if (res > 0) {
363                         return res;
364                 }
365                 return eval(v->as_tree.right,oget,sget);
366         default:
367                 return -1;
368         }
371 #define YY_INPUT(buf,result,max) {                                        \
372         result = (arg_off < arg_len) ? (*buf = arg_buf[arg_off++], 1)     \
373                 : (arg_off == arg_len) ? (*buf = '\n', ++arg_off, 1) : 0; \
376 #define YYSTYPE value_t *
379 Stmt = - BoolExpr EOL { *cur_expr = $$; }
380      | ( !EOL . )* EOL
382 BoolExpr = l:CompExpr ( - ( AND - r:CompExpr ) { $$ = make_tree(T_AND,l,r); }
383                       | - ( OR - r:CompExpr ) { $$ = make_tree(T_OR,l,r); } )* -
385 CompExpr = l:Unary
386          ( ( LESS - r:Unary { $$ = make_comp(C_LESSTHAN,l,r); } )
387          | ( LESS EQUAL - r:Unary { $$ = make_comp(C_LESSOREQ,l,r); } )
388          | ( EQUAL EQUAL - r:Unary { $$ = make_comp(C_EQUAL,l,r); } )
389          | ( NOT EQUAL - r:Unary { $$ = make_comp(C_DIFFERENT,l,r); } )
390          | ( GREATER EQUAL - r:Unary { $$ = make_comp(C_GREATEROREQ,l,r); } )
391          | ( GREATER - r:Unary { $$ = make_comp(C_GREATERTHAN,l,r); } ) )? -
393 Unary = Atom
394       | NOT - e:Atom - { $$ = make_tree(T_NOT,e,NULL); }
395       | NOT - e:Unary - { $$ = make_tree(T_NOT,e,NULL); }
397 Atom = ( Literal | Field | ParenExpr ) -
399 Literal = NUMBER | STRING | TIME
401 Field = DOLLAR i:ID { $$ = make_string((char *)$$,T_OFIELD); }
402       | WAFFLE i:ID { $$ = make_string((char *)$$,T_SFIELD); }
404 ParenExpr = OPEN v:BoolExpr CLOSE - { $$ = v; }
406 NUMBER  = < [0-9]+ >            { $$= make_number(yytext); }
407 STRING  = '"' < [^"]* > '"'     { $$ = make_string(yytext,T_STRING); }
408 TIME    = '~' < [^~]* > '~'     { $$ = make_number(yytext); }
409 ID      = < [a-z_]+ >           { $$ = (YYSTYPE)yytext; }
410 DOLLAR  = '$'
411 WAFFLE  = '#'
412 OPEN    = '('
413 CLOSE   = ')'
414 LESS    = '<'
415 GREATER = '>'
416 EQUAL   = '='
417 NOT     = '!'
418 AND     = '&&'
419 OR      = '||'
421 -       = [ \t]*
422 EOL     = '\n' | '\r\n' | '\r' | ';'
426 value_t *
427 parse (char *text)
429         value_t *expr = NULL;
431         arg_buf = text;
432         arg_len = strlen(text);
433         arg_off = 0;
434         cur_expr = &expr;
436         while (yyparse()) {
437         }
439         return expr;
442 #if defined(UNIT_TEST)
444 main (int argc, char **argv)
446         int     i;
447         value_t *expr;
449         for (i = 1; i < argc; ++i) {
450                 expr = parse(argv[i]);
451                 if (expr) {
452                         print_value(expr);
453                         printf("= %d\n",eval(expr,&unit_oget,&unit_sget));
454                 }
455                 else {
456                         printf("could not parse '%s'\n",argv[i]);
457                 }
458         }
460         return 0;
462 #endif