# HG changeset patch # User Mike Becker # Date 1432113273 -7200 # Node ID 412b06dc01621b683ccf1c98d525c08acd92f307 # Parent f62d271675bf9035c40265dd4459814760b0d7be completed field list parser + error messages do now provide more context information based on the source string diff -r f62d271675bf -r 412b06dc0162 libidav/davqlparser.c --- 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 ') " _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) { diff -r f62d271675bf -r 412b06dc0162 libidav/davqlparser.h --- a/libidav/davqlparser.h Wed May 20 10:05:04 2015 +0200 +++ b/libidav/davqlparser.h Wed May 20 11:14:33 2015 +0200 @@ -192,10 +192,11 @@ * Comparison = | "=" | "<" | ">" | "<=" | ">=" | "!="; * * FieldExpressions = "-" - * | "*", {",", Expression, " as ", Identifier} + * | "*", {",", NamedField} * | FieldExpression, {",", FieldExpression}; - * FieldExpression = Identifier - * | Expression, " as ", Identifier; + * FieldExpression = NamedField | Identifier; + * NamedField = Expression, " as ", Identifier; + * * SetExpressions = SetExpression, {",", SetExpression}; * SetExpression = Identifier, "=", Expression; *