mirror of https://github.com/redis/redis.git
				
				
				
			Expr filtering: conversions and other fixes.
This commit is contained in:
		
							parent
							
								
									a90d2ea290
								
							
						
					
					
						commit
						af6fa6f732
					
				
							
								
								
									
										72
									
								
								expr.c
								
								
								
								
							
							
						
						
									
										72
									
								
								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)) | ||||
|             { | ||||
|             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