Code

parser: Add support for analyzing conditional and arithmetic expressions.
authorSebastian Harl <sh@tokkee.org>
Tue, 5 May 2015 22:15:49 +0000 (00:15 +0200)
committerSebastian Harl <sh@tokkee.org>
Tue, 5 May 2015 22:15:49 +0000 (00:15 +0200)
Let all parser functions call the respective analyzer. For now, this is only
used to propagate data-type information but it will later ensure a certain
amount of semantical correctness. Note that the new analyzers usually don't
have any context information, so they might accept expressions that are not
right for their later target use case. If that matters, the caller will have
to check again providing the right context.

src/include/parser/parser.h
src/parser/analyzer.c
src/parser/parser.c
t/unit/parser/parser_test.c

index 0a7c7206d24a156bb1706003641fadc217076f84..92e49bbd39c5db674f5208c6259baffe5037e2d8 100644 (file)
@@ -84,6 +84,30 @@ sdb_parser_parse_arith(const char *expr, int len, sdb_strbuf_t *errbuf);
 int
 sdb_parser_analyze(sdb_ast_node_t *node, sdb_strbuf_t *errbuf);
 
+/*
+ * sdb_parser_analyze_conditional:
+ * Semantical analysis of a conditional node.
+ *
+ * Returns:
+ *  - 0 on success
+ *  - a negative value else; an error message will be written to the provided
+ *    error buffer
+ */
+int
+sdb_parser_analyze_conditional(sdb_ast_node_t *node, sdb_strbuf_t *errbuf);
+
+/*
+ * sdb_parser_analyze_arith:
+ * Semantical analysis of an arithmetic node.
+ *
+ * Returns:
+ *  - 0 on success
+ *  - a negative value else; an error message will be written to the provided
+ *    error buffer
+ */
+int
+sdb_parser_analyze_arith(sdb_ast_node_t *node, sdb_strbuf_t *errbuf);
+
 /*
  * Low-level interface.
  */
index 7193fea90bf4281eea185cc862c8014b6108430b..867af5211dbbbc5f36174236584c06fbb00e571a 100644 (file)
@@ -396,5 +396,35 @@ sdb_parser_analyze(sdb_ast_node_t *node, sdb_strbuf_t *errbuf)
        return -1;
 } /* sdb_parser_analyze */
 
+int
+sdb_parser_analyze_conditional(sdb_ast_node_t *node, sdb_strbuf_t *errbuf)
+{
+       if (! node) {
+               sdb_strbuf_sprintf(errbuf, "Empty conditional node");
+               return -1;
+       }
+       if (! SDB_AST_IS_LOGICAL(node)) {
+               sdb_strbuf_sprintf(errbuf, "Not a conditional node (got %s)",
+                               SDB_AST_TYPE_TO_STRING(node));
+               return -1;
+       }
+       return analyze_node(-1, node, errbuf);
+} /* sdb_parser_analyze_conditional */
+
+int
+sdb_parser_analyze_arith(sdb_ast_node_t *node, sdb_strbuf_t *errbuf)
+{
+       if (! node) {
+               sdb_strbuf_sprintf(errbuf, "Empty arithmetic node");
+               return -1;
+       }
+       if (! SDB_AST_IS_ARITHMETIC(node)) {
+               sdb_strbuf_sprintf(errbuf, "Not an arithmetic node (got %s)",
+                               SDB_AST_TYPE_TO_STRING(node));
+               return -1;
+       }
+       return analyze_node(-1, node, errbuf);
+} /* sdb_parser_analyze_arith */
+
 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */
 
index de6085fd176b7ae346b9f61d71369d8d3b2d5750..e3073a9a627534d6ea5c5ca2e6ac3859a0eeeafc 100644 (file)
@@ -140,6 +140,11 @@ sdb_parser_parse_conditional(const char *cond, int len, sdb_strbuf_t *errbuf)
 
        assert(SDB_AST_IS_LOGICAL(node));
        sdb_llist_destroy(yyextra.parsetree);
+
+       if (sdb_parser_analyze_conditional(node, errbuf)) {
+               sdb_object_deref(SDB_OBJ(node));
+               return NULL;
+       }
        return node;
 } /* sdb_parser_parse_conditional */
 
@@ -175,6 +180,11 @@ sdb_parser_parse_arith(const char *expr, int len, sdb_strbuf_t *errbuf)
 
        assert(SDB_AST_IS_ARITHMETIC(node));
        sdb_llist_destroy(yyextra.parsetree);
+
+       if (sdb_parser_analyze_arith(node, errbuf)) {
+               sdb_object_deref(SDB_OBJ(node));
+               return NULL;
+       }
        return node;
 } /* sdb_parser_parse_arith */
 
