Code

Drop the old frontend parser.
authorSebastian Harl <sh@tokkee.org>
Tue, 19 May 2015 18:10:22 +0000 (20:10 +0200)
committerSebastian Harl <sh@tokkee.org>
Tue, 19 May 2015 18:10:22 +0000 (20:10 +0200)
It's fully replaced by the new parser module now.

src/Makefile.am
src/frontend/analyzer.c [deleted file]
src/frontend/grammar.y [deleted file]
src/frontend/parser.c [deleted file]
src/frontend/scanner.l [deleted file]
src/include/frontend/parser.h [deleted file]
src/include/parser/parser.h
t/Makefile.am
t/unit/frontend/parser_test.c [deleted file]

index 48561c344ac23664299cedb2a65a7b59688526eb..0dbd1511cf58321432a1b55ee21e2d06b61b01f2 100644 (file)
@@ -12,7 +12,7 @@ AM_CPPFLAGS += -DPKGLIBDIR='"${pkglibdir}"'
 AM_YFLAGS = -d
 
 BUILT_SOURCES = include/client/sysdb.h include/sysdb.h \
-               frontend/grammar.h parser/grammar.h
+               parser/grammar.h
 EXTRA_DIST = include/client/sysdb.h.in include/sysdb.h.in
 
 pkginclude_HEADERS = include/sysdb.h
@@ -27,7 +27,6 @@ pkgcoreinclude_HEADERS = \
 pkgfeincludedir = $(pkgincludedir)/frontend
 pkgfeinclude_HEADERS = \
                include/frontend/connection.h \
-               include/frontend/parser.h \
                include/frontend/proto.h \
                include/frontend/sock.h
 pkgutilsincludedir = $(pkgincludedir)/utils
@@ -68,7 +67,6 @@ libsysdbclient_la_LIBADD = $(LIBLTDL) @OPENSSL_LIBS@
 # don't use strict CFLAGS for flex code
 noinst_LTLIBRARIES += libsysdb_fe_parser.la
 libsysdb_fe_parser_la_SOURCES = \
-               frontend/grammar.y frontend/scanner.l \
                parser/grammar.y parser/scanner.l
 libsysdb_fe_parser_la_CFLAGS = @COVERAGE_CFLAGS@ @PROFILING_CFLAGS@ \
                -DBUILD_DATE="\"$$( date --utc '+%F %T' ) (UTC)\""
@@ -86,10 +84,8 @@ libsysdb_la_SOURCES = \
                core/data.c include/core/data.h \
                core/time.c include/core/time.h \
                core/timeseries.c include/core/timeseries.h \
-               frontend/analyzer.c \
                frontend/connection.c include/frontend/connection.h \
                frontend/connection-private.h \
-               frontend/parser.c include/frontend/parser.h \
                frontend/sock.c include/frontend/sock.h \
                frontend/session.c \
                frontend/query.c \
diff --git a/src/frontend/analyzer.c b/src/frontend/analyzer.c
deleted file mode 100644 (file)
index 01e1ad3..0000000
+++ /dev/null
@@ -1,425 +0,0 @@
-/*
- * SysDB - src/frontend/analyzer.c
- * Copyright (C) 2014 Sebastian 'tokkee' Harl <sh@tokkee.org>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
- * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
- * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
- * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "sysdb.h"
-
-#include "core/store-private.h"
-#include "frontend/connection-private.h"
-#include "frontend/parser.h"
-#include "utils/error.h"
-#include "utils/strbuf.h"
-
-#include <assert.h>
-
-/*
- * private helper functions
- */
-
-static void
-iter_error(sdb_strbuf_t *errbuf, int op, sdb_store_expr_t *iter, int context)
-{
-       sdb_strbuf_sprintf(errbuf, "Invalid %s iterator: %s %s "
-                       "not iterable in %s context", MATCHER_SYM(op),
-                       EXPR_TO_STRING(iter), SDB_STORE_TYPE_TO_NAME(iter->data_type),
-                       context == -1 ? "generic" : SDB_STORE_TYPE_TO_NAME(context));
-} /* iter_error */
-
-static void
-iter_op_error(sdb_strbuf_t *errbuf, int op,
-               int iter_type, int cmp, int value_type)
-{
-       sdb_strbuf_sprintf(errbuf, "Invalid iterator %s %s %s %s",
-                       MATCHER_SYM(op), SDB_TYPE_TO_STRING(iter_type),
-                       MATCHER_SYM(cmp), SDB_TYPE_TO_STRING(value_type));
-       if ((iter_type & 0xff) != value_type)
-               sdb_strbuf_append(errbuf, " (type mismatch)");
-       else
-               sdb_strbuf_append(errbuf, " (invalid operator)");
-} /* iter_op_error */
-
-static void
-cmp_error(sdb_strbuf_t *errbuf, int op, int left, int right)
-{
-       sdb_strbuf_sprintf(errbuf, "Invalid operator %s for types %s and %s",
-                       MATCHER_SYM(op), SDB_TYPE_TO_STRING(left),
-                       SDB_TYPE_TO_STRING(right));
-} /* cmp_error */
-
-static void
-op_error(sdb_strbuf_t *errbuf, int op, int left, int right)
-{
-       sdb_strbuf_sprintf(errbuf, "Invalid operator %s for types %s and %s",
-                       SDB_DATA_OP_TO_STRING(op), SDB_TYPE_TO_STRING(left),
-                       SDB_TYPE_TO_STRING(right));
-} /* cmp_error */
-
-static int
-analyze_expr(int context, sdb_store_expr_t *e, sdb_strbuf_t *errbuf)
-{
-       if (! e)
-               return 0;
-
-       if ((e->type < TYPED_EXPR) || (SDB_DATA_CONCAT < e->type)) {
-               sdb_strbuf_sprintf(errbuf, "Invalid expression of type %d", e->type);
-               return -1;
-       }
-
-       switch (e->type) {
-               case TYPED_EXPR:
-                       if (analyze_expr((int)e->data.data.integer, e->left, errbuf))
-                               return -1;
-                       if (context == (int)e->data.data.integer)
-                               return 0;
-                       if ((e->data.data.integer == SDB_HOST) &&
-                                       ((context == SDB_SERVICE) || (context == SDB_METRIC)
-                                               || (context < 0)))
-                               return 0;
-                       sdb_strbuf_sprintf(errbuf, "Invalid expression %s.%s "
-                                       "in %s context",
-                                       SDB_STORE_TYPE_TO_NAME(e->data.data.integer),
-                                       EXPR_TO_STRING(e->left),
-                                       context == -1 ? "generic" : SDB_STORE_TYPE_TO_NAME(context));
-                       return -1;
-
-               case ATTR_VALUE:
-               case 0:
-                       break;
-
-               case FIELD_VALUE:
-                       if ((e->data.data.integer == SDB_FIELD_VALUE)
-                                       && (context != SDB_ATTRIBUTE)) {
-                               sdb_strbuf_sprintf(errbuf, "Invalid expression %s.value "
-                                               "(only attributes have a value)",
-                                               SDB_STORE_TYPE_TO_NAME(context));
-                               return -1;
-                       }
-                       break;
-
-               default:
-                       if (analyze_expr(context, e->left, errbuf))
-                               return -1;
-                       if (analyze_expr(context, e->right, errbuf))
-                               return -1;
-
-                       if ((e->left->data_type > 0) && (e->right->data_type > 0)) {
-                               if (sdb_data_expr_type(e->type, e->left->data_type,
-                                                       e->right->data_type) < 0) {
-                                       op_error(errbuf, e->type, e->left->data_type,
-                                                       e->right->data_type);
-                                       return -1;
-                               }
-                       }
-                       break;
-       }
-       return 0;
-} /* analyze_expr */
-
-static int
-analyze_matcher(int context, int parent_type,
-               sdb_store_matcher_t *m, sdb_strbuf_t *errbuf)
-{
-       if (! m)
-               return 0;
-
-       switch (m->type) {
-               case MATCHER_OR:
-               case MATCHER_AND:
-                       assert(OP_M(m)->left && OP_M(m)->right);
-                       if (analyze_matcher(context, m->type, OP_M(m)->left, errbuf))
-                               return -1;
-                       if (analyze_matcher(context, m->type, OP_M(m)->right, errbuf))
-                               return -1;
-                       break;
-
-               case MATCHER_NOT:
-                       assert(UOP_M(m)->op);
-                       if (analyze_matcher(context, m->type, UOP_M(m)->op, errbuf))
-                               return -1;
-                       break;
-
-               case MATCHER_ANY:
-               case MATCHER_ALL:
-               {
-                       int child_context = -1;
-                       int left_type = -1;
-                       int type = -1;
-
-                       assert(ITER_M(m)->m);
-
-                       if ((ITER_M(m)->iter->type == TYPED_EXPR)
-                                       || (ITER_M(m)->iter->type == FIELD_VALUE))
-                               type = (int)ITER_M(m)->iter->data.data.integer;
-
-                       if (context == -1) { /* inside a filter */
-                               /* attributes are always iterable */
-                               if ((ITER_M(m)->iter->type == TYPED_EXPR)
-                                               && (type != SDB_ATTRIBUTE)) {
-                                       iter_error(errbuf, m->type, ITER_M(m)->iter, context);
-                                       return -1;
-                               }
-                               /* backends are always iterable */
-                               if ((ITER_M(m)->iter->type == FIELD_VALUE)
-                                               && (! (type != SDB_FIELD_BACKEND))) {
-                                       iter_error(errbuf, m->type, ITER_M(m)->iter, context);
-                                       return -1;
-                               }
-                       }
-                       else if (! sdb_store_expr_iterable(ITER_M(m)->iter, context)) {
-                               iter_error(errbuf, m->type, ITER_M(m)->iter, context);
-                               return -1;
-                       }
-
-                       if (ITER_M(m)->iter->type == TYPED_EXPR) {
-                               child_context = type;
-                               left_type = ITER_M(m)->iter->data_type;
-                       }
-                       else if (ITER_M(m)->iter->type == FIELD_VALUE) {
-                               child_context = context;
-                               /* element type of the field */
-                               left_type = ITER_M(m)->iter->data_type & 0xff;
-                       }
-                       else if (! ITER_M(m)->iter->type) {
-                               child_context = context;
-                               /* elements of the array constant */
-                               left_type = ITER_M(m)->iter->data.type & 0xff;
-                       }
-                       else {
-                               iter_error(errbuf, m->type, ITER_M(m)->iter, context);
-                               return -1;
-                       }
-
-                       /* any ary operator will do but these are the once
-                        * we currently support */
-                       if ((ITER_M(m)->m->type != MATCHER_LT)
-                                       && (ITER_M(m)->m->type != MATCHER_LE)
-                                       && (ITER_M(m)->m->type != MATCHER_EQ)
-                                       && (ITER_M(m)->m->type != MATCHER_NE)
-                                       && (ITER_M(m)->m->type != MATCHER_GE)
-                                       && (ITER_M(m)->m->type != MATCHER_GT)
-                                       && (ITER_M(m)->m->type != MATCHER_REGEX)
-                                       && (ITER_M(m)->m->type != MATCHER_NREGEX)) {
-                               iter_op_error(errbuf, m->type,
-                                               left_type, ITER_M(m)->m->type,
-                                               CMP_M(ITER_M(m)->m)->right->data_type);
-                               return -1;
-                       }
-                       if ((left_type >= 0)
-                                       && (CMP_M(ITER_M(m)->m)->right->data_type >= 0)) {
-                               if (left_type != CMP_M(ITER_M(m)->m)->right->data_type) {
-                                       iter_op_error(errbuf, m->type,
-                                                       left_type, ITER_M(m)->m->type,
-                                                       CMP_M(ITER_M(m)->m)->right->data_type);
-                                       return -1;
-                               }
-                       }
-                       if (child_context <= 0) {
-                               sdb_strbuf_sprintf(errbuf, "Unable to determine the context "
-                                               "(object type) of iterator %s %s %s %s",
-                                               MATCHER_SYM(m->type), SDB_TYPE_TO_STRING(left_type),
-                                               MATCHER_SYM(ITER_M(m)->m->type),
-                                               SDB_TYPE_TO_STRING(CMP_M(ITER_M(m)->m)->right->data_type));
-                       }
-                       if (analyze_matcher(child_context, m->type, ITER_M(m)->m, errbuf))
-                               return -1;
-                       break;
-               }
-
-               case MATCHER_LT:
-               case MATCHER_LE:
-               case MATCHER_EQ:
-               case MATCHER_NE:
-               case MATCHER_GE:
-               case MATCHER_GT:
-               {
-                       int left_type = -1;
-
-                       assert(CMP_M(m)->right);
-                       if ((parent_type == MATCHER_ALL)
-                                       || (parent_type == MATCHER_ANY)) {
-                               assert(! CMP_M(m)->left);
-                       }
-                       else {
-                               assert(CMP_M(m)->left);
-                               left_type = CMP_M(m)->left->data_type;
-                       }
-
-                       if (analyze_expr(context, CMP_M(m)->left, errbuf))
-                               return -1;
-                       if (analyze_expr(context, CMP_M(m)->right, errbuf))
-                               return -1;
-
-                       if ((left_type > 0) && (CMP_M(m)->right->data_type > 0)) {
-                               if (left_type == CMP_M(m)->right->data_type)
-                                       return 0;
-                               cmp_error(errbuf, m->type, left_type,
-                                               CMP_M(m)->right->data_type);
-                               return -1;
-                       }
-                       if ((left_type > 0) && (left_type & SDB_TYPE_ARRAY)) {
-                               cmp_error(errbuf, m->type, left_type,
-                                               CMP_M(m)->right->data_type);
-                               return -1;
-                       }
-                       if ((CMP_M(m)->right->data_type > 0)
-                                       && (CMP_M(m)->right->data_type & SDB_TYPE_ARRAY)) {
-                               cmp_error(errbuf, m->type, left_type,
-                                               CMP_M(m)->right->data_type);
-                               return -1;
-                       }
-                       break;
-               }
-
-               case MATCHER_IN:
-               case MATCHER_NIN:
-                       if (analyze_expr(context, CMP_M(m)->left, errbuf))
-                               return -1;
-                       if (analyze_expr(context, CMP_M(m)->right, errbuf))
-                               return -1;
-
-                       /* the left operand may be a scalar or an array but the element
-                        * type has to match */
-                       if ((CMP_M(m)->right->data_type > 0)
-                                       && (! (CMP_M(m)->right->data_type & SDB_TYPE_ARRAY))) {
-                               cmp_error(errbuf, m->type, CMP_M(m)->left->data_type,
-                                               CMP_M(m)->right->data_type);
-                               return -1;
-                       }
-                       if ((CMP_M(m)->left->data_type > 0)
-                                       && (CMP_M(m)->right->data_type > 0)) {
-                               if ((CMP_M(m)->left->data_type & 0xff)
-                                               != (CMP_M(m)->right->data_type & 0xff)) {
-                                       cmp_error(errbuf, m->type, CMP_M(m)->left->data_type,
-                                                       CMP_M(m)->right->data_type);
-                                       return -1;
-                               }
-                       }
-                       break;
-
-               case MATCHER_REGEX:
-               case MATCHER_NREGEX:
-                       if (analyze_expr(context, CMP_M(m)->left, errbuf))
-                               return -1;
-                       if (analyze_expr(context, CMP_M(m)->right, errbuf))
-                               return -1;
-
-                       /* all types are supported for the left operand */
-                       if ((CMP_M(m)->right->data_type > 0)
-                                       && (CMP_M(m)->right->data_type != SDB_TYPE_REGEX)
-                                       && (CMP_M(m)->right->data_type != SDB_TYPE_STRING)) {
-                               cmp_error(errbuf, m->type, CMP_M(m)->left->data_type,
-                                               CMP_M(m)->right->data_type);
-                               return -1;
-                       }
-                       break;
-
-               case MATCHER_ISNULL:
-               case MATCHER_ISNNULL:
-                       if (analyze_expr(context, ISNULL_M(m)->expr, errbuf))
-                               return -1;
-                       break;
-
-               default:
-                       sdb_strbuf_sprintf(errbuf, "Unknown matcher type %d", m->type);
-                       return -1;
-       }
-       return 0;
-} /* analyze_matcher */
-
-/*
- * public API
- */
-
-int
-sdb_fe_analyze(sdb_conn_node_t *node, sdb_strbuf_t *errbuf)
-{
-       sdb_store_matcher_t *m = NULL, *filter = NULL;
-       int context = -1;
-       int status = 0;
-
-       if (! node)
-               return -1;
-
-       /* For now, this function checks basic matcher attributes only;
-        * later, this may be turned into one of multiple AST visitors. */
-       if (node->cmd == SDB_CONNECTION_FETCH) {
-               conn_fetch_t *fetch = CONN_FETCH(node);
-               if ((fetch->type == SDB_HOST) && fetch->name) {
-                       sdb_strbuf_sprintf(errbuf, "Unexpected STRING '%s'", fetch->name);
-                       return -1;
-               }
-               if ((fetch->type != SDB_HOST) && (! fetch->name)) {
-                       sdb_strbuf_sprintf(errbuf, "Missing %s name",
-                                       SDB_STORE_TYPE_TO_NAME(fetch->type));
-                       return -1;
-               }
-               if (fetch->filter)
-                       filter = fetch->filter->matcher;
-               context = fetch->type;
-       }
-       else if (node->cmd == SDB_CONNECTION_LIST) {
-               if (CONN_LIST(node)->filter)
-                       filter = CONN_LIST(node)->filter->matcher;
-               context = CONN_LIST(node)->type;
-       }
-       else if (node->cmd == SDB_CONNECTION_LOOKUP) {
-               if (CONN_LOOKUP(node)->matcher)
-                       m = CONN_LOOKUP(node)->matcher->matcher;
-               if (CONN_LOOKUP(node)->filter)
-                       filter = CONN_LOOKUP(node)->filter->matcher;
-               context = CONN_LOOKUP(node)->type;
-       }
-       else if ((node->cmd == SDB_CONNECTION_STORE_HOST)
-                       || (node->cmd == SDB_CONNECTION_STORE_SERVICE)
-                       || (node->cmd == SDB_CONNECTION_STORE_METRIC)
-                       || (node->cmd == SDB_CONNECTION_STORE_ATTRIBUTE)) {
-               return 0;
-       }
-       else if (node->cmd == SDB_CONNECTION_TIMESERIES) {
-               return 0;
-       }
-       else {
-               sdb_strbuf_sprintf(errbuf,
-                               "Don't know how to analyze %s command (id=%#x)",
-                               SDB_CONN_MSGTYPE_TO_STRING(node->cmd), node->cmd);
-               return -1;
-       }
-
-       if (context <= 0) {
-               sdb_strbuf_sprintf(errbuf, "Unable to determine the context "
-                               "(object type) for %s command (id=%#x)",
-                               SDB_CONN_MSGTYPE_TO_STRING(node->cmd), node->cmd);
-               return -1;
-       }
-       if (analyze_matcher(context, -1, m, errbuf))
-               status = -1;
-       if (analyze_matcher(-1, -1, filter, errbuf))
-               status = -1;
-       return status;
-} /* sdb_fe_analyze */
-
-/* vim: set tw=78 sw=4 ts=4 noexpandtab : */
-
diff --git a/src/frontend/grammar.y b/src/frontend/grammar.y
deleted file mode 100644 (file)
index 31b71a0..0000000
+++ /dev/null
@@ -1,882 +0,0 @@
-/*
- * SysDB - src/frontend/grammar.y
- * Copyright (C) 2013 Sebastian 'tokkee' Harl <sh@tokkee.org>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
- * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
- * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
- * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-%{
-
-#include "frontend/connection-private.h"
-#include "frontend/parser.h"
-#include "frontend/grammar.h"
-
-#include "core/store.h"
-#include "core/store-private.h"
-#include "core/time.h"
-
-#include "utils/error.h"
-#include "utils/llist.h"
-
-#include <assert.h>
-
-#include <stdio.h>
-#include <string.h>
-
-/*
- * private helper functions
- */
-
-static sdb_store_matcher_t *
-name_iter_matcher(int m_type, sdb_store_expr_t *iter, const char *cmp,
-               sdb_store_expr_t *expr);
-
-/*
- * public API
- */
-
-int
-sdb_fe_yylex(YYSTYPE *yylval, YYLTYPE *yylloc, sdb_fe_yyscan_t yyscanner);
-
-sdb_fe_yyextra_t *
-sdb_fe_yyget_extra(sdb_fe_yyscan_t scanner);
-
-void
-sdb_fe_yyerror(YYLTYPE *lval, sdb_fe_yyscan_t scanner, const char *msg);
-void
-sdb_fe_yyerrorf(YYLTYPE *lval, sdb_fe_yyscan_t scanner, const char *fmt, ...);
-
-/* quick access to the current parse tree */
-#define pt sdb_fe_yyget_extra(scanner)->parsetree
-
-/* quick access to the parser mode */
-#define parser_mode sdb_fe_yyget_extra(scanner)->mode
-
-/* quick access to the parser's error buffer */
-#define errbuf sdb_fe_yyget_extra(scanner)->errbuf
-
-#define MODE_TO_STRING(m) \
-       (((m) == SDB_PARSE_DEFAULT) ? "statement" \
-               : ((m) == SDB_PARSE_COND) ? "condition" \
-               : ((m) == SDB_PARSE_ARITH) ? "arithmetic expression" \
-               : "UNKNOWN")
-
-%}
-
-%pure-parser
-%lex-param {sdb_fe_yyscan_t scanner}
-%parse-param {sdb_fe_yyscan_t scanner}
-%locations
-%error-verbose
-%expect 0
-%name-prefix "sdb_fe_yy"
-
-%union {
-       const char *sstr; /* static string */
-       char *str;
-       int integer;
-
-       sdb_data_t data;
-       sdb_time_t datetime;
-
-       sdb_llist_t     *list;
-       sdb_conn_node_t *node;
-
-       sdb_store_matcher_t *m;
-       sdb_store_expr_t *expr;
-
-       sdb_metric_store_t metric_store;
-}
-
-%start statements
-
-%token SCANNER_ERROR
-
-%token AND OR IS NOT MATCHING FILTER
-%token CMP_EQUAL CMP_NEQUAL CMP_REGEX CMP_NREGEX
-%token CMP_LT CMP_LE CMP_GE CMP_GT ALL ANY IN
-%token CONCAT
-
-%token HOST_T HOSTS_T SERVICE_T SERVICES_T METRIC_T METRICS_T
-%token ATTRIBUTE_T ATTRIBUTES_T
-%token NAME_T LAST_UPDATE_T AGE_T INTERVAL_T BACKEND_T VALUE_T
-
-%token LAST UPDATE
-
-%token START END
-
-/* NULL token */
-%token NULL_T
-
-%token FETCH LIST LOOKUP STORE TIMESERIES
-
-%token <str> IDENTIFIER STRING
-
-%token <data> INTEGER FLOAT
-
-%token <datetime> DATE TIME
-
-/* Precedence (lowest first): */
-%left OR
-%left AND
-%right NOT
-%left CMP_EQUAL CMP_NEQUAL
-%left CMP_LT CMP_LE CMP_GE CMP_GT
-%nonassoc CMP_REGEX CMP_NREGEX
-%nonassoc IN
-%left CONCAT
-%nonassoc IS
-%left '+' '-'
-%left '*' '/' '%'
-%left '[' ']'
-%left '(' ')'
-%left '.'
-
-%type <list> statements
-%type <node> statement
-       fetch_statement
-       list_statement
-       lookup_statement
-       store_statement
-       timeseries_statement
-       matching_clause
-       filter_clause
-       condition
-
-%type <m> matcher
-       compare_matcher
-
-%type <expr> expression arithmetic_expression object_expression
-
-%type <integer> object_type object_type_plural
-%type <integer> field
-
-%type <sstr> cmp
-
-%type <data> data
-       interval interval_elem
-       array array_elem_list
-
-%type <datetime> datetime
-       start_clause end_clause
-       last_update_clause
-
-%type <metric_store> metric_store_clause
-
-%destructor { free($$); } <str>
-%destructor { sdb_object_deref(SDB_OBJ($$)); } <node> <m> <expr>
-%destructor { sdb_data_free_datum(&$$); } <data>
-
-%%
-
-statements:
-       statements ';' statement
-               {
-                       /* only accepted in default parse mode */
-                       if (parser_mode != SDB_PARSE_DEFAULT) {
-                               sdb_fe_yyerrorf(&yylloc, scanner,
-                                               YY_("syntax error, unexpected statement, "
-                                                       "expecting %s"), MODE_TO_STRING(parser_mode));
-                               sdb_object_deref(SDB_OBJ($3));
-                               YYABORT;
-                       }
-
-                       if ($3) {
-                               sdb_llist_append(pt, SDB_OBJ($3));
-                               sdb_object_deref(SDB_OBJ($3));
-                       }
-               }
-       |
-       statement
-               {
-                       /* only accepted in default parse mode */
-                       if (parser_mode != SDB_PARSE_DEFAULT) {
-                               sdb_fe_yyerrorf(&yylloc, scanner,
-                                               YY_("syntax error, unexpected statement, "
-                                                       "expecting %s"), MODE_TO_STRING(parser_mode));
-                               sdb_object_deref(SDB_OBJ($1));
-                               YYABORT;
-                       }
-
-                       if ($1) {
-                               sdb_llist_append(pt, SDB_OBJ($1));
-                               sdb_object_deref(SDB_OBJ($1));
-                       }
-               }
-       |
-       condition
-               {
-                       /* only accepted in condition parse mode */
-                       if (! (parser_mode & SDB_PARSE_COND)) {
-                               sdb_fe_yyerrorf(&yylloc, scanner,
-                                               YY_("syntax error, unexpected condition, "
-                                                       "expecting %s"), MODE_TO_STRING(parser_mode));
-                               sdb_object_deref(SDB_OBJ($1));
-                               YYABORT;
-                       }
-
-                       if ($1) {
-                               sdb_llist_append(pt, SDB_OBJ($1));
-                               sdb_object_deref(SDB_OBJ($1));
-                       }
-               }
-       |
-       expression
-               {
-                       /* only accepted in expression parse mode */
-                       if (! (parser_mode & SDB_PARSE_ARITH)) {
-                               sdb_fe_yyerrorf(&yylloc, scanner,
-                                               YY_("syntax error, unexpected expression, "
-                                                       "expecting %s"), MODE_TO_STRING(parser_mode));
-                               sdb_object_deref(SDB_OBJ($1));
-                               YYABORT;
-                       }
-
-                       if ($1) {
-                               sdb_conn_node_t *n;
-                               n = SDB_CONN_NODE(sdb_object_create_dT(/* name = */ NULL,
-                                                       conn_expr_t, conn_expr_destroy));
-                               n->cmd = SDB_CONNECTION_EXPR;
-                               CONN_EXPR(n)->expr = $1;
-
-                               sdb_llist_append(pt, SDB_OBJ(n));
-                               sdb_object_deref(SDB_OBJ(n));
-                       }
-               }
-       ;
-
-statement:
-       fetch_statement
-       |
-       list_statement
-       |
-       lookup_statement
-       |
-       store_statement
-       |
-       timeseries_statement
-       |
-       /* empty */
-               {
-                       $$ = NULL;
-               }
-       ;
-
-/*
- * FETCH <type> <hostname> [FILTER <condition>];
- *
- * Retrieve detailed information about a single host.
- */
-fetch_statement:
-       FETCH object_type STRING filter_clause
-               {
-                       $$ = SDB_CONN_NODE(sdb_object_create_dT(/* name = */ NULL,
-                                               conn_fetch_t, conn_fetch_destroy));
-                       CONN_FETCH($$)->type = $2;
-                       CONN_FETCH($$)->host = $3;
-                       CONN_FETCH($$)->name = NULL;
-                       CONN_FETCH($$)->filter = CONN_MATCHER($4);
-                       $$->cmd = SDB_CONNECTION_FETCH;
-               }
-       |
-       FETCH object_type STRING '.' STRING filter_clause
-               {
-                       $$ = SDB_CONN_NODE(sdb_object_create_dT(/* name = */ NULL,
-                                               conn_fetch_t, conn_fetch_destroy));
-                       CONN_FETCH($$)->type = $2;
-                       CONN_FETCH($$)->host = $3;
-                       CONN_FETCH($$)->name = $5;
-                       CONN_FETCH($$)->filter = CONN_MATCHER($6);
-                       $$->cmd = SDB_CONNECTION_FETCH;
-               }
-       ;
-
-/*
- * LIST <type> [FILTER <condition>];
- *
- * Returns a list of all hosts in the store.
- */
-list_statement:
-       LIST object_type_plural filter_clause
-               {
-                       $$ = SDB_CONN_NODE(sdb_object_create_dT(/* name = */ NULL,
-                                               conn_list_t, conn_list_destroy));
-                       CONN_LIST($$)->type = $2;
-                       CONN_LIST($$)->filter = CONN_MATCHER($3);
-                       $$->cmd = SDB_CONNECTION_LIST;
-               }
-       ;
-
-/*
- * LOOKUP <type> MATCHING <condition> [FILTER <condition>];
- *
- * Returns detailed information about <type> matching condition.
- */
-lookup_statement:
-       LOOKUP object_type_plural matching_clause filter_clause
-               {
-                       $$ = SDB_CONN_NODE(sdb_object_create_dT(/* name = */ NULL,
-                                               conn_lookup_t, conn_lookup_destroy));
-                       CONN_LOOKUP($$)->type = $2;
-                       CONN_LOOKUP($$)->matcher = CONN_MATCHER($3);
-                       CONN_LOOKUP($$)->filter = CONN_MATCHER($4);
-                       $$->cmd = SDB_CONNECTION_LOOKUP;
-               }
-       ;
-
-matching_clause:
-       MATCHING condition { $$ = $2; }
-       |
-       /* empty */ { $$ = NULL; }
-
-filter_clause:
-       FILTER condition { $$ = $2; }
-       |
-       /* empty */ { $$ = NULL; }
-
-/*
- * STORE <type> <name>|<host>.<name> [LAST UPDATE <datetime>];
- * STORE METRIC <host>.<name> STORE <type> <id> [LAST UPDATE <datetime>];
- * STORE <type> ATTRIBUTE <parent>.<key> <datum> [LAST UPDATE <datetime>];
- *
- * Store or update an object in the database.
- */
-store_statement:
-       STORE HOST_T STRING last_update_clause
-               {
-                       $$ = SDB_CONN_NODE(sdb_object_create_dT(/* name = */ NULL,
-                                               conn_store_host_t, conn_store_host_destroy));
-                       CONN_STORE_HOST($$)->name = $3;
-                       CONN_STORE_HOST($$)->last_update = $4;
-                       $$->cmd = SDB_CONNECTION_STORE_HOST;
-               }
-       |
-       STORE SERVICE_T STRING '.' STRING last_update_clause
-               {
-                       $$ = SDB_CONN_NODE(sdb_object_create_dT(/* name = */ NULL,
-                                               conn_store_svc_t, conn_store_svc_destroy));
-                       CONN_STORE_SVC($$)->hostname = $3;
-                       CONN_STORE_SVC($$)->name = $5;
-                       CONN_STORE_SVC($$)->last_update = $6;
-                       $$->cmd = SDB_CONNECTION_STORE_SERVICE;
-               }
-       |
-       STORE METRIC_T STRING '.' STRING metric_store_clause last_update_clause
-               {
-                       $$ = SDB_CONN_NODE(sdb_object_create_dT(/* name = */ NULL,
-                                               conn_store_metric_t, conn_store_metric_destroy));
-                       CONN_STORE_METRIC($$)->hostname = $3;
-                       CONN_STORE_METRIC($$)->name = $5;
-                       CONN_STORE_METRIC($$)->store_type = $6.type;
-                       CONN_STORE_METRIC($$)->store_id = $6.id;
-                       CONN_STORE_METRIC($$)->last_update = $7;
-                       $$->cmd = SDB_CONNECTION_STORE_METRIC;
-               }
-       |
-       STORE HOST_T ATTRIBUTE_T STRING '.' STRING data last_update_clause
-               {
-                       $$ = SDB_CONN_NODE(sdb_object_create_dT(/* name = */ NULL,
-                                               conn_store_attr_t, conn_store_attr_destroy));
-                       CONN_STORE_ATTR($$)->parent_type = SDB_HOST;
-                       CONN_STORE_ATTR($$)->hostname = NULL;
-                       CONN_STORE_ATTR($$)->parent = $4;
-                       CONN_STORE_ATTR($$)->key = $6;
-                       CONN_STORE_ATTR($$)->value = $7;
-                       CONN_STORE_ATTR($$)->last_update = $8;
-                       $$->cmd = SDB_CONNECTION_STORE_ATTRIBUTE;
-               }
-       |
-       STORE SERVICE_T ATTRIBUTE_T STRING '.' STRING '.' STRING data last_update_clause
-               {
-                       $$ = SDB_CONN_NODE(sdb_object_create_dT(/* name = */ NULL,
-                                               conn_store_attr_t, conn_store_attr_destroy));
-                       CONN_STORE_ATTR($$)->parent_type = SDB_SERVICE;
-                       CONN_STORE_ATTR($$)->hostname = $4;
-                       CONN_STORE_ATTR($$)->parent = $6;
-                       CONN_STORE_ATTR($$)->key = $8;
-                       CONN_STORE_ATTR($$)->value = $9;
-                       CONN_STORE_ATTR($$)->last_update = $10;
-                       $$->cmd = SDB_CONNECTION_STORE_ATTRIBUTE;
-               }
-       |
-       STORE METRIC_T ATTRIBUTE_T STRING '.' STRING '.' STRING data last_update_clause
-               {
-                       $$ = SDB_CONN_NODE(sdb_object_create_dT(/* name = */ NULL,
-                                               conn_store_attr_t, conn_store_attr_destroy));
-                       CONN_STORE_ATTR($$)->parent_type = SDB_METRIC;
-                       CONN_STORE_ATTR($$)->hostname = $4;
-                       CONN_STORE_ATTR($$)->parent = $6;
-                       CONN_STORE_ATTR($$)->key = $8;
-                       CONN_STORE_ATTR($$)->value = $9;
-                       CONN_STORE_ATTR($$)->last_update = $10;
-                       $$->cmd = SDB_CONNECTION_STORE_ATTRIBUTE;
-               }
-       ;
-
-last_update_clause:
-       LAST UPDATE datetime { $$ = $3; }
-       |
-       /* empty */ { $$ = sdb_gettime(); }
-
-metric_store_clause:
-       STORE STRING STRING { $$.type = $2; $$.id = $3; }
-       |
-       /* empty */ { $$.type = $$.id = NULL; }
-
-/*
- * TIMESERIES <host>.<metric> [START <datetime>] [END <datetime>];
- *
- * Returns a time-series for the specified host's metric.
- */
-timeseries_statement:
-       TIMESERIES STRING '.' STRING start_clause end_clause
-               {
-                       $$ = SDB_CONN_NODE(sdb_object_create_dT(/* name = */ NULL,
-                                               conn_ts_t, conn_ts_destroy));
-                       CONN_TS($$)->hostname = $2;
-                       CONN_TS($$)->metric = $4;
-                       CONN_TS($$)->opts.start = $5;
-                       CONN_TS($$)->opts.end = $6;
-                       $$->cmd = SDB_CONNECTION_TIMESERIES;
-               }
-       ;
-
-start_clause:
-       START datetime { $$ = $2; }
-       |
-       /* empty */ { $$ = sdb_gettime() - SDB_INTERVAL_HOUR; }
-
-end_clause:
-       END datetime { $$ = $2; }
-       |
-       /* empty */ { $$ = sdb_gettime(); }
-
-/*
- * Basic expressions.
- */
-
-condition:
-       matcher
-               {
-                       if (! $1) {
-                               /* TODO: improve error reporting */
-                               sdb_fe_yyerror(&yylloc, scanner,
-                                               YY_("syntax error, invalid condition"));
-                               YYABORT;
-                       }
-
-                       $$ = SDB_CONN_NODE(sdb_object_create_dT(/* name = */ NULL,
-                                               conn_matcher_t, conn_matcher_destroy));
-                       $$->cmd = SDB_CONNECTION_MATCHER;
-                       CONN_MATCHER($$)->matcher = $1;
-               }
-       ;
-
-matcher:
-       '(' matcher ')'
-               {
-                       $$ = $2;
-               }
-       |
-       matcher AND matcher
-               {
-                       $$ = sdb_store_con_matcher($1, $3);
-                       sdb_object_deref(SDB_OBJ($1));
-                       sdb_object_deref(SDB_OBJ($3));
-               }
-       |
-       matcher OR matcher
-               {
-                       $$ = sdb_store_dis_matcher($1, $3);
-                       sdb_object_deref(SDB_OBJ($1));
-                       sdb_object_deref(SDB_OBJ($3));
-               }
-       |
-       NOT matcher
-               {
-                       $$ = sdb_store_inv_matcher($2);
-                       sdb_object_deref(SDB_OBJ($2));
-               }
-       |
-       compare_matcher
-               {
-                       $$ = $1;
-               }
-       ;
-
-compare_matcher:
-       expression cmp expression
-               {
-                       sdb_store_matcher_op_cb cb = sdb_store_parse_matcher_op($2);
-                       assert(cb); /* else, the grammar accepts invalid 'cmp' */
-                       $$ = cb($1, $3);
-                       sdb_object_deref(SDB_OBJ($1));
-                       sdb_object_deref(SDB_OBJ($3));
-               }
-       |
-       ANY expression cmp expression
-               {
-                       $$ = name_iter_matcher(MATCHER_ANY, $2, $3, $4);
-                       sdb_object_deref(SDB_OBJ($2));
-                       sdb_object_deref(SDB_OBJ($4));
-               }
-       |
-       ALL expression cmp expression
-               {
-                       $$ = name_iter_matcher(MATCHER_ALL, $2, $3, $4);
-                       sdb_object_deref(SDB_OBJ($2));
-                       sdb_object_deref(SDB_OBJ($4));
-               }
-       |
-       expression IS NULL_T
-               {
-                       $$ = sdb_store_isnull_matcher($1);
-                       sdb_object_deref(SDB_OBJ($1));
-               }
-       |
-       expression IS NOT NULL_T
-               {
-                       $$ = sdb_store_isnnull_matcher($1);
-                       sdb_object_deref(SDB_OBJ($1));
-               }
-       |
-       expression IN expression
-               {
-                       $$ = sdb_store_in_matcher($1, $3);
-                       sdb_object_deref(SDB_OBJ($1));
-                       sdb_object_deref(SDB_OBJ($3));
-               }
-       |
-       expression NOT IN expression
-               {
-                       $$ = sdb_store_nin_matcher($1, $4);
-                       sdb_object_deref(SDB_OBJ($1));
-                       sdb_object_deref(SDB_OBJ($4));
-               }
-       ;
-
-expression:
-       arithmetic_expression
-               {
-                       if (! $1) {
-                               /* we should have better error messages here
-                                * TODO: maybe let the analyzer handle this instead */
-                               sdb_fe_yyerror(&yylloc, scanner,
-                                               YY_("syntax error, invalid arithmetic expression"));
-                               YYABORT;
-                       }
-                       $$ = $1;
-               }
-       |
-       object_expression
-               {
-                       $$ = $1;
-               }
-       |
-       data
-               {
-                       $$ = sdb_store_expr_constvalue(&$1);
-                       sdb_data_free_datum(&$1);
-               }
-       ;
-
-arithmetic_expression:
-       '(' expression ')'
-               {
-                       $$ = $2;
-               }
-       |
-       expression '+' expression
-               {
-                       $$ = sdb_store_expr_create(SDB_DATA_ADD, $1, $3);
-                       sdb_object_deref(SDB_OBJ($1)); $1 = NULL;
-                       sdb_object_deref(SDB_OBJ($3)); $3 = NULL;
-               }
-       |
-       expression '-' expression
-               {
-                       $$ = sdb_store_expr_create(SDB_DATA_SUB, $1, $3);
-                       sdb_object_deref(SDB_OBJ($1)); $1 = NULL;
-                       sdb_object_deref(SDB_OBJ($3)); $3 = NULL;
-               }
-       |
-       expression '*' expression
-               {
-                       $$ = sdb_store_expr_create(SDB_DATA_MUL, $1, $3);
-                       sdb_object_deref(SDB_OBJ($1)); $1 = NULL;
-                       sdb_object_deref(SDB_OBJ($3)); $3 = NULL;
-               }
-       |
-       expression '/' expression
-               {
-                       $$ = sdb_store_expr_create(SDB_DATA_DIV, $1, $3);
-                       sdb_object_deref(SDB_OBJ($1)); $1 = NULL;
-                       sdb_object_deref(SDB_OBJ($3)); $3 = NULL;
-               }
-       |
-       expression '%' expression
-               {
-                       $$ = sdb_store_expr_create(SDB_DATA_MOD, $1, $3);
-                       sdb_object_deref(SDB_OBJ($1)); $1 = NULL;
-                       sdb_object_deref(SDB_OBJ($3)); $3 = NULL;
-               }
-       |
-       expression CONCAT expression
-               {
-                       $$ = sdb_store_expr_create(SDB_DATA_CONCAT, $1, $3);
-                       sdb_object_deref(SDB_OBJ($1)); $1 = NULL;
-                       sdb_object_deref(SDB_OBJ($3)); $3 = NULL;
-               }
-       ;
-
-object_expression:
-       object_type '.' object_expression
-               {
-                       $$ = sdb_store_expr_typed($1, $3);
-                       sdb_object_deref(SDB_OBJ($3));
-               }
-       |
-       ATTRIBUTE_T '.' object_expression
-               {
-                       $$ = sdb_store_expr_typed(SDB_ATTRIBUTE, $3);
-                       sdb_object_deref(SDB_OBJ($3));
-               }
-       |
-       field
-               {
-                       $$ = sdb_store_expr_fieldvalue($1);
-               }
-       |
-       ATTRIBUTE_T '[' STRING ']'
-               {
-                       $$ = sdb_store_expr_attrvalue($3);
-                       free($3); $3 = NULL;
-               }
-       ;
-
-object_type:
-       HOST_T { $$ = SDB_HOST; }
-       |
-       SERVICE_T { $$ = SDB_SERVICE; }
-       |
-       METRIC_T { $$ = SDB_METRIC; }
-       ;
-
-object_type_plural:
-       HOSTS_T { $$ = SDB_HOST; }
-       |
-       SERVICES_T { $$ = SDB_SERVICE; }
-       |
-       METRICS_T { $$ = SDB_METRIC; }
-       ;
-
-field:
-       NAME_T { $$ = SDB_FIELD_NAME; }
-       |
-       LAST_UPDATE_T { $$ = SDB_FIELD_LAST_UPDATE; }
-       |
-       AGE_T { $$ = SDB_FIELD_AGE; }
-       |
-       INTERVAL_T { $$ = SDB_FIELD_INTERVAL; }
-       |
-       BACKEND_T { $$ = SDB_FIELD_BACKEND; }
-       |
-       VALUE_T { $$ = SDB_FIELD_VALUE; }
-       ;
-
-cmp:
-       CMP_EQUAL { $$ = "="; }
-       |
-       CMP_NEQUAL { $$ = "!="; }
-       |
-       CMP_REGEX { $$ = "=~"; }
-       |
-       CMP_NREGEX { $$ = "!~"; }
-       |
-       CMP_LT { $$ = "<"; }
-       |
-       CMP_LE { $$ = "<="; }
-       |
-       CMP_GE { $$ = ">="; }
-       |
-       CMP_GT { $$ = ">"; }
-       ;
-
-data:
-       STRING { $$.type = SDB_TYPE_STRING; $$.data.string = $1; }
-       |
-       INTEGER { $$ = $1; }
-       |
-       FLOAT { $$ = $1; }
-       |
-       datetime { $$.type = SDB_TYPE_DATETIME; $$.data.datetime = $1; }
-       |
-       interval { $$ = $1; }
-       |
-       array { $$ = $1; }
-       ;
-
-datetime:
-       DATE TIME { $$ = $1 + $2; }
-       |
-       DATE { $$ = $1; }
-       |
-       TIME { $$ = $1; }
-       ;
-
-interval:
-       interval interval_elem
-               {
-                       $$.data.datetime = $1.data.datetime + $2.data.datetime;
-               }
-       |
-       interval_elem { $$ = $1; }
-       ;
-
-interval_elem:
-       INTEGER IDENTIFIER
-               {
-                       sdb_time_t unit = 1;
-
-                       unit = sdb_strpunit($2);
-                       if (! unit) {
-                               sdb_fe_yyerrorf(&yylloc, scanner,
-                                               YY_("syntax error, invalid time unit %s"), $2);
-                               free($2); $2 = NULL;
-                               YYABORT;
-                       }
-                       free($2); $2 = NULL;
-
-                       $$.type = SDB_TYPE_DATETIME;
-                       $$.data.datetime = (sdb_time_t)$1.data.integer * unit;
-
-                       if ($1.data.integer < 0) {
-                               sdb_fe_yyerror(&yylloc, scanner,
-                                               YY_("syntax error, negative intervals not supported"));
-                               YYABORT;
-                       }
-               }
-       ;
-
-array:
-       '[' array_elem_list ']'
-               {
-                       $$ = $2;
-               }
-       ;
-
-array_elem_list:
-       array_elem_list ',' data
-               {
-                       size_t elem_size;
-
-                       if (($3.type & SDB_TYPE_ARRAY) || (($1.type & 0xff) != $3.type)) {
-                               sdb_fe_yyerrorf(&yylloc, scanner, YY_("syntax error, "
-                                               "cannot use element of type %s in array of type %s"),
-                                               SDB_TYPE_TO_STRING($3.type),
-                                               SDB_TYPE_TO_STRING($1.type));
-                               sdb_data_free_datum(&$1);
-                               sdb_data_free_datum(&$3);
-                               YYABORT;
-                       }
-
-                       elem_size = sdb_data_sizeof($3.type);
-                       $1.data.array.values = realloc($1.data.array.values,
-                                       ($1.data.array.length + 1) * elem_size);
-                       if (! $1.data.array.values) {
-                               sdb_fe_yyerror(&yylloc, scanner, YY_("out of memory"));
-                               YYABORT;
-                       }
-
-                       memcpy((char *)$1.data.array.values
-                                               + $1.data.array.length * elem_size,
-                                       &$3.data, elem_size);
-                       ++$1.data.array.length;
-
-                       $$ = $1;
-               }
-       |
-       data
-               {
-                       size_t elem_size;
-
-                       if ($1.type & SDB_TYPE_ARRAY) {
-                               sdb_fe_yyerrorf(&yylloc, scanner, YY_("syntax error, "
-                                               "cannot construct array of type %s"),
-                                               SDB_TYPE_TO_STRING($1.type));
-                               sdb_data_free_datum(&$1);
-                               YYABORT;
-                       }
-
-                       $$ = $1;
-                       $$.type = $1.type | SDB_TYPE_ARRAY;
-                       $$.data.array.length = 1;
-                       elem_size = sdb_data_sizeof($1.type);
-                       $$.data.array.values = malloc(elem_size);
-                       if (! $$.data.array.values ) {
-                               sdb_fe_yyerror(&yylloc, scanner, YY_("out of memory"));
-                               YYABORT;
-                       }
-                       memcpy($$.data.array.values, &$1.data, elem_size);
-               }
-       ;
-
-%%
-
-void
-sdb_fe_yyerror(YYLTYPE *lval, sdb_fe_yyscan_t scanner, const char *msg)
-{
-       sdb_log(SDB_LOG_ERR, "frontend: parse error: %s", msg);
-       sdb_strbuf_sprintf(errbuf, "%s", msg);
-} /* sdb_fe_yyerror */
-
-void
-sdb_fe_yyerrorf(YYLTYPE *lval, sdb_fe_yyscan_t scanner, const char *fmt, ...)
-{
-       va_list ap, aq;
-       va_start(ap, fmt);
-       va_copy(aq, ap);
-       sdb_vlog(SDB_LOG_ERR, fmt, ap);
-       sdb_strbuf_vsprintf(errbuf, fmt, aq);
-       va_end(ap);
-} /* sdb_fe_yyerrorf */
-
-static sdb_store_matcher_t *
-name_iter_matcher(int type, sdb_store_expr_t *iter, const char *cmp,
-               sdb_store_expr_t *expr)
-{
-       sdb_store_matcher_op_cb cb = sdb_store_parse_matcher_op(cmp);
-       sdb_store_matcher_t *m, *tmp = NULL;
-       assert(cb);
-
-       m = cb(NULL, expr);
-       if (type == MATCHER_ANY)
-               tmp = sdb_store_any_matcher(iter, m);
-       else if (type == MATCHER_ALL)
-               tmp = sdb_store_all_matcher(iter, m);
-       sdb_object_deref(SDB_OBJ(m));
-       return tmp;
-} /* name_iter_matcher */
-
-/* vim: set tw=78 sw=4 ts=4 noexpandtab : */
-
diff --git a/src/frontend/parser.c b/src/frontend/parser.c
deleted file mode 100644 (file)
index 8d608d6..0000000
+++ /dev/null
@@ -1,192 +0,0 @@
-/*
- * SysDB - src/frontend/parser.c
- * Copyright (C) 2013 Sebastian 'tokkee' Harl <sh@tokkee.org>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
- * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
- * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
- * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "sysdb.h"
-
-#include "frontend/connection-private.h"
-#include "frontend/parser.h"
-#include "frontend/grammar.h"
-
-#include "core/store.h"
-
-#include "utils/llist.h"
-#include "utils/strbuf.h"
-
-#include <assert.h>
-#include <string.h>
-
-/*
- * private helper functions
- */
-
-static int
-scanner_init(const char *input, int len,
-               sdb_fe_yyscan_t *scanner, sdb_fe_yyextra_t *extra,
-               sdb_strbuf_t *errbuf)
-{
-       if (! input) {
-               sdb_strbuf_sprintf(errbuf, "Missing scanner input");
-               return -1;
-       }
-
-       memset(extra, 0, sizeof(*extra));
-       extra->parsetree = sdb_llist_create();
-       extra->mode = SDB_PARSE_DEFAULT;
-       extra->errbuf = errbuf;
-
-       if (! extra->parsetree) {
-               sdb_strbuf_sprintf(errbuf, "Failed to allocate parse-tree");
-               return -1;
-       }
-
-       *scanner = sdb_fe_scanner_init(input, len, extra);
-       if (! scanner) {
-               sdb_llist_destroy(extra->parsetree);
-               return -1;
-       }
-       return 0;
-} /* scanner_init */
-
-/*
- * public API
- */
-
-sdb_llist_t *
-sdb_fe_parse(const char *query, int len, sdb_strbuf_t *errbuf)
-{
-       sdb_fe_yyscan_t scanner;
-       sdb_fe_yyextra_t yyextra;
-       sdb_llist_iter_t *iter;
-       int yyres;
-
-       if (scanner_init(query, len, &scanner, &yyextra, errbuf))
-               return NULL;
-
-       yyres = sdb_fe_yyparse(scanner);
-       sdb_fe_scanner_destroy(scanner);
-
-       if (yyres) {
-               sdb_llist_destroy(yyextra.parsetree);
-               return NULL;
-       }
-
-       iter = sdb_llist_get_iter(yyextra.parsetree);
-       while (sdb_llist_iter_has_next(iter)) {
-               sdb_conn_node_t *node;
-               node = SDB_CONN_NODE(sdb_llist_iter_get_next(iter));
-               if (sdb_fe_analyze(node, errbuf)) {
-                       sdb_llist_iter_destroy(iter);
-                       sdb_llist_destroy(yyextra.parsetree);
-                       return NULL;
-               }
-       }
-       sdb_llist_iter_destroy(iter);
-       return yyextra.parsetree;
-} /* sdb_fe_parse */
-
-sdb_store_matcher_t *
-sdb_fe_parse_matcher(const char *cond, int len, sdb_strbuf_t *errbuf)
-{
-       sdb_fe_yyscan_t scanner;
-       sdb_fe_yyextra_t yyextra;
-
-       sdb_conn_node_t *node;
-       sdb_store_matcher_t *m;
-
-       int yyres;
-
-       if (scanner_init(cond, len, &scanner, &yyextra, errbuf))
-               return NULL;
-
-       yyextra.mode = SDB_PARSE_COND;
-
-       yyres = sdb_fe_yyparse(scanner);
-       sdb_fe_scanner_destroy(scanner);
-
-       if (yyres) {
-               sdb_llist_destroy(yyextra.parsetree);
-               return NULL;
-       }
-
-       node = SDB_CONN_NODE(sdb_llist_get(yyextra.parsetree, 0));
-       if (! node) {
-               sdb_strbuf_sprintf(errbuf, "Empty matcher expression '%s'", cond);
-               sdb_llist_destroy(yyextra.parsetree);
-               return NULL;
-       }
-
-       assert(node->cmd == SDB_CONNECTION_MATCHER);
-       m = CONN_MATCHER(node)->matcher;
-       CONN_MATCHER(node)->matcher = NULL;
-
-       sdb_llist_destroy(yyextra.parsetree);
-       sdb_object_deref(SDB_OBJ(node));
-       return m;
-} /* sdb_fe_parse_matcher */
-
-sdb_store_expr_t *
-sdb_fe_parse_expr(const char *expr, int len, sdb_strbuf_t *errbuf)
-{
-       sdb_fe_yyscan_t scanner;
-       sdb_fe_yyextra_t yyextra;
-
-       sdb_conn_node_t *node;
-       sdb_store_expr_t *e;
-
-       int yyres;
-
-       if (scanner_init(expr, len, &scanner, &yyextra, errbuf))
-               return NULL;
-
-       yyextra.mode = SDB_PARSE_ARITH;
-
-       yyres = sdb_fe_yyparse(scanner);
-       sdb_fe_scanner_destroy(scanner);
-
-       if (yyres) {
-               sdb_llist_destroy(yyextra.parsetree);
-               return NULL;
-       }
-
-       node = SDB_CONN_NODE(sdb_llist_get(yyextra.parsetree, 0));
-       if (! node) {
-               sdb_strbuf_sprintf(errbuf, "Empty expression '%s'", expr);
-               sdb_llist_destroy(yyextra.parsetree);
-               return NULL;
-       }
-
-       assert(node->cmd == SDB_CONNECTION_EXPR);
-       e = CONN_EXPR(node)->expr;
-       CONN_EXPR(node)->expr = NULL;
-
-       sdb_llist_destroy(yyextra.parsetree);
-       sdb_object_deref(SDB_OBJ(node));
-       return e;
-} /* sdb_fe_parse_expr */
-
-/* vim: set tw=78 sw=4 ts=4 noexpandtab : */
-
diff --git a/src/frontend/scanner.l b/src/frontend/scanner.l
deleted file mode 100644 (file)
index ab8b80d..0000000
+++ /dev/null
@@ -1,307 +0,0 @@
-/*
- * SysDB - src/frontend/scanner.l
- * Copyright (C) 2013 Sebastian 'tokkee' Harl <sh@tokkee.org>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
- * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
- * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
- * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-%{
-
-#if HAVE_CONFIG_H
-#      include "config.h"
-#endif /* HAVE_CONFIG_H */
-
-#include "core/data.h"
-#include "frontend/connection.h"
-#include "frontend/parser.h"
-#include "frontend/grammar.h"
-#include "utils/error.h"
-
-#include <assert.h>
-#include <errno.h>
-
-#include <string.h>
-#include <stdlib.h>
-
-#include <time.h>
-
-#define YY_EXTRA_TYPE sdb_fe_yyextra_t *
-
-static struct {
-       const char *name;
-       int id;
-} reserved_words[] = {
-       { "ALL",         ALL },
-       { "AND",         AND },
-       { "ANY",         ANY },
-       { "END",         END },
-       { "FETCH",       FETCH },
-       { "FILTER",      FILTER },
-       { "IN",          IN },
-       { "IS",          IS },
-       { "LAST",        LAST },
-       { "LIST",        LIST },
-       { "LOOKUP",      LOOKUP },
-       { "MATCHING",    MATCHING },
-       { "NOT",         NOT },
-       { "NULL",        NULL_T },
-       { "OR",          OR },
-       { "START",       START },
-       { "STORE",       STORE },
-       { "TIMESERIES",  TIMESERIES },
-       { "UPDATE",      UPDATE },
-
-       /* object types */
-       { "host",        HOST_T },
-       { "hosts",       HOSTS_T },
-       { "service",     SERVICE_T },
-       { "services",    SERVICES_T },
-       { "metric",      METRIC_T },
-       { "metrics",     METRICS_T },
-       { "attribute",   ATTRIBUTE_T },
-       { "attributes",  ATTRIBUTES_T },
-       /* queryable fields */
-       { "name",        NAME_T },
-       { "last_update", LAST_UPDATE_T },
-       { "age",         AGE_T },
-       { "interval",    INTERVAL_T },
-       { "backend",     BACKEND_T },
-       { "value",       VALUE_T },
-};
-
-void
-sdb_fe_yyerror(YYLTYPE *lval, sdb_fe_yyscan_t scanner, const char *msg);
-
-%}
-
-%option never-interactive
-%option reentrant
-%option bison-bridge
-%option bison-locations
-%option 8bit
-%option yylineno
-%option nodefault
-%option noinput
-%option nounput
-%option noyywrap
-%option verbose
-%option warn
-%option prefix="sdb_fe_yy" outfile="lex.yy.c"
-
-%x CSC
-
-whitespace             ([ \t\n\r\f]+)
-simple_comment ("--"[^\n\r]*)
-
-/*
- * C style comments
- */
-csc_start      \/\*
-csc_inside     ([^*/]+|[^*]\/|\*[^/])
-csc_end                \*\/
-
-/*
- * Strings and identifiers.
- */
-identifier     ([A-Za-z_][A-Za-z_0-9$]*)
-/* TODO: fully support SQL strings */
-string         ('([^']|'')*')
-
-/*
- * Numeric constants.
- */
-dec                    ([\+\-]?[0-9]+)
-exp                    ([\+\-]?[0-9]+[Ee]\+?[0-9]+)
-integer                ({dec}|{exp})
-float1         ([\+\-]?[0-9]+\.[0-9]*([Ee][\+\-]?[0-9]+)?)
-float2         ([\+\-]?[0-9]*\.[0-9]+([Ee][\+\-]?[0-9]+)?)
-float3         ([\+\-]?[0-9]+[Ee]\-[0-9]+)
-float4         ([\+\-]?[Ii][Nn][Ff]([Ii][Nn][Ii][Tt][Yy])?)
-float5         ([Nn][Aa][Nn])
-float          ({float1}|{float2}|{float3}|{float4}|{float5})
-
-/*
- * Time constants.
- */
-date           ([0-9]{4}-[0-9]{2}-[0-9]{2})
-time           ([0-9]{1,2}:[0-9]{1,2}(:[0-9]{1,2}(\.[0-9]{1,9})?)?)
-
-%%
-
-{whitespace} |
-{simple_comment}       { /* ignore */ }
-
-{csc_start}                    { BEGIN(CSC); }
-<CSC>{csc_inside}      { /* ignore */ }
-<CSC>{csc_end}         { BEGIN(INITIAL); }
-<CSC><<EOF>> {
-               sdb_fe_yyerror(yylloc, yyscanner, "unterminated C-style comment");
-               return SCANNER_ERROR;
-       }
-
-{identifier} {
-               size_t i;
-               for (i = 0; i < SDB_STATIC_ARRAY_LEN(reserved_words); ++i)
-                       if (! strcasecmp(reserved_words[i].name, yytext))
-                               return reserved_words[i].id;
-
-               yylval->str = strdup(yytext);
-               return IDENTIFIER;
-       }
-{string} {
-               char *quot;
-               size_t len;
-
-               /* remove the leading and trailing quote */
-               yytext[yyleng - 1] = '\0';
-               yylval->str = strdup(yytext + 1);
-
-               quot = yylval->str;
-               len = yyleng - 2;
-               while ((quot = strstr(quot, "''")) != NULL) {
-                       memmove(quot, quot + 1, len - (quot - yylval->str) - 1);
-                       yylval->str[len - 1] = '\0';
-                       --len;
-                       ++quot;
-               }
-               return STRING;
-       }
-{integer} {
-               yylval->data.data.integer = (int64_t)strtoll(yytext, NULL, 10);
-               yylval->data.type = SDB_TYPE_INTEGER;
-               return INTEGER;
-       }
-{float} {
-               yylval->data.data.decimal = strtod(yytext, NULL);
-               yylval->data.type = SDB_TYPE_DECIMAL;
-               return FLOAT;
-       }
-
-{date} {
-               struct tm tm;
-               memset(&tm, 0, sizeof(tm));
-               if (! strptime(yytext, "%Y-%m-%d", &tm)) {
-                       char errmsg[1024];
-                       snprintf(errmsg, sizeof(errmsg),
-                               "Failed to parse '%s' as date", yytext);
-                       sdb_fe_yyerror(yylloc, yyscanner, errmsg);
-                       return SCANNER_ERROR;
-               }
-               yylval->datetime = SECS_TO_SDB_TIME(mktime(&tm));
-               return DATE;
-       }
-{time} {
-               struct tm tm;
-               char time[9], ns[10];
-               char *tmp;
-               int t;
-
-               memset(&tm, 0, sizeof(tm));
-               memset(time, '\0', sizeof(time));
-               memset(ns, '0', sizeof(ns));
-               ns[sizeof(ns) - 1] = '\0';
-
-               tmp = strchr(yytext, '.');
-               if (tmp) {
-                       size_t i;
-                       *tmp = '\0';
-                       ++tmp;
-                       strncpy(ns, tmp, sizeof(ns));
-                       for (i = strlen(ns); i < 9; ++i)
-                               ns[i] = '0';
-               }
-               strncpy(time, yytext, sizeof(time));
-               if (tmp) {
-                       /* reset for better error messages */
-                       --tmp;
-                       *tmp = '.';
-               }
-
-               tmp = strchr(time, ':');
-               assert(tmp);
-               tmp = strchr(tmp + 1, ':');
-               if (! tmp)
-                       strncat(time, ":00", sizeof(time) - strlen(time) - 1);
-
-               if (! strptime(time, "%H:%M:%S", &tm)) {
-                       char errmsg[1024];
-                       snprintf(errmsg, sizeof(errmsg),
-                               "Failed to parse '%s' as time", yytext);
-                       sdb_fe_yyerror(yylloc, yyscanner, errmsg);
-                       return SCANNER_ERROR;
-               }
-
-               t = tm.tm_sec + 60 * tm.tm_min + 3600 * tm.tm_hour;
-               yylval->datetime = SECS_TO_SDB_TIME(t);
-               yylval->datetime += (sdb_time_t)strtoll(ns, NULL, 10);
-               return TIME;
-       }
-
-=      { return CMP_EQUAL; }
-!=     { return CMP_NEQUAL; }
-=~     { return CMP_REGEX; }
-!~     { return CMP_NREGEX; }
-\<     { return CMP_LT; }
-\<=    { return CMP_LE; }
-\>=    { return CMP_GE; }
-\>     { return CMP_GT; }
-\|\|   { return CONCAT; }
-
-.      { /* XXX: */ return yytext[0]; }
-
-%%
-
-sdb_fe_yyscan_t
-sdb_fe_scanner_init(const char *str, int len, sdb_fe_yyextra_t *yyext)
-{
-       yyscan_t scanner;
-
-       if (! str)
-               return NULL;
-
-       if (sdb_fe_yylex_init(&scanner)) {
-               char errbuf[1024];
-               sdb_strbuf_sprintf(yyext->errbuf, "yylex_init_failed: %s",
-                       sdb_strerror(errno, errbuf, sizeof(errbuf)));
-               return NULL;
-       }
-
-       sdb_fe_yyset_extra(yyext, scanner);
-
-       if (len < 0)
-               len = strlen(str);
-
-       /* the newly allocated buffer state (YY_BUFFER_STATE) is stored inside the
-        * scanner and, thus, will be freed by yylex_destroy */
-       sdb_fe_yy_scan_bytes(str, len, scanner);
-       return scanner;
-} /* sdb_fe_scanner_init */
-
-void
-sdb_fe_scanner_destroy(sdb_fe_yyscan_t scanner)
-{
-       sdb_fe_yylex_destroy(scanner);
-} /* sdb_fe_scanner_destroy */
-
-/* vim: set tw=78 sw=4 ts=4 noexpandtab : */
-
diff --git a/src/include/frontend/parser.h b/src/include/frontend/parser.h
deleted file mode 100644 (file)
index 7665701..0000000
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * SysDB - src/include/frontend/parser.h
- * Copyright (C) 2013 Sebastian 'tokkee' Harl <sh@tokkee.org>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
- * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
- * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
- * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef SDB_FRONTEND_PARSER_H
-#define SDB_FRONTEND_PARSER_H 1
-
-#include "core/store.h"
-#include "frontend/connection.h"
-#include "utils/llist.h"
-#include "utils/strbuf.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* parser modes */
-enum {
-       /* parser accepts any command statement */
-       SDB_PARSE_DEFAULT = 0,
-
-       /* parser accepts any conditional statement */
-       SDB_PARSE_COND    = 1 << 1,
-
-       /* parser accepts any arithmetic expression */
-       SDB_PARSE_ARITH   = 1 << 2,
-};
-
-/* YY_EXTRA data */
-typedef struct {
-       /* list of sdb_conn_node_t objects */
-       sdb_llist_t *parsetree;
-
-       /* parser mode */
-       int mode;
-
-       /* buffer for parser error messages */
-       sdb_strbuf_t *errbuf;
-} sdb_fe_yyextra_t;
-
-/* see yyscan_t */
-typedef void *sdb_fe_yyscan_t;
-
-sdb_fe_yyscan_t
-sdb_fe_scanner_init(const char *str, int len, sdb_fe_yyextra_t *yyext);
-
-void
-sdb_fe_scanner_destroy(sdb_fe_yyscan_t scanner);
-
-int
-sdb_fe_yyparse(sdb_fe_yyscan_t scanner);
-
-sdb_store_matcher_t *
-sdb_fe_parse_matcher(const char *cond, int len, sdb_strbuf_t *errbuf);
-
-sdb_store_expr_t *
-sdb_fe_parse_expr(const char *expr, int len, sdb_strbuf_t *errbuf);
-
-/*
- * sdb_fe_analyze:
- * Analyze a parsed node, checking for semantical errors. Error messages will
- * be written to the string buffer, if provided.
- *
- * Returns:
- *  - 0 if the node is semantically correct
- *  - a negative value else
- */
-int
-sdb_fe_analyze(sdb_conn_node_t *node, sdb_strbuf_t *errbuf);
-
-#ifdef __cplusplus
-} /* extern "C" */
-#endif
-
-#endif /* ! SDB_FRONTEND_PARSER_H */
-
-/* vim: set tw=78 sw=4 ts=4 noexpandtab : */
-
index 92e49bbd39c5db674f5208c6259baffe5037e2d8..2830663f03d8f13843eaa7543e1c18c50ebeccc5 100644 (file)
@@ -28,9 +28,6 @@
 #ifndef SDB_PARSER_PARSER_H
 #define SDB_PARSER_PARSER_H 1
 
-/* TODO: move SDB_PARSE_* constants here as well */
-#include "frontend/parser.h"
-
 #include "core/store.h"
 #include "parser/ast.h"
 #include "utils/llist.h"
 extern "C" {
 #endif
 
+/* parser modes */
+enum {
+       /* parser accepts any command statement */
+       SDB_PARSE_DEFAULT = 0,
+
+       /* parser accepts any conditional statement */
+       SDB_PARSE_COND    = 1 << 1,
+
+       /* parser accepts any arithmetic expression */
+       SDB_PARSE_ARITH   = 1 << 2,
+};
+
 /*
  * sdb_parser_parse:
  * Parse the specified query of the specified length. If len is a negative
index 173cc8fbb2ecee6cfc0b725e0e704ae8d3d83f8c..3faaf94e89d6d27dcbe5c894678da13c87009a2f 100644 (file)
@@ -38,7 +38,6 @@ UNIT_TESTS = \
                unit/core/store_test \
                unit/core/time_test \
                unit/frontend/connection_test \
-               unit/frontend/parser_test \
                unit/frontend/query_test \
                unit/frontend/sock_test \
                unit/parser/ast_test \
diff --git a/t/unit/frontend/parser_test.c b/t/unit/frontend/parser_test.c
deleted file mode 100644 (file)
index 65ec6ca..0000000
+++ /dev/null
@@ -1,971 +0,0 @@
-/*
- * SysDB - t/unit/frontend/parser_test.c
- * Copyright (C) 2013-2015 Sebastian 'tokkee' Harl <sh@tokkee.org>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
- * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
- * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
- * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#if HAVE_CONFIG_H
-#      include "config.h"
-#endif
-
-#include "frontend/connection.h"
-#include "frontend/parser.h"
-#include "core/store-private.h"
-#include "core/object.h"
-#include "testutils.h"
-
-#include <check.h>
-#include <limits.h>
-
-/*
- * tests
- */
-
-struct {
-       const char *query;
-       int len;
-       int expected;
-       sdb_conn_state_t expected_cmd;
-} parse_data[] = {
-       /* empty commands */
-       { NULL,                  -1, -1, 0 },
-       { "",                    -1,  0, 0 },
-       { ";",                   -1,  0, 0 },
-       { ";;",                  -1,  0, 0 },
-
-       /* FETCH commands */
-       { "FETCH host 'host'",   -1,  1, SDB_CONNECTION_FETCH  },
-       { "FETCH host 'host' FILTER "
-         "age > 60s",           -1,  1, SDB_CONNECTION_FETCH  },
-       { "FETCH service "
-         "'host'.'service'",    -1,  1, SDB_CONNECTION_FETCH  },
-       { "FETCH metric "
-         "'host'.'metric'",     -1,  1, SDB_CONNECTION_FETCH  },
-
-       /* LIST commands */
-       { "LIST hosts",            -1,  1, SDB_CONNECTION_LIST   },
-       { "LIST hosts -- foo",     -1,  1, SDB_CONNECTION_LIST   },
-       { "LIST hosts;",           -1,  1, SDB_CONNECTION_LIST   },
-       { "LIST hosts; INVALID",   11,  1, SDB_CONNECTION_LIST   },
-       { "LIST hosts FILTER "
-         "age > 60s",             -1,  1, SDB_CONNECTION_LIST   },
-       { "LIST services",         -1,  1, SDB_CONNECTION_LIST   },
-       { "LIST services FILTER "
-         "age > 60s",             -1,  1, SDB_CONNECTION_LIST   },
-       { "LIST metrics",          -1,  1, SDB_CONNECTION_LIST   },
-       { "LIST metrics FILTER "
-         "age > 60s",             -1,  1, SDB_CONNECTION_LIST   },
-       /* field access */
-       { "LIST hosts FILTER "
-         "name = 'a'",            -1,  1, SDB_CONNECTION_LIST   },
-       { "LIST hosts FILTER "
-         "last_update > 1s",      -1,  1, SDB_CONNECTION_LIST   },
-       { "LIST hosts FILTER "
-         "age > 120s",            -1,  1, SDB_CONNECTION_LIST   },
-       { "LIST hosts FILTER "
-         "interval > 10s",        -1,  1, SDB_CONNECTION_LIST   },
-       { "LIST hosts FILTER "
-         "backend = ['b']",       -1,  1, SDB_CONNECTION_LIST   },
-       { "LIST hosts FILTER "
-         "value = 'a'",           -1, -1, 0 },
-       { "LIST hosts FILTER ANY "
-         "attribute.value = 'a'", -1,  1, SDB_CONNECTION_LIST   },
-       { "LIST services FILTER "
-         "name = 'a'",            -1,  1, SDB_CONNECTION_LIST   },
-       { "LIST services FILTER "
-         "last_update > 1s",      -1,  1, SDB_CONNECTION_LIST   },
-       { "LIST services FILTER "
-         "age > 120s",            -1,  1, SDB_CONNECTION_LIST   },
-       { "LIST services FILTER "
-         "interval > 10s",        -1,  1, SDB_CONNECTION_LIST   },
-       { "LIST services FILTER "
-         "backend = ['b']",       -1,  1, SDB_CONNECTION_LIST   },
-       { "LIST services FILTER "
-         "value = 'a'",           -1, -1, 0 },
-       { "LIST services FILTER ANY "
-         "attribute.value = 'a'", -1,  1, SDB_CONNECTION_LIST   },
-       { "LIST metrics FILTER "
-         "name = 'a'",            -1,  1, SDB_CONNECTION_LIST   },
-       { "LIST metrics FILTER "
-         "last_update > 1s",      -1,  1, SDB_CONNECTION_LIST   },
-       { "LIST metrics FILTER "
-         "age > 120s",            -1,  1, SDB_CONNECTION_LIST   },
-       { "LIST metrics FILTER "
-         "interval > 10s",        -1,  1, SDB_CONNECTION_LIST   },
-       { "LIST metrics FILTER "
-         "backend = ['b']",       -1,  1, SDB_CONNECTION_LIST   },
-       { "LIST metrics FILTER "
-         "value = 'a'",           -1, -1, 0 },
-       { "LIST metrics FILTER ANY "
-         "attribute.value = 'a'", -1,  1, SDB_CONNECTION_LIST   },
-
-       /* LOOKUP commands */
-       { "LOOKUP hosts",        -1,  1, SDB_CONNECTION_LOOKUP },
-       { "LOOKUP hosts MATCHING "
-         "name = 'host'",       -1,  1, SDB_CONNECTION_LOOKUP },
-       { "LOOKUP hosts MATCHING NOT "
-         "name = 'host'",       -1,  1, SDB_CONNECTION_LOOKUP },
-       { "LOOKUP hosts MATCHING "
-         "name =~ 'p' AND "
-         "ANY service.name =~ 'p'", -1,   1, SDB_CONNECTION_LOOKUP },
-       { "LOOKUP hosts MATCHING NOT "
-         "name =~ 'p' AND "
-         "ANY service.name =~ 'p'", -1,   1, SDB_CONNECTION_LOOKUP },
-       { "LOOKUP hosts MATCHING "
-         "name =~ 'p' AND "
-         "ANY service.name =~ 'p' OR "
-         "ANY service.name =~ 'r'", -1,   1, SDB_CONNECTION_LOOKUP },
-       { "LOOKUP hosts MATCHING NOT "
-         "name =~ 'p' AND "
-         "ANY service.name =~ 'p' OR "
-         "ANY service.name =~ 'r'", -1,   1, SDB_CONNECTION_LOOKUP },
-       { "LOOKUP hosts MATCHING "
-         "name =~ 'p' "
-         "FILTER age > 1D",         -1,   1, SDB_CONNECTION_LOOKUP },
-       { "LOOKUP hosts MATCHING "
-         "name =~ 'p' "
-         "FILTER age > 1D AND "
-         "interval < 240s" ,        -1,   1, SDB_CONNECTION_LOOKUP },
-       { "LOOKUP hosts MATCHING "
-         "name =~ 'p' "
-         "FILTER NOT age>1D",       -1,   1, SDB_CONNECTION_LOOKUP },
-       { "LOOKUP hosts MATCHING "
-         "name =~ 'p' "
-         "FILTER age>"
-         "interval",                -1,   1, SDB_CONNECTION_LOOKUP },
-       { "LOOKUP hosts MATCHING "
-         "host.name =~ 'p'",        -1,   1, SDB_CONNECTION_LOOKUP },
-       { "LOOKUP services",         -1,   1, SDB_CONNECTION_LOOKUP },
-       { "LOOKUP services MATCHING ANY "
-         "attribute.name =~ 'a'",   -1,   1, SDB_CONNECTION_LOOKUP },
-       { "LOOKUP services MATCHING "
-         "host.name = 'p'",         -1,   1, SDB_CONNECTION_LOOKUP },
-       { "LOOKUP services MATCHING "
-         "service.name = 'p'",      -1,   1, SDB_CONNECTION_LOOKUP },
-       { "LOOKUP metrics",          -1,   1, SDB_CONNECTION_LOOKUP },
-       { "LOOKUP metrics MATCHING ANY "
-         "attribute.name =~ 'a'",   -1,   1, SDB_CONNECTION_LOOKUP },
-       { "LOOKUP metrics MATCHING "
-         "host.name = 'p'",         -1,   1, SDB_CONNECTION_LOOKUP },
-       { "LOOKUP metrics MATCHING "
-         "metric.name = 'p'",       -1,   1, SDB_CONNECTION_LOOKUP },
-
-       /* TIMESERIES commands */
-       { "TIMESERIES 'host'.'metric' "
-         "START 2014-01-01 "
-         "END 2014-12-31 "
-         "23:59:59",            -1,  1, SDB_CONNECTION_TIMESERIES },
-       { "TIMESERIES 'host'.'metric' "
-         "START 2014-02-02 "
-         "14:02",               -1,  1, SDB_CONNECTION_TIMESERIES },
-       { "TIMESERIES 'host'.'metric' "
-         "END 2014-02-02",      -1,  1, SDB_CONNECTION_TIMESERIES },
-       { "TIMESERIES "
-         "'host'.'metric'",     -1,  1, SDB_CONNECTION_TIMESERIES },
-
-       /* STORE commands */
-       { "STORE host 'host'",   -1,  1, SDB_CONNECTION_STORE_HOST },
-       { "STORE host 'host' "
-         "LAST UPDATE "
-         "2015-02-01",          -1,  1, SDB_CONNECTION_STORE_HOST },
-       { "STORE host attribute "
-         "'host'.'key' 123",    -1,  1, SDB_CONNECTION_STORE_ATTRIBUTE },
-       { "STORE host attribute "
-         "'host'.'key' 123 "
-         "LAST UPDATE "
-         "2015-02-01",          -1,  1, SDB_CONNECTION_STORE_ATTRIBUTE },
-       { "STORE service "
-         "'host'.'svc'",        -1,  1, SDB_CONNECTION_STORE_SERVICE },
-       { "STORE service "
-         "'host'.'svc' "
-         "LAST UPDATE "
-         "2015-02-01",          -1,  1, SDB_CONNECTION_STORE_SERVICE },
-       { "STORE service attribute "
-         "'host'.'svc'.'key' "
-         "123",                 -1,  1, SDB_CONNECTION_STORE_ATTRIBUTE },
-       { "STORE service attribute "
-         "'host'.'svc'.'key' "
-         "123 "
-         "LAST UPDATE "
-         "2015-02-01",          -1,  1, SDB_CONNECTION_STORE_ATTRIBUTE },
-       { "STORE metric "
-         "'host'.'metric'",     -1,  1, SDB_CONNECTION_STORE_METRIC },
-       { "STORE metric "
-         "'host'.'metric' "
-         "LAST UPDATE "
-         "2015-02-01",          -1,  1, SDB_CONNECTION_STORE_METRIC },
-       { "STORE metric "
-         "'host'.'metric' "
-         "STORE 'typ' 'id' "
-         "LAST UPDATE "
-         "2015-02-01",          -1,  1, SDB_CONNECTION_STORE_METRIC },
-       { "STORE metric attribute "
-         "'host'.'metric'.'key' "
-         "123",                 -1,  1, SDB_CONNECTION_STORE_ATTRIBUTE },
-       { "STORE metric attribute "
-         "'host'.'metric'.'key' "
-         "123 "
-         "LAST UPDATE "
-         "2015-02-01",          -1,  1, SDB_CONNECTION_STORE_ATTRIBUTE },
-
-       /* string constants */
-       { "LOOKUP hosts MATCHING "
-         "name = ''''",         -1,  1, SDB_CONNECTION_LOOKUP },
-       { "LOOKUP hosts MATCHING "
-         "name = '''foo'",      -1,  1, SDB_CONNECTION_LOOKUP },
-       { "LOOKUP hosts MATCHING "
-         "name = 'f''oo'",      -1,  1, SDB_CONNECTION_LOOKUP },
-       { "LOOKUP hosts MATCHING "
-         "name = 'foo'''",      -1,  1, SDB_CONNECTION_LOOKUP },
-       { "LOOKUP hosts MATCHING "
-         "name = '''",          -1, -1, 0 },
-
-       /* numeric constants */
-       { "LOOKUP hosts MATCHING "
-         "attribute['foo'] = "
-         "1234",                -1,  1, SDB_CONNECTION_LOOKUP },
-       { "LOOKUP hosts MATCHING "
-         "attribute['foo'] != "
-         "+234",                -1,  1, SDB_CONNECTION_LOOKUP },
-       { "LOOKUP hosts MATCHING "
-         "attribute['foo'] < "
-         "-234",                -1,  1, SDB_CONNECTION_LOOKUP },
-       { "LOOKUP hosts MATCHING "
-         "attribute['foo'] > "
-         "12.4",                -1,  1, SDB_CONNECTION_LOOKUP },
-       { "LOOKUP hosts MATCHING "
-         "attribute['foo'] <= "
-         "12. + .3",            -1,  1, SDB_CONNECTION_LOOKUP },
-       { "LOOKUP hosts MATCHING "
-         "attribute['foo'] <= "
-         "'f' || 'oo'",         -1,  1, SDB_CONNECTION_LOOKUP },
-       { "LOOKUP hosts MATCHING "
-         "attribute['foo'] >= "
-         ".4",                  -1,  1, SDB_CONNECTION_LOOKUP },
-       { "LOOKUP hosts MATCHING "
-         "attribute['foo'] = "
-         "+12e3",               -1,  1, SDB_CONNECTION_LOOKUP },
-       { "LOOKUP hosts MATCHING "
-         "attribute['foo'] = "
-         "+12e-3",              -1,  1, SDB_CONNECTION_LOOKUP },
-       { "LOOKUP hosts MATCHING "
-         "attribute['foo'] = "
-         "-12e+3",              -1,  1, SDB_CONNECTION_LOOKUP },
-
-       /* date, time, interval constants */
-       { "LOOKUP hosts MATCHING "
-         "attribute['foo'] = "
-         "1 Y 42D",             -1,  1, SDB_CONNECTION_LOOKUP },
-       { "LOOKUP hosts MATCHING "
-         "attribute['foo'] = "
-         "1s 42D",              -1,  1, SDB_CONNECTION_LOOKUP },
-       /*
-        * TODO: Something like 1Y42D should work as well but it doesn't since
-        * the scanner will tokenize it into {digit}{identifier} :-/
-        *
-       { "LOOKUP hosts MATCHING "
-         "attribute['foo'] = "
-         "1Y42D",               -1,  1, SDB_CONNECTION_LOOKUP },
-        */
-
-       /* array constants */
-       { "LOOKUP hosts MATCHING "
-         "backend = ['foo']",   -1,  1, SDB_CONNECTION_LOOKUP },
-       { "LOOKUP hosts MATCHING "
-         "backend = ['a','b']", -1,  1, SDB_CONNECTION_LOOKUP },
-
-       /* array iteration */
-       { "LOOKUP hosts MATCHING "
-         "'foo' IN backend",   -1,   1, SDB_CONNECTION_LOOKUP },
-       { "LOOKUP hosts MATCHING 'foo' "
-         "NOT IN backend",   -1,   1, SDB_CONNECTION_LOOKUP },
-       { "LOOKUP hosts MATCHING "
-         "['foo','bar'] "
-         "IN backend ",        -1,   1, SDB_CONNECTION_LOOKUP },
-       /* attribute type is unknown */
-       { "LOOKUP hosts MATCHING "
-         "attribute['backend'] "
-         "IN backend ",        -1,   1, SDB_CONNECTION_LOOKUP },
-       /* type mismatch */
-       { "LOOKUP hosts MATCHING "
-         "1 IN backend ",      -1,  -1, 0 },
-       { "LOOKUP hosts MATCHING "
-         "1 NOT IN backend ",  -1,  -1, 0 },
-       { "LOOKUP hosts MATCHING "
-         "ANY backend < 'b'",  -1,   1, SDB_CONNECTION_LOOKUP },
-       { "LOOKUP hosts MATCHING "
-         "ANY backend <= 'b'", -1,   1, SDB_CONNECTION_LOOKUP },
-       { "LOOKUP hosts MATCHING "
-         "ANY backend = 'b'",  -1,   1, SDB_CONNECTION_LOOKUP },
-       { "LOOKUP hosts MATCHING "
-         "ANY backend != 'b'", -1,   1, SDB_CONNECTION_LOOKUP },
-       { "LOOKUP hosts MATCHING "
-         "ANY backend >= 'b'", -1,   1, SDB_CONNECTION_LOOKUP },
-       { "LOOKUP hosts MATCHING "
-         "ANY backend > 'b'",  -1,   1, SDB_CONNECTION_LOOKUP },
-       { "LOOKUP hosts MATCHING "
-         "ANY backend =~ 'b'", -1,   1, SDB_CONNECTION_LOOKUP },
-       { "LOOKUP hosts MATCHING "
-         "ANY backend !~ 'b'", -1,   1, SDB_CONNECTION_LOOKUP },
-       /* right operand is an array */
-       { "LOOKUP hosts MATCHING "
-         "ANY backend !~ backend",
-                               -1,  -1, 0 },
-       { "LOOKUP hosts MATCHING "
-         "ALL backend < 'b'",  -1,   1, SDB_CONNECTION_LOOKUP },
-       { "LOOKUP hosts MATCHING "
-         "ALL backend <= 'b'", -1,   1, SDB_CONNECTION_LOOKUP },
-       { "LOOKUP hosts MATCHING "
-         "ALL backend = 'b'",  -1,   1, SDB_CONNECTION_LOOKUP },
-       { "LOOKUP hosts MATCHING "
-         "ALL backend != 'b'", -1,   1, SDB_CONNECTION_LOOKUP },
-       { "LOOKUP hosts MATCHING "
-         "ALL backend >= 'b'", -1,   1, SDB_CONNECTION_LOOKUP },
-       { "LOOKUP hosts MATCHING "
-         "ALL backend > 'b'",  -1,   1, SDB_CONNECTION_LOOKUP },
-       { "LOOKUP hosts MATCHING "
-         "ALL backend =~ 'b'", -1,   1, SDB_CONNECTION_LOOKUP },
-       { "LOOKUP hosts MATCHING "
-         "ALL backend !~ 'b'", -1,   1, SDB_CONNECTION_LOOKUP },
-       /* attribute type is unknown */
-       { "LOOKUP hosts MATCHING "
-         "ANY backend = attribute['backend']",
-                               -1,   1, SDB_CONNECTION_LOOKUP },
-       /* type mismatch */
-       { "LOOKUP hosts MATCHING "
-         "ANY backend = 1",    -1,  -1, 0 },
-
-       /* valid operand types */
-       { "LOOKUP hosts MATCHING "
-         "age * 1 > 0s",        -1,  1, SDB_CONNECTION_LOOKUP },
-       { "LOOKUP hosts MATCHING "
-         "age / 1 > 0s",        -1,  1, SDB_CONNECTION_LOOKUP },
-       { "LOOKUP hosts MATCHING "
-         "name > ''",           -1,  1, SDB_CONNECTION_LOOKUP },
-       { "LOOKUP hosts MATCHING "
-         "name >= ''",          -1,  1, SDB_CONNECTION_LOOKUP },
-       { "LOOKUP hosts MATCHING "
-         "name != ''",          -1,  1, SDB_CONNECTION_LOOKUP },
-       { "LOOKUP hosts MATCHING "
-         "name = ''",           -1,  1, SDB_CONNECTION_LOOKUP },
-       { "LOOKUP hosts MATCHING "
-         "name <= ''",          -1,  1, SDB_CONNECTION_LOOKUP },
-       { "LOOKUP hosts MATCHING "
-         "name < ''",           -1,  1, SDB_CONNECTION_LOOKUP },
-
-       /* NULL */
-       { "LOOKUP hosts MATCHING "
-         "attribute['foo'] "
-         "IS NULL",             -1,  1, SDB_CONNECTION_LOOKUP },
-       { "LOOKUP hosts MATCHING "
-         "attribute['foo'] "
-         "IS NOT NULL",         -1,  1, SDB_CONNECTION_LOOKUP },
-       { "LOOKUP hosts MATCHING "
-         "NOT attribute['foo'] "
-         "IS NULL",             -1,  1, SDB_CONNECTION_LOOKUP },
-       { "LOOKUP hosts MATCHING "
-         "ANY service.name IS NULL", -1, -1, 0 },
-
-       /* invalid numeric constants */
-       { "LOOKUP hosts MATCHING "
-         "attribute['foo'] = "
-         "+-12e+3",             -1, -1, 0 },
-       { "LOOKUP hosts MATCHING "
-         "attribute['foo'] = "
-         "-12e-+3",             -1, -1, 0 },
-       { "LOOKUP hosts MATCHING "
-         "attribute['foo'] = "
-         "e+3",                 -1, -1, 0 },
-       { "LOOKUP hosts MATCHING "
-         "attribute['foo'] = "
-         "3e",                  -1, -1, 0 },
-       /* following SQL standard, we don't support hex numbers */
-       { "LOOKUP hosts MATCHING "
-         "attribute['foo'] = "
-         "0x12",                -1, -1, 0 },
-
-       /* invalid expressions */
-       { "LOOKUP hosts MATCHING "
-         "attribute['foo'] = "
-         "1.23 + 'foo'",        -1, -1, 0 },
-       { "LOOKUP hosts MATCHING "
-         "attr['foo'] = 1.23",  -1, -1, 0 },
-       { "LOOKUP hosts MATCHING "
-         "attr['foo'] IS NULL", -1, -1, 0 },
-
-       /* type mismatches */
-       { "LOOKUP hosts MATCHING "
-         "age > 0",             -1, -1, 0 },
-       { "LOOKUP hosts MATCHING "
-         "NOT age > 0",         -1, -1, 0 },
-       { "LOOKUP hosts MATCHING "
-         "age >= 0",            -1, -1, 0 },
-       { "LOOKUP hosts MATCHING "
-         "age = 0",             -1, -1, 0 },
-       { "LOOKUP hosts MATCHING "
-         "age != 0",            -1, -1, 0 },
-       { "LOOKUP hosts MATCHING "
-         "age <= 0",            -1, -1, 0 },
-       { "LOOKUP hosts MATCHING "
-         "age < 0",             -1, -1, 0 },
-       { "LOOKUP hosts MATCHING "
-         "age + 1 > 0s",        -1, -1, 0 },
-       { "LOOKUP hosts MATCHING "
-         "age - 1 > 0s",        -1, -1, 0 },
-       /* datetime <mul/div> integer is allowed */
-       { "LOOKUP hosts MATCHING "
-         "age || 1 > 0s",       -1, -1, 0 },
-       { "LOOKUP hosts MATCHING "
-         "name + 1 = ''",       -1, -1, 0 },
-       { "LOOKUP hosts MATCHING "
-         "name - 1 = ''",       -1, -1, 0 },
-       { "LOOKUP hosts MATCHING "
-         "name * 1 = ''",       -1, -1, 0 },
-       { "LOOKUP hosts MATCHING "
-         "name / 1 = ''",       -1, -1, 0 },
-       { "LOOKUP hosts MATCHING "
-         "name % 1 = ''",       -1, -1, 0 },
-       { "LOOKUP hosts MATCHING "
-         "(name % 1) + 1 = ''", -1, -1, 0 },
-       { "LOOKUP hosts MATCHING "
-         "1 + (name % 1) = ''", -1, -1, 0 },
-       { "LOOKUP hosts MATCHING "
-         "'' = 1 + (name % 1)", -1, -1, 0 },
-       { "LOOKUP hosts MATCHING "
-         "age > 0 AND "
-         "age = 0s",            -1, -1, 0 },
-       { "LOOKUP hosts MATCHING "
-         "age = 0s AND "
-         "age > 0",             -1, -1, 0 },
-       { "LOOKUP services MATCHING "
-         "host.name > 0",       -1, -1, 0 },
-       { "LOOKUP services MATCHING "
-         "backend > 'b'",       -1, -1, 0 },
-       { "LOOKUP services MATCHING "
-         "'b' > backend",       -1, -1, 0 },
-       { "LOOKUP services MATCHING "
-         "attribute['a'] > backend",
-                                -1, -1, 0 },
-       { "LOOKUP services MATCHING "
-         "backend > attribute['a']",
-                                -1, -1, 0 },
-       { "LOOKUP services MATCHING "
-         "host.name + 1 = ''",  -1, -1, 0 },
-       { "LOOKUP hosts MATCHING "
-         "'a' + 1 IN 'b'",      -1, -1, 0 },
-       { "LOOKUP hosts MATCHING "
-         "'a' IN 'b' - 1",      -1, -1, 0 },
-       { "LOOKUP hosts MATCHING "
-         "name + 1 IN 'b'",     -1, -1, 0 },
-       { "LOOKUP hosts MATCHING "
-         "'a' IN name - 1",     -1, -1, 0 },
-       { "LOOKUP hosts MATCHING "
-         "'b' IN 'abc'",        -1, -1, 0 },
-       { "LOOKUP hosts MATCHING "
-         "1 IN age",            -1, -1, 0 },
-       { "LOOKUP hosts MATCHING "
-         "name =~ 'a' + 1",     -1, -1, 0 },
-       { "LOOKUP hosts MATCHING "
-         "name =~ name + 1",    -1, -1, 0 },
-       { "LOOKUP hosts MATCHING "
-         "name + 1 =~ 'a'",     -1, -1, 0 },
-       { "LOOKUP hosts MATCHING "
-         "name =~ 1",           -1, -1, 0 },
-       { "LOOKUP hosts MATCHING "
-         "name + 1 IS NULL",    -1, -1, 0 },
-       { "LOOKUP hosts FILTER "
-         "name + 1 IS NULL",    -1, -1, 0 },
-       { "LOOKUP hosts MATCHING "
-         "ANY 'patt' =~ 'p'",  -1,  -1, 0 },
-
-       /* comments */
-       { "/* some comment */",  -1,  0, 0 },
-       { "-- another comment",  -1,  0, 0 },
-
-       /* syntax errors */
-       { "INVALID",             -1, -1, 0 },
-       { "FETCH host",          -1, -1, 0 },
-       { "FETCH 'host'",        -1, -1, 0 },
-       { "LIST hosts; INVALID", -1, -1, 0 },
-       { "/* some incomplete",  -1, -1, 0 },
-
-       /* invalid LIST commands */
-       { "LIST",                -1, -1, 0 },
-       { "LIST foo",            -1, -1, 0 },
-       { "LIST hosts MATCHING "
-         "name = 'host'",       -1, -1, 0 },
-       { "LIST foo FILTER "
-         "age > 60s",           -1, -1, 0 },
-
-       /* invalid FETCH commands */
-       { "FETCH host 'host' MATCHING "
-         "name = 'host'",       -1, -1, 0 },
-       { "FETCH service 'host'",-1, -1, 0 },
-       { "FETCH metric 'host'", -1, -1, 0 },
-       { "FETCH host "
-         "'host'.'localhost'",  -1, -1, 0 },
-       { "FETCH foo 'host'",    -1, -1, 0 },
-       { "FETCH foo 'host' FILTER "
-         "age > 60s",           -1, -1, 0 },
-
-       /* invalid LOOKUP commands */
-       { "LOOKUP foo",          -1, -1, 0 },
-       { "LOOKUP foo MATCHING "
-         "name = 'host'",       -1, -1, 0 },
-       { "LOOKUP foo FILTER "
-         "age > 60s",           -1, -1, 0 },
-       { "LOOKUP foo MATCHING "
-         "name = 'host' FILTER "
-         "age > 60s",           -1, -1, 0 },
-       { "LOOKUP hosts MATCHING "
-         "attribute['foo'] <= "
-         "f || 'oo'",           -1, -1, 0 },
-       { "LOOKUP hosts MATCHING "
-         "attribute['foo'] <= "
-         "'f' || oo",           -1, -1, 0 },
-       { "LOOKUP hosts MATCHING "
-         "ANY host.name = 'host'",   -1, -1, 0 },
-       { "LOOKUP hosts MATCHING "
-         "ANY service.name > 1",     -1, -1, 0 },
-       { "LOOKUP hosts MATCHING "
-         "service.name = 's'",       -1, -1, 0 },
-       { "LOOKUP services MATCHING "
-         "ANY host.name = 'host'",   -1, -1, 0 },
-       { "LOOKUP services MATCHING "
-         "ANY service.name = 'svc'", -1, -1, 0 },
-       { "LOOKUP services MATCHING "
-         "ANY metric.name = 'm'",    -1, -1, 0 },
-       { "LOOKUP services MATCHING "
-         "metric.name = 'm'",        -1, -1, 0 },
-       { "LOOKUP metrics MATCHING "
-         "ANY host.name = 'host'",   -1, -1, 0 },
-       { "LOOKUP metrics MATCHING "
-         "ANY service.name = 'svc'", -1, -1, 0 },
-       { "LOOKUP metrics MATCHING "
-         "ANY metric.name = 'm'",    -1, -1, 0 },
-       { "LOOKUP metrics MATCHING "
-         "service.name = 'm'",       -1, -1, 0 },
-
-       /* invalid STORE commands */
-       { "STORE host "
-         "'obj'.'host'",        -1, -1, 0 },
-       { "STORE host attribute "
-         ".'key' 123",          -1, -1, 0 },
-       { "STORE host attribute "
-         "'o'.'h'.'key' 123",   -1, -1, 0 },
-       { "STORE service 'svc'", -1, -1, 0 },
-       { "STORE service "
-         "'host'.'svc' "
-         "STORE 'typ' 'id' "
-         "LAST UPDATE "
-         "2015-02-01",          -1, -1, 0 },
-       { "STORE service attribute "
-         "'svc'.'key' 123",     -1, -1, 0 },
-       { "STORE metric 'm'",    -1, -1, 0 },
-       { "STORE metric "
-         "'host'.'metric' "
-         "STORE 'typ'.'id' "
-         "LAST UPDATE "
-         "2015-02-01",          -1, -1, 0 },
-       { "STORE metric attribute "
-         "'metric'.'key' 123",  -1, -1, 0 },
-};
-
-START_TEST(test_parse)
-{
-       sdb_strbuf_t *errbuf = sdb_strbuf_create(64);
-       sdb_llist_t *check;
-       sdb_object_t *obj;
-       _Bool ok;
-
-       check = sdb_fe_parse(parse_data[_i].query,
-                       parse_data[_i].len, errbuf);
-       if (parse_data[_i].expected < 0)
-               ok = check == 0;
-       else
-               ok = sdb_llist_len(check) == (size_t)parse_data[_i].expected;
-
-       fail_unless(ok, "sdb_fe_parse(%s) = %p (len: %zu); expected: %d "
-                       "(parser error: %s)", parse_data[_i].query, check,
-                       sdb_llist_len(check), parse_data[_i].expected,
-                       sdb_strbuf_string(errbuf));
-
-       if (! check) {
-               sdb_strbuf_destroy(errbuf);
-               return;
-       }
-
-       if ((! parse_data[_i].expected_cmd)
-                       || (parse_data[_i].expected <= 0)) {
-               sdb_llist_destroy(check);
-               sdb_strbuf_destroy(errbuf);
-               return;
-       }
-
-       obj = sdb_llist_get(check, 0);
-       fail_unless(SDB_CONN_NODE(obj)->cmd == parse_data[_i].expected_cmd,
-                       "sdb_fe_parse(%s)->cmd = %i; expected: %d",
-                       parse_data[_i].query, SDB_CONN_NODE(obj)->cmd,
-                       parse_data[_i].expected_cmd);
-
-       sdb_object_deref(obj);
-       sdb_llist_destroy(check);
-       sdb_strbuf_destroy(errbuf);
-}
-END_TEST
-
-struct {
-       const char *expr;
-       int len;
-       int expected;
-} parse_matcher_data[] = {
-       /* empty expressions */
-       { NULL,                           -1, -1 },
-       { "",                             -1, -1 },
-
-       /* match hosts by name */
-       { "name < 'localhost'",           -1,  MATCHER_LT },
-       { "name <= 'localhost'",          -1,  MATCHER_LE },
-       { "name = 'localhost'",           -1,  MATCHER_EQ },
-       { "name != 'localhost'",          -1,  MATCHER_NE },
-       { "name >= 'localhost'",          -1,  MATCHER_GE },
-       { "name > 'localhost'",           -1,  MATCHER_GT },
-       { "name =~ 'host'",               -1,  MATCHER_REGEX },
-       { "name !~ 'host'",               -1,  MATCHER_NREGEX },
-       { "name = 'localhost' -- foo",    -1,  MATCHER_EQ },
-       { "name = 'host' <garbage>",      13,  MATCHER_EQ },
-       { "name &^ 'localhost'",          -1,  -1 },
-       /* match by backend */
-       { "ANY backend < 'be'",           -1,  MATCHER_ANY },
-       { "ANY backend <= 'be'",          -1,  MATCHER_ANY },
-       { "ANY backend = 'be'",           -1,  MATCHER_ANY },
-       { "ANY backend != 'be'",          -1,  MATCHER_ANY },
-       { "ANY backend >= 'be'",          -1,  MATCHER_ANY },
-       { "ANY backend > 'be'",           -1,  MATCHER_ANY },
-       { "ALL backend < 'be'",           -1,  MATCHER_ALL },
-       { "ALL backend <= 'be'",          -1,  MATCHER_ALL },
-       { "ALL backend = 'be'",           -1,  MATCHER_ALL },
-       { "ALL backend != 'be'",          -1,  MATCHER_ALL },
-       { "ALL backend >= 'be'",          -1,  MATCHER_ALL },
-       { "ALL backend > 'be'",           -1,  MATCHER_ALL },
-       { "ANY backend &^ 'be'",          -1,  -1 },
-       /* match hosts by service */
-       { "ANY service.name < 'name'",         -1,  MATCHER_ANY },
-       { "ANY service.name <= 'name'",        -1,  MATCHER_ANY },
-       { "ANY service.name = 'name'",         -1,  MATCHER_ANY },
-       { "ANY service.name != 'name'",        -1,  MATCHER_ANY },
-       { "ANY service.name >= 'name'",        -1,  MATCHER_ANY },
-       { "ANY service.name > 'name'",         -1,  MATCHER_ANY },
-       { "ANY service.name =~ 'pattern'",     -1,  MATCHER_ANY },
-       { "ANY service.name !~ 'pattern'",     -1,  MATCHER_ANY },
-       { "ANY service.name &^ 'name'",        -1,  -1 },
-       { "ALL service.name < 'name'",         -1,  MATCHER_ALL },
-       { "ALL service.name <= 'name'",        -1,  MATCHER_ALL },
-       { "ALL service.name = 'name'",         -1,  MATCHER_ALL },
-       { "ALL service.name != 'name'",        -1,  MATCHER_ALL },
-       { "ALL service.name >= 'name'",        -1,  MATCHER_ALL },
-       { "ALL service.name > 'name'",         -1,  MATCHER_ALL },
-       { "ALL service.name =~ 'pattern'",     -1,  MATCHER_ALL },
-       { "ALL service.name !~ 'pattern'",     -1,  MATCHER_ALL },
-       { "ALL service.name &^ 'name'",        -1,  -1 },
-       { "ANY service < 'name'",              -1,  -1 },
-       /* match hosts by metric */
-       { "ANY metric.name < 'name'",          -1,  MATCHER_ANY },
-       { "ANY metric.name <= 'name'",         -1,  MATCHER_ANY },
-       { "ANY metric.name = 'name'",          -1,  MATCHER_ANY },
-       { "ANY metric.name != 'name'",         -1,  MATCHER_ANY },
-       { "ANY metric.name >= 'name'",         -1,  MATCHER_ANY },
-       { "ANY metric.name > 'name'",          -1,  MATCHER_ANY },
-       { "ANY metric.name =~ 'pattern'",      -1,  MATCHER_ANY },
-       { "ANY metric.name !~ 'pattern'",      -1,  MATCHER_ANY },
-       { "ANY metric.name &^ 'pattern'",      -1,  -1 },
-       { "ALL metric.name < 'name'",          -1,  MATCHER_ALL },
-       { "ALL metric.name <= 'name'",         -1,  MATCHER_ALL },
-       { "ALL metric.name = 'name'",          -1,  MATCHER_ALL },
-       { "ALL metric.name != 'name'",         -1,  MATCHER_ALL },
-       { "ALL metric.name >= 'name'",         -1,  MATCHER_ALL },
-       { "ALL metric.name > 'name'",          -1,  MATCHER_ALL },
-       { "ALL metric.name =~ 'pattern'",      -1,  MATCHER_ALL },
-       { "ALL metric.name !~ 'pattern'",      -1,  MATCHER_ALL },
-       { "ALL metric.name &^ 'pattern'",      -1,  -1 },
-       { "ANY metric <= 'name'",              -1,  -1 },
-       /* match hosts by attribute */
-       { "ANY attribute.name < 'name'",       -1,  MATCHER_ANY },
-       { "ANY attribute.name <= 'name'",      -1,  MATCHER_ANY },
-       { "ANY attribute.name = 'name'",       -1,  MATCHER_ANY },
-       { "ANY attribute.name != 'name'",      -1,  MATCHER_ANY },
-       { "ANY attribute.name >= 'name'",      -1,  MATCHER_ANY },
-       { "ANY attribute.name > 'name'",       -1,  MATCHER_ANY },
-       { "ANY attribute.name =~ 'pattern'",   -1,  MATCHER_ANY },
-       { "ANY attribute.name !~ 'pattern'",   -1,  MATCHER_ANY },
-       { "ANY attribute.name &^ 'pattern'",   -1,  -1 },
-       { "ALL attribute.name < 'name'",       -1,  MATCHER_ALL },
-       { "ALL attribute.name <= 'name'",      -1,  MATCHER_ALL },
-       { "ALL attribute.name = 'name'",       -1,  MATCHER_ALL },
-       { "ALL attribute.name != 'name'",      -1,  MATCHER_ALL },
-       { "ALL attribute.name >= 'name'",      -1,  MATCHER_ALL },
-       { "ALL attribute.name > 'name'",       -1,  MATCHER_ALL },
-       { "ALL attribute.name =~ 'pattern'",   -1,  MATCHER_ALL },
-       { "ALL attribute.name !~ 'pattern'",   -1,  MATCHER_ALL },
-       { "ALL attribute.name &^ 'pattern'",   -1,  -1 },
-       { "ANY attribute !~ 'pattern'",        -1,  -1 },
-       /* composite expressions */
-       { "name =~ 'pattern' AND "
-         "ANY service.name =~ 'pattern'",     -1,  MATCHER_AND },
-       { "name =~ 'pattern' OR "
-         "ANY service.name =~ 'pattern'",     -1,  MATCHER_OR },
-       { "NOT name = 'host'",                 -1,  MATCHER_NOT },
-       /* numeric expressions */
-       { "attribute['foo'] < 123",         -1,  MATCHER_LT },
-       { "attribute['foo'] <= 123",        -1,  MATCHER_LE },
-       { "attribute['foo'] = 123",         -1,  MATCHER_EQ },
-       { "attribute['foo'] >= 123",        -1,  MATCHER_GE },
-       { "attribute['foo'] > 123",         -1,  MATCHER_GT },
-       /* datetime expressions */
-       { "attribute['foo'] = "
-         "2014-08-16",                     -1,  MATCHER_EQ },
-       { "attribute['foo'] = "
-         "17:23",                          -1,  MATCHER_EQ },
-       { "attribute['foo'] = "
-         "17:23:53",                       -1,  MATCHER_EQ },
-       { "attribute['foo'] = "
-         "17:23:53.123",                   -1,  MATCHER_EQ },
-       { "attribute['foo'] = "
-         "17:23:53.123456789",             -1,  MATCHER_EQ },
-       { "attribute['foo'] = "
-         "2014-08-16 17:23",               -1,  MATCHER_EQ },
-       { "attribute['foo'] = "
-         "2014-08-16 17:23:53",            -1,  MATCHER_EQ },
-       /* NULL; while this is an implementation detail,
-        * IS NULL currently maps to an equality matcher */
-       { "attribute['foo'] IS NULL",       -1,  MATCHER_ISNULL },
-       { "attribute['foo'] IS NOT NULL",   -1,  MATCHER_ISNNULL },
-       /* array expressions */
-       { "backend < ['a']",                -1,  MATCHER_LT },
-       { "backend <= ['a']",               -1,  MATCHER_LE },
-       { "backend = ['a']",                -1,  MATCHER_EQ },
-       { "backend != ['a']",               -1,  MATCHER_NE },
-       { "backend >= ['a']",               -1,  MATCHER_GE },
-       { "backend > ['a']",                -1,  MATCHER_GT },
-       { "backend &^ ['a']",               -1,  -1 },
-
-       /* object field matchers */
-       { "name < 'a'",                     -1,  MATCHER_LT },
-       { "name <= 'a'",                    -1,  MATCHER_LE },
-       { "name = 'a'",                     -1,  MATCHER_EQ },
-       { "name != 'a'",                    -1,  MATCHER_NE },
-       { "name >= 'a'",                    -1,  MATCHER_GE },
-       { "name > 'a'",                     -1,  MATCHER_GT },
-       { "last_update < 2014-10-01",       -1,  MATCHER_LT },
-       { "last_update <= 2014-10-01",      -1,  MATCHER_LE },
-       { "last_update = 2014-10-01",       -1,  MATCHER_EQ },
-       { "last_update != 2014-10-01",      -1,  MATCHER_NE },
-       { "last_update >= 2014-10-01",      -1,  MATCHER_GE },
-       { "last_update > 2014-10-01",       -1,  MATCHER_GT },
-       { "Last_Update >= 24D",             -1,  MATCHER_GE },
-       { "age < 20s",                      -1,  MATCHER_LT },
-       { "age <= 20s",                     -1,  MATCHER_LE },
-       { "age = 20s",                      -1,  MATCHER_EQ },
-       { "age != 20s",                     -1,  MATCHER_NE },
-       { "age >= 20s",                     -1,  MATCHER_GE },
-       { "age > 20s",                      -1,  MATCHER_GT },
-       { "AGE <= 1m",                      -1,  MATCHER_LE },
-       { "age > 1M",                       -1,  MATCHER_GT },
-       { "age != 20Y",                     -1,  MATCHER_NE },
-       { "age <= 2 * interval",            -1,  MATCHER_LE },
-       { "interval < 20s",                 -1,  MATCHER_LT },
-       { "interval <= 20s",                -1,  MATCHER_LE },
-       { "interval = 20s",                 -1,  MATCHER_EQ },
-       { "interval != 20s",                -1,  MATCHER_NE },
-       { "interval >= 20s",                -1,  MATCHER_GE },
-       { "interval > 20s",                 -1,  MATCHER_GT },
-       { "'be' IN backend",                -1,  MATCHER_IN },
-       { "'be' NOT IN backend",            -1,  MATCHER_NIN },
-       { "['a','b'] IN backend",           -1,  MATCHER_IN },
-       { "['a','b'] NOT IN backend",       -1,  MATCHER_NIN },
-
-       /* check operator precedence */
-       { "name = 'name' OR "
-         "ANY service.name = 'name' AND "
-         "ANY attribute.name = 'name' OR "
-         "attribute['foo'] = 'bar'",       -1,  MATCHER_OR },
-       { "name = 'name' AND "
-         "ANY service.name = 'name' AND "
-         "ANY attribute.name = 'name' OR "
-         "attribute['foo'] = 'bar'",       -1,  MATCHER_OR },
-       { "name = 'name' AND "
-         "ANY service.name = 'name' OR "
-         "ANY attribute.name = 'name' AND "
-         "attribute['foo'] = 'bar'",       -1,  MATCHER_OR },
-       { "(name = 'name' OR "
-         "ANY service.name = 'name') AND "
-         "(ANY attribute.name = 'name' OR "
-         "attribute['foo'] = 'bar')",      -1,  MATCHER_AND },
-       { "NOT name = 'name' OR "
-         "ANY service.name = 'name'",      -1,  MATCHER_OR },
-       { "NOT name = 'name' OR "
-         "NOT ANY service.name = 'name'",  -1,  MATCHER_OR },
-       { "NOT (name = 'name' OR "
-         "NOT ANY service.name = 'name')", -1,  MATCHER_NOT },
-
-       /* syntax errors */
-       { "LIST",                           -1, -1 },
-       { "foo &^ bar",                     -1, -1 },
-       { "invalid",                        -1, -1 },
-};
-
-START_TEST(test_parse_matcher)
-{
-       sdb_strbuf_t *errbuf = sdb_strbuf_create(64);
-       sdb_store_matcher_t *m;
-
-       m = sdb_fe_parse_matcher(parse_matcher_data[_i].expr,
-                       parse_matcher_data[_i].len, errbuf);
-
-       if (parse_matcher_data[_i].expected < 0) {
-               fail_unless(m == NULL,
-                               "sdb_fe_parse_matcher(%s) = %p; expected: NULL",
-                               parse_matcher_data[_i].expr, m);
-               sdb_object_deref(SDB_OBJ(m));
-               sdb_strbuf_destroy(errbuf);
-               return;
-       }
-
-       fail_unless(m != NULL, "sdb_fe_parse_matcher(%s) = NULL; "
-                       "expected: <matcher> (parser error: %s)",
-                       parse_matcher_data[_i].expr, sdb_strbuf_string(errbuf));
-       fail_unless(M(m)->type == parse_matcher_data[_i].expected,
-                       "sdb_fe_parse_matcher(%s) returned matcher of type %d; "
-                       "expected: %d", parse_matcher_data[_i].expr, M(m)->type,
-                       parse_matcher_data[_i].expected);
-
-       sdb_object_deref(SDB_OBJ(m));
-       sdb_strbuf_destroy(errbuf);
-}
-END_TEST
-
-struct {
-       const char *expr;
-       int len;
-       int expected;
-} parse_expr_data[] = {
-       /* empty expressions */
-       { NULL,                   -1, INT_MAX },
-       { "",                     -1, INT_MAX },
-
-       /* constant expressions */
-       { "'localhost'",          -1, 0 },
-       { "123",                  -1, 0 },
-       { "2014-08-16",           -1, 0 },
-       { "17:23",                -1, 0 },
-       { "17:23:53",             -1, 0 },
-       { "17:23:53.123",         -1, 0 },
-       { "17:23:53.123456789",   -1, 0 },
-       { "2014-08-16 17:23",     -1, 0 },
-       { "2014-08-16 17:23:53",  -1, 0 },
-       { "10s",                  -1, 0 },
-       { "60m",                  -1, 0 },
-       { "10Y 24D 1h",           -1, 0 },
-
-       { "123 + 456",            -1, 0 },
-       { "'foo' || 'bar'",       -1, 0 },
-       { "456 - 123",            -1, 0 },
-       { "1.2 * 3.4",            -1, 0 },
-       { "1.2 / 3.4",            -1, 0 },
-       { "5 % 2",                -1, 0 },
-
-       /* queryable fields */
-       { "last_update",          -1, FIELD_VALUE },
-       { "AGE",                  -1, FIELD_VALUE },
-       { "interval",             -1, FIELD_VALUE },
-       { "Last_Update",          -1, FIELD_VALUE },
-       { "backend",              -1, FIELD_VALUE },
-
-       /* attributes */
-       { "attribute['foo']",     -1, ATTR_VALUE },
-
-       /* arithmetic expressions */
-       { "age + age",            -1, SDB_DATA_ADD },
-       { "age - age",            -1, SDB_DATA_SUB },
-       { "age * age",            -1, SDB_DATA_MUL },
-       { "age / age",            -1, SDB_DATA_DIV },
-       { "age % age",            -1, SDB_DATA_MOD },
-       { "age || age",           -1, SDB_DATA_CONCAT },
-
-       /* operator precedence */
-       { "age + age * age",      -1, SDB_DATA_ADD },
-       { "age * age + age",      -1, SDB_DATA_ADD },
-       { "age + age - age",      -1, SDB_DATA_SUB },
-       { "age - age + age",      -1, SDB_DATA_ADD },
-       { "(age + age) * age",    -1, SDB_DATA_MUL },
-       { "age + (age * age)",    -1, SDB_DATA_ADD },
-
-       /* syntax errors */
-       { "LIST",                 -1, INT_MAX },
-       { "foo &^ bar",           -1, INT_MAX },
-       { "invalid",              -1, INT_MAX },
-};
-
-START_TEST(test_parse_expr)
-{
-       sdb_strbuf_t *errbuf = sdb_strbuf_create(64);
-       sdb_store_expr_t *e;
-
-       e = sdb_fe_parse_expr(parse_expr_data[_i].expr,
-                       parse_expr_data[_i].len, errbuf);
-
-       if (parse_expr_data[_i].expected == INT_MAX) {
-               fail_unless(e == NULL,
-                               "sdb_fe_parse_expr(%s) = %p; expected: NULL",
-                               parse_expr_data[_i].expr, e);
-               sdb_object_deref(SDB_OBJ(e));
-               sdb_strbuf_destroy(errbuf);
-               return;
-       }
-
-       fail_unless(e != NULL, "sdb_fe_parse_expr(%s) = NULL; "
-                       "expected: <expr> (parser error: %s)",
-                       parse_expr_data[_i].expr, sdb_strbuf_string(errbuf));
-       fail_unless(e->type == parse_expr_data[_i].expected,
-                       "sdb_fe_parse_expr(%s) returned expression of type %d; "
-                       "expected: %d", parse_expr_data[_i].expr, e->type,
-                       parse_expr_data[_i].expected);
-
-       sdb_object_deref(SDB_OBJ(e));
-       sdb_strbuf_destroy(errbuf);
-}
-END_TEST
-
-TEST_MAIN("frontend::parser")
-{
-       TCase *tc = tcase_create("core");
-       TC_ADD_LOOP_TEST(tc, parse);
-       TC_ADD_LOOP_TEST(tc, parse_matcher);
-       TC_ADD_LOOP_TEST(tc, parse_expr);
-       ADD_TCASE(tc);
-}
-TEST_MAIN_END
-
-/* vim: set tw=78 sw=4 ts=4 noexpandtab : */
-