Code

frontend/grammar: Access fields by their name rather than '.<name>'.
[sysdb.git] / src / frontend / grammar.y
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 /*
47  * private helper functions
48  */
50 static sdb_store_matcher_t *
51 name_iter_matcher(int m_type, const char *type_name, const char *cmp,
52                 sdb_store_expr_t *expr);
54 /*
55  * public API
56  */
58 int
59 sdb_fe_yylex(YYSTYPE *yylval, YYLTYPE *yylloc, sdb_fe_yyscan_t yyscanner);
61 sdb_fe_yyextra_t *
62 sdb_fe_yyget_extra(sdb_fe_yyscan_t scanner);
64 void
65 sdb_fe_yyerror(YYLTYPE *lval, sdb_fe_yyscan_t scanner, const char *msg);
67 /* quick access to the current parse tree */
68 #define pt sdb_fe_yyget_extra(scanner)->parsetree
70 /* quick access to the parser mode */
71 #define parser_mode sdb_fe_yyget_extra(scanner)->mode
73 #define MODE_TO_STRING(m) \
74         (((m) == SDB_PARSE_DEFAULT) ? "statement" \
75                 : ((m) == SDB_PARSE_COND) ? "condition" \
76                 : ((m) == SDB_PARSE_EXPR) ? "expression" \
77                 : "UNKNOWN")
79 %}
81 %pure-parser
82 %lex-param {sdb_fe_yyscan_t scanner}
83 %parse-param {sdb_fe_yyscan_t scanner}
84 %locations
85 %error-verbose
86 %expect 0
87 %name-prefix "sdb_fe_yy"
89 %union {
90         const char *sstr; /* static string */
91         char *str;
93         sdb_data_t data;
94         sdb_time_t datetime;
96         sdb_llist_t     *list;
97         sdb_conn_node_t *node;
99         sdb_store_matcher_t *m;
100         sdb_store_expr_t *expr;
103 %start statements
105 %token SCANNER_ERROR
107 %token AND OR IS NOT MATCHING FILTER
108 %token CMP_EQUAL CMP_NEQUAL CMP_REGEX CMP_NREGEX
109 %token CMP_LT CMP_LE CMP_GE CMP_GT ALL ANY IN
110 %token CONCAT
112 %token START END
114 /* NULL token */
115 %token NULL_T
117 %token FETCH LIST LOOKUP TIMESERIES
119 %token <str> IDENTIFIER STRING
121 %token <data> INTEGER FLOAT
123 %token <datetime> DATE TIME
125 /* Precedence (lowest first): */
126 %left OR
127 %left AND
128 %right NOT
129 %left CMP_EQUAL CMP_NEQUAL
130 %left CMP_LT CMP_LE CMP_GE CMP_GT
131 %nonassoc CMP_REGEX CMP_NREGEX
132 %nonassoc IN
133 %left CONCAT
134 %nonassoc IS
135 %left '+' '-'
136 %left '*' '/' '%'
137 %left '[' ']'
138 %left '(' ')'
139 %left '.'
141 %type <list> statements
142 %type <node> statement
143         fetch_statement
144         list_statement
145         lookup_statement
146         timeseries_statement
147         matching_clause
148         filter_clause
149         condition
151 %type <m> matcher
152         compare_matcher
154 %type <expr> expression
156 %type <sstr> cmp
158 %type <data> data
159         interval interval_elem
161 %type <datetime> datetime
162         start_clause end_clause
164 %destructor { free($$); } <str>
165 %destructor { sdb_object_deref(SDB_OBJ($$)); } <node> <m> <expr>
166 %destructor { sdb_data_free_datum(&$$); } <data>
168 %%
170 statements:
171         statements ';' statement
172                 {
173                         /* only accepted in default parse mode */
174                         if (parser_mode != SDB_PARSE_DEFAULT) {
175                                 char errmsg[1024];
176                                 snprintf(errmsg, sizeof(errmsg),
177                                                 YY_("syntax error, unexpected statement, "
178                                                         "expecting %s"), MODE_TO_STRING(parser_mode));
179                                 sdb_fe_yyerror(&yylloc, scanner, errmsg);
180                                 sdb_object_deref(SDB_OBJ($3));
181                                 YYABORT;
182                         }
184                         if ($3) {
185                                 sdb_llist_append(pt, SDB_OBJ($3));
186                                 sdb_object_deref(SDB_OBJ($3));
187                         }
188                 }
189         |
190         statement
191                 {
192                         /* only accepted in default parse mode */
193                         if (parser_mode != SDB_PARSE_DEFAULT) {
194                                 char errmsg[1024];
195                                 snprintf(errmsg, sizeof(errmsg),
196                                                 YY_("syntax error, unexpected statement, "
197                                                         "expecting %s"), MODE_TO_STRING(parser_mode));
198                                 sdb_fe_yyerror(&yylloc, scanner, errmsg);
199                                 sdb_object_deref(SDB_OBJ($1));
200                                 YYABORT;
201                         }
203                         if ($1) {
204                                 sdb_llist_append(pt, SDB_OBJ($1));
205                                 sdb_object_deref(SDB_OBJ($1));
206                         }
207                 }
208         |
209         condition
210                 {
211                         /* only accepted in condition parse mode */
212                         if (! (parser_mode & SDB_PARSE_COND)) {
213                                 char errmsg[1024];
214                                 snprintf(errmsg, sizeof(errmsg),
215                                                 YY_("syntax error, unexpected condition, "
216                                                         "expecting %s"), MODE_TO_STRING(parser_mode));
217                                 sdb_fe_yyerror(&yylloc, scanner, errmsg);
218                                 sdb_object_deref(SDB_OBJ($1));
219                                 YYABORT;
220                         }
222                         if ($1) {
223                                 sdb_llist_append(pt, SDB_OBJ($1));
224                                 sdb_object_deref(SDB_OBJ($1));
225                         }
226                 }
227         |
228         expression
229                 {
230                         /* only accepted in expression parse mode */
231                         if (! (parser_mode & SDB_PARSE_EXPR)) {
232                                 char errmsg[1024];
233                                 snprintf(errmsg, sizeof(errmsg),
234                                                 YY_("syntax error, unexpected expression, "
235                                                         "expecting %s"), MODE_TO_STRING(parser_mode));
236                                 sdb_fe_yyerror(&yylloc, scanner, errmsg);
237                                 sdb_object_deref(SDB_OBJ($1));
238                                 YYABORT;
239                         }
241                         if ($1) {
242                                 sdb_conn_node_t *n;
243                                 n = SDB_CONN_NODE(sdb_object_create_dT(/* name = */ NULL,
244                                                         conn_expr_t, conn_expr_destroy));
245                                 n->cmd = CONNECTION_EXPR;
246                                 CONN_EXPR(n)->expr = $1;
248                                 sdb_llist_append(pt, SDB_OBJ(n));
249                                 sdb_object_deref(SDB_OBJ(n));
250                         }
251                 }
252         ;
254 statement:
255         fetch_statement
256         |
257         list_statement
258         |
259         lookup_statement
260         |
261         timeseries_statement
262         |
263         /* empty */
264                 {
265                         $$ = NULL;
266                 }
267         ;
269 /*
270  * FETCH <type> <hostname> [FILTER <condition>];
271  *
272  * Retrieve detailed information about a single host.
273  */
274 fetch_statement:
275         FETCH IDENTIFIER STRING filter_clause
276                 {
277                         /* TODO: support other types as well */
278                         if (strcasecmp($2, "host")) {
279                                 char errmsg[strlen($2) + 32];
280                                 snprintf(errmsg, sizeof(errmsg),
281                                                 YY_("unknown data-source %s"), $2);
282                                 sdb_fe_yyerror(&yylloc, scanner, errmsg);
283                                 free($2); $2 = NULL;
284                                 free($3); $3 = NULL;
285                                 sdb_object_deref(SDB_OBJ($4));
286                                 YYABORT;
287                         }
289                         $$ = SDB_CONN_NODE(sdb_object_create_dT(/* name = */ NULL,
290                                                 conn_fetch_t, conn_fetch_destroy));
291                         CONN_FETCH($$)->type = SDB_HOST;
292                         CONN_FETCH($$)->name = $3;
293                         CONN_FETCH($$)->filter = CONN_MATCHER($4);
294                         $$->cmd = CONNECTION_FETCH;
295                         free($2); $2 = NULL;
296                 }
297         ;
299 /*
300  * LIST <type> [FILTER <condition>];
301  *
302  * Returns a list of all hosts in the store.
303  */
304 list_statement:
305         LIST IDENTIFIER filter_clause
306                 {
307                         int type = sdb_store_parse_object_type_plural($2);
308                         if ((type < 0) || (type == SDB_ATTRIBUTE)) {
309                                 char errmsg[strlen($2) + 32];
310                                 snprintf(errmsg, sizeof(errmsg),
311                                                 YY_("unknown data-source %s"), $2);
312                                 sdb_fe_yyerror(&yylloc, scanner, errmsg);
313                                 free($2); $2 = NULL;
314                                 sdb_object_deref(SDB_OBJ($3));
315                                 YYABORT;
316                         }
318                         $$ = SDB_CONN_NODE(sdb_object_create_dT(/* name = */ NULL,
319                                                 conn_list_t, conn_list_destroy));
320                         CONN_LIST($$)->type = type;
321                         CONN_LIST($$)->filter = CONN_MATCHER($3);
322                         $$->cmd = CONNECTION_LIST;
323                         free($2); $2 = NULL;
324                 }
325         ;
327 /*
328  * LOOKUP <type> MATCHING <condition> [FILTER <condition>];
329  *
330  * Returns detailed information about <type> matching condition.
331  */
332 lookup_statement:
333         LOOKUP IDENTIFIER matching_clause filter_clause
334                 {
335                         /* TODO: support other types as well */
336                         if (strcasecmp($2, "hosts")) {
337                                 char errmsg[strlen($2) + 32];
338                                 snprintf(errmsg, sizeof(errmsg),
339                                                 YY_("unknown data-source %s"), $2);
340                                 sdb_fe_yyerror(&yylloc, scanner, errmsg);
341                                 free($2); $2 = NULL;
342                                 sdb_object_deref(SDB_OBJ($3));
343                                 sdb_object_deref(SDB_OBJ($4));
344                                 YYABORT;
345                         }
347                         $$ = SDB_CONN_NODE(sdb_object_create_dT(/* name = */ NULL,
348                                                 conn_lookup_t, conn_lookup_destroy));
349                         CONN_LOOKUP($$)->type = SDB_HOST;
350                         CONN_LOOKUP($$)->matcher = CONN_MATCHER($3);
351                         CONN_LOOKUP($$)->filter = CONN_MATCHER($4);
352                         $$->cmd = CONNECTION_LOOKUP;
353                         free($2); $2 = NULL;
354                 }
355         ;
357 matching_clause:
358         MATCHING condition { $$ = $2; }
359         |
360         /* empty */ { $$ = NULL; }
362 filter_clause:
363         FILTER condition { $$ = $2; }
364         |
365         /* empty */ { $$ = NULL; }
367 /*
368  * TIMESERIES <host>.<metric> [START <datetime>] [END <datetime>];
369  *
370  * Returns a time-series for the specified host's metric.
371  */
372 timeseries_statement:
373         TIMESERIES STRING '.' STRING start_clause end_clause
374                 {
375                         $$ = SDB_CONN_NODE(sdb_object_create_dT(/* name = */ NULL,
376                                                 conn_ts_t, conn_ts_destroy));
377                         CONN_TS($$)->hostname = $2;
378                         CONN_TS($$)->metric = $4;
379                         CONN_TS($$)->opts.start = $5;
380                         CONN_TS($$)->opts.end = $6;
381                         $$->cmd = CONNECTION_TIMESERIES;
382                 }
383         ;
385 start_clause:
386         START datetime { $$ = $2; }
387         |
388         /* empty */ { $$ = sdb_gettime() - SDB_INTERVAL_HOUR; }
390 end_clause:
391         END datetime { $$ = $2; }
392         |
393         /* empty */ { $$ = sdb_gettime(); }
395 /*
396  * Basic expressions.
397  */
399 condition:
400         matcher
401                 {
402                         if (! $1) {
403                                 /* TODO: improve error reporting */
404                                 sdb_fe_yyerror(&yylloc, scanner,
405                                                 YY_("syntax error, invalid condition"));
406                                 YYABORT;
407                         }
409                         $$ = SDB_CONN_NODE(sdb_object_create_dT(/* name = */ NULL,
410                                                 conn_matcher_t, conn_matcher_destroy));
411                         $$->cmd = CONNECTION_MATCHER;
412                         CONN_MATCHER($$)->matcher = $1;
413                 }
414         ;
416 matcher:
417         '(' matcher ')'
418                 {
419                         $$ = $2;
420                 }
421         |
422         matcher AND matcher
423                 {
424                         $$ = sdb_store_con_matcher($1, $3);
425                         sdb_object_deref(SDB_OBJ($1));
426                         sdb_object_deref(SDB_OBJ($3));
427                 }
428         |
429         matcher OR matcher
430                 {
431                         $$ = sdb_store_dis_matcher($1, $3);
432                         sdb_object_deref(SDB_OBJ($1));
433                         sdb_object_deref(SDB_OBJ($3));
434                 }
435         |
436         NOT matcher
437                 {
438                         $$ = sdb_store_inv_matcher($2);
439                         sdb_object_deref(SDB_OBJ($2));
440                 }
441         |
442         compare_matcher
443                 {
444                         $$ = $1;
445                 }
446         ;
448 compare_matcher:
449         expression cmp expression
450                 {
451                         sdb_store_matcher_op_cb cb = sdb_store_parse_matcher_op($2);
452                         assert(cb); /* else, the grammar accepts invalid 'cmp' */
453                         $$ = cb($1, $3);
454                         sdb_object_deref(SDB_OBJ($1));
455                         sdb_object_deref(SDB_OBJ($3));
456                 }
457         |
458         ANY IDENTIFIER cmp expression
459                 {
460                         $$ = name_iter_matcher(MATCHER_ANY, $2, $3, $4);
461                         free($2); $2 = NULL;
462                         sdb_object_deref(SDB_OBJ($4));
463                 }
464         |
465         ALL IDENTIFIER cmp expression
466                 {
467                         $$ = name_iter_matcher(MATCHER_ALL, $2, $3, $4);
468                         free($2); $2 = NULL;
469                         sdb_object_deref(SDB_OBJ($4));
470                 }
471         |
472         expression IS NULL_T
473                 {
474                         $$ = sdb_store_isnull_matcher($1);
475                         sdb_object_deref(SDB_OBJ($1));
476                 }
477         |
478         expression IS NOT NULL_T
479                 {
480                         $$ = sdb_store_isnnull_matcher($1);
481                         sdb_object_deref(SDB_OBJ($1));
482                 }
483         |
484         expression IN expression
485                 {
486                         $$ = sdb_store_in_matcher($1, $3);
487                         sdb_object_deref(SDB_OBJ($1));
488                         sdb_object_deref(SDB_OBJ($3));
489                 }
490         ;
492 expression:
493         '(' expression ')'
494                 {
495                         $$ = $2;
496                 }
497         |
498         expression '+' expression
499                 {
500                         $$ = sdb_store_expr_create(SDB_DATA_ADD, $1, $3);
501                         sdb_object_deref(SDB_OBJ($1)); $1 = NULL;
502                         sdb_object_deref(SDB_OBJ($3)); $3 = NULL;
503                 }
504         |
505         expression '-' expression
506                 {
507                         $$ = sdb_store_expr_create(SDB_DATA_SUB, $1, $3);
508                         sdb_object_deref(SDB_OBJ($1)); $1 = NULL;
509                         sdb_object_deref(SDB_OBJ($3)); $3 = NULL;
510                 }
511         |
512         expression '*' expression
513                 {
514                         $$ = sdb_store_expr_create(SDB_DATA_MUL, $1, $3);
515                         sdb_object_deref(SDB_OBJ($1)); $1 = NULL;
516                         sdb_object_deref(SDB_OBJ($3)); $3 = NULL;
517                 }
518         |
519         expression '/' expression
520                 {
521                         $$ = sdb_store_expr_create(SDB_DATA_DIV, $1, $3);
522                         sdb_object_deref(SDB_OBJ($1)); $1 = NULL;
523                         sdb_object_deref(SDB_OBJ($3)); $3 = NULL;
524                 }
525         |
526         expression '%' expression
527                 {
528                         $$ = sdb_store_expr_create(SDB_DATA_MOD, $1, $3);
529                         sdb_object_deref(SDB_OBJ($1)); $1 = NULL;
530                         sdb_object_deref(SDB_OBJ($3)); $3 = NULL;
531                 }
532         |
533         expression CONCAT expression
534                 {
535                         $$ = sdb_store_expr_create(SDB_DATA_CONCAT, $1, $3);
536                         sdb_object_deref(SDB_OBJ($1)); $1 = NULL;
537                         sdb_object_deref(SDB_OBJ($3)); $3 = NULL;
538                 }
539         |
540         IDENTIFIER
541                 {
542                         int field;
543                         /* TODO: this only works as long as queries
544                          * are limited to hosts */
545                         if (!strcasecmp($1, "host"))
546                                 field = SDB_FIELD_NAME;
547                         else
548                                 field = sdb_store_parse_field_name($1);
549                         free($1); $1 = NULL;
550                         $$ = sdb_store_expr_fieldvalue(field);
551                 }
552         |
553         IDENTIFIER '[' STRING ']'
554                 {
555                         if (strcasecmp($1, "attribute")) {
556                                 char errmsg[strlen($1) + strlen($3) + 32];
557                                 snprintf(errmsg, sizeof(errmsg),
558                                                 YY_("unknown value %s[%s]"), $1, $3);
559                                 sdb_fe_yyerror(&yylloc, scanner, errmsg);
560                                 free($1); $1 = NULL;
561                                 free($3); $3 = NULL;
562                                 YYABORT;
563                         }
564                         $$ = sdb_store_expr_attrvalue($3);
565                         free($1); $1 = NULL;
566                         free($3); $3 = NULL;
567                 }
568         |
569         data
570                 {
571                         $$ = sdb_store_expr_constvalue(&$1);
572                         sdb_data_free_datum(&$1);
573                 }
574         ;
576 cmp:
577         CMP_EQUAL { $$ = "="; }
578         |
579         CMP_NEQUAL { $$ = "!="; }
580         |
581         CMP_REGEX { $$ = "=~"; }
582         |
583         CMP_NREGEX { $$ = "!~"; }
584         |
585         CMP_LT { $$ = "<"; }
586         |
587         CMP_LE { $$ = "<="; }
588         |
589         CMP_GE { $$ = ">="; }
590         |
591         CMP_GT { $$ = ">"; }
592         ;
594 data:
595         STRING { $$.type = SDB_TYPE_STRING; $$.data.string = $1; }
596         |
597         INTEGER { $$ = $1; }
598         |
599         FLOAT { $$ = $1; }
600         |
601         datetime { $$.type = SDB_TYPE_DATETIME; $$.data.datetime = $1; }
602         |
603         interval { $$ = $1; }
604         ;
606 datetime:
607         DATE TIME { $$ = $1 + $2; }
608         |
609         DATE { $$ = $1; }
610         |
611         TIME { $$ = $1; }
612         ;
614 interval:
615         interval interval_elem
616                 {
617                         $$.data.datetime = $1.data.datetime + $2.data.datetime;
618                 }
619         |
620         interval_elem { $$ = $1; }
621         ;
623 interval_elem:
624         INTEGER IDENTIFIER
625                 {
626                         sdb_time_t unit = 1;
628                         unit = sdb_strpunit($2);
629                         if (! unit) {
630                                 char errmsg[strlen($2) + 32];
631                                 snprintf(errmsg, sizeof(errmsg),
632                                                 YY_("invalid time unit %s"), $2);
633                                 sdb_fe_yyerror(&yylloc, scanner, errmsg);
634                                 free($2); $2 = NULL;
635                                 YYABORT;
636                         }
637                         free($2); $2 = NULL;
639                         $$.type = SDB_TYPE_DATETIME;
640                         $$.data.datetime = (sdb_time_t)$1.data.integer * unit;
642                         if ($1.data.integer < 0) {
643                                 sdb_fe_yyerror(&yylloc, scanner,
644                                                 YY_("syntax error, negative intervals not supported"));
645                                 YYABORT;
646                         }
647                 }
648         ;
650 %%
652 void
653 sdb_fe_yyerror(YYLTYPE *lval, sdb_fe_yyscan_t scanner, const char *msg)
655         sdb_log(SDB_LOG_ERR, "frontend: parse error: %s", msg);
656 } /* sdb_fe_yyerror */
658 static sdb_store_matcher_t *
659 name_iter_matcher(int m_type, const char *type_name, const char *cmp,
660                 sdb_store_expr_t *expr)
662         int type = sdb_store_parse_object_type(type_name);
663         sdb_store_matcher_op_cb cb = sdb_store_parse_matcher_op(cmp);
664         sdb_store_expr_t *e;
665         sdb_store_matcher_t *m, *tmp = NULL;
666         assert(cb);
668         /* TODO: this only works as long as queries
669          * are limited to hosts */
670         if (type == SDB_HOST) {
671                 return NULL;
672         }
674         e = sdb_store_expr_fieldvalue(SDB_FIELD_NAME);
675         m = cb(e, expr);
676         if (m_type == MATCHER_ANY)
677                 tmp = sdb_store_any_matcher(type, m);
678         else if (m_type == MATCHER_ALL)
679                 tmp = sdb_store_all_matcher(type, m);
680         sdb_object_deref(SDB_OBJ(m));
681         sdb_object_deref(SDB_OBJ(e));
682         return tmp;
683 } /* name_iter_matcher */
685 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */