Sun, 24 May 2015 20:27:51 +0200
added function call parser + fixed some memory management mistakes
libidav/davqlparser.c | file | annotate | diff | comparison | revisions | |
libidav/davqlparser.h | file | annotate | diff | comparison | revisions |
--- a/libidav/davqlparser.c Wed May 20 11:14:33 2015 +0200 +++ b/libidav/davqlparser.c Sun May 24 20:27:51 2015 +0200 @@ -75,6 +75,7 @@ // don't use string array, because enum values may change switch(op) { case DAVQL_NOOP: return "no operator"; + case DAVQL_CALL: return "function call"; case DAVQL_ARGLIST: return ","; case DAVQL_ADD: return "+"; case DAVQL_SUB: return "-"; case DAVQL_MUL: return "*"; case DAVQL_DIV: return "/"; case DAVQL_AND: return "&"; case DAVQL_OR: return "|"; @@ -422,7 +423,7 @@ sstr_t keywords[] = {ST("get"), ST("set"), ST("from"), ST("at"), ST("as"), ST("where"), ST("with"), ST("order"), ST("by"), ST("asc"), ST("desc") }; - for (int i = 0 ; i < sizeof(keywords)/sizeof(char*) ; i++) { + for (int i = 0 ; i < sizeof(keywords)/sizeof(sstr_t) ; i++) { if (!sstrcasecmp(token->value, keywords[i])) { return 1; } @@ -547,13 +548,15 @@ } static void dav_free_expression(DavQLExpression *expr) { - if (expr->left) { - dav_free_expression(expr->left); + if (expr) { + if (expr->left) { + dav_free_expression(expr->left); + } + if (expr->right) { + dav_free_expression(expr->right); + } + free(expr); } - if (expr->right) { - dav_free_expression(expr->right); - } - free(expr); } static void dav_free_field(DavQLField *field) { @@ -592,7 +595,7 @@ // RULE: LEFT, [Operator, RIGHT] memset(&left, 0, sizeof(DavQLExpression)); consumed = parseL(stmt, token, &left); - if (!consumed) { + if (!consumed || stmt->errorcode) { return 0; } total_consumed += consumed; @@ -607,6 +610,9 @@ token = token->next; memset(&right, 0, sizeof(DavQLExpression)); consumed = parseR(stmt, token, &right); + if (stmt->errorcode) { + return 0; + } if (!consumed) { dav_error_in_context(DAVQL_ERROR_MISSING_EXPR, _error_missing_expr, stmt, token); @@ -660,16 +666,106 @@ return 1; } +// forward declaration +static int dav_parse_expression(DavQLStatement* stmt, UcxList* token, + DavQLExpression* expr); + +static int dav_parse_arglist(DavQLStatement* stmt, UcxList* token, + DavQLExpression* expr) { + + expr->srctext.ptr = token_sstr(token).ptr; + expr->srctext.length = 0; + expr->left = expr->right = NULL; // in case we fail, we want them to be sane + + int total_consumed = 0; + + // RULE: Expression, {",", Expression}; + DavQLExpression *arglist = expr; + DavQLExpression arg; + char *lastchar = expr->srctext.ptr; + int consumed; + do { + memset(&arg, 0, sizeof(DavQLExpression)); + consumed = dav_parse_expression(stmt, token, &arg); + if (consumed) { + lastchar = arg.srctext.ptr + arg.srctext.length; + total_consumed += consumed; + token = ucx_list_get(token, consumed); + // look ahead for a comma + if (token_is(token, DAVQL_TOKEN_COMMA)) { + total_consumed++; + token = token->next; + /* we have more arguments, so put the current argument to the + * left subtree and create a new node to the right + */ + arglist->left = malloc(sizeof(DavQLExpression)); + memcpy(arglist->left, &arg, sizeof(DavQLExpression)); + arglist->srctext.ptr = arg.srctext.ptr; + arglist->op = DAVQL_ARGLIST; + arglist->type = DAVQL_FUNCCALL; + arglist->right = calloc(1, sizeof(DavQLExpression)); + arglist = arglist->right; + } else { + // this was the last argument, so write it to the current node + memcpy(arglist, &arg, sizeof(DavQLExpression)); + consumed = 0; + } + } + } while (consumed && !stmt->errorcode); + + // recover source text + arglist = expr; + while (arglist && arglist->type == DAVQL_FUNCCALL) { + arglist->srctext.length = lastchar - arglist->srctext.ptr; + arglist = arglist->right; + } + + return total_consumed; +} + static int dav_parse_funccall(DavQLStatement* stmt, UcxList* token, DavQLExpression* expr) { - // TODO: make it so - return 0; -} + // RULE: Identifier, "(", ArgumentList, ")"; + if (token_is(token, DAVQL_TOKEN_IDENTIFIER) && + token_is(token->next, DAVQL_TOKEN_OPENP)) { -// forward declaration -static int dav_parse_expression(DavQLStatement* stmt, UcxList* token, - DavQLExpression* expr); + expr->type = DAVQL_FUNCCALL; + expr->op = DAVQL_CALL; + + expr->left = calloc(1, sizeof(DavQLExpression)); + expr->left->type = DAVQL_IDENTIFIER; + expr->left->srctext = token_sstr(token); + expr->right = NULL; + + token = token->next->next; + + DavQLExpression arg; + int argtokens = dav_parse_arglist(stmt, token, &arg); + if (stmt->errorcode) { + // if an error occurred while parsing the arglist, return now + return 2; + } + if (argtokens) { + token = ucx_list_get(token, argtokens); + expr->right = malloc(sizeof(DavQLExpression)); + memcpy(expr->right, &arg, sizeof(DavQLExpression)); + } else { + // arg list may be empty + expr->right = NULL; + } + + if (token_is(token, DAVQL_TOKEN_CLOSEP)) { + return 3 + argtokens; + } else { + dav_error_in_context(DAVQL_ERROR_MISSING_PAR, _error_missing_par, + stmt, token); + return 2; // it MUST be a function call, but it is invalid + } + } else { + return 0; + } +} static int dav_parse_unary_expr(DavQLStatement* stmt, UcxList* token, DavQLExpression* expr) { @@ -689,7 +785,7 @@ case '-': expr->op = DAVQL_SUB; break; case '~': expr->op = DAVQL_NEG; break; } - expr->left = calloc(sizeof(DavQLExpression), 1); + expr->left = calloc(1, sizeof(DavQLExpression)); atom = expr->left; total_consumed++; token = token->next; @@ -740,8 +836,15 @@ // recover source text expr->srctext.ptr = token_sstr(firsttoken).ptr; - sstr_t lasttoken = token_sstr(ucx_list_get(firsttoken, total_consumed-1)); - expr->srctext.length = lasttoken.ptr - expr->srctext.ptr + lasttoken.length; + if (total_consumed > 0) { + sstr_t lasttoken = + token_sstr(ucx_list_get(firsttoken, total_consumed-1)); + expr->srctext.length = + lasttoken.ptr - expr->srctext.ptr + lasttoken.length; + } else { + // the expression should not be used anyway, but we want to be safe + expr->srctext.length = 0; + } return total_consumed; @@ -779,8 +882,12 @@ int total_consumed = 0, consumed; // RULE: Expression, " as ", Identifier; - DavQLExpression * expr = calloc(sizeof(DavQLExpression), 1); + DavQLExpression *expr = calloc(1, sizeof(DavQLExpression)); consumed = dav_parse_expression(stmt, token, expr); + if (stmt->errorcode) { + dav_free_expression(expr); + return 0; + } if (expr->type == DAVQL_UNDEFINED_TYPE) { dav_free_expression(expr); dav_error_in_context(DAVQL_ERROR_INVALID_EXPR, @@ -794,7 +901,7 @@ if (token_is(token, DAVQL_TOKEN_KEYWORD) && tokenvalue_is(token, "as")) { token = token->next; total_consumed++; } else { - free(expr); + dav_free_expression(expr); dav_error_in_context(DAVQL_ERROR_MISSING_TOKEN, _error_missing_as, stmt, token); return 0; @@ -805,7 +912,7 @@ field->expr = expr; return total_consumed + 1; } else { - free(expr); + dav_free_expression(expr); dav_error_in_context(DAVQL_ERROR_MISSING_TOKEN, _error_missing_identifier, stmt, token); return 0; @@ -817,7 +924,7 @@ // RULE: "-" if (token_is(token, DAVQL_TOKEN_OPERATOR) && tokenvalue_is(token, "-")) { DavQLField *field = malloc(sizeof(DavQLField)); - field->expr = calloc(sizeof(DavQLExpression), 1); + field->expr = calloc(1, sizeof(DavQLExpression)); field->expr->type = DAVQL_IDENTIFIER; field->expr->srctext = field->name = token_sstr(token); stmt->fields = ucx_list_append(stmt->fields, field); @@ -827,7 +934,7 @@ // RULE: "*", {",", NamedExpression} if (token_is(token, DAVQL_TOKEN_OPERATOR) && tokenvalue_is(token, "*")) { DavQLField *field = malloc(sizeof(DavQLField)); - field->expr = calloc(sizeof(DavQLExpression), 1); + field->expr = calloc(1, sizeof(DavQLExpression)); field->expr->type = DAVQL_IDENTIFIER; field->expr->srctext = field->name = token_sstr(token); stmt->fields = ucx_list_append(stmt->fields, field); @@ -875,7 +982,7 @@ tokenvalue_is(token->next, "from"))) { DavQLField *field = malloc(sizeof(DavQLField)); - field->expr = calloc(sizeof(DavQLExpression), 1); + field->expr = calloc(1, sizeof(DavQLExpression)); field->expr->type = DAVQL_IDENTIFIER; field->expr->srctext = field->name = token_sstr(token); stmt->fields = ucx_list_append(stmt->fields, field); @@ -926,7 +1033,7 @@ stmt->depth = DAV_DEPTH_INFINITY; token = token->next; total_consumed++; } else { - DavQLExpression *depthexpr = calloc(sizeof(DavQLExpression), 1); + DavQLExpression *depthexpr = calloc(1, sizeof(DavQLExpression)); int consumed = dav_parse_expression(stmt, token, depthexpr);
--- a/libidav/davqlparser.h Wed May 20 11:14:33 2015 +0200 +++ b/libidav/davqlparser.h Sun May 24 20:27:51 2015 +0200 @@ -171,7 +171,7 @@ * AddOperator = "+" | "-"; * UnaryOperator = "+" | "-" | "~"; * - * FunctionCall = Identifier, "(", ArgumentList, ")"; + * FunctionCall = Identifier, "(", [ArgumentList], ")"; * ArgumentList = Expression, {",", Expression}; * Identifier = IdentifierChar - ?Digit?, {IdentifierChar} * | "`", ?Character? - "`", {?Character? - "`"}, "`";