mirror of https://github.com/redis/redis.git
Expr filtering: conversions and other fixes.
This commit is contained in:
parent
a90d2ea290
commit
af6fa6f732
76
expr.c
76
expr.c
|
|
@ -10,6 +10,7 @@
|
|||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
#include <math.h>
|
||||
#include "cJSON.h"
|
||||
|
||||
#define EXPR_TOKEN_EOF 0
|
||||
#define EXPR_TOKEN_NUM 1
|
||||
|
|
@ -173,6 +174,14 @@ void exprStackFree(exprstack *stack) {
|
|||
free(stack->items);
|
||||
}
|
||||
|
||||
/* Just reset the stack removing all the items, but leaving it in a state
|
||||
* that makes it still usable for new elements. */
|
||||
void exprStackReset(exprstack *stack) {
|
||||
for (int j = 0; j < stack->numitems; j++)
|
||||
exprFreeToken(stack->items[j]);
|
||||
stack->numitems = 0;
|
||||
}
|
||||
|
||||
/* =========================== Expression compilation ======================= */
|
||||
|
||||
void exprConsumeSpaces(exprstate *es) {
|
||||
|
|
@ -553,21 +562,31 @@ exprstate *exprCompile(char *expr, int *errpos) {
|
|||
/* Convert a token to its numeric value. For strings we attempt to parse them
|
||||
* as numbers, returning 0 if conversion fails. */
|
||||
double exprTokenToNum(exprtoken *t) {
|
||||
char buf[128];
|
||||
if (t->token_type == EXPR_TOKEN_NUM) {
|
||||
return t->num;
|
||||
} else if (t->token_type == EXPR_TOKEN_STR) {
|
||||
char *str = malloc(t->str.len + 1);
|
||||
memcpy(str, t->str.start, t->str.len);
|
||||
str[t->str.len] = '\0';
|
||||
} else if (t->token_type == EXPR_TOKEN_STR && t->str.len < sizeof(buf)) {
|
||||
memcpy(buf, t->str.start, t->str.len);
|
||||
buf[t->str.len] = '\0';
|
||||
char *endptr;
|
||||
double val = strtod(str, &endptr);
|
||||
free(str);
|
||||
double val = strtod(buf, &endptr);
|
||||
return *endptr == '\0' ? val : 0;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Conver obejct to true/false (0 or 1) */
|
||||
double exprTokenToBool(exprtoken *t) {
|
||||
if (t->token_type == EXPR_TOKEN_NUM) {
|
||||
return t->num != 0;
|
||||
} else if (t->token_type == EXPR_TOKEN_STR && t->str.len == 0) {
|
||||
return 0; // Empty string are false, like in Javascript.
|
||||
} else {
|
||||
return 1; // Every non numerical type is true.
|
||||
}
|
||||
}
|
||||
|
||||
/* Compare two tokens. Returns true if they are equal. */
|
||||
int exprTokensEqual(exprtoken *a, exprtoken *b) {
|
||||
// If both are strings, do string comparison.
|
||||
|
|
@ -587,9 +606,9 @@ int exprTokensEqual(exprtoken *a, exprtoken *b) {
|
|||
|
||||
/* Execute the compiled expression program. Returns 1 if the final stack value
|
||||
* evaluates to true, 0 otherwise. Also returns 0 if any selector callback fails. */
|
||||
int exprRun(exprstate *es, int (*get_attrib_callback)(void *privdata, char *attrname, size_t attrnamelen, exprtoken *token), void *privdata) {
|
||||
// Reset the values stack.
|
||||
es->values_stack.numitems = 0;
|
||||
int exprRun(exprstate *es, char *json, size_t json_len) {
|
||||
exprStackReset(&es->values_stack);
|
||||
cJSON *parsed_json = NULL;
|
||||
|
||||
// Execute each instruction in the program.
|
||||
for (int i = 0; i < es->program.numitems; i++) {
|
||||
|
|
@ -598,14 +617,17 @@ int exprRun(exprstate *es, int (*get_attrib_callback)(void *privdata, char *attr
|
|||
// Handle selectors by calling the callback.
|
||||
if (t->token_type == EXPR_TOKEN_SELECTOR) {
|
||||
exprtoken *result = malloc(sizeof(exprtoken));
|
||||
if (result != NULL &&
|
||||
get_attrib_callback(privdata, t->str.start, t->str.len, result))
|
||||
{
|
||||
exprStackPush(&es->values_stack, result);
|
||||
continue;
|
||||
if (result != NULL && json != NULL) {
|
||||
if (parsed_json == NULL)
|
||||
parsed_json = cJSON_ParseWithLength(json,json_len);
|
||||
if (parsed_json) {
|
||||
exprStackPush(&es->values_stack, result);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
exprFreeToken(result);
|
||||
return 0; // Selector not found, expression fails.
|
||||
if (parsed_json) cJSON_Delete(parsed_json);
|
||||
return 0; // Selector not found or OOM: expression fails.
|
||||
}
|
||||
|
||||
// Push non-operator values directly onto the stack.
|
||||
|
|
@ -629,7 +651,7 @@ int exprRun(exprstate *es, int (*get_attrib_callback)(void *privdata, char *attr
|
|||
|
||||
switch(t->opcode) {
|
||||
case EXPR_OP_NOT:
|
||||
result->num = exprTokenToNum(b) == 0 ? 1 : 0;
|
||||
result->num = exprTokenToBool(b) == 0 ? 1 : 0;
|
||||
break;
|
||||
case EXPR_OP_POW: {
|
||||
double base = exprTokenToNum(a);
|
||||
|
|
@ -688,11 +710,11 @@ int exprRun(exprstate *es, int (*get_attrib_callback)(void *privdata, char *attr
|
|||
}
|
||||
case EXPR_OP_AND:
|
||||
result->num =
|
||||
exprTokenToNum(a) != 0 && exprTokenToNum(b) != 0 ? 1 : 0;
|
||||
exprTokenToBool(a) != 0 && exprTokenToBool(b) != 0 ? 1 : 0;
|
||||
break;
|
||||
case EXPR_OP_OR:
|
||||
result->num =
|
||||
exprTokenToNum(a) != 0 || exprTokenToNum(b) != 0 ? 1 : 0;
|
||||
exprTokenToBool(a) != 0 || exprTokenToBool(b) != 0 ? 1 : 0;
|
||||
break;
|
||||
default:
|
||||
// Do nothing: we don't want runtime errors.
|
||||
|
|
@ -705,19 +727,14 @@ int exprRun(exprstate *es, int (*get_attrib_callback)(void *privdata, char *attr
|
|||
exprStackPush(&es->values_stack, result);
|
||||
}
|
||||
|
||||
if (parsed_json) cJSON_Delete(parsed_json);
|
||||
|
||||
// Get final result from stack.
|
||||
exprtoken *final = exprStackPop(&es->values_stack);
|
||||
if (final == NULL) return 0;
|
||||
|
||||
// Convert result to boolean.
|
||||
int retval;
|
||||
if (final->token_type == EXPR_TOKEN_NUM) {
|
||||
printf("FINAL %f\n", final->num);
|
||||
retval = final->num != 0;
|
||||
} else {
|
||||
retval = 1; // Non-numeric types evaluate to true.
|
||||
}
|
||||
|
||||
int retval = exprTokenToBool(final);
|
||||
exprFreeToken(final);
|
||||
return retval;
|
||||
}
|
||||
|
|
@ -765,7 +782,9 @@ void exprPrintStack(exprstack *stack, const char *name) {
|
|||
|
||||
int main(int argc, char **argv) {
|
||||
char *testexpr = "(5+2)*3 and 'foo'";
|
||||
char *testjson = "{\"year\": 1984, \"name\": \"The Matrix\"}";
|
||||
if (argc >= 2) testexpr = argv[1];
|
||||
if (argc >= 3) testjson = argv[2];
|
||||
|
||||
printf("Compiling expression: %s\n", testexpr);
|
||||
|
||||
|
|
@ -778,8 +797,9 @@ int main(int argc, char **argv) {
|
|||
|
||||
exprPrintStack(&es->tokens, "Tokens");
|
||||
exprPrintStack(&es->program, "Program");
|
||||
int result = exprRun(es,NULL,NULL);
|
||||
printf("Result: %d\n", result);
|
||||
printf("Running against object: %s\n", testjson);
|
||||
int result = exprRun(es,testjson,strlen(testjson));
|
||||
printf("Result: %s\n", result ? "True" : "False");
|
||||
exprFree(es);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue