# HG changeset patch # User Mike Becker # Date 1428167109 -7200 # Node ID ecba8bdf974118045f86d3029475a94d00f5d60b # Parent 0ab1cf261a44b8d08e7716ed3a8ce37e909eaf2b davql: error handling + minor grammar changes diff -r 0ab1cf261a44 -r ecba8bdf9741 libidav/davqlparser.c --- a/libidav/davqlparser.c Sat Apr 04 17:44:33 2015 +0200 +++ b/libidav/davqlparser.c Sat Apr 04 19:05:09 2015 +0200 @@ -27,6 +27,7 @@ */ #include "davqlparser.h" +#include #include #include #include @@ -58,6 +59,15 @@ } } +static const char* _map_specialfield(int info) { + switch(info) { + case 0: return ""; + case 1: return "with wildcard"; + case 2: return "(resource data only)"; + default: return "with mysterious identifier"; + } +} + static const char* _map_operator(davqloperator_t op) { // don't use string array, because enum values may change switch(op) { @@ -80,22 +90,27 @@ sstr_t empty = ST("(empty)"); // Basic information - printf("Statement: %.*s\nType: %s\nField count: %zu", + size_t fieldcount = ucx_list_size(stmt->fields); + int specialfield = 0; + UCX_FOREACH(elm, stmt->fields) { + DavQLExpression* expr = (DavQLExpression*)elm->data; + if (expr->type == DAVQL_IDENTIFIER && expr->srctext.length == 1) { + if (expr->srctext.ptr[0] == '*') { + specialfield = 1; + } else if (expr->srctext.ptr[0] == '-') { + specialfield = 2; + } + } + } + if (specialfield) { + fieldcount--; + } + printf("Statement: %.*s\nType: %s\nField count: %zu %s\nPath: %.*s\n" + "Has where clause: %s\n", sfmtarg(stmt->srctext), _map_querytype(stmt->type), - ucx_list_size(stmt->fields)); - - // Has wildcard - _Bool wildcard = 0; - UCX_FOREACH(elm, stmt->fields) { - DavQLExpression* expr = (DavQLExpression*)elm->data; - if (expr->type == DAVQL_IDENTIFIER && - expr->srctext.length == 1 && expr->srctext.ptr[0] == '*') { - wildcard = 1; - } - } - printf(" %s wildcard\nPath: %.*s\nHas where clause: %s\n", - wildcard?"with":"without", + fieldcount, + _map_specialfield(specialfield), sfmtarg(stmt->path ? stmt->path->srctext : empty), stmt->where ? "yes" : "no"); if (stmt->type == DAVQL_SET) { @@ -110,6 +125,10 @@ } else { printf("Depth: %d\n", stmt->depth); } + if (stmt->errorcode) { + printf("\nError code: %d\nError: %s\n", + stmt->errorcode, stmt->errormessage); + } } static int dav_debug_ql_expr_selected(DavQLExpression *expr) { @@ -176,6 +195,10 @@ printf("Starting DavQL debugger (type 'h' for help)...\n\n"); dav_debug_ql_stmt_print(stmt); + if (stmt->errorcode) { + return; + } + DavQLExpression *examineexpr = NULL; while(1) { @@ -228,6 +251,10 @@ // P A R S E R // ------------------------------------------------------------------------ +#define _unexpected_end_msg "unexpected end of statement" +#define _invalid_msg "invalid statement" +#define _unexpected_token "unexpected token (%.*s [->]%.*s %.*s)" + static UcxList* dav_parse_tokenize(sstr_t src) { UcxList *tokens = NULL; @@ -328,6 +355,16 @@ return expr; } +static void dav_parse_unexpected_token(DavQLStatement *stmt, UcxList *token) { + sstr_t emptystring = ST(""); + stmt->errorcode = DAVQL_ERROR_UNEXPECTED_TOKEN; + sstr_t errormsg = ucx_sprintf(_unexpected_token, + sfmtarg(token->prev?*token_sstr(token->prev):emptystring), + sfmtarg(*token_sstr(token)), + sfmtarg(token->next?*token_sstr(token->next):emptystring)); + stmt->errormessage = errormsg.ptr; +} + static void dav_parse_get_statement(DavQLStatement *stmt, UcxList *tokens) { stmt->type = DAVQL_GET; @@ -339,6 +376,7 @@ * 530: expecting WITH clause * 40: WITH clause * 500: ready to quit + * 999: error * */ int step = 10; @@ -357,10 +395,13 @@ if (!sstrcasecmp(tokendata, S("where"))) { step = 30; } - /* no break */ + /* no break and no else*/ case 530: if (!sstrcasecmp(tokendata, S("with"))) { step = 40; + } else { + dav_parse_unexpected_token(stmt, token); + step = 999; } break; // field list @@ -407,8 +448,8 @@ } if (step < 500) { - stmt->type = DAVQL_ERROR; - // TODO: save parse error message + stmt->errorcode = DAVQL_ERROR_UNEXPECTED_END; + stmt->errormessage = strdup(_unexpected_end_msg); } } @@ -446,6 +487,8 @@ dav_parse_set_statement(stmt, tokens); } else { stmt->type = DAVQL_ERROR; + stmt->errorcode = DAVQL_ERROR_INVALID; + stmt->errormessage = strdup(_invalid_msg); } // free token data @@ -455,6 +498,8 @@ ucx_list_free(tokens); } else { stmt->type = DAVQL_ERROR; + stmt->errorcode = DAVQL_ERROR_INVALID; + stmt->errormessage = strdup(_invalid_msg); } return stmt; @@ -486,5 +531,8 @@ if (stmt->where) { dav_free_expression(stmt->where); } + if (stmt->errormessage) { + free(stmt->errormessage); + } free(stmt); } diff -r 0ab1cf261a44 -r ecba8bdf9741 libidav/davqlparser.h --- a/libidav/davqlparser.h Sat Apr 04 17:44:33 2015 +0200 +++ b/libidav/davqlparser.h Sat Apr 04 19:05:09 2015 +0200 @@ -130,7 +130,8 @@ * Comparison = | "=" | "<" | ">" | "<=" | ">=" | "!="; * * FieldExpressions = "*", {",", Expression, " as ", String} - * | FieldExpression, {",", FieldExpression}; + * | FieldExpression, {",", FieldExpression} + * | "-"; * FieldExpression = Identifier * | Expression, " as ", String; * SetExpressions = SetExpression, {",", {SetExpressions}; @@ -171,6 +172,14 @@ */ davqltype_t type; /** + * Error code, if any error occurred. Zero otherwise. + */ + int errorcode; + /** + * Error message, if any error occurred. + */ + char* errormessage; + /** * The list of field expressions. */ UcxList* fields; @@ -195,11 +204,17 @@ int depth; } DavQLStatement; -/** - * Infinity recursion depth for a DavQLStatement. - */ +/** Infinity recursion depth for a DavQLStatement. */ #define DAV_DEPTH_INFINITY -1 +/** No more tokens to parse, but the parser expected more. */ +#define DAVQL_ERROR_UNEXPECTED_END 1000 + +/** A token was found, which has not been expected. */ +#define DAVQL_ERROR_UNEXPECTED_TOKEN 1010 + +/** Nothing about the statement seems legit. */ +#define DAVQL_ERROR_INVALID -1 /** * Starts an interactive debugger for a DavQLStatement. diff -r 0ab1cf261a44 -r ecba8bdf9741 ucx/utils.h --- a/ucx/utils.h Sat Apr 04 17:44:33 2015 +0200 +++ b/ucx/utils.h Sat Apr 04 19:05:09 2015 +0200 @@ -219,6 +219,10 @@ */ sstr_t ucx_asprintf(UcxAllocator *allocator, const char *fmt, ...); +/** Shortcut for ucx_asprintf() with default allocator. */ +#define ucx_sprintf(fmt, ...) \ + ucx_asprintf(ucx_default_allocator(), fmt, __VA_ARGS__) + /** * va_list version of ucx_asprintf(). *