merge

Mon, 01 Jan 2018 19:54:37 +0100

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Mon, 01 Jan 2018 19:54:37 +0100
changeset 362
9a75b6df3307
parent 361
b6f2462ee055 (current diff)
parent 360
125b9c976f39 (diff)
child 363
e9ed8e130ccf

merge

--- a/bsd.mk	Mon Jan 01 19:53:36 2018 +0100
+++ b/bsd.mk	Mon Jan 01 19:54:37 2018 +0100
@@ -43,5 +43,5 @@
 APP_EXT =
 
 DAV_CFLAGS = `curl-config --cflags` `pkg-config --cflags libxml-2.0`
-DAV_LDFLAGS = `curl-config --libs` `pkg-config --libs libxml-2.0` -lssl -lcrypto -lpthread
+DAV_LDFLAGS = -L/usr/lib -lssl -lcrypto `curl-config --libs` `pkg-config --libs libxml-2.0` -lpthread
 
--- a/libidav/davqlparser.c	Mon Jan 01 19:53:36 2018 +0100
+++ b/libidav/davqlparser.c	Mon Jan 01 19:54:37 2018 +0100
@@ -182,6 +182,19 @@
     }
 }
 
+static void dav_debug_ql_field_print(DavQLField *field) {
+    if (field) {
+        printf("Name: %.*s\n", sfmtarg(field->name));
+        if (field->expr) {
+            dav_debug_ql_expr_print(field->expr);
+        } else {
+            printf("No expression.\n");
+        }
+    } else {
+        printf("No field selected.\n");
+    }
+}
+
 static void dav_debug_ql_tree_print(DavQLExpression *expr, int depth) {
     if (expr) {
         if (expr->left) {
@@ -279,12 +292,14 @@
         case DQLD_CMD_PT: dav_debug_ql_tree_print(examineexpr, 1); break;
         case DQLD_CMD_PF: dav_debug_ql_fnames_print(stmt); break;
         case DQLD_CMD_F:
-            if (examineclause != DQLD_CMD_F) {
-                examineclause = DQLD_CMD_F;
-                examineelem = stmt->fields;
-                examineexpr = stmt->fields ?
-                    ((DavQLField*)stmt->fields->data)->expr : NULL;
-                dav_debug_ql_expr_print(examineexpr);
+            examineclause = DQLD_CMD_F;
+            examineelem = stmt->fields;
+            if (stmt->fields) {
+                DavQLField* field = ((DavQLField*)stmt->fields->data);
+                examineexpr = field->expr;
+                dav_debug_ql_field_print(field);
+            } else {
+                examineexpr = NULL;
             }
             break;
         case DQLD_CMD_W:
@@ -293,13 +308,11 @@
             dav_debug_ql_expr_print(examineexpr);
             break;
         case DQLD_CMD_O:
-            if (examineclause != DQLD_CMD_O) {
-                examineclause = DQLD_CMD_O;
-                examineelem = stmt->orderby;
-                examineexpr = stmt->orderby ?
-                    ((DavQLOrderCriterion*)stmt->orderby->data)->column : NULL;
-                dav_debug_ql_expr_print(examineexpr);
-            }
+            examineclause = DQLD_CMD_O;
+            examineelem = stmt->orderby;
+            examineexpr = stmt->orderby ?
+                ((DavQLOrderCriterion*)stmt->orderby->data)->column : NULL;
+            dav_debug_ql_expr_print(examineexpr);
             break;
         case DQLD_CMD_N:
         case DQLD_CMD_P:
@@ -311,12 +324,14 @@
                     if (examineclause == DQLD_CMD_O) {
                         examineexpr = ((DavQLOrderCriterion*)
                             examineelem->data)->column;
+                        dav_debug_ql_expr_print(examineexpr);
                     } else if (examineclause == DQLD_CMD_F) {
-                        examineexpr = ((DavQLField*)examineelem->data)->expr;
+                        DavQLField* field = (DavQLField*)examineelem->data;
+                        examineexpr = field->expr;
+                        dav_debug_ql_field_print(field);
                     } else {
                         printf("Examining unknown clause type.");
                     }
-                    dav_debug_ql_expr_print(examineexpr);
                 } else {
                     printf("Reached end of list.\n");
                 }
@@ -379,10 +394,14 @@
 #define _error_invalid_token "invalid token " _error_context
 #define _error_missing_path "expected path " _error_context
 #define _error_missing_from "expecting FROM keyword " _error_context
+#define _error_missing_at "expecting AT keyword " _error_context
 #define _error_missing_by "expecting BY keyword " _error_context
 #define _error_missing_as "expecting alias ('as <identifier>') " _error_context
 #define _error_missing_identifier "expecting identifier " _error_context
 #define _error_missing_par "missing closed parenthesis " _error_context
+#define _error_missing_assign "expecting assignment ('=') " _error_context
+#define _error_missing_where "SET statements must have a WHERE clause or " \
+                        "explicitly use ANYWHERE " _error_context
 #define _error_invalid_depth "invalid depth " _error_context
 #define _error_missing_expr "missing expression " _error_context
 #define _error_invalid_expr "invalid expression " _error_context
@@ -1462,6 +1481,91 @@
     return total_consumed;
 }
 
+
+static int dav_parse_assignments(DavQLStatement *stmt, UcxList *token) {
+    
+    // RULE:    Assignment, {",", Assignment}
+    int total_consumed = 0, consumed;
+    do {
+        // RULE:    Identifier, "=", Expression
+        if (token_is(token, DAVQL_TOKEN_IDENTIFIER)) {
+
+            // Identifier
+            DavQLField *field;
+            dqlsec_malloc(stmt, field, DavQLField);
+            field->name = token_sstr(token);
+            total_consumed++;
+            token = token->next;
+            
+            // "="
+            if (!token_is(token, DAVQL_TOKEN_OPERATOR)
+                    || !tokenvalue_is(token, "=")) {
+                dav_free_field(field);
+                
+                dav_error_in_context(DAVQL_ERROR_MISSING_ASSIGN,
+                    _error_missing_assign, stmt, token);
+                return total_consumed;
+            }
+            total_consumed++;
+            token = token->next;
+
+            // Expression
+            dqlsec_mallocz(stmt, field->expr, DavQLExpression);
+            consumed = dav_parse_expression(stmt, token, field->expr);
+            if (stmt->errorcode) {
+                dav_free_field(field);
+                return total_consumed;
+            }
+            token = ucx_list_get(token, consumed);
+            total_consumed += consumed;
+            
+            // Add assignment to list and check if there's another one
+            dqlsec_list_append_or_free(stmt, stmt->fields, field);
+            consumed = token_is(token, DAVQL_TOKEN_COMMA) ? 1 : 0;
+            if (consumed) {
+                token = token->next;
+                total_consumed++;
+            }
+        } else {
+            dav_error_in_context(DAVQL_ERROR_MISSING_TOKEN,
+                    _error_missing_identifier, stmt, token);
+            return total_consumed;
+        }
+    } while (consumed);
+
+    return total_consumed;
+}
+
+static int dav_parse_path(DavQLStatement *stmt, UcxList *tokens) {
+    if (token_is(tokens, DAVQL_TOKEN_STRING)) {
+        stmt->path = token_sstr(tokens);
+        tokens = tokens->next;
+        return 1;
+    } else if (token_is(tokens, DAVQL_TOKEN_OPERATOR)
+            && tokenvalue_is(tokens, "/")) {
+        stmt->path = token_sstr(tokens);
+        tokens = tokens->next;
+        int consumed = 1;
+        while (!token_is(tokens, DAVQL_TOKEN_KEYWORD) &&
+                !token_is(tokens, DAVQL_TOKEN_END)) {
+            sstr_t toksstr = token_sstr(tokens);
+            stmt->path.length = toksstr.ptr-stmt->path.ptr+toksstr.length;
+            tokens = tokens->next;
+            consumed++;
+        }
+        return consumed;
+    } else if (token_is(tokens, DAVQL_TOKEN_FMTSPEC) &&
+            tokenvalue_is(tokens, "%s")) {
+        stmt->path = token_sstr(tokens);
+        tokens = tokens->next;
+        return 1;
+    } else {
+        dav_error_in_context(DAVQL_ERROR_MISSING_TOKEN,
+            _error_missing_path, stmt, tokens);
+        return 0;
+    }
+}
+
 /**
  * Parser of a select statement.
  * @param stmt the statement object that shall contain the syntax tree
@@ -1476,7 +1580,7 @@
         return;
     }
     
-    // Consume from keyword
+    // Consume FROM keyword
     if (token_is(tokens, DAVQL_TOKEN_KEYWORD)
             && tokenvalue_is(tokens, "from")) {
         tokens = tokens->next;
@@ -1487,26 +1591,8 @@
     }
     
     // Consume path
-    if (token_is(tokens, DAVQL_TOKEN_STRING)) {
-        stmt->path = token_sstr(tokens);
-        tokens = tokens->next;
-    } else if (token_is(tokens, DAVQL_TOKEN_OPERATOR)
-            && tokenvalue_is(tokens, "/")) {
-        stmt->path = token_sstr(tokens);
-        tokens = tokens->next;
-        while (!token_is(tokens, DAVQL_TOKEN_KEYWORD) &&
-                !token_is(tokens, DAVQL_TOKEN_END)) {
-            sstr_t toksstr = token_sstr(tokens);
-            stmt->path.length = toksstr.ptr-stmt->path.ptr+toksstr.length;
-            tokens = tokens->next;
-        }
-    } else if (token_is(tokens, DAVQL_TOKEN_FMTSPEC) &&
-            tokenvalue_is(tokens, "%s")) {
-        stmt->path = token_sstr(tokens);
-        tokens = tokens->next;
-    } else {
-        dav_error_in_context(DAVQL_ERROR_MISSING_TOKEN,
-            _error_missing_path, stmt, tokens);
+    tokens = ucx_list_get(tokens, dav_parse_path(stmt, tokens));
+    if (stmt->errorcode) {
         return;
     }
     
@@ -1527,6 +1613,11 @@
         tokens = tokens->next;
         tokens = ucx_list_get(tokens,
             dav_parse_where_clause(stmt, tokens));
+    } else if (token_is(tokens, DAVQL_TOKEN_KEYWORD)
+            && tokenvalue_is(tokens, "anywhere")) {
+        // useless, but the user may want to explicitly express his intent
+        tokens = tokens->next;
+        stmt->where = NULL;
     }
     if (stmt->errorcode) {
         return;
@@ -1566,7 +1657,55 @@
 static void dav_parse_set_statement(DavQLStatement *stmt, UcxList *tokens) {
     stmt->type = DAVQL_SET;
     
-    // TODO: make it so
+    // Consume assignments
+    tokens = ucx_list_get(tokens, dav_parse_assignments(stmt, tokens));
+    if (stmt->errorcode) {
+        return;
+    }
+    
+    // Consume AT keyword
+    if (token_is(tokens, DAVQL_TOKEN_KEYWORD)
+            && tokenvalue_is(tokens, "at")) {
+        tokens = tokens->next;
+    } else {
+        dav_error_in_context(DAVQL_ERROR_MISSING_TOKEN,
+                _error_missing_at, stmt, tokens);
+        return;
+    }
+
+    // Consume path
+    tokens = ucx_list_get(tokens, dav_parse_path(stmt, tokens));
+    if (stmt->errorcode) {
+        return;
+    }
+    
+    // Consume with clause (if any)
+    if (token_is(tokens, DAVQL_TOKEN_KEYWORD)
+            && tokenvalue_is(tokens, "with")) {
+        tokens = tokens->next;
+        tokens = ucx_list_get(tokens,
+            dav_parse_with_clause(stmt, tokens));
+    }
+    if (stmt->errorcode) {
+        return;
+    }
+    
+    // Consume mandatory where clause (or anywhere keyword)
+    if (token_is(tokens, DAVQL_TOKEN_KEYWORD)
+            && tokenvalue_is(tokens, "where")) {
+        tokens = tokens->next;
+        tokens = ucx_list_get(tokens,
+            dav_parse_where_clause(stmt, tokens));
+    } else if (token_is(tokens, DAVQL_TOKEN_KEYWORD)
+            && tokenvalue_is(tokens, "anywhere")) {
+        // no-op, but we want the user to be explicit about this
+        tokens = tokens->next;
+        stmt->where = NULL;
+    } else {
+        dav_error_in_context(DAVQL_ERROR_MISSING_TOKEN,
+                _error_missing_where, stmt, tokens);
+        return;
+    }
 }
 
 DavQLStatement* dav_parse_statement(sstr_t srctext) {
--- a/libidav/davqlparser.h	Mon Jan 01 19:53:36 2018 +0100
+++ b/libidav/davqlparser.h	Mon Jan 01 19:54:37 2018 +0100
@@ -200,10 +200,10 @@
  *                  | "*", {",", NamedField}
  *                  | FieldExpression, {",", FieldExpression};
  * FieldExpression  = NamedField | Identifier;
- * NamedField  = Expression, " as ", Identifier;
+ * NamedField       = Expression, " as ", Identifier;
  * 
- * SetExpressions   = SetExpression, {",", SetExpression};
- * SetExpression    = Identifier, "=", Expression;
+ * Assignments   = Assignment, {",", Assignment};
+ * Assignment    = Identifier, "=", Expression;
  * 
  * Path     = String
  *          | "/", [PathNode, {"/", PathNode}], ["/"];
@@ -225,13 +225,13 @@
  * SelectStatement = "select ", FieldExpressions,
  * " from ", Path,
  * [" with ", WithClause],
- * [" where ", LogicalExpression],
+ * [(" where ", LogicalExpression) | " anywhere"],
  * [" order by ", OrderByClause];
   * </pre>
  * 
  * <b>SET:</b>
  * <pre>
- * SetStatement = "set ",SetExpressions,
+ * SetStatement = "set ",Assignments,
  * " at ", Path,
  * [" with ", WithClause],
  * (" where ", LogicalExpression) | " anywhere";
@@ -304,6 +304,9 @@
 /** A closed parenthesis ')' is missing. */
 #define DAVQL_ERROR_MISSING_PAR 13
 
+/** An assignment operator '=' is missing. */
+#define DAVQL_ERROR_MISSING_ASSIGN 14
+
 /** The type of the expression could not be determined. */
 #define DAVQL_ERROR_INVALID_EXPR 21
 

mercurial