diff options
author | Campbell Barton <ideasman42@gmail.com> | 2018-09-19 03:40:35 +0300 |
---|---|---|
committer | Campbell Barton <ideasman42@gmail.com> | 2018-09-19 04:08:04 +0300 |
commit | 3aea5695bbdcf83c5c7769a629e0a2e4db0c85bc (patch) | |
tree | 6825ecce4a5a937f03bbd0b8dcb79faddd3be7be | |
parent | 345c34826296907d85b4b367a830ff4f73ab293f (diff) |
Cleanup: rename BLI_simple_expr -> BLI_expr_pylike_eval
Simple isn't a good prefix for library names since
lots of unrelated modules could be called 'simple'.
Include 'py' in module name since this is a subset of Python,
one of the main motivations for this is to be Python like/compatible.
-rw-r--r-- | source/blender/blenkernel/intern/fcurve.c | 30 | ||||
-rw-r--r-- | source/blender/blenlib/BLI_expr_pylike_eval.h | 63 | ||||
-rw-r--r-- | source/blender/blenlib/BLI_simple_expr.h | 96 | ||||
-rw-r--r-- | source/blender/blenlib/CMakeLists.txt | 3 | ||||
-rw-r--r-- | source/blender/blenlib/intern/expr_pylike_eval.c (renamed from source/blender/blenlib/intern/simple_expr.c) | 116 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_anim_types.h | 2 | ||||
-rw-r--r-- | tests/gtests/blenlib/BLI_expr_pylike_eval_test.cc (renamed from tests/gtests/blenlib/BLI_simple_expr_test.cc) | 124 | ||||
-rw-r--r-- | tests/gtests/blenlib/CMakeLists.txt | 2 |
8 files changed, 219 insertions, 217 deletions
diff --git a/source/blender/blenkernel/intern/fcurve.c b/source/blender/blenkernel/intern/fcurve.c index bf8d259d59c..ddbe203d1af 100644 --- a/source/blender/blenkernel/intern/fcurve.c +++ b/source/blender/blenkernel/intern/fcurve.c @@ -49,7 +49,7 @@ #include "BLI_threads.h" #include "BLI_string_utils.h" #include "BLI_utildefines.h" -#include "BLI_simple_expr.h" +#include "BLI_expr_pylike_eval.h" #include "BLI_alloca.h" #include "BLT_translation.h" @@ -1866,7 +1866,7 @@ void fcurve_free_driver(FCurve *fcu) BPY_DECREF(driver->expr_comp); #endif - BLI_simple_expr_free(driver->expr_simple); + BLI_expr_pylike_free(driver->expr_simple); /* free driver itself, then set F-Curve's point to this to NULL (as the curve may still be used) */ MEM_freeN(driver); @@ -1897,7 +1897,7 @@ ChannelDriver *fcurve_copy_driver(const ChannelDriver *driver) /* Driver Expression Evaluation --------------- */ -static ParsedSimpleExpr *driver_compile_simple_expr_impl(ChannelDriver *driver) +static ExprPyLike_Parsed *driver_compile_simple_expr_impl(ChannelDriver *driver) { /* Prepare parameter names. */ int num_vars = BLI_listbase_count(&driver->variables); @@ -1910,10 +1910,10 @@ static ParsedSimpleExpr *driver_compile_simple_expr_impl(ChannelDriver *driver) names[i++] = dvar->name; } - return BLI_simple_expr_parse(driver->expression, num_vars + 1, names); + return BLI_expr_pylike_parse(driver->expression, num_vars + 1, names); } -static bool driver_evaluate_simple_expr(ChannelDriver *driver, ParsedSimpleExpr *expr, float *result, float time) +static bool driver_evaluate_simple_expr(ChannelDriver *driver, ExprPyLike_Parsed *expr, float *result, float time) { /* Prepare parameter values. */ int num_vars = BLI_listbase_count(&driver->variables); @@ -1928,19 +1928,19 @@ static bool driver_evaluate_simple_expr(ChannelDriver *driver, ParsedSimpleExpr /* Evaluate expression. */ double result_val; - eSimpleExpr_EvalStatus status = BLI_simple_expr_evaluate(expr, &result_val, num_vars + 1, vars); + eExprPyLike_EvalStatus status = BLI_expr_pylike_eval(expr, &result_val, num_vars + 1, vars); const char *message; switch (status) { - case SIMPLE_EXPR_SUCCESS: + case EXPR_PYLIKE_SUCCESS: if (isfinite(result_val)) { *result = (float)result_val; } return true; - case SIMPLE_EXPR_DIV_BY_ZERO: - case SIMPLE_EXPR_MATH_ERROR: - message = (status == SIMPLE_EXPR_DIV_BY_ZERO) ? "Division by Zero" : "Math Domain Error"; + case EXPR_PYLIKE_DIV_BY_ZERO: + case EXPR_PYLIKE_MATH_ERROR: + message = (status == EXPR_PYLIKE_DIV_BY_ZERO) ? "Division by Zero" : "Math Domain Error"; fprintf(stderr, "\n%s in Driver: '%s'\n", message, driver->expression); driver->flag |= DRIVER_FLAG_INVALID; @@ -1966,12 +1966,12 @@ static bool driver_compile_simple_expr(ChannelDriver *driver) /* It's safe to parse in multiple threads; at worst it'll * waste some effort, but in return avoids mutex contention. */ - ParsedSimpleExpr *expr = driver_compile_simple_expr_impl(driver); + ExprPyLike_Parsed *expr = driver_compile_simple_expr_impl(driver); /* Store the result if the field is still NULL, or discard * it if another thread got here first. */ if (atomic_cas_ptr((void **)&driver->expr_simple, NULL, expr) != NULL) { - BLI_simple_expr_free(expr); + BLI_expr_pylike_free(expr); } return true; @@ -1984,21 +1984,21 @@ static bool driver_try_evaluate_simple_expr(ChannelDriver *driver, ChannelDriver *result = 0.0f; return driver_compile_simple_expr(driver_orig) && - BLI_simple_expr_is_valid(driver_orig->expr_simple) && + BLI_expr_pylike_is_valid(driver_orig->expr_simple) && driver_evaluate_simple_expr(driver, driver_orig->expr_simple, result, time); } /* Check if the expression in the driver conforms to the simple subset. */ bool BKE_driver_has_simple_expression(ChannelDriver *driver) { - return driver_compile_simple_expr(driver) && BLI_simple_expr_is_valid(driver->expr_simple); + return driver_compile_simple_expr(driver) && BLI_expr_pylike_is_valid(driver->expr_simple); } /* Reset cached compiled expression data */ void BKE_driver_invalidate_expression(ChannelDriver *driver, bool expr_changed, bool varname_changed) { if (expr_changed || varname_changed) { - BLI_simple_expr_free(driver->expr_simple); + BLI_expr_pylike_free(driver->expr_simple); driver->expr_simple = NULL; } diff --git a/source/blender/blenlib/BLI_expr_pylike_eval.h b/source/blender/blenlib/BLI_expr_pylike_eval.h new file mode 100644 index 00000000000..b627664cc14 --- /dev/null +++ b/source/blender/blenlib/BLI_expr_pylike_eval.h @@ -0,0 +1,63 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2018 Blender Foundation, Alexander Gavrilov + * All rights reserved. + * + * Contributor(s): Alexander Gavrilov + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __BLI_EXPR_PYLIKE_EVAL_H__ +#define __BLI_EXPR_PYLIKE_EVAL_H__ + +/** \file BLI_expr_pylike_eval.h + * \ingroup bli + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** Opaque structure containing pre-parsed data for evaluation. */ +typedef struct ExprPyLike_Parsed ExprPyLike_Parsed; + +/** Expression evaluation return code. */ +typedef enum eExprPyLike_EvalStatus { + EXPR_PYLIKE_SUCCESS = 0, + /* Computation errors; result is still set, but may be NaN */ + EXPR_PYLIKE_DIV_BY_ZERO, + EXPR_PYLIKE_MATH_ERROR, + /* Expression dependent errors or bugs; result is 0 */ + EXPR_PYLIKE_INVALID, + EXPR_PYLIKE_FATAL_ERROR, +} eExprPyLike_EvalStatus; + +void BLI_expr_pylike_free(struct ExprPyLike_Parsed *expr); +bool BLI_expr_pylike_is_valid(struct ExprPyLike_Parsed *expr); +bool BLI_expr_pylike_is_constant(struct ExprPyLike_Parsed *expr); +ExprPyLike_Parsed *BLI_expr_pylike_parse( + const char *expression, int num_params, const char **param_names); +eExprPyLike_EvalStatus BLI_expr_pylike_eval( + struct ExprPyLike_Parsed *expr, double *result, int num_params, const double *params); + +#ifdef __cplusplus +} +#endif + +#endif /* __BLI_EXPR_PYLIKE_EVALUATE_H__ */ diff --git a/source/blender/blenlib/BLI_simple_expr.h b/source/blender/blenlib/BLI_simple_expr.h deleted file mode 100644 index 8498f1a02e7..00000000000 --- a/source/blender/blenlib/BLI_simple_expr.h +++ /dev/null @@ -1,96 +0,0 @@ -/* - * ***** BEGIN GPL LICENSE BLOCK ***** - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2018 Blender Foundation, Alexander Gavrilov - * All rights reserved. - * - * Contributor(s): Alexander Gavrilov - * - * ***** END GPL LICENSE BLOCK ***** - */ - -#ifndef __BLI_SIMPLE_EXPR_H__ -#define __BLI_SIMPLE_EXPR_H__ - -/** \file BLI_simple_expr.h - * \ingroup bli - * \author Alexander Gavrilov - * \since 2018 - * - * Simple evaluator for a subset of Python expressions that can be - * computed using purely double precision floating point values. - * - * Supported subset: - * - * - Identifiers use only ASCII characters. - * - Literals: - * floating point and decimal integer. - * - Constants: - * pi, True, False - * - Operators: - * +, -, *, /, ==, !=, <, <=, >, >=, and, or, not, ternary if - * - Functions: - * radians, degrees, - * abs, fabs, floor, ceil, trunc, int, - * sin, cos, tan, asin, acos, atan, atan2, - * exp, log, sqrt, pow, fmod - * - * The implementation has no global state and can be used multithreaded. - */ - -#ifdef __cplusplus -extern "C" { -#endif - -/** Opaque structure containing pre-parsed data for evaluation. */ -typedef struct ParsedSimpleExpr ParsedSimpleExpr; - -/** Simple expression evaluation return code. */ -typedef enum eSimpleExpr_EvalStatus { - SIMPLE_EXPR_SUCCESS = 0, - /* Computation errors; result is still set, but may be NaN */ - SIMPLE_EXPR_DIV_BY_ZERO, - SIMPLE_EXPR_MATH_ERROR, - /* Expression dependent errors or bugs; result is 0 */ - SIMPLE_EXPR_INVALID, - SIMPLE_EXPR_FATAL_ERROR, -} eSimpleExpr_EvalStatus; - -/** Free the parsed data; NULL argument is ok. */ -void BLI_simple_expr_free(struct ParsedSimpleExpr *expr); - -/** Check if the parsing result is valid for evaluation. */ -bool BLI_simple_expr_is_valid(struct ParsedSimpleExpr *expr); - -/** Check if the parsed expression always evaluates to the same value. */ -bool BLI_simple_expr_is_constant(struct ParsedSimpleExpr *expr); - -/** Parse the expression for evaluation later. - * Returns non-NULL even on failure; use is_valid to check. - */ -ParsedSimpleExpr *BLI_simple_expr_parse(const char *expression, int num_params, const char **param_names); - -/** Evaluate the expression with the given parameters. - * The order and number of parameters must match the names given to parse. - */ -eSimpleExpr_EvalStatus BLI_simple_expr_evaluate(struct ParsedSimpleExpr *expr, double *result, int num_params, const double *params); - -#ifdef __cplusplus -} -#endif - -#endif /* __BLI_SIMPLE_EXPR_H__*/ diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index 9dd89834bbd..6c56b06bc0e 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -68,6 +68,7 @@ set(SRC intern/easing.c intern/edgehash.c intern/endian_switch.c + intern/expr_pylike_eval.c intern/fileops.c intern/fnmatch.c intern/freetypefont.c @@ -104,7 +105,6 @@ set(SRC intern/rct.c intern/scanfill.c intern/scanfill_utils.c - intern/simple_expr.c intern/smallhash.c intern/sort.c intern/sort_utils.c @@ -152,6 +152,7 @@ set(SRC BLI_edgehash.h BLI_endian_switch.h BLI_endian_switch_inline.h + BLI_expr_pylike_eval.h BLI_fileops.h BLI_fileops_types.h BLI_fnmatch.h diff --git a/source/blender/blenlib/intern/simple_expr.c b/source/blender/blenlib/intern/expr_pylike_eval.c index cfab74f9681..c80cd505efa 100644 --- a/source/blender/blenlib/intern/simple_expr.c +++ b/source/blender/blenlib/intern/expr_pylike_eval.c @@ -25,8 +25,30 @@ * ***** END GPL LICENSE BLOCK ***** */ -/** \file blender/blenlib/intern/simple_expr.c +/** \file blender/blenlib/intern/expr_pylike_eval.c * \ingroup bli + * \author Alexander Gavrilov + * \since 2018 + * + * Simple evaluator for a subset of Python expressions that can be + * computed using purely double precision floating point values. + * + * Supported subset: + * + * - Identifiers use only ASCII characters. + * - Literals: + * floating point and decimal integer. + * - Constants: + * pi, True, False + * - Operators: + * +, -, *, /, ==, !=, <, <=, >, >=, and, or, not, ternary if + * - Functions: + * radians, degrees, + * abs, fabs, floor, ceil, trunc, int, + * sin, cos, tan, asin, acos, atan, atan2, + * exp, log, sqrt, pow, fmod + * + * The implementation has no global state and can be used multithreaded. */ #include <math.h> @@ -40,7 +62,7 @@ #include "MEM_guardedalloc.h" -#include "BLI_simple_expr.h" +#include "BLI_expr_pylike_eval.h" #include "BLI_blenlib.h" #include "BLI_math.h" #include "BLI_string_utils.h" @@ -53,7 +75,7 @@ /* Simple Expression Stack Machine ------------------------- */ -typedef enum eSimpleExpr_Opcode { +typedef enum eOpCode { /* Double constant: (-> dval) */ OPCODE_CONST, /* 1 argument function call: (a -> func1(a)) */ @@ -76,13 +98,13 @@ typedef enum eSimpleExpr_Opcode { OPCODE_JMP_AND, /* For comparison chaining: (a b -> 0 JUMP) IF NOT func2(a,b) ELSE (a b -> b) */ OPCODE_CMP_CHAIN, -} eSimpleExpr_Opcode; +} eOpCode; typedef double (*UnaryOpFunc)(double); typedef double (*BinaryOpFunc)(double, double); -typedef struct SimpleExprOp { - eSimpleExpr_Opcode opcode; +typedef struct ExprOp { + eOpCode opcode; int jmp_offset; @@ -93,43 +115,50 @@ typedef struct SimpleExprOp { UnaryOpFunc func1; BinaryOpFunc func2; } arg; -} SimpleExprOp; +} ExprOp; -struct ParsedSimpleExpr { +struct ExprPyLike_Parsed { int ops_count; int max_stack; - SimpleExprOp ops[]; + ExprOp ops[]; }; -void BLI_simple_expr_free(ParsedSimpleExpr *expr) +/** Free the parsed data; NULL argument is ok. */ +void BLI_expr_pylike_free(ExprPyLike_Parsed *expr) { if (expr != NULL) { MEM_freeN(expr); } } -bool BLI_simple_expr_is_valid(ParsedSimpleExpr *expr) +/** Check if the parsing result is valid for evaluation. */ +bool BLI_expr_pylike_is_valid(ExprPyLike_Parsed *expr) { return expr != NULL && expr->ops_count > 0; } -bool BLI_simple_expr_is_constant(ParsedSimpleExpr *expr) +/** Check if the parsed expression always evaluates to the same value. */ +bool BLI_expr_pylike_is_constant(ExprPyLike_Parsed *expr) { return expr != NULL && expr->ops_count == 1 && expr->ops[0].opcode == OPCODE_CONST; } /* Stack Machine Evaluation -------------------------------- */ -eSimpleExpr_EvalStatus BLI_simple_expr_evaluate(ParsedSimpleExpr *expr, double *result, int num_params, const double *params) +/** + * Evaluate the expression with the given parameters. + * The order and number of parameters must match the names given to parse. + */ +eExprPyLike_EvalStatus BLI_expr_pylike_eval(ExprPyLike_Parsed *expr, double *result, int num_params, const double *params) { *result = 0.0; - if (!BLI_simple_expr_is_valid(expr)) { - return SIMPLE_EXPR_INVALID; + if (!BLI_expr_pylike_is_valid(expr)) { + return EXPR_PYLIKE_INVALID; } -#define FAIL_IF(condition) if (condition) { return SIMPLE_EXPR_FATAL_ERROR; } +#define FAIL_IF(condition) if (condition) { return EXPR_PYLIKE_FATAL_ERROR; } /* Check the stack requirement is at least remotely sane and allocate on the actual stack. */ FAIL_IF(expr->max_stack <= 0 || expr->max_stack > 1000); @@ -137,7 +166,7 @@ eSimpleExpr_EvalStatus BLI_simple_expr_evaluate(ParsedSimpleExpr *expr, double * double *stack = BLI_array_alloca(stack, expr->max_stack); /* Evaluate expression. */ - SimpleExprOp *ops = expr->ops; + ExprOp *ops = expr->ops; int sp = 0, pc; feclearexcept(FE_ALL_EXCEPT); @@ -212,7 +241,7 @@ eSimpleExpr_EvalStatus BLI_simple_expr_evaluate(ParsedSimpleExpr *expr, double * break; default: - return SIMPLE_EXPR_FATAL_ERROR; + return EXPR_PYLIKE_FATAL_ERROR; } } @@ -225,10 +254,10 @@ eSimpleExpr_EvalStatus BLI_simple_expr_evaluate(ParsedSimpleExpr *expr, double * /* Detect floating point evaluation errors. */ int flags = fetestexcept(FE_DIVBYZERO | FE_INVALID); if (flags) { - return (flags & FE_INVALID) ? SIMPLE_EXPR_MATH_ERROR : SIMPLE_EXPR_DIV_BY_ZERO; + return (flags & FE_INVALID) ? EXPR_PYLIKE_MATH_ERROR : EXPR_PYLIKE_DIV_BY_ZERO; } - return SIMPLE_EXPR_SUCCESS; + return EXPR_PYLIKE_SUCCESS; } /* Simple Expression Built-In Operations ------------------- */ @@ -317,7 +346,7 @@ static BuiltinConstDef builtin_consts[] = { typedef struct BuiltinOpDef { const char *name; - eSimpleExpr_Opcode op; + eOpCode op; void *funcptr; } BuiltinOpDef; @@ -397,27 +426,27 @@ typedef struct SimpleExprParseState { /* Opcode buffer */ int ops_count, max_ops, last_jmp; - SimpleExprOp *ops; + ExprOp *ops; /* Stack space requirement tracking */ int stack_ptr, max_stack; } SimpleExprParseState; /* Reserve space for the specified number of operations in the buffer. */ -static SimpleExprOp *parse_alloc_ops(SimpleExprParseState *state, int count) +static ExprOp *parse_alloc_ops(SimpleExprParseState *state, int count) { if (state->ops_count + count > state->max_ops) { state->max_ops = power_of_2_max_i(state->ops_count + count); - state->ops = MEM_reallocN(state->ops, state->max_ops * sizeof(SimpleExprOp)); + state->ops = MEM_reallocN(state->ops, state->max_ops * sizeof(ExprOp)); } - SimpleExprOp *op = &state->ops[state->ops_count]; + ExprOp *op = &state->ops[state->ops_count]; state->ops_count += count; return op; } /* Add one operation and track stack usage. */ -static SimpleExprOp *parse_add_op(SimpleExprParseState *state, eSimpleExpr_Opcode code, int stack_delta) +static ExprOp *parse_add_op(SimpleExprParseState *state, eOpCode code, int stack_delta) { /* track evaluation stack depth */ state->stack_ptr += stack_delta; @@ -425,14 +454,14 @@ static SimpleExprOp *parse_add_op(SimpleExprParseState *state, eSimpleExpr_Opcod CLAMP_MIN(state->max_stack, state->stack_ptr); /* allocate the new instruction */ - SimpleExprOp *op = parse_alloc_ops(state, 1); - memset(op, 0, sizeof(SimpleExprOp)); + ExprOp *op = parse_alloc_ops(state, 1); + memset(op, 0, sizeof(ExprOp)); op->opcode = code; return op; } /* Add one jump operation and return an index for parse_set_jump. */ -static int parse_add_jump(SimpleExprParseState *state, eSimpleExpr_Opcode code) +static int parse_add_jump(SimpleExprParseState *state, eOpCode code) { parse_add_op(state, code, -1); return state->last_jmp = state->ops_count; @@ -446,9 +475,9 @@ static void parse_set_jump(SimpleExprParseState *state, int jump) } /* Add a function call operation, applying constant folding when possible. */ -static bool parse_add_func(SimpleExprParseState *state, eSimpleExpr_Opcode code, int args, void *funcptr) +static bool parse_add_func(SimpleExprParseState *state, eOpCode code, int args, void *funcptr) { - SimpleExprOp *prev_ops = &state->ops[state->ops_count]; + ExprOp *prev_ops = &state->ops[state->ops_count]; int jmp_gap = state->ops_count - state->last_jmp; feclearexcept(FE_ALL_EXCEPT); @@ -853,9 +882,9 @@ static bool parse_expr(SimpleExprParseState *state) /* Ternary IF expression in python requires swapping the * main body with condition, so stash the body opcodes. */ int size = state->ops_count - start; - int bytes = size * sizeof(SimpleExprOp); + int bytes = size * sizeof(ExprOp); - SimpleExprOp *body = MEM_mallocN(bytes, "driver if body"); + ExprOp *body = MEM_mallocN(bytes, "driver if body"); memcpy(body, state->ops + start, bytes); state->last_jmp = state->ops_count = start; @@ -896,8 +925,13 @@ static bool parse_expr(SimpleExprParseState *state) /* Main Parsing Function ----------------------------------- */ -/* Compile the expression and return the result. */ -ParsedSimpleExpr *BLI_simple_expr_parse(const char *expression, int num_params, const char **param_names) +/** + * Compile the expression and return the result. + * + * Parse the expression for evaluation later. + * Returns non-NULL even on failure; use is_valid to check. + */ +ExprPyLike_Parsed *BLI_expr_pylike_parse(const char *expression, int num_params, const char **param_names) { /* Prepare the parser state. */ SimpleExprParseState state; @@ -911,25 +945,25 @@ ParsedSimpleExpr *BLI_simple_expr_parse(const char *expression, int num_params, state.tokenbuf = MEM_mallocN(strlen(expression) + 1, __func__); state.max_ops = 16; - state.ops = MEM_mallocN(state.max_ops * sizeof(SimpleExprOp), __func__); + state.ops = MEM_mallocN(state.max_ops * sizeof(ExprOp), __func__); /* Parse the expression. */ - ParsedSimpleExpr *expr; + ExprPyLike_Parsed *expr; if (parse_next_token(&state) && parse_expr(&state) && state.token == 0) { BLI_assert(state.stack_ptr == 1); - int bytesize = sizeof(ParsedSimpleExpr) + state.ops_count * sizeof(SimpleExprOp); + int bytesize = sizeof(ExprPyLike_Parsed) + state.ops_count * sizeof(ExprOp); - expr = MEM_mallocN(bytesize, "ParsedSimpleExpr"); + expr = MEM_mallocN(bytesize, "ExprPyLike_Parsed"); expr->ops_count = state.ops_count; expr->max_stack = state.max_stack; - memcpy(expr->ops, state.ops, state.ops_count * sizeof(SimpleExprOp)); + memcpy(expr->ops, state.ops, state.ops_count * sizeof(ExprOp)); } else { /* Always return a non-NULL object so that parse failure can be cached. */ - expr = MEM_callocN(sizeof(ParsedSimpleExpr), "ParsedSimpleExpr(empty)"); + expr = MEM_callocN(sizeof(ExprPyLike_Parsed), "ExprPyLike_Parsed(empty)"); } MEM_freeN(state.tokenbuf); diff --git a/source/blender/makesdna/DNA_anim_types.h b/source/blender/makesdna/DNA_anim_types.h index edf137ca386..eed84ac7aac 100644 --- a/source/blender/makesdna/DNA_anim_types.h +++ b/source/blender/makesdna/DNA_anim_types.h @@ -415,7 +415,7 @@ typedef struct ChannelDriver { char expression[256]; /* expression to compile for evaluation */ void *expr_comp; /* PyObject - compiled expression, don't save this */ - struct ParsedSimpleExpr *expr_simple; /* compiled simple arithmetic expression */ + struct ExprPyLike_Parsed *expr_simple; /* compiled simple arithmetic expression */ float curval; /* result of previous evaluation */ float influence; /* influence of driver on result */ // XXX to be implemented... this is like the constraint influence setting diff --git a/tests/gtests/blenlib/BLI_simple_expr_test.cc b/tests/gtests/blenlib/BLI_expr_pylike_eval_test.cc index 58b33e85a5f..3aaf0d3a5c8 100644 --- a/tests/gtests/blenlib/BLI_simple_expr_test.cc +++ b/tests/gtests/blenlib/BLI_expr_pylike_eval_test.cc @@ -5,87 +5,87 @@ #include <string.h> extern "C" { -#include "BLI_simple_expr.h" +#include "BLI_expr_pylike_eval.h" #include "BLI_math.h" }; #define TRUE_VAL 1.0 #define FALSE_VAL 0.0 -static void simple_expr_parse_fail_test(const char *str) +static void expr_pylike_parse_fail_test(const char *str) { - ParsedSimpleExpr *expr = BLI_simple_expr_parse(str, 0, NULL); + ExprPyLike_Parsed *expr = BLI_expr_pylike_parse(str, 0, NULL); - EXPECT_FALSE(BLI_simple_expr_is_valid(expr)); + EXPECT_FALSE(BLI_expr_pylike_is_valid(expr)); - BLI_simple_expr_free(expr); + BLI_expr_pylike_free(expr); } -static void simple_expr_const_test(const char *str, double value, bool force_const) +static void expr_pylike_const_test(const char *str, double value, bool force_const) { - ParsedSimpleExpr *expr = BLI_simple_expr_parse(str, 0, NULL); + ExprPyLike_Parsed *expr = BLI_expr_pylike_parse(str, 0, NULL); if (force_const) { - EXPECT_TRUE(BLI_simple_expr_is_constant(expr)); + EXPECT_TRUE(BLI_expr_pylike_is_constant(expr)); } else { - EXPECT_TRUE(BLI_simple_expr_is_valid(expr)); - EXPECT_FALSE(BLI_simple_expr_is_constant(expr)); + EXPECT_TRUE(BLI_expr_pylike_is_valid(expr)); + EXPECT_FALSE(BLI_expr_pylike_is_constant(expr)); } double result; - eSimpleExpr_EvalStatus status = BLI_simple_expr_evaluate(expr, &result, 0, NULL); + eExprPyLike_EvalStatus status = BLI_expr_pylike_eval(expr, &result, 0, NULL); - EXPECT_EQ(status, SIMPLE_EXPR_SUCCESS); + EXPECT_EQ(status, EXPR_PYLIKE_SUCCESS); EXPECT_EQ(result, value); - BLI_simple_expr_free(expr); + BLI_expr_pylike_free(expr); } -static ParsedSimpleExpr *parse_for_eval(const char *str, bool nonconst) +static ExprPyLike_Parsed *parse_for_eval(const char *str, bool nonconst) { const char *names[1] = { "x" }; - ParsedSimpleExpr *expr = BLI_simple_expr_parse(str, 1, names); + ExprPyLike_Parsed *expr = BLI_expr_pylike_parse(str, 1, names); - EXPECT_TRUE(BLI_simple_expr_is_valid(expr)); + EXPECT_TRUE(BLI_expr_pylike_is_valid(expr)); if (nonconst) { - EXPECT_FALSE(BLI_simple_expr_is_constant(expr)); + EXPECT_FALSE(BLI_expr_pylike_is_constant(expr)); } return expr; } -static void verify_eval_result(ParsedSimpleExpr *expr, double x, double value) +static void verify_eval_result(ExprPyLike_Parsed *expr, double x, double value) { double result; - eSimpleExpr_EvalStatus status = BLI_simple_expr_evaluate(expr, &result, 1, &x); + eExprPyLike_EvalStatus status = BLI_expr_pylike_eval(expr, &result, 1, &x); - EXPECT_EQ(status, SIMPLE_EXPR_SUCCESS); + EXPECT_EQ(status, EXPR_PYLIKE_SUCCESS); EXPECT_EQ(result, value); } -static void simple_expr_eval_test(const char *str, double x, double value) +static void expr_pylike_eval_test(const char *str, double x, double value) { - ParsedSimpleExpr *expr = parse_for_eval(str, true); + ExprPyLike_Parsed *expr = parse_for_eval(str, true); verify_eval_result(expr, x, value); - BLI_simple_expr_free(expr); + BLI_expr_pylike_free(expr); } -static void simple_expr_error_test(const char *str, double x, eSimpleExpr_EvalStatus error) +static void expr_pylike_error_test(const char *str, double x, eExprPyLike_EvalStatus error) { - ParsedSimpleExpr *expr = parse_for_eval(str, false); + ExprPyLike_Parsed *expr = parse_for_eval(str, false); double result; - eSimpleExpr_EvalStatus status = BLI_simple_expr_evaluate(expr, &result, 1, &x); + eExprPyLike_EvalStatus status = BLI_expr_pylike_eval(expr, &result, 1, &x); EXPECT_EQ(status, error); - BLI_simple_expr_free(expr); + BLI_expr_pylike_free(expr); } #define TEST_PARSE_FAIL(name, str) \ - TEST(simple_expr, ParseFail_##name) { simple_expr_parse_fail_test(str); } + TEST(expr_pylike, ParseFail_##name) { expr_pylike_parse_fail_test(str); } TEST_PARSE_FAIL(Empty, "") TEST_PARSE_FAIL(ConstHex, "0x0") @@ -113,15 +113,15 @@ TEST_PARSE_FAIL(Truncated10, "fmod(1,") /* Constant expression with working constant folding */ #define TEST_CONST(name, str, value) \ - TEST(simple_expr, Const_##name) { simple_expr_const_test(str, value, true); } + TEST(expr_pylike, Const_##name) { expr_pylike_const_test(str, value, true); } /* Constant expression but constant folding is not supported */ #define TEST_RESULT(name, str, value) \ - TEST(simple_expr, Result_##name) { simple_expr_const_test(str, value, false); } + TEST(expr_pylike, Result_##name) { expr_pylike_const_test(str, value, false); } /* Expression with an argument */ #define TEST_EVAL(name, str, x, value) \ - TEST(simple_expr, Eval_##name) { simple_expr_eval_test(str, x, value); } + TEST(expr_pylike, Eval_##name) { expr_pylike_eval_test(str, x, value); } TEST_CONST(Zero, "0", 0.0) TEST_CONST(Zero2, "00", 0.0) @@ -237,9 +237,9 @@ TEST_RESULT(Or2, "0 or 3", 3.0) TEST_RESULT(Bool1, "2 or 3 and 4", 2.0) TEST_RESULT(Bool2, "not 2 or 3 and 4", 4.0) -TEST(simple_expr, Eval_Ternary1) +TEST(expr_pylike, Eval_Ternary1) { - ParsedSimpleExpr *expr = parse_for_eval("x / 2 if x < 4 else x - 2 if x < 8 else x*2 - 12", true); + ExprPyLike_Parsed *expr = parse_for_eval("x / 2 if x < 4 else x - 2 if x < 8 else x*2 - 12", true); for (int i = 0; i <= 10; i++) { double x = i; @@ -248,63 +248,63 @@ TEST(simple_expr, Eval_Ternary1) verify_eval_result(expr, x, v); } - BLI_simple_expr_free(expr); + BLI_expr_pylike_free(expr); } -TEST(simple_expr, MultipleArgs) +TEST(expr_pylike, MultipleArgs) { const char* names[3] = { "x", "y", "x" }; double values[3] = { 1.0, 2.0, 3.0 }; - ParsedSimpleExpr *expr = BLI_simple_expr_parse("x*10 + y", 3, names); + ExprPyLike_Parsed *expr = BLI_expr_pylike_parse("x*10 + y", 3, names); - EXPECT_TRUE(BLI_simple_expr_is_valid(expr)); + EXPECT_TRUE(BLI_expr_pylike_is_valid(expr)); double result; - eSimpleExpr_EvalStatus status = BLI_simple_expr_evaluate(expr, &result, 3, values); + eExprPyLike_EvalStatus status = BLI_expr_pylike_eval(expr, &result, 3, values); - EXPECT_EQ(status, SIMPLE_EXPR_SUCCESS); + EXPECT_EQ(status, EXPR_PYLIKE_SUCCESS); EXPECT_EQ(result, 32.0); - BLI_simple_expr_free(expr); + BLI_expr_pylike_free(expr); } #define TEST_ERROR(name, str, x, code) \ - TEST(simple_expr, Error_##name) { simple_expr_error_test(str, x, code); } + TEST(expr_pylike, Error_##name) { expr_pylike_error_test(str, x, code); } -TEST_ERROR(DivZero1, "0 / 0", 0.0, SIMPLE_EXPR_MATH_ERROR) -TEST_ERROR(DivZero2, "1 / 0", 0.0, SIMPLE_EXPR_DIV_BY_ZERO) -TEST_ERROR(DivZero3, "1 / x", 0.0, SIMPLE_EXPR_DIV_BY_ZERO) -TEST_ERROR(DivZero4, "1 / x", 1.0, SIMPLE_EXPR_SUCCESS) +TEST_ERROR(DivZero1, "0 / 0", 0.0, EXPR_PYLIKE_MATH_ERROR) +TEST_ERROR(DivZero2, "1 / 0", 0.0, EXPR_PYLIKE_DIV_BY_ZERO) +TEST_ERROR(DivZero3, "1 / x", 0.0, EXPR_PYLIKE_DIV_BY_ZERO) +TEST_ERROR(DivZero4, "1 / x", 1.0, EXPR_PYLIKE_SUCCESS) -TEST_ERROR(SqrtDomain1, "sqrt(-1)", 0.0, SIMPLE_EXPR_MATH_ERROR) -TEST_ERROR(SqrtDomain2, "sqrt(x)", -1.0, SIMPLE_EXPR_MATH_ERROR) -TEST_ERROR(SqrtDomain3, "sqrt(x)", 0.0, SIMPLE_EXPR_SUCCESS) +TEST_ERROR(SqrtDomain1, "sqrt(-1)", 0.0, EXPR_PYLIKE_MATH_ERROR) +TEST_ERROR(SqrtDomain2, "sqrt(x)", -1.0, EXPR_PYLIKE_MATH_ERROR) +TEST_ERROR(SqrtDomain3, "sqrt(x)", 0.0, EXPR_PYLIKE_SUCCESS) -TEST_ERROR(PowDomain1, "pow(-1, 0.5)", 0.0, SIMPLE_EXPR_MATH_ERROR) -TEST_ERROR(PowDomain2, "pow(-1, x)", 0.5, SIMPLE_EXPR_MATH_ERROR) -TEST_ERROR(PowDomain3, "pow(-1, x)", 2.0, SIMPLE_EXPR_SUCCESS) +TEST_ERROR(PowDomain1, "pow(-1, 0.5)", 0.0, EXPR_PYLIKE_MATH_ERROR) +TEST_ERROR(PowDomain2, "pow(-1, x)", 0.5, EXPR_PYLIKE_MATH_ERROR) +TEST_ERROR(PowDomain3, "pow(-1, x)", 2.0, EXPR_PYLIKE_SUCCESS) -TEST_ERROR(Mixed1, "sqrt(x) + 1 / max(0, x)", -1.0, SIMPLE_EXPR_MATH_ERROR) -TEST_ERROR(Mixed2, "sqrt(x) + 1 / max(0, x)", 0.0, SIMPLE_EXPR_DIV_BY_ZERO) -TEST_ERROR(Mixed3, "sqrt(x) + 1 / max(0, x)", 1.0, SIMPLE_EXPR_SUCCESS) +TEST_ERROR(Mixed1, "sqrt(x) + 1 / max(0, x)", -1.0, EXPR_PYLIKE_MATH_ERROR) +TEST_ERROR(Mixed2, "sqrt(x) + 1 / max(0, x)", 0.0, EXPR_PYLIKE_DIV_BY_ZERO) +TEST_ERROR(Mixed3, "sqrt(x) + 1 / max(0, x)", 1.0, EXPR_PYLIKE_SUCCESS) -TEST(simple_expr, Error_Invalid) +TEST(expr_pylike, Error_Invalid) { - ParsedSimpleExpr *expr = BLI_simple_expr_parse("", 0, NULL); + ExprPyLike_Parsed *expr = BLI_expr_pylike_parse("", 0, NULL); double result; - EXPECT_EQ(BLI_simple_expr_evaluate(expr, &result, 0, NULL), SIMPLE_EXPR_INVALID); + EXPECT_EQ(BLI_expr_pylike_eval(expr, &result, 0, NULL), EXPR_PYLIKE_INVALID); - BLI_simple_expr_free(expr); + BLI_expr_pylike_free(expr); } -TEST(simple_expr, Error_ArgumentCount) +TEST(expr_pylike, Error_ArgumentCount) { - ParsedSimpleExpr *expr = parse_for_eval("x", false); + ExprPyLike_Parsed *expr = parse_for_eval("x", false); double result; - EXPECT_EQ(BLI_simple_expr_evaluate(expr, &result, 0, NULL), SIMPLE_EXPR_FATAL_ERROR); + EXPECT_EQ(BLI_expr_pylike_eval(expr, &result, 0, NULL), EXPR_PYLIKE_FATAL_ERROR); - BLI_simple_expr_free(expr); + BLI_expr_pylike_free(expr); } diff --git a/tests/gtests/blenlib/CMakeLists.txt b/tests/gtests/blenlib/CMakeLists.txt index 1db7949d3a5..7574d42d5a1 100644 --- a/tests/gtests/blenlib/CMakeLists.txt +++ b/tests/gtests/blenlib/CMakeLists.txt @@ -43,6 +43,7 @@ endif() BLENDER_TEST(BLI_array_store "bf_blenlib") BLENDER_TEST(BLI_array_utils "bf_blenlib") +BLENDER_TEST(BLI_expr_pylike_eval "bf_blenlib") BLENDER_TEST(BLI_ghash "bf_blenlib") BLENDER_TEST(BLI_hash_mm2a "bf_blenlib") BLENDER_TEST(BLI_heap "bf_blenlib") @@ -55,7 +56,6 @@ BLENDER_TEST(BLI_math_geom "bf_blenlib") BLENDER_TEST(BLI_memiter "bf_blenlib") BLENDER_TEST(BLI_path_util "${BLI_path_util_extra_libs}") BLENDER_TEST(BLI_polyfill_2d "bf_blenlib") -BLENDER_TEST(BLI_simple_expr "bf_blenlib") BLENDER_TEST(BLI_stack "bf_blenlib") BLENDER_TEST(BLI_string "bf_blenlib") BLENDER_TEST(BLI_string_utf8 "bf_blenlib") |