Code

parser: Let the TIMESERIES command accept optional data-source names.
[sysdb.git] / src / parser / grammar.y
1 /*
2  * SysDB - src/parser/grammar.y
3  * Copyright (C) 2013-2015 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 /*
29  * Grammar for the SysDB Query Language (SysQL).
30  */
32 %{
34 #include "core/store.h"
35 #include "core/time.h"
37 #include "parser/ast.h"
38 #include "parser/parser.h"
39 #include "parser/grammar.h"
41 #include "utils/error.h"
42 #include "utils/llist.h"
44 #include <assert.h>
46 #include <stdio.h>
47 #include <string.h>
49 /*
50  * public API
51  */
53 int
54 sdb_parser_yylex(YYSTYPE *yylval, YYLTYPE *yylloc, sdb_parser_yyscan_t yyscanner);
56 sdb_parser_yyextra_t *
57 sdb_parser_yyget_extra(sdb_parser_yyscan_t scanner);
59 void
60 sdb_parser_yyerror(YYLTYPE *lval, sdb_parser_yyscan_t scanner, const char *msg);
61 void
62 sdb_parser_yyerrorf(YYLTYPE *lval, sdb_parser_yyscan_t scanner, const char *fmt, ...);
64 /* quick access to the current parse tree */
65 #define pt sdb_parser_yyget_extra(scanner)->parsetree
67 /* quick access to the parser mode */
68 #define parser_mode sdb_parser_yyget_extra(scanner)->mode
70 /* quick access to the parser's error buffer */
71 #define errbuf sdb_parser_yyget_extra(scanner)->errbuf
73 #define CK_OOM(p) \
74         do { \
75                 if (! (p)) { \
76                         sdb_parser_yyerror(&yylloc, scanner, YY_("out of memory")); \
77                         YYABORT; \
78                 } \
79         } while (0)
81 #define MODE_TO_STRING(m) \
82         (((m) == SDB_PARSE_DEFAULT) ? "statement" \
83                 : ((m) == SDB_PARSE_COND) ? "condition" \
84                 : ((m) == SDB_PARSE_ARITH) ? "arithmetic expression" \
85                 : "UNKNOWN")
87 %}
89 %pure-parser
90 %lex-param {sdb_parser_yyscan_t scanner}
91 %parse-param {sdb_parser_yyscan_t scanner}
92 %locations
93 %error-verbose
94 %expect 0
95 %name-prefix "sdb_parser_yy"
97 %union {
98         char *str;
99         int integer;
101         sdb_data_t data;
102         sdb_time_t datetime;
104         sdb_llist_t    *list;
105         sdb_ast_node_t *node;
107         struct { char *type; char *id; sdb_time_t last_update; } metric_store;
110 %start statements
112 %token SCANNER_ERROR
114 %token AND OR IS NOT MATCHING FILTER
115 %token CMP_EQUAL CMP_NEQUAL CMP_REGEX CMP_NREGEX
116 %token CMP_LT CMP_LE CMP_GE CMP_GT ALL ANY IN
117 %token CONCAT
119 %token HOST_T HOSTS_T SERVICE_T SERVICES_T METRIC_T METRICS_T
120 %token ATTRIBUTE_T ATTRIBUTES_T
121 %token NAME_T LAST_UPDATE_T AGE_T INTERVAL_T BACKEND_T VALUE_T
123 %token LAST UPDATE
125 %token START END
127 /* NULL token */
128 %token NULL_T
130 %token TRUE FALSE
132 %token FETCH LIST LOOKUP STORE TIMESERIES
134 %token <str> IDENTIFIER STRING
136 %token <data> INTEGER FLOAT
138 %token <datetime> DATE TIME
140 /* Precedence (lowest first): */
141 %left OR
142 %left AND
143 %right NOT
144 %left CMP_EQUAL CMP_NEQUAL
145 %left CMP_LT CMP_LE CMP_GE CMP_GT
146 %nonassoc CMP_REGEX CMP_NREGEX
147 %nonassoc IN
148 %left CONCAT
149 %nonassoc IS
150 %left '+' '-'
151 %left '*' '/' '%'
152 %left '[' ']'
153 %left '(' ')'
154 %left '.'
156 %type <list> statements
157 %type <node> statement
158         fetch_statement
159         list_statement
160         lookup_statement
161         store_statement
162         timeseries_statement
163         matching_clause
164         filter_clause
165         condition comparison
166         expression object_expression
168 %type <integer> object_type object_type_plural
169 %type <integer> field
170 %type <integer> cmp
172 %type <data> data
173         interval interval_elem
174         array array_elem_list
176 %type <datetime> datetime
177         start_clause end_clause
178         last_update_clause
180 %type <metric_store> metric_store_clause
182 %destructor { free($$); } <str>
183 %destructor { sdb_object_deref(SDB_OBJ($$)); } <node>
184 %destructor { sdb_data_free_datum(&$$); } <data>
186 %%
188 statements:
189         statements ';' statement
190                 {
191                         /* only accepted in default parse mode */
192                         if (parser_mode != SDB_PARSE_DEFAULT) {
193                                 sdb_parser_yyerrorf(&yylloc, scanner,
194                                                 YY_("syntax error, unexpected statement, "
195                                                         "expecting %s"), MODE_TO_STRING(parser_mode));
196                                 sdb_object_deref(SDB_OBJ($3));
197                                 YYABORT;
198                         }
200                         if ($3) {
201                                 sdb_llist_append(pt, SDB_OBJ($3));
202                                 sdb_object_deref(SDB_OBJ($3));
203                         }
204                 }
205         |
206         statement
207                 {
208                         /* only accepted in default parse mode */
209                         if (parser_mode != SDB_PARSE_DEFAULT) {
210                                 sdb_parser_yyerrorf(&yylloc, scanner,
211                                                 YY_("syntax error, unexpected statement, "
212                                                         "expecting %s"), MODE_TO_STRING(parser_mode));
213                                 sdb_object_deref(SDB_OBJ($1));
214                                 YYABORT;
215                         }
217                         if ($1) {
218                                 sdb_llist_append(pt, SDB_OBJ($1));
219                                 sdb_object_deref(SDB_OBJ($1));
220                         }
221                 }
222         |
223         condition
224                 {
225                         /* only accepted in condition parse mode */
226                         if (! (parser_mode & SDB_PARSE_COND)) {
227                                 sdb_parser_yyerrorf(&yylloc, scanner,
228                                                 YY_("syntax error, unexpected condition, "
229                                                         "expecting %s"), MODE_TO_STRING(parser_mode));
230                                 sdb_object_deref(SDB_OBJ($1));
231                                 YYABORT;
232                         }
234                         if ($1) {
235                                 sdb_llist_append(pt, SDB_OBJ($1));
236                                 sdb_object_deref(SDB_OBJ($1));
237                         }
238                 }
239         |
240         expression
241                 {
242                         /* only accepted in expression parse mode */
243                         if (! (parser_mode & SDB_PARSE_ARITH)) {
244                                 sdb_parser_yyerrorf(&yylloc, scanner,
245                                                 YY_("syntax error, unexpected expression, "
246                                                         "expecting %s"), MODE_TO_STRING(parser_mode));
247                                 sdb_object_deref(SDB_OBJ($1));
248                                 YYABORT;
249                         }
251                         if ($1) {
252                                 sdb_llist_append(pt, SDB_OBJ($1));
253                                 sdb_object_deref(SDB_OBJ($1));
254                         }
255                 }
256         ;
258 statement:
259         fetch_statement
260         |
261         list_statement
262         |
263         lookup_statement
264         |
265         store_statement
266         |
267         timeseries_statement
268         |
269         /* empty */
270                 {
271                         $$ = NULL;
272                 }
273         ;
275 /*
276  * FETCH host <hostname> [FILTER <condition>];
277  * FETCH <type> <hostname>.<name> [FILTER <condition>];
278  *
279  * Retrieve detailed information about a single object.
280  */
281 fetch_statement:
282         FETCH object_type STRING filter_clause
283                 {
284                         $$ = sdb_ast_fetch_create($2, NULL, -1, NULL, $3, 1, $4);
285                         CK_OOM($$);
286                 }
287         |
288         FETCH object_type STRING '.' STRING filter_clause
289                 {
290                         $$ = sdb_ast_fetch_create($2, $3, -1, NULL, $5, 1, $6);
291                         CK_OOM($$);
292                 }
293         ;
295 /*
296  * LIST <type> [FILTER <condition>];
297  *
298  * Returns a list of all objects in the store.
299  */
300 list_statement:
301         LIST object_type_plural filter_clause
302                 {
303                         $$ = sdb_ast_list_create($2, $3);
304                         CK_OOM($$);
305                 }
306         ;
308 /*
309  * LOOKUP <type> [MATCHING <condition>] [FILTER <condition>];
310  *
311  * Returns detailed information about objects matching a condition.
312  */
313 lookup_statement:
314         LOOKUP object_type_plural matching_clause filter_clause
315                 {
316                         $$ = sdb_ast_lookup_create($2, $3, $4);
317                         CK_OOM($$);
318                 }
319         ;
321 matching_clause:
322         MATCHING condition { $$ = $2; }
323         |
324         /* empty */ { $$ = NULL; }
326 filter_clause:
327         FILTER condition { $$ = $2; }
328         |
329         /* empty */ { $$ = NULL; }
331 /*
332  * STORE <type> <name>|<host>.<name> [LAST UPDATE <datetime>];
333  * STORE METRIC <host>.<name> STORE <type> <id> [LAST UPDATE <datetime>];
334  * STORE <type> ATTRIBUTE <parent>.<key> <datum> [LAST UPDATE <datetime>];
335  *
336  * Store or update an object in the database.
337  */
338 store_statement:
339         STORE HOST_T STRING last_update_clause
340                 {
341                         $$ = sdb_ast_store_create(SDB_HOST, NULL, 0, NULL,
342                                         $3, $4, NULL, NULL, 0, SDB_DATA_NULL);
343                         CK_OOM($$);
344                 }
345         |
346         STORE SERVICE_T STRING '.' STRING last_update_clause
347                 {
348                         $$ = sdb_ast_store_create(SDB_SERVICE, $3, 0, NULL,
349                                         $5, $6, NULL, NULL, 0, SDB_DATA_NULL);
350                         CK_OOM($$);
351                 }
352         |
353         STORE METRIC_T STRING '.' STRING metric_store_clause last_update_clause
354                 {
355                         $$ = sdb_ast_store_create(SDB_METRIC, $3, 0, NULL,
356                                         $5, $7, $6.type, $6.id, $6.last_update, SDB_DATA_NULL);
357                         CK_OOM($$);
358                 }
359         |
360         STORE HOST_T ATTRIBUTE_T STRING '.' STRING data last_update_clause
361                 {
362                         $$ = sdb_ast_store_create(SDB_ATTRIBUTE, $4, 0, NULL,
363                                         $6, $8, NULL, NULL, 0, $7);
364                         CK_OOM($$);
365                 }
366         |
367         STORE SERVICE_T ATTRIBUTE_T STRING '.' STRING '.' STRING data last_update_clause
368                 {
369                         $$ = sdb_ast_store_create(SDB_ATTRIBUTE, $4, SDB_SERVICE, $6,
370                                         $8, $10, NULL, NULL, 0, $9);
371                         CK_OOM($$);
372                 }
373         |
374         STORE METRIC_T ATTRIBUTE_T STRING '.' STRING '.' STRING data last_update_clause
375                 {
376                         $$ = sdb_ast_store_create(SDB_ATTRIBUTE, $4, SDB_METRIC, $6,
377                                         $8, $10, NULL, NULL, 0, $9);
378                         CK_OOM($$);
379                 }
380         ;
382 last_update_clause:
383         LAST UPDATE datetime { $$ = $3; }
384         |
385         /* empty */ { $$ = sdb_gettime(); }
387 metric_store_clause:
388         STORE STRING STRING datetime { $$.type = $2; $$.id = $3; $$.last_update = $4; }
389         |
390         STORE STRING STRING { $$.type = $2; $$.id = $3; $$.last_update = 0; }
391         |
392         /* empty */ { $$.type = $$.id = NULL; $$.last_update = 0; }
394 /*
395  * TIMESERIES <host>.<metric>[<data-source>...] [START <datetime>] [END <datetime>];
396  *
397  * Returns a time-series for the specified host's metric.
398  */
399 timeseries_statement:
400         TIMESERIES STRING '.' STRING start_clause end_clause
401                 {
402                         $$ = sdb_ast_timeseries_create($2, $4, NULL, 0, $5, $6);
403                         CK_OOM($$);
404                 }
405         |
406         TIMESERIES STRING '.' STRING array start_clause end_clause
407                 {
408                         char **ds;
409                         size_t ds_num;
411                         if ($5.type != (SDB_TYPE_ARRAY | SDB_TYPE_STRING)) {
412                                 sdb_parser_yyerrorf(&yylloc, scanner, YY_("syntax error, "
413                                                 "unexpected array of type %s; expected STRING"),
414                                                 SDB_TYPE_TO_STRING($5.type));
415                                 sdb_data_free_datum(&$5);
416                                 free($2);
417                                 free($4);
418                                 YYABORT;
419                         }
421                         ds = $5.data.array.values;
422                         ds_num = $5.data.array.length;
424                         $$ = sdb_ast_timeseries_create($2, $4, ds, ds_num, $6, $7);
425                         CK_OOM($$);
426                 }
427         ;
429 start_clause:
430         START datetime { $$ = $2; }
431         |
432         /* empty */ { $$ = sdb_gettime() - SDB_INTERVAL_HOUR; }
434 end_clause:
435         END datetime { $$ = $2; }
436         |
437         /* empty */ { $$ = sdb_gettime(); }
439 /*
440  * Basic expressions.
441  */
443 condition:
444         '(' condition ')'
445                 {
446                         $$ = $2;
447                 }
448         |
449         condition AND condition
450                 {
451                         $$ = sdb_ast_op_create(SDB_AST_AND, $1, $3);
452                         CK_OOM($$);
453                 }
454         |
455         condition OR condition
456                 {
457                         $$ = sdb_ast_op_create(SDB_AST_OR, $1, $3);
458                         CK_OOM($$);
459                 }
460         |
461         NOT condition
462                 {
463                         $$ = sdb_ast_op_create(SDB_AST_NOT, NULL, $2);
464                         CK_OOM($$);
465                 }
466         |
467         comparison
468                 {
469                         $$ = $1;
470                 }
471         ;
473 comparison:
474         expression cmp expression
475                 {
476                         $$ = sdb_ast_op_create($2, $1, $3);
477                         CK_OOM($$);
478                 }
479         |
480         ANY expression cmp expression
481                 {
482                         sdb_ast_node_t *n = sdb_ast_op_create($3, NULL, $4);
483                         CK_OOM(n);
484                         $$ = sdb_ast_iter_create(SDB_AST_ANY, $2, n);
485                         CK_OOM($$);
486                 }
487         |
488         ALL expression cmp expression
489                 {
490                         sdb_ast_node_t *n = sdb_ast_op_create($3, NULL, $4);
491                         CK_OOM(n);
492                         $$ = sdb_ast_iter_create(SDB_AST_ALL, $2, n);
493                         CK_OOM($$);
494                 }
495         |
496         expression IS NULL_T
497                 {
498                         $$ = sdb_ast_op_create(SDB_AST_ISNULL, NULL, $1);
499                         CK_OOM($$);
500                 }
501         |
502         expression IS NOT NULL_T
503                 {
504                         $$ = sdb_ast_op_create(SDB_AST_ISNULL, NULL, $1);
505                         CK_OOM($$);
506                         $$ = sdb_ast_op_create(SDB_AST_NOT, NULL, $$);
507                         CK_OOM($$);
508                 }
509         |
510         expression IS TRUE
511                 {
512                         $$ = sdb_ast_op_create(SDB_AST_ISTRUE, NULL, $1);
513                         CK_OOM($$);
514                 }
515         |
516         expression IS NOT TRUE
517                 {
518                         $$ = sdb_ast_op_create(SDB_AST_ISTRUE, NULL, $1);
519                         CK_OOM($$);
520                         $$ = sdb_ast_op_create(SDB_AST_NOT, NULL, $$);
521                         CK_OOM($$);
522                 }
523         |
524         expression IS FALSE
525                 {
526                         $$ = sdb_ast_op_create(SDB_AST_ISFALSE, NULL, $1);
527                         CK_OOM($$);
528                 }
529         |
530         expression IS NOT FALSE
531                 {
532                         $$ = sdb_ast_op_create(SDB_AST_ISFALSE, NULL, $1);
533                         CK_OOM($$);
534                         $$ = sdb_ast_op_create(SDB_AST_NOT, NULL, $$);
535                         CK_OOM($$);
536                 }
537         |
538         expression IN expression
539                 {
540                         $$ = sdb_ast_op_create(SDB_AST_IN, $1, $3);
541                         CK_OOM($$);
542                 }
543         |
544         expression NOT IN expression
545                 {
546                         $$ = sdb_ast_op_create(SDB_AST_IN, $1, $4);
547                         CK_OOM($$);
548                         $$ = sdb_ast_op_create(SDB_AST_NOT, NULL, $$);
549                         CK_OOM($$);
550                 }
551         ;
553 expression:
554         '(' expression ')'
555                 {
556                         $$ = $2;
557                 }
558         |
559         expression '+' expression
560                 {
561                         $$ = sdb_ast_op_create(SDB_AST_ADD, $1, $3);
562                         CK_OOM($$);
563                 }
564         |
565         expression '-' expression
566                 {
567                         $$ = sdb_ast_op_create(SDB_AST_SUB, $1, $3);
568                         CK_OOM($$);
569                 }
570         |
571         expression '*' expression
572                 {
573                         $$ = sdb_ast_op_create(SDB_AST_MUL, $1, $3);
574                         CK_OOM($$);
575                 }
576         |
577         expression '/' expression
578                 {
579                         $$ = sdb_ast_op_create(SDB_AST_DIV, $1, $3);
580                         CK_OOM($$);
581                 }
582         |
583         expression '%' expression
584                 {
585                         $$ = sdb_ast_op_create(SDB_AST_MOD, $1, $3);
586                         CK_OOM($$);
587                 }
588         |
589         expression CONCAT expression
590                 {
591                         $$ = sdb_ast_op_create(SDB_AST_CONCAT, $1, $3);
592                         CK_OOM($$);
593                 }
594         |
595         object_expression
596                 {
597                         $$ = $1;
598                 }
599         |
600         data
601                 {
602                         $$ = sdb_ast_const_create($1);
603                         CK_OOM($$);
604                 }
605         ;
607 object_expression:
608         object_type '.' object_expression
609                 {
610                         $$ = sdb_ast_typed_create($1, $3);
611                         CK_OOM($$);
612                 }
613         |
614         ATTRIBUTE_T '.' object_expression
615                 {
616                         $$ = sdb_ast_typed_create(SDB_ATTRIBUTE, $3);
617                         CK_OOM($$);
618                 }
619         |
620         field
621                 {
622                         $$ = sdb_ast_value_create($1, NULL);
623                         CK_OOM($$);
624                 }
625         |
626         ATTRIBUTE_T '[' STRING ']'
627                 {
628                         $$ = sdb_ast_value_create(SDB_ATTRIBUTE, $3);
629                         CK_OOM($$);
630                 }
631         ;
633 object_type:
634         HOST_T { $$ = SDB_HOST; }
635         |
636         SERVICE_T { $$ = SDB_SERVICE; }
637         |
638         METRIC_T { $$ = SDB_METRIC; }
639         ;
641 object_type_plural:
642         HOSTS_T { $$ = SDB_HOST; }
643         |
644         SERVICES_T { $$ = SDB_SERVICE; }
645         |
646         METRICS_T { $$ = SDB_METRIC; }
647         ;
649 field:
650         NAME_T { $$ = SDB_FIELD_NAME; }
651         |
652         LAST_UPDATE_T { $$ = SDB_FIELD_LAST_UPDATE; }
653         |
654         AGE_T { $$ = SDB_FIELD_AGE; }
655         |
656         INTERVAL_T { $$ = SDB_FIELD_INTERVAL; }
657         |
658         BACKEND_T { $$ = SDB_FIELD_BACKEND; }
659         |
660         VALUE_T { $$ = SDB_FIELD_VALUE; }
661         |
662         TIMESERIES { $$ = SDB_FIELD_TIMESERIES; }
663         ;
665 cmp:
666         CMP_EQUAL { $$ = SDB_AST_EQ; }
667         |
668         CMP_NEQUAL { $$ = SDB_AST_NE; }
669         |
670         CMP_REGEX { $$ = SDB_AST_REGEX; }
671         |
672         CMP_NREGEX { $$ = SDB_AST_NREGEX; }
673         |
674         CMP_LT { $$ = SDB_AST_LT; }
675         |
676         CMP_LE { $$ = SDB_AST_LE; }
677         |
678         CMP_GE { $$ = SDB_AST_GE; }
679         |
680         CMP_GT { $$ = SDB_AST_GT; }
681         ;
683 data:
684         STRING { $$.type = SDB_TYPE_STRING; $$.data.string = $1; }
685         |
686         INTEGER { $$ = $1; }
687         |
688         FLOAT { $$ = $1; }
689         |
690         datetime { $$.type = SDB_TYPE_DATETIME; $$.data.datetime = $1; }
691         |
692         interval { $$ = $1; }
693         |
694         array { $$ = $1; }
695         ;
697 datetime:
698         DATE TIME { $$ = $1 + $2; }
699         |
700         DATE { $$ = $1; }
701         |
702         TIME { $$ = $1; }
703         ;
705 interval:
706         interval interval_elem
707                 {
708                         $$.data.datetime = $1.data.datetime + $2.data.datetime;
709                 }
710         |
711         interval_elem { $$ = $1; }
712         ;
714 interval_elem:
715         INTEGER IDENTIFIER
716                 {
717                         sdb_time_t unit = sdb_strpunit($2);
718                         if (! unit) {
719                                 sdb_parser_yyerrorf(&yylloc, scanner,
720                                                 YY_("syntax error, invalid time unit %s"), $2);
721                                 free($2); $2 = NULL;
722                                 YYABORT;
723                         }
724                         free($2); $2 = NULL;
726                         if ($1.data.integer < 0) {
727                                 sdb_parser_yyerror(&yylloc, scanner,
728                                                 YY_("syntax error, negative intervals not supported"));
729                                 YYABORT;
730                         }
732                         $$.type = SDB_TYPE_DATETIME;
733                         $$.data.datetime = (sdb_time_t)$1.data.integer * unit;
734                 }
735         ;
737 array:
738         '[' array_elem_list ']'
739                 {
740                         $$ = $2;
741                 }
742         ;
744 array_elem_list:
745         array_elem_list ',' data
746                 {
747                         size_t elem_size = sdb_data_sizeof($3.type);
749                         if (($3.type & SDB_TYPE_ARRAY) || (($1.type & 0xff) != $3.type)) {
750                                 sdb_parser_yyerrorf(&yylloc, scanner, YY_("syntax error, "
751                                                 "cannot use element of type %s in array of type %s"),
752                                                 SDB_TYPE_TO_STRING($3.type),
753                                                 SDB_TYPE_TO_STRING($1.type));
754                                 sdb_data_free_datum(&$1);
755                                 sdb_data_free_datum(&$3);
756                                 YYABORT;
757                         }
759                         $$ = $1;
760                         $$.data.array.values = realloc($$.data.array.values,
761                                         ($$.data.array.length + 1) * elem_size);
762                         CK_OOM($$.data.array.values);
764                         memcpy((char *)$$.data.array.values + $$.data.array.length * elem_size,
765                                         &$3.data, elem_size);
766                         ++$$.data.array.length;
767                 }
768         |
769         data
770                 {
771                         size_t elem_size = sdb_data_sizeof($1.type);
773                         if ($1.type & SDB_TYPE_ARRAY) {
774                                 sdb_parser_yyerrorf(&yylloc, scanner, YY_("syntax error, "
775                                                 "cannot construct array of type %s"),
776                                                 SDB_TYPE_TO_STRING($1.type));
777                                 sdb_data_free_datum(&$1);
778                                 YYABORT;
779                         }
781                         $$ = $1;
782                         $$.type |= SDB_TYPE_ARRAY;
783                         $$.data.array.values = malloc(elem_size);
784                         CK_OOM($$.data.array.values);
786                         memcpy($$.data.array.values, &$1.data, elem_size);
787                         $$.data.array.length = 1;
788                 }
789         ;
791 %%
793 void
794 sdb_parser_yyerror(YYLTYPE *lval, sdb_parser_yyscan_t scanner, const char *msg)
796         sdb_log(SDB_LOG_ERR, "parser: parse error: %s", msg);
797         sdb_strbuf_sprintf(errbuf, "%s", msg);
798 } /* sdb_parser_yyerror */
800 void
801 sdb_parser_yyerrorf(YYLTYPE *lval, sdb_parser_yyscan_t scanner, const char *fmt, ...)
803         va_list ap, aq;
804         va_start(ap, fmt);
805         va_copy(aq, ap);
806         sdb_vlog(SDB_LOG_ERR, fmt, ap);
807         sdb_strbuf_vsprintf(errbuf, fmt, aq);
808         va_end(ap);
809 } /* sdb_parser_yyerrorf */
811 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */