libidav/davqlparser.c

changeset 113
412b06dc0162
parent 111
39f4c5fcaa60
child 114
943548492a47
--- a/libidav/davqlparser.c	Wed May 20 10:05:04 2015 +0200
+++ b/libidav/davqlparser.c	Wed May 20 11:14:33 2015 +0200
@@ -371,7 +371,7 @@
 //                         P A R S E R
 // ------------------------------------------------------------------------
 
-#define _error_context "(%.*s [->]%.*s %.*s)"
+#define _error_context "(%.*s[->]%.*s%.*s)"
 #define _error_invalid "invalid statement"
 #define _error_unhandled "unhandled error " _error_context
 #define _error_unexpected_token "unexpected token " _error_context
@@ -379,6 +379,8 @@
 #define _error_missing_path "expected path " _error_context
 #define _error_missing_from "expecting FROM 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_invalid_depth "invalid depth " _error_context
 #define _error_missing_expr "missing expression " _error_context
@@ -390,12 +392,27 @@
 
 static void dav_error_in_context(int errorcode, const char *errormsg,
         DavQLStatement *stmt, UcxList *token) {
+    
+    // we try to achieve two things: get as many information as possible
+    // and recover the concrete source string (and not the token strings)
     sstr_t emptystring = ST("");
+    sstr_t prev = token->prev ? (token->prev->prev ?
+        token_sstr(token->prev->prev) : token_sstr(token->prev))
+        : emptystring;
+    sstr_t tokenstr = token_sstr(token);
+    sstr_t next = token->next ? (token->next->next ?
+        token_sstr(token->next->next) : token_sstr(token->next))
+        : emptystring;
+    
+    int lp = prev.length == 0 ? 0 : tokenstr.ptr-prev.ptr;
+    char *pn = tokenstr.ptr + tokenstr.length;
+    int ln = next.ptr+next.length - pn;
+    
     stmt->errorcode = errorcode;
     stmt->errormessage = ucx_sprintf(errormsg,
-        sfmtarg(token->prev?token_sstr(token->prev):emptystring),
-        sfmtarg(token_sstr(token)),
-        sfmtarg(token->next?token_sstr(token->next):emptystring)).ptr;
+        lp, prev.ptr,
+        sfmtarg(tokenstr),
+        ln, pn).ptr;
 }
 
 // special symbols are single tokens - the % sign MUST NOT be a special symbol
@@ -757,6 +774,44 @@
         dav_parse_expression);
 }
 
+static int dav_parse_named_field(DavQLStatement *stmt, UcxList *token,
+        DavQLField *field) {
+    int total_consumed = 0, consumed;
+    
+    // RULE:    Expression, " as ", Identifier;
+    DavQLExpression * expr = calloc(sizeof(DavQLExpression), 1);
+    consumed = dav_parse_expression(stmt, token, expr);
+    if (expr->type == DAVQL_UNDEFINED_TYPE) {
+        dav_free_expression(expr);
+        dav_error_in_context(DAVQL_ERROR_INVALID_EXPR,
+            _error_invalid_expr, stmt, token);
+        return 0;
+    }
+
+    token = ucx_list_get(token, consumed);
+    total_consumed += consumed;    
+    
+    if (token_is(token, DAVQL_TOKEN_KEYWORD) && tokenvalue_is(token, "as")) {
+        token = token->next; total_consumed++;
+    } else {
+        free(expr);
+        dav_error_in_context(DAVQL_ERROR_MISSING_TOKEN,
+            _error_missing_as, stmt, token);
+        return 0;
+    }
+
+    if (token_is(token, DAVQL_TOKEN_IDENTIFIER)) {
+        field->name = token_sstr(token);
+        field->expr = expr;
+        return total_consumed + 1;
+    } else {
+        free(expr);
+        dav_error_in_context(DAVQL_ERROR_MISSING_TOKEN,
+            _error_missing_identifier, stmt, token);
+        return 0;
+    }
+}
+
 static int dav_parse_fieldlist(DavQLStatement *stmt, UcxList *token) {
     
     // RULE:    "-"
@@ -769,7 +824,7 @@
         return 1;
     }
     
