Code

store: Removed now unused support for attribute matchers in old code.
[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 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, $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)
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 : */