129b4613009c9d5fc12aa71d2a470c0b02feed31
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 <stdio.h>
42 #include <string.h>
44 int
45 sdb_fe_yylex(YYSTYPE *yylval, YYLTYPE *yylloc, sdb_fe_yyscan_t yyscanner);
47 sdb_fe_yyextra_t *
48 sdb_fe_yyget_extra(sdb_fe_yyscan_t scanner);
50 void
51 sdb_fe_yyerror(YYLTYPE *lval, sdb_fe_yyscan_t scanner, const char *msg);
53 /* quick access to the current parse tree */
54 #define pt sdb_fe_yyget_extra(scanner)->parsetree
56 /* quick access to the parser mode */
57 #define parser_mode sdb_fe_yyget_extra(scanner)->mode
59 %}
61 %pure-parser
62 %lex-param {sdb_fe_yyscan_t scanner}
63 %parse-param {sdb_fe_yyscan_t scanner}
64 %locations
65 %error-verbose
66 %expect 0
67 %name-prefix "sdb_fe_yy"
69 %union {
70 const char *sstr; /* static string */
71 char *str;
73 sdb_data_t data;
74 sdb_time_t datetime;
76 sdb_llist_t *list;
77 sdb_conn_node_t *node;
79 sdb_store_matcher_t *m;
80 sdb_store_expr_t *expr;
81 }
83 %start statements
85 %token SCANNER_ERROR
87 %token AND OR IS NOT MATCHING FILTER
88 %token CMP_EQUAL CMP_NEQUAL CMP_REGEX CMP_NREGEX
89 %token CMP_LT CMP_LE CMP_GE CMP_GT
90 %token CONCAT
92 %token START END
94 /* NULL token */
95 %token NULL_T
97 %token FETCH LIST LOOKUP TIMESERIES
99 %token <str> IDENTIFIER STRING
101 %token <data> INTEGER FLOAT
103 %token <datetime> DATE TIME
105 /* Precedence (lowest first): */
106 %left OR
107 %left AND
108 %right NOT
109 %left CMP_EQUAL CMP_NEQUAL
110 %left CMP_LT CMP_LE CMP_GE CMP_GT
111 %nonassoc CMP_REGEX CMP_NREGEX
112 %left CONCAT
113 %nonassoc IS
114 %left '+' '-'
115 %left '*' '/' '%'
116 %left '(' ')'
117 %left '.'
119 %type <list> statements
120 %type <node> statement
121 fetch_statement
122 list_statement
123 lookup_statement
124 timeseries_statement
125 matching_clause
126 filter_clause
127 condition
129 %type <m> matcher
130 compare_matcher
132 %type <expr> expression
134 %type <sstr> op
136 %type <data> data
137 interval interval_elem
139 %type <datetime> datetime
140 start_clause end_clause
142 %destructor { free($$); } <str>
143 %destructor { sdb_object_deref(SDB_OBJ($$)); } <node> <m> <expr>
145 %%
147 statements:
148 statements ';' statement
149 {
150 /* only accept this in default parse mode */
151 if (parser_mode != SDB_PARSE_DEFAULT) {
152 sdb_fe_yyerror(&yylloc, scanner,
153 YY_("syntax error, unexpected statement, "
154 "expecting condition"));
155 sdb_object_deref(SDB_OBJ($3));
156 YYABORT;
157 }
159 if ($3) {
160 sdb_llist_append(pt, SDB_OBJ($3));
161 sdb_object_deref(SDB_OBJ($3));
162 }
163 }
164 |
165 statement
166 {
167 /* only accept this in default parse mode */
168 if (parser_mode != SDB_PARSE_DEFAULT) {
169 sdb_fe_yyerror(&yylloc, scanner,
170 YY_("syntax error, unexpected statement, "
171 "expecting condition"));
172 sdb_object_deref(SDB_OBJ($1));
173 YYABORT;
174 }
176 if ($1) {
177 sdb_llist_append(pt, SDB_OBJ($1));
178 sdb_object_deref(SDB_OBJ($1));
179 }
180 }
181 |
182 condition
183 {
184 /* only accept this in condition parse mode */
185 if (! (parser_mode & SDB_PARSE_COND)) {
186 sdb_fe_yyerror(&yylloc, scanner,
187 YY_("syntax error, unexpected condition, "
188 "expecting statement"));
189 sdb_object_deref(SDB_OBJ($1));
190 YYABORT;
191 }
193 if ($1) {
194 sdb_llist_append(pt, SDB_OBJ($1));
195 sdb_object_deref(SDB_OBJ($1));
196 }
197 }
198 ;
200 statement:
201 fetch_statement
202 |
203 list_statement
204 |
205 lookup_statement
206 |
207 timeseries_statement
208 |
209 /* empty */
210 {
211 $$ = NULL;
212 }
213 ;
215 /*
216 * FETCH <type> <hostname> [FILTER <condition>];
217 *
218 * Retrieve detailed information about a single host.
219 */
220 fetch_statement:
221 FETCH IDENTIFIER STRING filter_clause
222 {
223 /* TODO: support other types as well */
224 if (strcasecmp($2, "host")) {
225 char errmsg[strlen($2) + 32];
226 snprintf(errmsg, sizeof(errmsg),
227 YY_("unknown data-source %s"), $2);
228 sdb_fe_yyerror(&yylloc, scanner, errmsg);
229 free($2); $2 = NULL;
230 free($3); $3 = NULL;
231 sdb_object_deref(SDB_OBJ($4));
232 YYABORT;
233 }
235 $$ = SDB_CONN_NODE(sdb_object_create_dT(/* name = */ NULL,
236 conn_fetch_t, conn_fetch_destroy));
237 CONN_FETCH($$)->name = $3;
238 CONN_FETCH($$)->filter = CONN_MATCHER($4);
239 $$->cmd = CONNECTION_FETCH;
240 free($2); $2 = NULL;
241 }
242 ;
244 /*
245 * LIST <type> [FILTER <condition>];
246 *
247 * Returns a list of all hosts in the store.
248 */
249 list_statement:
250 LIST IDENTIFIER filter_clause
251 {
252 int type = sdb_store_parse_object_type_plural($2);
253 if (type < 0) {
254 char errmsg[strlen($2) + 32];
255 snprintf(errmsg, sizeof(errmsg),
256 YY_("unknown data-source %s"), $2);
257 sdb_fe_yyerror(&yylloc, scanner, errmsg);
258 free($2); $2 = NULL;
259 sdb_object_deref(SDB_OBJ($3));
260 YYABORT;
261 }
263 $$ = SDB_CONN_NODE(sdb_object_create_dT(/* name = */ NULL,
264 conn_list_t, conn_list_destroy));
265 CONN_LIST($$)->type = type;
266 CONN_LIST($$)->filter = CONN_MATCHER($3);
267 $$->cmd = CONNECTION_LIST;
268 free($2); $2 = NULL;
269 }
270 ;
272 /*
273 * LOOKUP <type> MATCHING <condition> [FILTER <condition>];
274 *
275 * Returns detailed information about <type> matching condition.
276 */
277 lookup_statement:
278 LOOKUP IDENTIFIER matching_clause filter_clause
279 {
280 /* TODO: support other types as well */
281 if (strcasecmp($2, "hosts")) {
282 char errmsg[strlen($2) + 32];
283 snprintf(errmsg, sizeof(errmsg),
284 YY_("unknown data-source %s"), $2);
285 sdb_fe_yyerror(&yylloc, scanner, errmsg);
286 free($2); $2 = NULL;
287 sdb_object_deref(SDB_OBJ($3));
288 sdb_object_deref(SDB_OBJ($4));
289 YYABORT;
290 }
292 $$ = SDB_CONN_NODE(sdb_object_create_dT(/* name = */ NULL,
293 conn_lookup_t, conn_lookup_destroy));
294 CONN_LOOKUP($$)->matcher = CONN_MATCHER($3);
295 CONN_LOOKUP($$)->filter = CONN_MATCHER($4);
296 $$->cmd = CONNECTION_LOOKUP;
297 free($2); $2 = NULL;
298 }
299 ;
301 matching_clause:
302 MATCHING condition { $$ = $2; }
303 |
304 /* empty */ { $$ = NULL; }
306 filter_clause:
307 FILTER condition { $$ = $2; }
308 |
309 /* empty */ { $$ = NULL; }
311 /*
312 * TIMESERIES <host>.<metric> [START <datetime>] [END <datetime>];
313 *
314 * Returns a time-series for the specified host's metric.
315 */
316 timeseries_statement:
317 TIMESERIES STRING '.' STRING start_clause end_clause
318 {
319 $$ = SDB_CONN_NODE(sdb_object_create_dT(/* name = */ NULL,
320 conn_ts_t, conn_ts_destroy));
321 CONN_TS($$)->hostname = $2;
322 CONN_TS($$)->metric = $4;
323 CONN_TS($$)->opts.start = $5;
324 CONN_TS($$)->opts.end = $6;
325 $$->cmd = CONNECTION_TIMESERIES;
326 }
327 ;
329 start_clause:
330 START datetime { $$ = $2; }
331 |
332 /* empty */ { $$ = sdb_gettime() - SDB_INTERVAL_HOUR; }
334 end_clause:
335 END datetime { $$ = $2; }
336 |
337 /* empty */ { $$ = sdb_gettime(); }
339 /*
340 * Basic expressions.
341 */
343 condition:
344 matcher
345 {
346 if (! $1) {
347 /* TODO: improve error reporting */
348 sdb_fe_yyerror(&yylloc, scanner,
349 YY_("syntax error, invalid condition"));
350 YYABORT;
351 }
353 $$ = SDB_CONN_NODE(sdb_object_create_dT(/* name = */ NULL,
354 conn_matcher_t, conn_matcher_destroy));
355 $$->cmd = CONNECTION_EXPR;
356 CONN_MATCHER($$)->matcher = $1;
357 }
358 ;
360 matcher:
361 '(' matcher ')'
362 {
363 $$ = $2;
364 }
365 |
366 matcher AND matcher
367 {
368 $$ = sdb_store_con_matcher($1, $3);
369 sdb_object_deref(SDB_OBJ($1));
370 sdb_object_deref(SDB_OBJ($3));
371 }
372 |
373 matcher OR matcher
374 {
375 $$ = sdb_store_dis_matcher($1, $3);
376 sdb_object_deref(SDB_OBJ($1));
377 sdb_object_deref(SDB_OBJ($3));
378 }
379 |
380 NOT matcher
381 {
382 $$ = sdb_store_inv_matcher($2);
383 sdb_object_deref(SDB_OBJ($2));
384 }
385 |
386 compare_matcher
387 {
388 $$ = $1;
389 }
390 ;
392 /*
393 * <object_type>.<object_attr> <op> <value>
394 *
395 * Parse matchers comparing object attributes with a value.
396 */
397 compare_matcher:
398 ':' IDENTIFIER op expression
399 {
400 $$ = sdb_store_matcher_parse_field_cmp($2, $3, $4);
401 free($2); $2 = NULL;
402 sdb_object_deref(SDB_OBJ($4));
403 }
404 |
405 IDENTIFIER op expression
406 {
407 $$ = sdb_store_matcher_parse_cmp($1, NULL, $2, $3);
408 free($1); $1 = NULL;
409 sdb_object_deref(SDB_OBJ($3));
410 }
411 |
412 IDENTIFIER '.' IDENTIFIER op expression
413 {
414 $$ = sdb_store_matcher_parse_cmp($1, $3, $4, $5);
415 free($1); $1 = NULL;
416 free($3); $3 = NULL;
417 sdb_object_deref(SDB_OBJ($5));
418 }
419 |
420 IDENTIFIER '.' IDENTIFIER IS NULL_T
421 {
422 $$ = sdb_store_matcher_parse_cmp($1, $3, "IS", NULL);
423 free($1); $1 = NULL;
424 free($3); $3 = NULL;
425 }
426 |
427 IDENTIFIER '.' IDENTIFIER IS NOT NULL_T
428 {
429 sdb_store_matcher_t *m;
430 m = sdb_store_matcher_parse_cmp($1, $3, "IS", NULL);
431 free($1); $1 = NULL;
432 free($3); $3 = NULL;
434 /* sdb_store_inv_matcher return NULL if m==NULL */
435 $$ = sdb_store_inv_matcher(m);
436 sdb_object_deref(SDB_OBJ(m));
437 }
438 ;
440 expression:
441 '(' expression ')'
442 {
443 $$ = $2;
444 }
445 |
446 expression '+' expression
447 {
448 $$ = sdb_store_expr_create(SDB_DATA_ADD, $1, $3);
449 sdb_object_deref(SDB_OBJ($1)); $1 = NULL;
450 sdb_object_deref(SDB_OBJ($3)); $3 = NULL;
451 }
452 |
453 expression '-' expression
454 {
455 $$ = sdb_store_expr_create(SDB_DATA_SUB, $1, $3);
456 sdb_object_deref(SDB_OBJ($1)); $1 = NULL;
457 sdb_object_deref(SDB_OBJ($3)); $3 = NULL;
458 }
459 |
460 expression '*' expression
461 {
462 $$ = sdb_store_expr_create(SDB_DATA_MUL, $1, $3);
463 sdb_object_deref(SDB_OBJ($1)); $1 = NULL;
464 sdb_object_deref(SDB_OBJ($3)); $3 = NULL;
465 }
466 |
467 expression '/' expression
468 {
469 $$ = sdb_store_expr_create(SDB_DATA_DIV, $1, $3);
470 sdb_object_deref(SDB_OBJ($1)); $1 = NULL;
471 sdb_object_deref(SDB_OBJ($3)); $3 = NULL;
472 }
473 |
474 expression '%' expression
475 {
476 $$ = sdb_store_expr_create(SDB_DATA_MOD, $1, $3);
477 sdb_object_deref(SDB_OBJ($1)); $1 = NULL;
478 sdb_object_deref(SDB_OBJ($3)); $3 = NULL;
479 }
480 |
481 ':' IDENTIFIER
482 {
483 int field = sdb_store_parse_field_name($2);
484 free($2); $2 = NULL;
485 $$ = sdb_store_expr_fieldvalue(field);
486 }
487 |
488 data
489 {
490 $$ = sdb_store_expr_constvalue(&$1);
491 sdb_data_free_datum(&$1);
492 }
493 ;
495 op:
496 CMP_EQUAL { $$ = "="; }
497 |
498 CMP_NEQUAL { $$ = "!="; }
499 |
500 CMP_REGEX { $$ = "=~"; }
501 |
502 CMP_NREGEX { $$ = "!~"; }
503 |
504 CMP_LT { $$ = "<"; }
505 |
506 CMP_LE { $$ = "<="; }
507 |
508 CMP_GE { $$ = ">="; }
509 |
510 CMP_GT { $$ = ">"; }
511 ;
513 data:
514 STRING { $$.type = SDB_TYPE_STRING; $$.data.string = $1; }
515 |
516 INTEGER { $$ = $1; }
517 |
518 FLOAT { $$ = $1; }
519 |
520 datetime { $$.type = SDB_TYPE_DATETIME; $$.data.datetime = $1; }
521 |
522 interval { $$ = $1; }
523 ;
525 datetime:
526 DATE TIME { $$ = $1 + $2; }
527 |
528 DATE { $$ = $1; }
529 |
530 TIME { $$ = $1; }
531 ;
533 interval:
534 interval interval_elem
535 {
536 $$.data.datetime = $1.data.datetime + $2.data.datetime;
537 }
538 |
539 interval_elem { $$ = $1; }
540 ;
542 interval_elem:
543 INTEGER IDENTIFIER
544 {
545 sdb_time_t unit = 1;
547 unit = sdb_strpunit($2);
548 if (! unit) {
549 char errmsg[strlen($2) + 32];
550 snprintf(errmsg, sizeof(errmsg),
551 YY_("invalid time unit %s"), $2);
552 sdb_fe_yyerror(&yylloc, scanner, errmsg);
553 free($2); $2 = NULL;
554 YYABORT;
555 }
556 free($2); $2 = NULL;
558 $$.type = SDB_TYPE_DATETIME;
559 $$.data.datetime = (sdb_time_t)$1.data.integer * unit;
561 if ($1.data.integer < 0) {
562 sdb_fe_yyerror(&yylloc, scanner,
563 YY_("syntax error, negative intervals not supported"));
564 YYABORT;
565 }
566 }
567 ;
569 %%
571 void
572 sdb_fe_yyerror(YYLTYPE *lval, sdb_fe_yyscan_t scanner, const char *msg)
573 {
574 sdb_log(SDB_LOG_ERR, "frontend: parse error: %s", msg);
575 } /* sdb_fe_yyerror */
577 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */