completed logical expression parser - NEED TO TEST A LOT!

Thu, 28 May 2015 12:22:55 +0200

author
Mike Becker <universe@uap-core.de>
date
Thu, 28 May 2015 12:22:55 +0200
changeset 115
5744a3dee766
parent 114
943548492a47
child 116
44ffe073b5e3

completed logical expression parser - NEED TO TEST A LOT!

libidav/davqlparser.c file | annotate | diff | comparison | revisions
libidav/davqlparser.h file | annotate | diff | comparison | revisions
--- a/libidav/davqlparser.c	Sun May 24 20:27:51 2015 +0200
+++ b/libidav/davqlparser.c	Thu May 28 12:22:55 2015 +0200
@@ -387,7 +387,9 @@
 #define _error_missing_expr "missing expression " _error_context
 #define _error_invalid_expr "invalid expression " _error_context
 #define _error_invalid_unary_op "invalid unary operator "  _error_context
+#define _error_invalid_logical_op "invalid logical operator "  _error_context
 #define _error_invalid_fmtspec "invalid format specifier " _error_context
+#define _error_invalid_string "string expected " _error_context
 
 #define token_sstr(token) (((DavQLToken*)(token)->data)->value)
 
@@ -431,6 +433,18 @@
     return 0;
 }
 
+static _Bool islongoperator(DavQLToken *token) {
+    sstr_t operators[] = {ST("and"), ST("or"), ST("not"), ST("xor"),
+        ST("like"), ST("unlike")
+    };
+    for (int i = 0 ; i < sizeof(operators)/sizeof(sstr_t) ; i++) {
+        if (!sstrcasecmp(token->value, operators[i])) {
+            return 1;
+        }
+    }
+    return 0;
+}
+
 static UcxList* dav_parse_add_token(UcxList *tokenlist, DavQLToken *token) {
     
     // determine token class (order of if-statements is very important!)
@@ -453,6 +467,8 @@
             token->tokenclass = strchr(special_token_symbols, firstchar) ?
                 DAVQL_TOKEN_OPERATOR : DAVQL_TOKEN_IDENTIFIER;
         }
+    } else if (islongoperator(token)) {
+        token->tokenclass = DAVQL_TOKEN_OPERATOR;
     } else if (firstchar == '\'') {
         token->tokenclass = DAVQL_TOKEN_STRING;
     } else if (firstchar == '`') {
@@ -1016,8 +1032,241 @@
     }
 }
 