index ad9f8f92b995aa27676574e38a382623894bd40d..68e152740368d029ba9dd3ad96acb5be9cd7c81d 100644 (file)
@@ -900,6 +900,11 @@ START_TEST(test_parse_conditional)
                                "expected: %d", parse_conditional_data[_i].expr,
                                SDB_AST_ITER(node)->kind, parse_conditional_data[_i].expected);
 
+       fail_unless(node->data_type == -1,
+                       "sdb_parser_parse_conditional(%s) returned conditional of data-type %s; "
+                       "expected: %s", parse_conditional_data[_i].expr,
+                       SDB_TYPE_TO_STRING(node->data_type), SDB_TYPE_TO_STRING(-1));
+
        sdb_object_deref(SDB_OBJ(node));
        sdb_strbuf_destroy(errbuf);
 }
@@ -909,63 +914,64 @@ struct {
        const char *expr;
        int len;
        int expected;
+       int data_type;
 } parse_arith_data[] = {
        /* empty expressions */
-       { NULL,                   -1, -1 },
-       { "",                     -1, -1 },
+       { NULL,                   -1, -1, -1 },
+       { "",                     -1, -1, -1 },
 
        /* constant expressions */
-       { "'localhost'",          -1, SDB_AST_TYPE_CONST },
-       { "123",                  -1, SDB_AST_TYPE_CONST },
-       { "2014-08-16",           -1, SDB_AST_TYPE_CONST },
-       { "17:23",                -1, SDB_AST_TYPE_CONST },
-       { "17:23:53",             -1, SDB_AST_TYPE_CONST },
-       { "17:23:53.123",         -1, SDB_AST_TYPE_CONST },
-       { "17:23:53.123456789",   -1, SDB_AST_TYPE_CONST },
-       { "2014-08-16 17:23",     -1, SDB_AST_TYPE_CONST },
-       { "2014-08-16 17:23:53",  -1, SDB_AST_TYPE_CONST },
-       { "10s",                  -1, SDB_AST_TYPE_CONST },
-       { "60m",                  -1, SDB_AST_TYPE_CONST },
-       { "10Y 24D 1h",           -1, SDB_AST_TYPE_CONST },
+       { "'localhost'",          -1, SDB_AST_TYPE_CONST, SDB_TYPE_STRING },
+       { "123",                  -1, SDB_AST_TYPE_CONST, SDB_TYPE_INTEGER },
+       { "42.3",                 -1, SDB_AST_TYPE_CONST, SDB_TYPE_DECIMAL },
+       { "2014-08-16",           -1, SDB_AST_TYPE_CONST, SDB_TYPE_DATETIME },
+       { "17:23",                -1, SDB_AST_TYPE_CONST, SDB_TYPE_DATETIME },
+       { "17:23:53",             -1, SDB_AST_TYPE_CONST, SDB_TYPE_DATETIME },
+       { "17:23:53.123",         -1, SDB_AST_TYPE_CONST, SDB_TYPE_DATETIME },
+       { "17:23:53.123456789",   -1, SDB_AST_TYPE_CONST, SDB_TYPE_DATETIME },
+       { "2014-08-16 17:23",     -1, SDB_AST_TYPE_CONST, SDB_TYPE_DATETIME },
+       { "2014-08-16 17:23:53",  -1, SDB_AST_TYPE_CONST, SDB_TYPE_DATETIME },
+       { "10s",                  -1, SDB_AST_TYPE_CONST, SDB_TYPE_DATETIME },
+       { "60m",                  -1, SDB_AST_TYPE_CONST, SDB_TYPE_DATETIME },
+       { "10Y 24D 1h",           -1, SDB_AST_TYPE_CONST, SDB_TYPE_DATETIME },
 
        /* TODO: the analyzer and/or optimizer should turn these into constants */
-       { "123 + 456",            -1, SDB_AST_ADD },
-       { "'foo' || 'bar'",       -1, SDB_AST_CONCAT },
-       { "456 - 123",            -1, SDB_AST_SUB },
-       { "1.2 * 3.4",            -1, SDB_AST_MUL },
-       { "1.2 / 3.4",            -1, SDB_AST_DIV },
-       { "5 % 2",                -1, SDB_AST_MOD },
+       { "123 + 456",            -1, SDB_AST_ADD, SDB_TYPE_INTEGER },
+       { "'foo' || 'bar'",       -1, SDB_AST_CONCAT, SDB_TYPE_STRING },
+       { "456 - 123",            -1, SDB_AST_SUB, SDB_TYPE_INTEGER },
+       { "1.2 * 3.4",            -1, SDB_AST_MUL, SDB_TYPE_DECIMAL },
+       { "1.2 / 3.4",            -1, SDB_AST_DIV, SDB_TYPE_DECIMAL },
+       { "5 % 2",                -1, SDB_AST_MOD, SDB_TYPE_INTEGER },
 
        /* queryable fields */
-       { "last_update",          -1, SDB_AST_TYPE_VALUE },
-       { "AGE",                  -1, SDB_AST_TYPE_VALUE },
-       { "interval",             -1, SDB_AST_TYPE_VALUE },
-       { "Last_Update",          -1, SDB_AST_TYPE_VALUE },
-       { "backend",              -1, SDB_AST_TYPE_VALUE },
+       { "last_update",          -1, SDB_AST_TYPE_VALUE, SDB_TYPE_DATETIME },
+       { "AGE",                  -1, SDB_AST_TYPE_VALUE, SDB_TYPE_DATETIME },
+       { "interval",             -1, SDB_AST_TYPE_VALUE, SDB_TYPE_DATETIME },
+       { "Last_Update",          -1, SDB_AST_TYPE_VALUE, SDB_TYPE_DATETIME },
+       { "backend",              -1, SDB_AST_TYPE_VALUE, SDB_TYPE_ARRAY | SDB_TYPE_STRING },
 
        /* attributes */
-       { "attribute['foo']",     -1, SDB_AST_TYPE_VALUE },
+       { "attribute['foo']",     -1, SDB_AST_TYPE_VALUE, -1 },
 
        /* arithmetic expressions */
-       { "age + age",            -1, SDB_AST_ADD },
-       { "age - age",            -1, SDB_AST_SUB },
-       { "age * age",            -1, SDB_AST_MUL },
-       { "age / age",            -1, SDB_AST_DIV },
-       { "age \% age",           -1, SDB_AST_MOD },
-       { "age || age",           -1, SDB_AST_CONCAT },
+       { "age + age",            -1, SDB_AST_ADD, SDB_TYPE_DATETIME },
+       { "age - age",            -1, SDB_AST_SUB, SDB_TYPE_DATETIME },
+       { "age * age",            -1, SDB_AST_MUL, SDB_TYPE_DATETIME },
+       { "age / age",            -1, SDB_AST_DIV, SDB_TYPE_DATETIME },
+       { "age \% age",           -1, SDB_AST_MOD, SDB_TYPE_DATETIME },
 
        /* operator precedence */
-       { "age + age * age",      -1, SDB_AST_ADD },
-       { "age * age + age",      -1, SDB_AST_ADD },
-       { "age + age - age",      -1, SDB_AST_SUB },
-       { "age - age + age",      -1, SDB_AST_ADD },
-       { "(age + age) * age",    -1, SDB_AST_MUL },
-       { "age + (age * age)",    -1, SDB_AST_ADD },
+       { "age + age * age",      -1, SDB_AST_ADD, SDB_TYPE_DATETIME },
+       { "age * age + age",      -1, SDB_AST_ADD, SDB_TYPE_DATETIME },
+       { "age + age - age",      -1, SDB_AST_SUB, SDB_TYPE_DATETIME },
+       { "age - age + age",      -1, SDB_AST_ADD, SDB_TYPE_DATETIME },
+       { "(age + age) * age",    -1, SDB_AST_MUL, SDB_TYPE_DATETIME },
+       { "age + (age * age)",    -1, SDB_AST_ADD, SDB_TYPE_DATETIME },
 
        /* syntax errors */
-       { "LIST",                 -1, -1 },
-       { "foo &^ bar",           -1, -1 },
-       { "invalid",              -1, -1 },
+       { "LIST",                 -1, -1, -1 },
+       { "foo &^ bar",           -1, -1, -1 },
+       { "invalid",              -1, -1, -1 },
 };
 
 START_TEST(test_parse_arith)
@@ -999,6 +1005,12 @@ START_TEST(test_parse_arith)
                                "expected: %d", parse_arith_data[_i].expr, node->type,
                                parse_arith_data[_i].expected);
 
+       fail_unless(node->data_type == parse_arith_data[_i].data_type,
+                       "sdb_parser_parse_arith(%s) returned expression of data-type %s; "
+                       "expected: %s", parse_arith_data[_i].expr,
+                       SDB_TYPE_TO_STRING(node->data_type),
+                       SDB_TYPE_TO_STRING(parse_arith_data[_i].data_type));
+
        sdb_object_deref(SDB_OBJ(node));
        sdb_strbuf_destroy(errbuf);
 }