Thu, 28 May 2015 12:22:55 +0200
completed logical expression parser - NEED TO TEST A LOT!
libidav/davqlparser.c | file | annotate | diff | comparison | revisions | |
libidav/davqlparser.h | file | annotate | diff | comparison | revisions |
--- a/libidav/davqlparser.c Sun May 24 20:27:51 2015 +0200 +++ b/libidav/davqlparser.c Thu May 28 12:22:55 2015 +0200 @@ -387,7 +387,9 @@ #define _error_missing_expr "missing expression " _error_context #define _error_invalid_expr "invalid expression " _error_context #define _error_invalid_unary_op "invalid unary operator " _error_context +#define _error_invalid_logical_op "invalid logical operator " _error_context #define _error_invalid_fmtspec "invalid format specifier " _error_context +#define _error_invalid_string "string expected " _error_context #define token_sstr(token) (((DavQLToken*)(token)->data)->value) @@ -431,6 +433,18 @@ return 0; } +static _Bool islongoperator(DavQLToken *token) { + sstr_t operators[] = {ST("and"), ST("or"), ST("not"), ST("xor"), + ST("like"), ST("unlike") + }; + for (int i = 0 ; i < sizeof(operators)/sizeof(sstr_t) ; i++) { + if (!sstrcasecmp(token->value, operators[i])) { + return 1; + } + } + return 0; +} + static UcxList* dav_parse_add_token(UcxList *tokenlist, DavQLToken *token) { // determine token class (order of if-statements is very important!) @@ -453,6 +467,8 @@ token->tokenclass = strchr(special_token_symbols, firstchar) ? DAVQL_TOKEN_OPERATOR : DAVQL_TOKEN_IDENTIFIER; } + } else if (islongoperator(token)) { + token->tokenclass = DAVQL_TOKEN_OPERATOR; } else if (firstchar == '\'') { token->tokenclass = DAVQL_TOKEN_STRING; } else if (firstchar == '`') { @@ -1016,8 +1032,241 @@ } } +// forward declaration +static int dav_parse_logical_expr(DavQLStatement *stmt, UcxList *token, + DavQLExpression *expr); + +static int dav_parse_bool_prim(DavQLStatement *stmt, UcxList *token, + DavQLExpression *expr) { + + expr->type = DAVQL_LOGICAL; + + int total_consumed = 0; + + DavQLExpression bexpr; + memset(&bexpr, 0, sizeof(DavQLExpression)); + total_consumed = dav_parse_expression(stmt, token, &bexpr); + if (!total_consumed || stmt->errorcode) { + return 0; + } + token = ucx_list_get(token, total_consumed); + + UcxList* optok = token; + // RULE: Expression, (" like " | " unlike "), String + if (token_is(optok, DAVQL_TOKEN_OPERATOR) && (tokenvalue_is(optok, + "like") || tokenvalue_is(optok, "unlike"))) { + + total_consumed++; + token = token->next; + if (token_is(token, DAVQL_TOKEN_STRING)) { + expr->op = tokenvalue_is(optok, "like") ? + DAVQL_LIKE : DAVQL_UNLIKE; + expr->left = malloc(sizeof(DavQLExpression)); + memcpy(expr->left, &bexpr, sizeof(DavQLExpression)); + expr->right = calloc(1, sizeof(DavQLExpression)); + expr->right->type = DAVQL_STRING; + expr->right->srctext = token_sstr(token); + + return total_consumed + 1; + } else { + dav_error_in_context(DAVQL_ERROR_INVALID_STRING, + _error_invalid_string, stmt, token); + return 0; + } + } + // RULE: Expression, Comparison, Expression + else if (token_is(optok, DAVQL_TOKEN_OPERATOR) && ( + tokenvalue_is(optok, "=") || tokenvalue_is(optok, "!") || + tokenvalue_is(optok, "<") || tokenvalue_is(optok, ">"))) { + + total_consumed++; + token = token->next; + + if (tokenvalue_is(optok, "=")) { + expr->op = DAVQL_EQ; + } else { + if (tokenvalue_is(token, "=")) { + if (tokenvalue_is(optok, "!")) { + expr->type = DAVQL_NEQ; + } else if (tokenvalue_is(optok, "<")) { + expr->type = DAVQL_LE; + } else if (tokenvalue_is(optok, ">")) { + expr->type = DAVQL_GE; + } + total_consumed++; + token = token->next; + } else { + if (tokenvalue_is(optok, "<")) { + expr->type = DAVQL_LT; + } else if (tokenvalue_is(optok, ">")) { + expr->type = DAVQL_GT; + } + } + } + + int consumed = dav_parse_expression(stmt, token, expr->right); + if (stmt->errorcode) { + return 0; + } + if (!consumed) { + dav_error_in_context( + DAVQL_ERROR_MISSING_EXPR, _error_missing_expr, + stmt, token); + return 0; + } + + total_consumed += consumed; + expr->left = malloc(sizeof(DavQLExpression)); + memcpy(expr->left, &bexpr, sizeof(DavQLExpression)); + + return total_consumed; + } + // RULE: FunctionCall | Identifier; + else if (bexpr.type == DAVQL_FUNCCALL || bexpr.type == DAVQL_IDENTIFIER) { + memcpy(expr, &bexpr, sizeof(DavQLExpression)); + + return total_consumed; + } else { + return 0; + } +} + +static int dav_parse_bool_expr(DavQLStatement *stmt, UcxList *token, + DavQLExpression *expr) { + + // RULE: "not ", LogicalExpression + if (token_is(token, DAVQL_TOKEN_OPERATOR) && tokenvalue_is(token, "not")) { + token = token->next; + expr->type = DAVQL_LOGICAL; + expr->op = DAVQL_NOT; + expr->left = calloc(1, sizeof(DavQLExpression)); + expr->srctext = token_sstr(token); + + int consumed = dav_parse_logical_expr(stmt, token, expr->left); + if (stmt->errorcode) { + return 0; + } + if (consumed) { + sstr_t lasttok = token_sstr(ucx_list_get(token, consumed-1)); + expr->srctext.length = + lasttok.ptr - expr->srctext.ptr + lasttok.length; + return consumed + 1; + } else { + dav_error_in_context(DAVQL_ERROR_MISSING_EXPR, + _error_missing_expr, stmt, token); + return 0; + } + } + // RULE: "(", LogicalExpression, ")" + else if (token_is(token, DAVQL_TOKEN_OPENP)) { + token = token->next; + + int consumed = dav_parse_logical_expr(stmt, token, expr); + if (stmt->errorcode) { + return 0; + } + if (!consumed) { + dav_error_in_context(DAVQL_ERROR_MISSING_EXPR, + _error_missing_expr, stmt, token); + return 0; + } + + token = ucx_list_get(token, consumed); + + if (token_is(token, DAVQL_TOKEN_CLOSEP)) { + token = token->next; + return consumed + 2; + } else { + dav_error_in_context(DAVQL_ERROR_MISSING_PAR, _error_missing_par, + stmt, token); + return 0; + } + } + // RULE: BooleanExpression + else { + return dav_parse_bool_prim(stmt, token, expr); + } +} + +static int dav_parse_logical_expr(DavQLStatement *stmt, UcxList *token, + DavQLExpression *expr) { + + UcxList *firsttoken = token; + int total_consumed = 0; + + // RULE: BooleanLiteral, [LogicalOperator, LogicalExpression]; + DavQLExpression left, right; + memset(&left, 0, sizeof(DavQLExpression)); + int consumed = dav_parse_bool_expr(stmt, token, &left); + if (stmt->errorcode) { + return 0; + } + if (!consumed) { + dav_error_in_context(DAVQL_ERROR_MISSING_EXPR, + _error_missing_expr, stmt, token); + return 0; + } + total_consumed += consumed; + token = ucx_list_get(token, consumed); + + if (token_is(token, DAVQL_TOKEN_OPERATOR)) { + + davqloperator_t op = DAVQL_NOOP; + if (tokenvalue_is(token, "and")) { + op = DAVQL_LAND; + } else if (tokenvalue_is(token, "or")) { + op = DAVQL_LOR; + } else if (tokenvalue_is(token, "xor")) { + op = DAVQL_LXOR; + } + + if (op == DAVQL_NOOP) { + dav_error_in_context(DAVQL_ERROR_INVALID_LOGICAL_OP, + _error_invalid_logical_op, stmt, token); + return 0; + } else { + expr->op = op; + total_consumed++; + token = token->next; + + memset(&right, 0, sizeof(DavQLExpression)); + consumed = dav_parse_logical_expr(stmt, token, &right); + if (stmt->errorcode) { + return 0; + } + if (!consumed) { + dav_error_in_context(DAVQL_ERROR_MISSING_EXPR, + _error_missing_expr, stmt, token); + return 0; + } + total_consumed += consumed; + token = ucx_list_get(token, consumed); + + expr->left = malloc(sizeof(DavQLExpression)); + memcpy(expr->left, &left, sizeof(DavQLExpression)); + expr->right = malloc(sizeof(DavQLExpression)); + memcpy(expr->right, &right, sizeof(DavQLExpression)); + } + } else { + memcpy(expr, &left, sizeof(DavQLExpression)); + } + + // set type and recover source text + if (total_consumed > 0) { + expr->type = DAVQL_LOGICAL; + + expr->srctext.ptr = token_sstr(firsttoken).ptr; + sstr_t lasttok = token_sstr(ucx_list_get(firsttoken, total_consumed-1)); + expr->srctext.length = lasttok.ptr-expr->srctext.ptr+lasttok.length; + } + + return total_consumed; +} + static int dav_parse_where_clause(DavQLStatement *stmt, UcxList *token) { - return 0; + stmt->where = calloc(1, sizeof(DavQLExpression)); + + return dav_parse_logical_expr(stmt, token, stmt->where); } static int dav_parse_with_clause(DavQLStatement *stmt, UcxList *token) {
--- a/libidav/davqlparser.h Sun May 24 20:27:51 2015 +0200 +++ b/libidav/davqlparser.h Thu May 28 12:22:55 2015 +0200 @@ -181,11 +181,12 @@ * String = "'", {?Character? - "'" | "'''"} , "'" | "%s"; * Timestamp = "%t"; // TODO: maybe introduce a real literal * - * LogicalExpression = "not ", LogicalExpression + * LogicalExpression = BooleanLiteral, [LogicalOperator, LogicalExpression]; + * BooleanExpression = "not ", LogicalExpression * | "(", LogicalExpression, ")" - * | BooleanExpression, [LogicalOperator, LogicalExpression]; - * BooleanExpression = Expression, Comparison, Expression - * | Expression, (" like " | " unlike "), String + * | BooleanPrimary; + * BooleanPrimary = Expression, (" like " | " unlike "), String + * | Expression, Comparison, Expression * | FunctionCall | Identifier; * * LogicalOperator = " and " | " or " | " xor "; @@ -302,11 +303,17 @@ /** The type of the expression could not be determined. */ #define DAVQL_ERROR_INVALID_EXPR 21 -/** An operator has been found for a unary expression, but it is invalid. */ +/** An operator has been found for an unary expression, but it is invalid. */ #define DAVQL_ERROR_INVALID_UNARY_OP 22 +/** An operator has been found for a logical expression, but it is invalid. */ +#define DAVQL_ERROR_INVALID_LOGICAL_OP 23 + /** Invalid format specifier. */ -#define DAVQL_ERROR_INVALID_FMTSPEC 23 +#define DAVQL_ERROR_INVALID_FMTSPEC 24 + +/** A string has been expected. */ +#define DAVQL_ERROR_INVALID_STRING 25 /** The depth is invalid. */ #define DAVQL_ERROR_INVALID_DEPTH 101