-    // RULE:    "*", {",", Expression, " as ", Identifier}
+    // RULE:    "*", {",", NamedExpression}
     if (token_is(token, DAVQL_TOKEN_OPERATOR) && tokenvalue_is(token, "*")) {
         DavQLField *field = malloc(sizeof(DavQLField));
         field->expr = calloc(sizeof(DavQLExpression), 1);
@@ -786,21 +841,13 @@
             
             if (token_is(token, DAVQL_TOKEN_COMMA)) {
                 total_consumed++; token = token->next;
-                DavQLExpression * expr = calloc(sizeof(DavQLExpression), 1);
-                consumed = dav_parse_expression(stmt, token, expr);
-                if (expr->type == DAVQL_UNDEFINED_TYPE) {
-                    dav_free_expression(expr);
-                    consumed = 0;
-                    dav_error_in_context(DAVQL_ERROR_INVALID_EXPR,
-                        _error_invalid_expr, stmt, token);
-                } else {
+                DavQLField localfield;
+                consumed = dav_parse_named_field(stmt, token, &localfield);
+                if (!stmt->errorcode && consumed) {
                     DavQLField *field = malloc(sizeof(DavQLField));
-                    field->expr = expr;
-                    field->name = expr->srctext;
+                    memcpy(field, &localfield, sizeof(DavQLField));
                     stmt->fields = ucx_list_append(stmt->fields, field);
-                }
-                
-                // TODO: parse "as"
+                }                
             } else {
                 consumed = 0;
             }
@@ -810,9 +857,56 @@
     }
     
     // RULE:    FieldExpression, {",", FieldExpression}
-    // TODO: make it so
-    
-    return 0;
+    {
+        int total_consumed = 0, consumed;
+        do {
+            // RULE:    NamedField | Identifier
+            DavQLField localfield;
+            consumed = dav_parse_named_field(stmt, token, &localfield);
+            if (consumed) {
+                DavQLField *field = malloc(sizeof(DavQLField));
+                memcpy(field, &localfield, sizeof(DavQLField));
+                stmt->fields = ucx_list_append(stmt->fields, field);
+                token = ucx_list_get(token, consumed);
+                total_consumed += consumed;
+            } else if (token_is(token, DAVQL_TOKEN_IDENTIFIER)
+                // look ahead, if the field is JUST the identifier
+                && (token_is(token->next, DAVQL_TOKEN_COMMA) ||
+                    tokenvalue_is(token->next, "from"))) {
+                
+                DavQLField *field = malloc(sizeof(DavQLField));
+                field->expr = calloc(sizeof(DavQLExpression), 1);
+                field->expr->type = DAVQL_IDENTIFIER;
+                field->expr->srctext = field->name = token_sstr(token);
+                stmt->fields = ucx_list_append(stmt->fields, field);
+
+                consumed = 1;
+                total_consumed++;
+                token = token->next;
+                
+                // we found a valid solution, so erase any errors
+                stmt->errorcode = 0;
+                if (stmt->errormessage) {
+                    free(stmt->errormessage);
+                    stmt->errormessage = NULL;
+                }
+            } else {
+                // dav_parse_named_field has already thrown a good error
+                consumed = 0;
+            }
+            
+            // field has been parsed, now try to get a comma
+            if (consumed) {
+                consumed = token_is(token, DAVQL_TOKEN_COMMA) ? 1 : 0;
+                if (consumed) {
+                    token = token->next;
+                    total_consumed++;
+                }
+            }
+        } while (consumed);
+
+        return total_consumed;
+    }
 }
 
 static int dav_parse_where_clause(DavQLStatement *stmt, UcxList *token) {

mercurial