1 /*
2 * SysDB - src/frontend/grammar.y
3 * Copyright (C) 2013 Sebastian 'tokkee' Harl <sh@tokkee.org>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
17 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
19 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
28 %{
30 #include "frontend/connection-private.h"
31 #include "frontend/parser.h"
32 #include "frontend/grammar.h"
34 #include "core/store.h"
35 #include "core/store-private.h"
36 #include "core/time.h"
38 #include "utils/error.h"
39 #include "utils/llist.h"
41 #include <assert.h>
43 #include <stdio.h>
44 #include <string.h>
46 int
47 sdb_fe_yylex(YYSTYPE *yylval, YYLTYPE *yylloc, sdb_fe_yyscan_t yyscanner);
49 sdb_fe_yyextra_t *
50 sdb_fe_yyget_extra(sdb_fe_yyscan_t scanner);
52 void
53 sdb_fe_yyerror(YYLTYPE *lval, sdb_fe_yyscan_t scanner, const char *msg);
55 /* quick access to the current parse tree */
56 #define pt sdb_fe_yyget_extra(scanner)->parsetree
58 /* quick access to the parser mode */
59 #define parser_mode sdb_fe_yyget_extra(scanner)->mode
61 #define MODE_TO_STRING(m) \
62 (((m) == SDB_PARSE_DEFAULT) ? "statement" \
63 : ((m) == SDB_PARSE_COND) ? "condition" \
64 : ((m) == SDB_PARSE_EXPR) ? "expression" \
65 : "UNKNOWN")
67 %}
69 %pure-parser
70 %lex-param {sdb_fe_yyscan_t scanner}
71 %parse-param {sdb_fe_yyscan_t scanner}
72 %locations
73 %error-verbose
74 %expect 0
75 %name-prefix "sdb_fe_yy"
77 %union {
78 const char *sstr; /* static string */
79 char *str;
81 sdb_data_t data;
82 sdb_time_t datetime;
84 sdb_llist_t *list;
85 sdb_conn_node_t *node;
87 sdb_store_matcher_t *m;
88 sdb_store_expr_t *expr;
89 }
91 %start statements
93 %token SCANNER_ERROR
95 %token AND OR IS NOT MATCHING FILTER
96 %token CMP_EQUAL CMP_NEQUAL CMP_REGEX CMP_NREGEX
97 %token CMP_LT CMP_LE CMP_GE CMP_GT IN
98 %token CONCAT
100 %token START END
102 /* NULL token */
103 %token NULL_T
105 %token FETCH LIST LOOKUP TIMESERIES
107 %token <str> IDENTIFIER STRING
109 %token <data> INTEGER FLOAT
111 %token <datetime> DATE TIME
113 /* Precedence (lowest first): */
114 %left OR
115 %left AND
116 %right NOT
117 %left CMP_EQUAL CMP_NEQUAL
118 %left CMP_LT CMP_LE CMP_GE CMP_GT
119 %nonassoc CMP_REGEX CMP_NREGEX
120 %nonassoc IN
121 %left CONCAT
122 %nonassoc IS
123 %left '+' '-'
124 %left '*' '/' '%'
125 %left '[' ']'
126 %left '(' ')'
127 %left '.'
129 %type <list> statements
130 %type <node> statement
131 fetch_statement
132 list_statement
133 lookup_statement
134 timeseries_statement
135 matching_clause
136 filter_clause
137 condition
139 %type <m> matcher
140 compare_matcher
142 %type <expr> expression
144 %type <sstr> cmp
146 %type <data> data
147 interval interval_elem
149 %type <datetime> datetime
150 start_clause end_clause
152 %destructor { free($$); } <str>
153 %destructor { sdb_object_deref(SDB_OBJ($$)); } <node> <m> <expr>
154 %destructor { sdb_data_free_datum(&$$); } <data>
156 %%
158 statements:
159 statements ';' statement
160 {
161 /* only accepted in default parse mode */
162 if (parser_mode != SDB_PARSE_DEFAULT) {
163 char errmsg[1024];
164 snprintf(errmsg, sizeof(errmsg),
165 YY_("syntax error, unexpected statement, "
166 "expecting %s"), MODE_TO_STRING(parser_mode));
167 sdb_fe_yyerror(&yylloc, scanner, errmsg);
168 sdb_object_deref(SDB_OBJ($3));
169 YYABORT;
170 }
172 if ($3) {
173 sdb_llist_append(pt, SDB_OBJ($3));
174 sdb_object_deref(SDB_OBJ($3));
175 }
176 }
177 |
178 statement
179 {
180 /* only accepted in default parse mode */
181 if (parser_mode != SDB_PARSE_DEFAULT) {
182 char errmsg[1024];
183 snprintf(errmsg, sizeof(errmsg),
184 YY_("syntax error, unexpected statement, "
185 "expecting %s"), MODE_TO_STRING(parser_mode));
186 sdb_fe_yyerror(&yylloc, scanner, errmsg);
187 sdb_object_deref(SDB_OBJ($1));
188 YYABORT;
189 }
191 if ($1) {
192 sdb_llist_append(pt, SDB_OBJ($1));
193 sdb_object_deref(SDB_OBJ($1));
194 }
195 }
196 |
197 condition
198 {
199 /* only accepted in condition parse mode */
200 if (! (parser_mode & SDB_PARSE_COND)) {
201 char errmsg[1024];
202 snprintf(errmsg, sizeof(errmsg),
203 YY_("syntax error, unexpected condition, "
204 "expecting %s"), MODE_TO_STRING(parser_mode));
205 sdb_fe_yyerror(&yylloc, scanner, errmsg);
206 sdb_object_deref(SDB_OBJ($1));
207 YYABORT;
208 }
210 if ($1) {
211 sdb_llist_append(pt, SDB_OBJ($1));
212 sdb_object_deref(SDB_OBJ($1));
213 }
214 }
215 |
216 expression
217 {
218 /* only accepted in expression parse mode */
219 if (! (parser_mode & SDB_PARSE_EXPR)) {
220 char errmsg[1024];
221 snprintf(errmsg, sizeof(errmsg),
222 YY_("syntax error, unexpected expression, "
223 "expecting %s"), MODE_TO_STRING(parser_mode));
224 sdb_fe_yyerror(&yylloc, scanner, errmsg);
225 sdb_object_deref(SDB_OBJ($1));
226 YYABORT;
227 }
229 if ($1) {
230 sdb_conn_node_t *n;
231 n = SDB_CONN_NODE(sdb_object_create_dT(/* name = */ NULL,
232 conn_expr_t, conn_expr_destroy));
233 n->cmd = CONNECTION_EXPR;
234 CONN_EXPR(n)->expr = $1;
236 sdb_llist_append(pt, SDB_OBJ(n));
237 sdb_object_deref(SDB_OBJ(n));
238 }
239 }
240 ;
242 statement:
243 fetch_statement
244 |
245 list_statement
246 |
247 lookup_statement
248 |
249 timeseries_statement
250 |
251 /* empty */
252 {
253 $$ = NULL;
254 }
255 ;
257 /*
258 * FETCH <type> <hostname> [FILTER <condition>];
259 *
260 * Retrieve detailed information about a single host.
261 */
262 fetch_statement:
263 FETCH IDENTIFIER STRING filter_clause
264 {
265 /* TODO: support other types as well */
266 if (strcasecmp($2, "host")) {
267 char errmsg[strlen($2) + 32];
268 snprintf(errmsg, sizeof(errmsg),
269 YY_("unknown data-source %s"), $2);
270 sdb_fe_yyerror(&yylloc, scanner, errmsg);
271 free($2); $2 = NULL;
272 free($3); $3 = NULL;
273 sdb_object_deref(SDB_OBJ($4));
274 YYABORT;
275 }
277 $$ = SDB_CONN_NODE(sdb_object_create_dT(/* name = */ NULL,
278 conn_fetch_t, conn_fetch_destroy));
279 CONN_FETCH($$)->type = SDB_HOST;
280 CONN_FETCH($$)->name = $3;
281 CONN_FETCH($$)->filter = CONN_MATCHER($4);
282 $$->cmd = CONNECTION_FETCH;
283 free($2); $2 = NULL;
284 }
285 ;
287 /*
288 * LIST <type> [FILTER <condition>];
289 *
290 * Returns a list of all hosts in the store.
291 */
292 list_statement:
293 LIST IDENTIFIER filter_clause
294 {
295 int type = sdb_store_parse_object_type_plural($2);
296 if (type < 0) {
297 char errmsg[strlen($2) + 32];
298 snprintf(errmsg, sizeof(errmsg),
299 YY_("unknown data-source %s"), $2);
300 sdb_fe_yyerror(&yylloc, scanner, errmsg);
301 free($2); $2 = NULL;
302 sdb_object_deref(SDB_OBJ($3));
303 YYABORT;
304 }
306 $$ = SDB_CONN_NODE(sdb_object_create_dT(/* name = */ NULL,
307 conn_list_t, conn_list_destroy));
308 CONN_LIST($$)->type = type;
309 CONN_LIST($$)->filter = CONN_MATCHER($3);
310 $$->cmd = CONNECTION_LIST;
311 free($2); $2 = NULL;
312 }
313 ;
315 /*
316 * LOOKUP <type> MATCHING <condition> [FILTER <condition>];
317 *
318 * Returns detailed information about <type> matching condition.
319 */
320 lookup_statement:
321 LOOKUP IDENTIFIER matching_clause filter_clause
322 {
323 /* TODO: support other types as well */
324 if (strcasecmp($2, "hosts")) {
325 char errmsg[strlen($2) + 32];
326 snprintf(errmsg, sizeof(errmsg),
327 YY_("unknown data-source %s"), $2);
328 sdb_fe_yyerror(&yylloc, scanner, errmsg);
329 free($2); $2 = NULL;
330 sdb_object_deref(SDB_OBJ($3));
331 sdb_object_deref(SDB_OBJ($4));
332 YYABORT;
333 }
335 $$ = SDB_CONN_NODE(sdb_object_create_dT(/* name = */ NULL,
336 conn_lookup_t, conn_lookup_destroy));
337 CONN_LOOKUP($$)->type = SDB_HOST;
338 CONN_LOOKUP($$)->matcher = CONN_MATCHER($3);
339 CONN_LOOKUP($$)->filter = CONN_MATCHER($4);
340 $$->cmd = CONNECTION_LOOKUP;
341 free($2); $2 = NULL;
342 }
343 ;
345 matching_clause:
346 MATCHING condition { $$ = $2; }
347 |
348 /* empty */ { $$ = NULL; }
350 filter_clause:
351 FILTER condition { $$ = $2; }
352 |
353 /* empty */ { $$ = NULL; }
355 /*
356 * TIMESERIES <host>.<metric> [START <datetime>] [END <datetime>];
357 *
358 * Returns a time-series for the specified host's metric.
359 */
360 timeseries_statement:
361 TIMESERIES STRING '.' STRING start_clause end_clause
362 {
363 $$ = SDB_CONN_NODE(sdb_object_create_dT(/* name = */ NULL,
364 conn_ts_t, conn_ts_destroy));
365 CONN_TS($$)->hostname = $2;
366 CONN_TS($$)->metric = $4;
367 CONN_TS($$)->opts.start = $5;
368 CONN_TS($$)->opts.end = $6;
369 $$->cmd = CONNECTION_TIMESERIES;
370 }
371 ;
373 start_clause:
374 START datetime { $$ = $2; }
375 |
376 /* empty */ { $$ = sdb_gettime() - SDB_INTERVAL_HOUR; }
378 end_clause:
379 END datetime { $$ = $2; }
380 |
381 /* empty */ { $$ = sdb_gettime(); }
383 /*
384 * Basic expressions.
385 */
387 condition:
388 matcher
389 {
390 if (! $1) {
391 /* TODO: improve error reporting */
392 sdb_fe_yyerror(&yylloc, scanner,
393 YY_("syntax error, invalid condition"));
394 YYABORT;
395 }
397 $$ = SDB_CONN_NODE(sdb_object_create_dT(/* name = */ NULL,
398 conn_matcher_t, conn_matcher_destroy));
399 $$->cmd = CONNECTION_MATCHER;
400 CONN_MATCHER($$)->matcher = $1;
401 }
402 ;
404 matcher:
405 '(' matcher ')'
406 {
407 $$ = $2;
408 }
409 |
410 matcher AND matcher
411 {
412 $$ = sdb_store_con_matcher($1, $3);
413 sdb_object_deref(SDB_OBJ($1));
414 sdb_object_deref(SDB_OBJ($3));
415 }
416 |
417 matcher OR matcher
418 {
419 $$ = sdb_store_dis_matcher($1, $3);
420 sdb_object_deref(SDB_OBJ($1));
421 sdb_object_deref(SDB_OBJ($3));
422 }
423 |
424 NOT matcher
425 {
426 $$ = sdb_store_inv_matcher($2);
427 sdb_object_deref(SDB_OBJ($2));
428 }
429 |
430 compare_matcher
431 {
432 $$ = $1;
433 }
434 ;
436 compare_matcher:
437 expression cmp expression
438 {
439 sdb_store_matcher_op_cb cb = sdb_store_parse_matcher_op($2);
440 assert(cb); /* else, the grammar accepts invalid 'cmp' */
441 $$ = cb($1, $3);
442 sdb_object_deref(SDB_OBJ($1));
443 sdb_object_deref(SDB_OBJ($3));
444 }
445 |
446 IDENTIFIER cmp expression
447 {
448 $$ = sdb_store_matcher_parse_cmp($1, NULL, $2, $3);
449 free($1); $1 = NULL;
450 sdb_object_deref(SDB_OBJ($3));
451 }
452 |
453 expression IS NULL_T
454 {
455 $$ = sdb_store_isnull_matcher($1);
456 sdb_object_deref(SDB_OBJ($1));
457 }
458 |
459 expression IS NOT NULL_T
460 {
461 $$ = sdb_store_isnnull_matcher($1);
462 sdb_object_deref(SDB_OBJ($1));
463 }
464 |
465 expression IN expression
466 {
467 $$ = sdb_store_in_matcher($1, $3);
468 sdb_object_deref(SDB_OBJ($1));
469 sdb_object_deref(SDB_OBJ($3));
470 }
471 ;
473 expression:
474 '(' expression ')'
475 {
476 $$ = $2;
477 }
478 |
479 expression '+' expression
480 {
481 $$ = sdb_store_expr_create(SDB_DATA_ADD, $1, $3);
482 sdb_object_deref(SDB_OBJ($1)); $1 = NULL;
483 sdb_object_deref(SDB_OBJ($3)); $3 = NULL;
484 }
485 |
486 expression '-' expression
487 {
488 $$ = sdb_store_expr_create(SDB_DATA_SUB, $1, $3);
489 sdb_object_deref(SDB_OBJ($1)); $1 = NULL;
490 sdb_object_deref(SDB_OBJ($3)); $3 = NULL;
491 }
492 |
493 expression '*' expression
494 {
495 $$ = sdb_store_expr_create(SDB_DATA_MUL, $1, $3);
496 sdb_object_deref(SDB_OBJ($1)); $1 = NULL;
497 sdb_object_deref(SDB_OBJ($3)); $3 = NULL;
498 }
499 |
500 expression '/' expression
501 {
502 $$ = sdb_store_expr_create(SDB_DATA_DIV, $1, $3);
503 sdb_object_deref(SDB_OBJ($1)); $1 = NULL;
504 sdb_object_deref(SDB_OBJ($3)); $3 = NULL;
505 }
506 |
507 expression '%' expression
508 {
509 $$ = sdb_store_expr_create(SDB_DATA_MOD, $1, $3);
510 sdb_object_deref(SDB_OBJ($1)); $1 = NULL;
511 sdb_object_deref(SDB_OBJ($3)); $3 = NULL;
512 }
513 |
514 expression CONCAT expression
515 {
516 $$ = sdb_store_expr_create(SDB_DATA_CONCAT, $1, $3);
517 sdb_object_deref(SDB_OBJ($1)); $1 = NULL;
518 sdb_object_deref(SDB_OBJ($3)); $3 = NULL;
519 }
520 |
521 '.' IDENTIFIER
522 {
523 int field = sdb_store_parse_field_name($2);
524 free($2); $2 = NULL;
525 $$ = sdb_store_expr_fieldvalue(field);
526 }
527 |
528 IDENTIFIER '[' STRING ']'
529 {
530 if (strcasecmp($1, "attribute")) {
531 char errmsg[strlen($1) + strlen($3) + 32];
532 snprintf(errmsg, sizeof(errmsg),
533 YY_("unknown value %s[%s]"), $1, $3);
534 sdb_fe_yyerror(&yylloc, scanner, errmsg);
535 free($1); $1 = NULL;
536 free($3); $3 = NULL;
537 YYABORT;
538 }
539 $$ = sdb_store_expr_attrvalue($3);
540 free($1); $1 = NULL;
541 free($3); $3 = NULL;
542 }
543 |
544 data
545 {
546 $$ = sdb_store_expr_constvalue(&$1);
547 sdb_data_free_datum(&$1);
548 }
549 ;
551 cmp:
552 CMP_EQUAL { $$ = "="; }
553 |
554 CMP_NEQUAL { $$ = "!="; }
555 |
556 CMP_REGEX { $$ = "=~"; }
557 |
558 CMP_NREGEX { $$ = "!~"; }
559 |
560 CMP_LT { $$ = "<"; }
561 |
562 CMP_LE { $$ = "<="; }
563 |
564 CMP_GE { $$ = ">="; }
565 |
566 CMP_GT { $$ = ">"; }
567 ;
569 data:
570 STRING { $$.type = SDB_TYPE_STRING; $$.data.string = $1; }
571 |
572 INTEGER { $$ = $1; }
573 |
574 FLOAT { $$ = $1; }
575 |
576 datetime { $$.type = SDB_TYPE_DATETIME; $$.data.datetime = $1; }
577 |
578 interval { $$ = $1; }
579 ;
581 datetime:
582 DATE TIME { $$ = $1 + $2; }
583 |
584 DATE { $$ = $1; }
585 |
586 TIME { $$ = $1; }
587 ;
589 interval:
590 interval interval_elem
591 {
592 $$.data.datetime = $1.data.datetime + $2.data.datetime;
593 }
594 |
595 interval_elem { $$ = $1; }
596 ;
598 interval_elem:
599 INTEGER IDENTIFIER
600 {
601 sdb_time_t unit = 1;
603 unit = sdb_strpunit($2);
604 if (! unit) {
605 char errmsg[strlen($2) + 32];
606 snprintf(errmsg, sizeof(errmsg),
607 YY_("invalid time unit %s"), $2);
608 sdb_fe_yyerror(&yylloc, scanner, errmsg);
609 free($2); $2 = NULL;
610 YYABORT;
611 }
612 free($2); $2 = NULL;
614 $$.type = SDB_TYPE_DATETIME;
615 $$.data.datetime = (sdb_time_t)$1.data.integer * unit;
617 if ($1.data.integer < 0) {
618 sdb_fe_yyerror(&yylloc, scanner,
619 YY_("syntax error, negative intervals not supported"));
620 YYABORT;
621 }
622 }
623 ;
625 %%
627 void
628 sdb_fe_yyerror(YYLTYPE *lval, sdb_fe_yyscan_t scanner, const char *msg)
629 {
630 sdb_log(SDB_LOG_ERR, "frontend: parse error: %s", msg);
631 } /* sdb_fe_yyerror */
633 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */