7 #include "iwhd-qparser.h"
24 /* Return a pointer to this when allocation fails in a value_t-returning
26 static value_t invalid
= { T_INVALID
, {0}, NULL
};
28 #define YY_DECL int yylex(YYSTYPE *, void *scanner);
31 /* TBD: use separate function to parse dates differently */
33 make_number
(const char *text
)
35 value_t
*tmp
= malloc
(sizeof
(*tmp
));
40 tmp
->as_num
= strtoll
(text
,NULL
,10);
47 /* Return a malloc'd value_t buffer, with its type to T and using TEXT
48 (already malloc'd) as its string. */
50 make_string
(const char *text
, type_t t
)
52 value_t
*tmp
= malloc
(sizeof
(*tmp
));
57 tmp
->as_str
= (char *) text
;
63 /* Return a malloc'd tree_t, with type T and branches LEFT and RIGHT.
64 LEFT must be non-NULL. RIGHT may be NULL (solely for use in handling
65 a negated expression). */
67 make_tree
(type_t t
, const value_t
*left
, const value_t
*right
)
69 if
(left
->type
== T_INVALID
)
70 return
(value_t
*) left
;
71 if
(t
!= T_LINK
&& right
&& right
->type
== T_INVALID
)
72 return
(value_t
*) right
;
73 value_t
*tmp
= malloc
(sizeof
(*tmp
));
79 tmp
->as_tree.left
= (value_t
*) left
;
80 tmp
->as_tree.right
= (value_t
*) right
;
86 /* Return a malloc'd comp_t, with type T and branches LEFT and RIGHT.
87 LEFT and RIGHT must both be non-NULL. */
89 make_comp
(comp_t c
, const value_t
*left
, const value_t
*right
)
91 if
(left
->type
== T_INVALID
)
92 return
(value_t
*) left
;
93 if
(right
->type
== T_INVALID
)
94 return
(value_t
*) right
;
95 value_t
*tmp
= make_tree
(T_COMP
,left
,right
);
106 make_link
(const value_t
*left
, const char *right
)
108 return make_tree
(T_LINK
,left
,(value_t
*)right
);
112 yyerror (void *scanner ATTRIBUTE_UNUSED
,
113 value_t
**result ATTRIBUTE_UNUSED
,
114 const char *msg ATTRIBUTE_UNUSED
)
121 %lex
-param
{ yyscan_t scanner
}
122 %parse
-param
{ void *scanner
}
123 %parse
-param
{ value_t
**result
}
125 %token
<str
> T_STRING T_COMP T_DATE T_ID T_LINK T_NUMBER T_OFIELD T_SFIELD
126 %token T_EQ T_NE T_NOT T_AND T_OR T_INVALID
127 %token T_LT T_GT T_LE T_GE
129 %type
<val
> atom bbool_expr comp_expr field
130 %type
<val
> link_field literal paren_expr ubool_expr
143 // printf("promoting ubool_expr to bbool_expr\n");
146 bbool_expr T_AND ubool_expr
{
147 // printf("found AND expression\n");
148 $$
= make_tree
(T_AND
,$1,$3);
150 bbool_expr T_OR ubool_expr
{
151 // printf("found OR expression\n");
152 $$
= make_tree
(T_OR
,$1,$3);
157 // printf("promoting comp_expr to ubool_expr\n");
161 // printf("found NOT expression\n");
162 $$
= make_tree
(T_NOT
,$2,NULL
);
168 // printf("promoting atom to comp_expr\n");
172 // printf("found LESS THAN expression\n");
173 $$
= make_comp
(C_LESSTHAN
,$1,$3);
176 // printf("found LESS OR EQUAL expression\n");
177 $$
= make_comp
(C_LESSOREQ
,$1,$3);
180 // printf("found EQUAL expression\n");
181 $$
= make_comp
(C_EQUAL
,$1,$3);
184 // printf("found NOT EQUAL expression\n");
185 $$
= make_comp
(C_DIFFERENT
,$1,$3);
188 // printf("found GREATER OR EQUAL expression\n");
189 $$
= make_comp
(C_GREATEROREQ
,$1,$3);
192 // printf("found GREATER THAN expression\n");
193 $$
= make_comp
(C_GREATERTHAN
,$1,$3);
198 // printf("promoting link_field to atom\n");
202 // printf("promoting literal to atom\n");
206 // printf("promoting paren_expr to atom\n");
212 // printf("promoting field to link_field\n");
215 link_field
'.' T_ID
{
216 // printf("found LINK FIELD\n");
217 $$
= make_link
($1,$3);
222 // printf("found DOLLAR FIELD\n");
223 $$
= make_string
($2,T_OFIELD
);
226 // printf("found WAFFLE FIELD\n");
227 $$
= make_string
($2,T_SFIELD
);
232 // printf("found NUMBER %s\n",$1);
233 $$
= make_number
($1);
236 // printf("found STRING %s\n",$1);
237 $$
= make_string
($1,T_STRING
);
240 // printf("found DATE\n");
241 $$
= make_string
($1,T_DATE
);
244 // printf("found ID %s\n",$1);
245 $$
= make_string
($1,T_ID
);
250 // printf("found PAREN expression\n");
256 #if defined PARSER_UNIT_TEST
260 static const struct { char *name
; char *value
; } hacked_obj_fields
[] = {
261 /* Fake object fields for generic unit testing. */
262 { "a", "2" }, { "b", "7" }, { "c", "11" },
263 /* This one's here to test links (e.g. $template.owner.name). */
264 { "template", "templates/the_tmpl" },
268 /* Fake out the eval code for unit testing. */
270 unit_oget_func
(void * notused
, const char *text
)
274 for
(i
= 0; hacked_obj_fields
[i
].name
; ++i
) {
275 if
(!strcmp
(hacked_obj_fields
[i
].name
,text
)) {
276 return xstrdup
(hacked_obj_fields
[i
].value
);
282 static const getter_t unit_oget
= { unit_oget_func
};
285 * Same as above, but the site-field stuff is so similar to the object-field
286 * stuff that it's not worth exercising too much separately.
289 unit_sget_func
(void * notused
, const char *text
)
293 static const getter_t unit_sget
= { unit_sget_func
};
295 /* Fake links from an object/key tuple to an object/key string. */
296 static const struct { char *obj
; char *key
; char *value
; } hacked_links
[] = {
297 { "templates/the_tmpl", "owner", "users/the_user" },
298 { "users/the_user", "name", "Jeff Darcy" },
303 follow_link
(const char *object
, const char *key
)
307 for
(i
= 0; hacked_links
[i
].obj
; ++i
) {
308 if
(strcmp
(object
,hacked_links
[i
].obj
)) {
311 if
(strcmp
(key
,hacked_links
[i
].key
)) {
314 return hacked_links
[i
].value
;
320 extern
char *follow_link
(const char *object
, const char *key
);
324 _print_value
(const value_t
*v
, int level
)
327 printf
("%*sNULL\n",level
,"");
333 printf
("%*sNUMBER %lld\n",level
,"",v
->as_num
);
336 printf
("%*sSTRING %s\n",level
,"",v
->as_str
);
339 #if defined PARSER_UNIT_TEST
340 printf
("%*sOBJECT FIELD %s (%s)\n",level
,"",v
->as_str
,
341 unit_oget_func
(NULL
,v
->as_str
));
343 printf
("%*sOBJECT FIELD %s\n",level
,"",v
->as_str
);
347 #if defined PARSER_UNIT_TEST
348 printf
("%*sSERVER FIELD %s (%s)\n",level
,"",v
->as_str
,
349 unit_sget_func
(NULL
,v
->as_str
));
351 printf
("%*sSERVER FIELD %s\n",level
,"",v
->as_str
);
355 printf
("%*sCOMPARISON\n",level
,"");
356 _print_value
(v
->as_tree.left
,level
+2);
357 _print_value
(v
->as_tree.right
,level
+2);
360 printf
("%*sNOT\n",level
,"");
361 _print_value
(v
->as_tree.left
,level
+2);
364 printf
("%*sAND\n",level
,"");
365 _print_value
(v
->as_tree.left
,level
+2);
366 _print_value
(v
->as_tree.right
,level
+2);
369 printf
("%*sOR\n",level
,"");
370 _print_value
(v
->as_tree.left
,level
+2);
371 _print_value
(v
->as_tree.right
,level
+2);
374 printf
("%*sLINK\n",level
,"");
375 _print_value
(v
->as_tree.left
,level
+2);
376 printf
("%*sDEST FIELD %s\n",level
+2,"",
377 (char *)v
->as_tree.right
);
380 printf
("%*sUNKNOWN %d\n",level
,"",v
->type
);
385 print_value
(const value_t
*v
)
391 free_value
(value_t
*v
)
393 if
(v
== NULL || v
== &invalid
) {
397 free
((void *)v
->resolved
);
408 free_value
(v
->as_tree.left
);
409 free
(v
->as_tree.right
);
415 free_value
(v
->as_tree.right
);
418 free_value
(v
->as_tree.left
);
428 parse
(const char *text
)
431 if
(yylex_init
(&scanner
))
432 error (0, errno
, "failed to initialize query parser");
433 YY_BUFFER_STATE buf
= yy_scan_string
(text
, scanner
);
434 value_t
*result
= NULL
;
435 value_t
*r
= yyparse (scanner
, &result
) == 0 ? result
: NULL
;
438 yy_delete_buffer
(buf
, scanner
);
439 yylex_destroy
(scanner
);
444 * Return the string value of an expression for comparison or display, iff
445 * all component parts are string-valued themselves. That excludes numbers
449 string_value
(value_t
*v
, const getter_t
*oget
, const getter_t
*sget
)
458 v
->resolved
= oget ? CALL_GETTER
(oget
,v
->as_str
) : NULL
;
462 return sget ? CALL_GETTER
(sget
,v
->as_str
) : NULL
;
465 left
= string_value
(v
->as_tree.left
,oget
,sget
);
467 v
->resolved
= follow_link
((char *)left
,
468 (char *)v
->as_tree.right
);
478 * Check whether a string looks like a simple decimal number. There's
479 * probably a library function for this somewhere.
482 is_ok_number
(const char *a_str
)
490 for
(p
= a_str
; *p
; ++p
) {
500 * Comparisons are a bit messy. If both sides are numbers, strings that look
501 * like numbers, or expressions that evaluate to numbers (booleans evaluate
502 * to 0/1), then we do a numeric comparison. Otherwise, if both sides
503 * evaluate to strings, we attempt a string comparison. That's the logic,
504 * but the code is actually structured a different way to allow re-use of
505 * common operator-specific code at the end for both cases.
508 compare
(value_t
*left
, comp_t op
, value_t
*right
,
509 const getter_t
*oget
, const getter_t
*sget
)
513 int lval
= 0; // solely to placate gcc
517 lstr
= string_value
(left
,oget
,sget
);
518 rstr
= string_value
(right
,oget
,sget
);
520 if
(left
->type
== T_NUMBER
) {
524 if
(is_ok_number
(lstr
)) {
525 lval
= strtoll
(lstr
,NULL
,0);
532 lval
= eval
(left
,oget
,sget
);
538 if
(right
->type
== T_NUMBER
) {
539 rval
= right
->as_num
;
542 if
(is_ok_number
(rstr
)) {
543 rval
= strtoll
(rstr
,NULL
,0);
550 rval
= eval
(right
,oget
,sget
);
557 * Strcmp returns -1/0/1, but -1 for us would mean an error and
558 * which of 0/1 we return depends on which comparison operatoer
559 * we're dealing with. Therefore, we stick the strcmp result on
560 * the left side and let the switch below do an operator-appropriate
561 * compare against zero on the right.
564 if
(!lstr ||
!rstr
) {
567 lval
= strcmp
(lstr
,rstr
);
572 case C_LESSTHAN
: return
(lval
< rval
);
573 case C_LESSOREQ
: return
(lval
<= rval
);
574 case C_EQUAL
: return
(lval
== rval
);
575 case C_DIFFERENT
: return
(lval
!= rval
);
576 case C_GREATEROREQ
: return
(lval
>= rval
);
577 case C_GREATERTHAN
: return
(lval
> rval
);
584 * Evaluate an AST in the current context to one of:
588 * It's up to the caller whether error is functionally the same as false.
589 * Note that even T_NUMBER gets squeezed down to these three values. The
590 * only thing numbers are used for is comparing against other numbers to
591 * yield a boolean for the query or replication-policy code. If you want
592 * something that returns a number, this is the wrong language for it.
596 eval
(const value_t
*v
, const getter_t
*oget
, const getter_t
*sget
)
603 return v
->as_num
!= 0;
605 return v
->as_str
&& *v
->as_str
;
607 str
= CALL_GETTER
(oget
,v
->as_str
);
610 str
= CALL_GETTER
(sget
,v
->as_str
);
613 str
= string_value
(v
->as_tree.left
,oget
,sget
);
615 str
= follow_link
(str
,(char *)v
->as_tree.right
);
619 return compare
(v
->as_tree.left
,(comp_t
)v
->as_tree.op
,
620 v
->as_tree.right
, oget
, sget
);
622 res
= eval
(v
->as_tree.left
,oget
,sget
);
623 return
(res
>= 0) ?
!res
: res
;
625 res
= eval
(v
->as_tree.left
,oget
,sget
);
627 res
= eval
(v
->as_tree.right
,oget
,sget
);
631 res
= eval
(v
->as_tree.left
,oget
,sget
);
635 return eval
(v
->as_tree.right
,oget
,sget
);
641 #ifdef PARSER_UNIT_TEST
643 main
(int argc
, char **argv
)
647 for
(i
= 1; i
< argc
; ++i
)
649 value_t
*expr
= parse
(argv
[i
]);
652 printf
("could not parse '%s'\n", argv
[i
]);
659 const char *str
= string_value
(expr
, &unit_oget
, &unit_sget
);
662 printf
("s= %s\n", str
);
665 printf
("d= %d\n", eval
(expr
, &unit_oget
, &unit_sget
));