+// forward declaration
+static int dav_parse_logical_expr(DavQLStatement *stmt, UcxList *token,
+        DavQLExpression *expr);
+
+static int dav_parse_bool_prim(DavQLStatement *stmt, UcxList *token,
+        DavQLExpression *expr) {
+    
+    expr->type = DAVQL_LOGICAL;
+    
+    int total_consumed = 0;
+
+    DavQLExpression bexpr;
+    memset(&bexpr, 0, sizeof(DavQLExpression));
+    total_consumed = dav_parse_expression(stmt, token, &bexpr);
+    if (!total_consumed || stmt->errorcode) {
+        return 0;
+    }
+    token = ucx_list_get(token, total_consumed);
+
+    UcxList* optok = token;
+    // RULE:    Expression, (" like " | " unlike "), String
+    if (token_is(optok, DAVQL_TOKEN_OPERATOR) && (tokenvalue_is(optok,
+            "like") || tokenvalue_is(optok, "unlike"))) {
+
+        total_consumed++;
+        token = token->next;
+        if (token_is(token, DAVQL_TOKEN_STRING)) {
+            expr->op = tokenvalue_is(optok, "like") ?
+                DAVQL_LIKE : DAVQL_UNLIKE;
+            expr->left = malloc(sizeof(DavQLExpression));
+            memcpy(expr->left, &bexpr, sizeof(DavQLExpression));
+            expr->right = calloc(1, sizeof(DavQLExpression));
+            expr->right->type = DAVQL_STRING;
+            expr->right->srctext = token_sstr(token);
+
+            return total_consumed + 1;
+        } else {
+            dav_error_in_context(DAVQL_ERROR_INVALID_STRING,
+                _error_invalid_string, stmt, token);
+            return 0;
+        }        
+    }
+    // RULE:    Expression, Comparison, Expression
+    else if (token_is(optok, DAVQL_TOKEN_OPERATOR) && (
+            tokenvalue_is(optok, "=") || tokenvalue_is(optok, "!") ||
+            tokenvalue_is(optok, "<") || tokenvalue_is(optok, ">"))) {
+
+        total_consumed++;
+        token = token->next;
+
+        if (tokenvalue_is(optok, "=")) {
+            expr->op = DAVQL_EQ;
+        } else {
+            if (tokenvalue_is(token, "=")) {
+                if (tokenvalue_is(optok, "!")) {
+                    expr->type = DAVQL_NEQ;
+                } else if (tokenvalue_is(optok, "<")) {
+                    expr->type = DAVQL_LE;
+                } else if (tokenvalue_is(optok, ">")) {
+                    expr->type = DAVQL_GE;
+                }
+                total_consumed++;
+                token = token->next;
+            } else {
+                if (tokenvalue_is(optok, "<")) {
+                    expr->type = DAVQL_LT;
+                } else if (tokenvalue_is(optok, ">")) {
+                    expr->type = DAVQL_GT;
+                }
+            }
+        }
+
+        int consumed = dav_parse_expression(stmt, token, expr->right);
+        if (stmt->errorcode) {
+            return 0;
+        }
+        if (!consumed) {
+            dav_error_in_context(
+                DAVQL_ERROR_MISSING_EXPR, _error_missing_expr,
+                stmt, token);
+            return 0;
+        }
+
+        total_consumed += consumed;
+        expr->left = malloc(sizeof(DavQLExpression));
+        memcpy(expr->left, &bexpr, sizeof(DavQLExpression));
+
+        return total_consumed;
+    }
+    // RULE:    FunctionCall | Identifier;
+    else if (bexpr.type == DAVQL_FUNCCALL || bexpr.type == DAVQL_IDENTIFIER) {
+        memcpy(expr, &bexpr, sizeof(DavQLExpression));
+
+        return total_consumed;
+    } else {
+        return 0;
+    }
+}
+
+static int dav_parse_bool_expr(DavQLStatement *stmt, UcxList *token,
+        DavQLExpression *expr) {
+    
+    // RULE:    "not ", LogicalExpression
+    if (token_is(token, DAVQL_TOKEN_OPERATOR) && tokenvalue_is(token, "not")) {
+        token = token->next;
+        expr->type = DAVQL_LOGICAL;
+        expr->op = DAVQL_NOT;
+        expr->left = calloc(1, sizeof(DavQLExpression));
+        expr->srctext = token_sstr(token);
+        
+        int consumed = dav_parse_logical_expr(stmt, token, expr->left);
+        if (stmt->errorcode) {
+            return 0;
+        }
+        if (consumed) {
+            sstr_t lasttok = token_sstr(ucx_list_get(token, consumed-1));
+            expr->srctext.length =
+                lasttok.ptr - expr->srctext.ptr + lasttok.length;
+            return consumed + 1;
+        } else {
+            dav_error_in_context(DAVQL_ERROR_MISSING_EXPR,
+                _error_missing_expr, stmt, token);
+            return 0;
+        }
+    }
+    // RULE:    "(", LogicalExpression, ")"
+    else if (token_is(token, DAVQL_TOKEN_OPENP)) {
+        token = token->next;
+        
+        int consumed = dav_parse_logical_expr(stmt, token, expr);
+        if (stmt->errorcode) {
+            return 0;
+        }
+        if (!consumed) {
+            dav_error_in_context(DAVQL_ERROR_MISSING_EXPR,
+                _error_missing_expr, stmt, token);
+            return 0;
+        }
+        
+        token = ucx_list_get(token, consumed);
+        
+        if (token_is(token, DAVQL_TOKEN_CLOSEP)) {
+            token = token->next;
+            return consumed + 2;
+        } else {
+            dav_error_in_context(DAVQL_ERROR_MISSING_PAR, _error_missing_par,
+                stmt, token);
+            return 0;
+        }
+    }
+    // RULE:    BooleanExpression
+    else {
+        return dav_parse_bool_prim(stmt, token, expr);
+    }
+}
+
+static int dav_parse_logical_expr(DavQLStatement *stmt, UcxList *token,
+        DavQLExpression *expr) {
+    
+    UcxList *firsttoken = token;
+    int total_consumed = 0;
+    
+    // RULE:    BooleanLiteral, [LogicalOperator, LogicalExpression];
+    DavQLExpression left, right;
+    memset(&left, 0, sizeof(DavQLExpression));
+    int consumed = dav_parse_bool_expr(stmt, token, &left);
+    if (stmt->errorcode) {
+        return 0;
+    }
+    if (!consumed) {
+        dav_error_in_context(DAVQL_ERROR_MISSING_EXPR,
+            _error_missing_expr, stmt, token);
+        return 0;
+    }
+    total_consumed += consumed;
+    token = ucx_list_get(token, consumed);
+
+    if (token_is(token, DAVQL_TOKEN_OPERATOR)) {
+
+        davqloperator_t op = DAVQL_NOOP;
+        if (tokenvalue_is(token, "and")) {
+            op = DAVQL_LAND;
+        } else if (tokenvalue_is(token, "or")) {
+            op = DAVQL_LOR;
+        } else if (tokenvalue_is(token, "xor")) {
+            op = DAVQL_LXOR;
+        }
+
+        if (op == DAVQL_NOOP) {
+            dav_error_in_context(DAVQL_ERROR_INVALID_LOGICAL_OP,
+                _error_invalid_logical_op, stmt, token);
+            return 0;
+        } else {
+            expr->op = op;
+            total_consumed++;
+            token = token->next;
+
+            memset(&right, 0, sizeof(DavQLExpression));
+            consumed = dav_parse_logical_expr(stmt, token, &right);
+            if (stmt->errorcode) {
+                return 0;
+            }
+            if (!consumed) {
+                dav_error_in_context(DAVQL_ERROR_MISSING_EXPR,
+                    _error_missing_expr, stmt, token);
+                return 0;
+            }
+            total_consumed += consumed;
+            token = ucx_list_get(token, consumed);
+
+            expr->left = malloc(sizeof(DavQLExpression));
+            memcpy(expr->left, &left, sizeof(DavQLExpression));
+            expr->right = malloc(sizeof(DavQLExpression));
+            memcpy(expr->right, &right, sizeof(DavQLExpression));
+        }
+    } else {
+        memcpy(expr, &left, sizeof(DavQLExpression));
+    }
+    
+    // set type and recover source text
+    if (total_consumed > 0) {
+        expr->type = DAVQL_LOGICAL;
+        
+        expr->srctext.ptr = token_sstr(firsttoken).ptr;
+        sstr_t lasttok = token_sstr(ucx_list_get(firsttoken, total_consumed-1));
+        expr->srctext.length = lasttok.ptr-expr->srctext.ptr+lasttok.length;
+    }
+    
+    return total_consumed;
+}
+
 static int dav_parse_where_clause(DavQLStatement *stmt, UcxList *token) {
-    return 0;
+    stmt->where = calloc(1, sizeof(DavQLExpression));
+    
+    return dav_parse_logical_expr(stmt, token, stmt->where);
 }
 
 static int dav_parse_with_clause(DavQLStatement *stmt, UcxList *token) {
--- a/libidav/davqlparser.h	Sun May 24 20:27:51 2015 +0200
+++ b/libidav/davqlparser.h	Thu May 28 12:22:55 2015 +0200
@@ -181,11 +181,12 @@
  * String          = "'", {?Character? - "'" | "'''"} , "'" | "%s";
  * Timestamp       = "%t"; // TODO: maybe introduce a real literal 
  * 
- * LogicalExpression = "not ", LogicalExpression
+ * LogicalExpression = BooleanLiteral, [LogicalOperator, LogicalExpression];
+ * BooleanExpression = "not ", LogicalExpression
  *                   | "(", LogicalExpression, ")"
- *                   | BooleanExpression, [LogicalOperator, LogicalExpression];
- * BooleanExpression = Expression, Comparison, Expression
- *                   | Expression, (" like " | " unlike "), String
+ *                   | BooleanPrimary;
+ * BooleanPrimary    = Expression, (" like " | " unlike "), String
+ *                   | Expression, Comparison, Expression
  *                   | FunctionCall | Identifier;
  * 
  * LogicalOperator = " and " | " or " | " xor ";
@@ -302,11 +303,17 @@
 /** The type of the expression could not be determined. */
 #define DAVQL_ERROR_INVALID_EXPR 21
 
-/** An operator has been found for a unary expression, but it is invalid. */
+/** An operator has been found for an unary expression, but it is invalid. */
 #define DAVQL_ERROR_INVALID_UNARY_OP 22
 
+/** An operator has been found for a logical expression, but it is invalid. */
+#define DAVQL_ERROR_INVALID_LOGICAL_OP 23
+
 /** Invalid format specifier. */
-#define DAVQL_ERROR_INVALID_FMTSPEC 23
+#define DAVQL_ERROR_INVALID_FMTSPEC 24
+
+/** A string has been expected. */
+#define DAVQL_ERROR_INVALID_STRING 25
 
 /** The depth is invalid. */
 #define DAVQL_ERROR_INVALID_DEPTH 101

mercurial