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
261 error (EXIT_FAILURE
, 0, "%s", "memory exhausted");
263 /* The `noreturn' cannot be given to error, since it may return if
264 its first argument is 0. To help compilers understand the
265 xalloc_die does not return, call abort. Also, the abort is a
266 safety feature if exit_failure is 0 (which shouldn't happen). */
270 /* Allocate N bytes of memory dynamically, with error checking. */
274 void *p
= malloc
(n
);
280 /* Clone an object P of size S, with error checking. There's no need
281 for xnmemdup (P, N, S), since xmemdup (P, N * S) works without any
282 need for an arithmetic overflow check. */
284 xmemdup
(void const *p
, size_t s
)
286 return memcpy
(xmalloc
(s
), p
, s
);
291 xstrdup
(char const *string)
293 return xmemdup
(string, strlen
(string) + 1);
296 static const struct { char *name
; char *value
; } hacked_obj_fields
[] = {
297 /* Fake object fields for generic unit testing. */
298 { "a", "2" }, { "b", "7" }, { "c", "11" },
299 /* This one's here to test links (e.g. $template.owner.name). */
300 { "template", "templates/the_tmpl" },
304 /* Fake out the eval code for unit testing. */
306 unit_oget_func
(void * notused
, const char *text
)
310 for
(i
= 0; hacked_obj_fields
[i
].name
; ++i
) {
311 if
(!strcmp
(hacked_obj_fields
[i
].name
,text
)) {
312 return xstrdup
(hacked_obj_fields
[i
].value
);
318 static const getter_t unit_oget
= { unit_oget_func
};
321 * Same as above, but the site-field stuff is so similar to the object-field
322 * stuff that it's not worth exercising too much separately.
325 unit_sget_func
(void * notused
, const char *text
)
329 static const getter_t unit_sget
= { unit_sget_func
};
331 /* Fake links from an object/key tuple to an object/key string. */
332 static const struct { char *obj
; char *key
; char *value
; } hacked_links
[] = {
333 { "templates/the_tmpl", "owner", "users/the_user" },
334 { "users/the_user", "name", "Jeff Darcy" },
339 follow_link
(const char *object
, const char *key
)
343 for
(i
= 0; hacked_links
[i
].obj
; ++i
) {
344 if
(strcmp
(object
,hacked_links
[i
].obj
)) {
347 if
(strcmp
(key
,hacked_links
[i
].key
)) {
350 return hacked_links
[i
].value
;
356 extern
char *follow_link
(const char *object
, const char *key
);
360 _print_value
(const value_t
*v
, int level
)
363 printf
("%*sNULL\n",level
,"");
369 printf
("%*sNUMBER %lld\n",level
,"",v
->as_num
);
372 printf
("%*sSTRING %s\n",level
,"",v
->as_str
);
375 #if defined PARSER_UNIT_TEST
376 printf
("%*sOBJECT FIELD %s (%s)\n",level
,"",v
->as_str
,
377 unit_oget_func
(NULL
,v
->as_str
));
379 printf
("%*sOBJECT FIELD %s\n",level
,"",v
->as_str
);
383 #if defined PARSER_UNIT_TEST
384 printf
("%*sSERVER FIELD %s (%s)\n",level
,"",v
->as_str
,
385 unit_sget_func
(NULL
,v
->as_str
));
387 printf
("%*sSERVER FIELD %s\n",level
,"",v
->as_str
);
391 printf
("%*sCOMPARISON\n",level
,"");
392 _print_value
(v
->as_tree.left
,level
+2);
393 _print_value
(v
->as_tree.right
,level
+2);
396 printf
("%*sNOT\n",level
,"");
397 _print_value
(v
->as_tree.left
,level
+2);
400 printf
("%*sAND\n",level
,"");
401 _print_value
(v
->as_tree.left
,level
+2);
402 _print_value
(v
->as_tree.right
,level
+2);
405 printf
("%*sOR\n",level
,"");
406 _print_value
(v
->as_tree.left
,level
+2);
407 _print_value
(v
->as_tree.right
,level
+2);
410 printf
("%*sLINK\n",level
,"");
411 _print_value
(v
->as_tree.left
,level
+2);
412 printf
("%*sDEST FIELD %s\n",level
+2,"",
413 (char *)v
->as_tree.right
);
416 printf
("%*sUNKNOWN %d\n",level
,"",v
->type
);
421 print_value
(const value_t
*v
)
427 free_value
(value_t
*v
)
429 if
(v
== NULL || v
== &invalid
) {
433 free
((void *)v
->resolved
);
444 free_value
(v
->as_tree.left
);
445 free
(v
->as_tree.right
);
451 free_value
(v
->as_tree.right
);
454 free_value
(v
->as_tree.left
);
464 parse
(const char *text
)
467 if
(yylex_init
(&scanner
))
468 error (0, errno
, "failed to initialize query parser");
469 YY_BUFFER_STATE buf
= yy_scan_string
(text
, scanner
);
470 value_t
*result
= NULL
;
471 value_t
*r
= yyparse (scanner
, &result
) == 0 ? result
: NULL
;
474 yy_delete_buffer
(buf
, scanner
);
475 yylex_destroy
(scanner
);
480 * Return the string value of an expression for comparison or display, iff
481 * all component parts are string-valued themselves. That excludes numbers
485 string_value
(value_t
*v
, const getter_t
*oget
, const getter_t
*sget
)
494 v
->resolved
= oget ? CALL_GETTER
(oget
,v
->as_str
) : NULL
;
498 return sget ? CALL_GETTER
(sget
,v
->as_str
) : NULL
;
501 left
= string_value
(v
->as_tree.left
,oget
,sget
);
503 v
->resolved
= follow_link
((char *)left
,
504 (char *)v
->as_tree.right
);
514 * Check whether a string looks like a simple decimal number. There's
515 * probably a library function for this somewhere.
518 is_ok_number
(const char *a_str
)
526 for
(p
= a_str
; *p
; ++p
) {
536 * Comparisons are a bit messy. If both sides are numbers, strings that look
537 * like numbers, or expressions that evaluate to numbers (booleans evaluate
538 * to 0/1), then we do a numeric comparison. Otherwise, if both sides
539 * evaluate to strings, we attempt a string comparison. That's the logic,
540 * but the code is actually structured a different way to allow re-use of
541 * common operator-specific code at the end for both cases.
544 compare
(value_t
*left
, comp_t op
, value_t
*right
,
545 const getter_t
*oget
, const getter_t
*sget
)
549 int lval
= 0; // solely to placate gcc
553 lstr
= string_value
(left
,oget
,sget
);
554 rstr
= string_value
(right
,oget
,sget
);
556 if
(left
->type
== T_NUMBER
) {
560 if
(is_ok_number
(lstr
)) {
561 lval
= strtoll
(lstr
,NULL
,0);
568 lval
= eval
(left
,oget
,sget
);
574 if
(right
->type
== T_NUMBER
) {
575 rval
= right
->as_num
;
578 if
(is_ok_number
(rstr
)) {
579 rval
= strtoll
(rstr
,NULL
,0);
586 rval
= eval
(right
,oget
,sget
);
593 * Strcmp returns -1/0/1, but -1 for us would mean an error and
594 * which of 0/1 we return depends on which comparison operatoer
595 * we're dealing with. Therefore, we stick the strcmp result on
596 * the left side and let the switch below do an operator-appropriate
597 * compare against zero on the right.
600 if
(!lstr ||
!rstr
) {
603 lval
= strcmp
(lstr
,rstr
);
608 case C_LESSTHAN
: return
(lval
< rval
);
609 case C_LESSOREQ
: return
(lval
<= rval
);
610 case C_EQUAL
: return
(lval
== rval
);
611 case C_DIFFERENT
: return
(lval
!= rval
);
612 case C_GREATEROREQ
: return
(lval
>= rval
);
613 case C_GREATERTHAN
: return
(lval
> rval
);
620 * Evaluate an AST in the current context to one of:
624 * It's up to the caller whether error is functionally the same as false.
625 * Note that even T_NUMBER gets squeezed down to these three values. The
626 * only thing numbers are used for is comparing against other numbers to
627 * yield a boolean for the query or replication-policy code. If you want
628 * something that returns a number, this is the wrong language for it.
632 eval
(const value_t
*v
, const getter_t
*oget
, const getter_t
*sget
)
639 return v
->as_num
!= 0;
641 return v
->as_str
&& *v
->as_str
;
643 str
= CALL_GETTER
(oget
,v
->as_str
);
646 str
= CALL_GETTER
(sget
,v
->as_str
);
649 str
= string_value
(v
->as_tree.left
,oget
,sget
);
651 str
= follow_link
(str
,(char *)v
->as_tree.right
);
655 return compare
(v
->as_tree.left
,(comp_t
)v
->as_tree.op
,
656 v
->as_tree.right
, oget
, sget
);
658 res
= eval
(v
->as_tree.left
,oget
,sget
);
659 return
(res
>= 0) ?
!res
: res
;
661 res
= eval
(v
->as_tree.left
,oget
,sget
);
663 res
= eval
(v
->as_tree.right
,oget
,sget
);
667 res
= eval
(v
->as_tree.left
,oget
,sget
);
671 return eval
(v
->as_tree.right
,oget
,sget
);
677 #ifdef PARSER_UNIT_TEST
679 main
(int argc
, char **argv
)
683 for
(i
= 1; i
< argc
; ++i
)
685 value_t
*expr
= parse
(argv
[i
]);
688 printf
("could not parse '%s'\n", argv
[i
]);
695 const char *str
= string_value
(expr
, &unit_oget
, &unit_sget
);
698 printf
("s= %s\n", str
);
701 printf
("d= %d\n", eval
(expr
, &unit_oget
, &unit_sget
));