Blender V4.3
expr_pylike_eval.c
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2018 Blender Authors, Alexander Gavrilov. All rights reserved.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
29#include <ctype.h>
30#include <fenv.h>
31#include <float.h>
32#include <math.h>
33#include <stddef.h>
34#include <stdio.h>
35#include <stdlib.h>
36#include <string.h>
37
38#include "MEM_guardedalloc.h"
39
40#include "BLI_alloca.h"
42#include "BLI_math_base.h"
43#include "BLI_utildefines.h"
44
45#ifdef _MSC_VER
46# pragma fenv_access(on)
47#endif
48
49/* -------------------------------------------------------------------- */
53typedef enum eOpCode {
54 /* Double constant: (-> dval). */
56 /* 1 argument function call: (a -> func1(a)). */
58 /* 2 argument function call: (a b -> func2(a,b)). */
60 /* 3 argument function call: (a b c -> func3(a,b,c)). */
62 /* Parameter access: (-> params[ival]) */
64 /* Minimum of multiple inputs: (a b c... -> min); ival = arg count. */
66 /* Maximum of multiple inputs: (a b c... -> max); ival = arg count. */
68 /* Jump (pc += jmp_offset) */
70 /* Pop and jump if zero: (a -> ); JUMP IF NOT a. */
72 /* Jump if nonzero, or pop: (a -> a JUMP) IF a ELSE (a -> ). */
74 /* Jump if zero, or pop: (a -> a JUMP) IF NOT a ELSE (a -> ). */
76 /* For comparison chaining: (a b -> 0 JUMP) IF NOT func2(a,b) ELSE (a b -> b). */
79
83
98
105
108/* -------------------------------------------------------------------- */
113{
114 if (expr != NULL) {
115 MEM_freeN(expr);
116 }
117}
118
120{
121 return expr != NULL && expr->ops_count > 0;
122}
123
125{
126 return expr != NULL && expr->ops_count == 1 && expr->ops[0].opcode == OPCODE_CONST;
127}
128
130{
131 int i;
132
133 if (expr == NULL) {
134 return false;
135 }
136
137 for (i = 0; i < expr->ops_count; i++) {
138 if (expr->ops[i].opcode == OPCODE_PARAMETER && expr->ops[i].arg.ival == index) {
139 return true;
140 }
141 }
142
143 return false;
144}
145
148/* -------------------------------------------------------------------- */
153 const double *param_values,
154 int param_values_len,
155 double *r_result)
156{
157 *r_result = 0.0;
158
159 if (!BLI_expr_pylike_is_valid(expr)) {
160 return EXPR_PYLIKE_INVALID;
161 }
162
163#define FAIL_IF(condition) \
164 if (condition) { \
165 return EXPR_PYLIKE_FATAL_ERROR; \
166 } \
167 ((void)0)
168
169 /* Check the stack requirement is at least remotely sane and allocate on the actual stack. */
170 FAIL_IF(expr->max_stack <= 0 || expr->max_stack > 1000);
171
172 double *stack = BLI_array_alloca(stack, expr->max_stack);
173
174 /* Evaluate expression. */
175 ExprOp *ops = expr->ops;
176 int sp = 0, pc;
177
178 feclearexcept(FE_ALL_EXCEPT);
179
180 for (pc = 0; pc >= 0 && pc < expr->ops_count; pc++) {
181 switch (ops[pc].opcode) {
182 /* Arithmetic */
183 case OPCODE_CONST:
184 FAIL_IF(sp >= expr->max_stack);
185 stack[sp++] = ops[pc].arg.dval;
186 break;
187 case OPCODE_PARAMETER:
188 FAIL_IF(sp >= expr->max_stack || ops[pc].arg.ival >= param_values_len);
189 stack[sp++] = param_values[ops[pc].arg.ival];
190 break;
191 case OPCODE_FUNC1:
192 FAIL_IF(sp < 1);
193 stack[sp - 1] = ops[pc].arg.func1(stack[sp - 1]);
194 break;
195 case OPCODE_FUNC2:
196 FAIL_IF(sp < 2);
197 stack[sp - 2] = ops[pc].arg.func2(stack[sp - 2], stack[sp - 1]);
198 sp--;
199 break;
200 case OPCODE_FUNC3:
201 FAIL_IF(sp < 3);
202 stack[sp - 3] = ops[pc].arg.func3(stack[sp - 3], stack[sp - 2], stack[sp - 1]);
203 sp -= 2;
204 break;
205 case OPCODE_MIN:
206 FAIL_IF(sp < ops[pc].arg.ival);
207 for (int j = 1; j < ops[pc].arg.ival; j++, sp--) {
208 CLAMP_MAX(stack[sp - 2], stack[sp - 1]);
209 }
210 break;
211 case OPCODE_MAX:
212 FAIL_IF(sp < ops[pc].arg.ival);
213 for (int j = 1; j < ops[pc].arg.ival; j++, sp--) {
214 CLAMP_MIN(stack[sp - 2], stack[sp - 1]);
215 }
216 break;
217
218 /* Jumps */
219 case OPCODE_JMP:
220 pc += ops[pc].jmp_offset;
221 break;
222 case OPCODE_JMP_ELSE:
223 FAIL_IF(sp < 1);
224 if (!stack[--sp]) {
225 pc += ops[pc].jmp_offset;
226 }
227 break;
228 case OPCODE_JMP_OR:
229 case OPCODE_JMP_AND:
230 FAIL_IF(sp < 1);
231 if (!stack[sp - 1] == !(ops[pc].opcode == OPCODE_JMP_OR)) {
232 pc += ops[pc].jmp_offset;
233 }
234 else {
235 sp--;
236 }
237 break;
238
239 /* For chaining comparisons, i.e. "a < b < c" as "a < b and b < c" */
240 case OPCODE_CMP_CHAIN:
241 FAIL_IF(sp < 2);
242 /* If comparison fails, return 0 and jump to end. */
243 if (!ops[pc].arg.func2(stack[sp - 2], stack[sp - 1])) {
244 stack[sp - 2] = 0.0;
245 pc += ops[pc].jmp_offset;
246 }
247 /* Otherwise keep b on the stack and proceed. */
248 else {
249 stack[sp - 2] = stack[sp - 1];
250 }
251 sp--;
252 break;
253
254 default:
256 }
257 }
258
259 FAIL_IF(sp != 1 || pc != expr->ops_count);
260
261#undef FAIL_IF
262
263 *r_result = stack[0];
264
265 /* Detect floating point evaluation errors. */
266 int flags = fetestexcept(FE_DIVBYZERO | FE_INVALID);
267 if (flags) {
268 return (flags & FE_INVALID) ? EXPR_PYLIKE_MATH_ERROR : EXPR_PYLIKE_DIV_BY_ZERO;
269 }
270
271 return EXPR_PYLIKE_SUCCESS;
272}
273
276/* -------------------------------------------------------------------- */
280static double op_negate(double arg)
281{
282 return -arg;
283}
284
285static double op_mul(double a, double b)
286{
287 return a * b;
288}
289
290static double op_div(double a, double b)
291{
292 return a / b;
293}
294
295static double op_add(double a, double b)
296{
297 return a + b;
298}
299
300static double op_sub(double a, double b)
301{
302 return a - b;
303}
304
305static double op_radians(double arg)
306{
307 return arg * M_PI / 180.0;
308}
309
310static double op_degrees(double arg)
311{
312 return arg * 180.0 / M_PI;
313}
314
315static double op_log2(double a, double b)
316{
317 return log(a) / log(b);
318}
319
320static double op_lerp(double a, double b, double x)
321{
322 return a * (1.0 - x) + b * x;
323}
324
325static double op_clamp(double arg)
326{
327 CLAMP(arg, 0.0, 1.0);
328 return arg;
329}
330
331static double op_clamp3(double arg, double minv, double maxv)
332{
333 CLAMP(arg, minv, maxv);
334 return arg;
335}
336
337static double op_smoothstep(double a, double b, double x)
338{
339 double t = (x - a) / (b - a);
340 CLAMP(t, 0.0, 1.0);
341 return t * t * (3.0 - 2.0 * t);
342}
343
344static double op_not(double a)
345{
346 return a ? 0.0 : 1.0;
347}
348
349static double op_eq(double a, double b)
350{
351 return a == b ? 1.0 : 0.0;
352}
353
354static double op_ne(double a, double b)
355{
356 return a != b ? 1.0 : 0.0;
357}
358
359static double op_lt(double a, double b)
360{
361 return a < b ? 1.0 : 0.0;
362}
363
364static double op_le(double a, double b)
365{
366 return a <= b ? 1.0 : 0.0;
367}
368
369static double op_gt(double a, double b)
370{
371 return a > b ? 1.0 : 0.0;
372}
373
374static double op_ge(double a, double b)
375{
376 return a >= b ? 1.0 : 0.0;
377}
378
379typedef struct BuiltinConstDef {
380 const char *name;
381 double value;
383
385 {"pi", M_PI}, {"True", 1.0}, {"False", 0.0}, {NULL, 0.0}};
386
387typedef struct BuiltinOpDef {
388 const char *name;
390 void *funcptr;
392
393#ifdef _MSC_VER
394/* Prevent MSVC from inlining calls to ceil/floor so the table below can get a function pointer to
395 * them. */
396# pragma function(ceil)
397# pragma function(floor)
398#endif
399
401 {"radians", OPCODE_FUNC1, op_radians},
402 {"degrees", OPCODE_FUNC1, op_degrees},
403 {"abs", OPCODE_FUNC1, fabs},
404 {"fabs", OPCODE_FUNC1, fabs},
405 {"floor", OPCODE_FUNC1, floor},
406 {"ceil", OPCODE_FUNC1, ceil},
407 {"trunc", OPCODE_FUNC1, trunc},
408 {"round", OPCODE_FUNC1, round},
409 {"int", OPCODE_FUNC1, trunc},
410 {"sin", OPCODE_FUNC1, sin},
411 {"cos", OPCODE_FUNC1, cos},
412 {"tan", OPCODE_FUNC1, tan},
413 {"asin", OPCODE_FUNC1, asin},
414 {"acos", OPCODE_FUNC1, acos},
415 {"atan", OPCODE_FUNC1, atan},
416 {"atan2", OPCODE_FUNC2, atan2},
417 {"exp", OPCODE_FUNC1, exp},
418 {"log", OPCODE_FUNC1, log},
419 {"log", OPCODE_FUNC2, op_log2},
420 {"sqrt", OPCODE_FUNC1, sqrt},
421 {"pow", OPCODE_FUNC2, pow},
422 {"fmod", OPCODE_FUNC2, fmod},
423 {"lerp", OPCODE_FUNC3, op_lerp},
424 {"clamp", OPCODE_FUNC1, op_clamp},
425 {"clamp", OPCODE_FUNC3, op_clamp3},
426 {"smoothstep", OPCODE_FUNC3, op_smoothstep},
428};
429
432/* -------------------------------------------------------------------- */
436#define MAKE_CHAR2(a, b) (((a) << 8) | (b))
437
438#define CHECK_ERROR(condition) \
439 if (!(condition)) { \
440 return false; \
441 } \
442 ((void)0)
443
444/* For simplicity simple token types are represented by their own character;
445 * these are special identifiers for multi-character tokens. */
446#define TOKEN_ID MAKE_CHAR2('I', 'D')
447#define TOKEN_NUMBER MAKE_CHAR2('0', '0')
448#define TOKEN_GE MAKE_CHAR2('>', '=')
449#define TOKEN_LE MAKE_CHAR2('<', '=')
450#define TOKEN_NE MAKE_CHAR2('!', '=')
451#define TOKEN_EQ MAKE_CHAR2('=', '=')
452#define TOKEN_AND MAKE_CHAR2('A', 'N')
453#define TOKEN_OR MAKE_CHAR2('O', 'R')
454#define TOKEN_NOT MAKE_CHAR2('N', 'O')
455#define TOKEN_IF MAKE_CHAR2('I', 'F')
456#define TOKEN_ELSE MAKE_CHAR2('E', 'L')
457
458static const char *token_eq_characters = "!=><";
459static const char *token_characters = "~`!@#$%^&*+-=/\\?:;<>(){}[]|.,\"'";
460
461typedef struct KeywordTokenDef {
462 const char *name;
463 short token;
465
467 {"and", TOKEN_AND},
468 {"or", TOKEN_OR},
469 {"not", TOKEN_NOT},
470 {"if", TOKEN_IF},
471 {"else", TOKEN_ELSE},
472 {NULL, TOKEN_ID},
473};
474
475typedef struct ExprParseState {
477 const char **param_names;
478
479 /* Original expression */
480 const char *expr;
481 const char *cur;
482
483 /* Current token */
484 short token;
485 char *tokenbuf;
486 double tokenval;
487
488 /* Opcode buffer */
491
492 /* Stack space requirement tracking */
495
496/* Reserve space for the specified number of operations in the buffer. */
498{
499 if (state->ops_count + count > state->max_ops) {
500 state->max_ops = power_of_2_max_i(state->ops_count + count);
501 state->ops = MEM_reallocN(state->ops, state->max_ops * sizeof(ExprOp));
502 }
503
504 ExprOp *op = &state->ops[state->ops_count];
505 state->ops_count += count;
506 return op;
507}
508
509/* Add one operation and track stack usage. */
510static ExprOp *parse_add_op(ExprParseState *state, eOpCode code, int stack_delta)
511{
512 /* track evaluation stack depth */
513 state->stack_ptr += stack_delta;
514 CLAMP_MIN(state->stack_ptr, 0);
515 CLAMP_MIN(state->max_stack, state->stack_ptr);
516
517 /* allocate the new instruction */
518 ExprOp *op = parse_alloc_ops(state, 1);
519 memset(op, 0, sizeof(ExprOp));
520 op->opcode = code;
521 return op;
522}
523
524/* Add one jump operation and return an index for parse_set_jump. */
526{
527 parse_add_op(state, code, -1);
528 return state->last_jmp = state->ops_count;
529}
530
531/* Set the jump offset in a previously added jump operation. */
533{
534 state->last_jmp = state->ops_count;
535 state->ops[jump - 1].jmp_offset = state->ops_count - jump;
536}
537
538/* Returns the required argument count of the given function call code. */
539static int opcode_arg_count(eOpCode code)
540{
541 switch (code) {
542 case OPCODE_FUNC1:
543 return 1;
544 case OPCODE_FUNC2:
545 return 2;
546 case OPCODE_FUNC3:
547 return 3;
548 default:
549 BLI_assert_msg(0, "unexpected opcode");
550 return -1;
551 }
552}
553
554/* Add a function call operation, applying constant folding when possible. */
555static bool parse_add_func(ExprParseState *state, eOpCode code, int args, void *funcptr)
556{
557 ExprOp *prev_ops = &state->ops[state->ops_count];
558 int jmp_gap = state->ops_count - state->last_jmp;
559
560 feclearexcept(FE_ALL_EXCEPT);
561
562 switch (code) {
563 case OPCODE_FUNC1:
564 CHECK_ERROR(args == 1);
565
566 if (jmp_gap >= 1 && prev_ops[-1].opcode == OPCODE_CONST) {
567 UnaryOpFunc func = funcptr;
568
569 /* volatile because some compilers overly aggressive optimize this call out.
570 * see D6012 for details. */
571 volatile double result = func(prev_ops[-1].arg.dval);
572
573 if (fetestexcept(FE_DIVBYZERO | FE_INVALID) == 0) {
574 prev_ops[-1].arg.dval = result;
575 return true;
576 }
577 }
578 break;
579
580 case OPCODE_FUNC2:
581 CHECK_ERROR(args == 2);
582
583 if (jmp_gap >= 2 && prev_ops[-2].opcode == OPCODE_CONST &&
584 prev_ops[-1].opcode == OPCODE_CONST)
585 {
586 BinaryOpFunc func = funcptr;
587
588 /* volatile because some compilers overly aggressive optimize this call out.
589 * see D6012 for details. */
590 volatile double result = func(prev_ops[-2].arg.dval, prev_ops[-1].arg.dval);
591
592 if (fetestexcept(FE_DIVBYZERO | FE_INVALID) == 0) {
593 prev_ops[-2].arg.dval = result;
594 state->ops_count--;
595 state->stack_ptr--;
596 return true;
597 }
598 }
599 break;
600
601 case OPCODE_FUNC3:
602 CHECK_ERROR(args == 3);
603
604 if (jmp_gap >= 3 && prev_ops[-3].opcode == OPCODE_CONST &&
605 prev_ops[-2].opcode == OPCODE_CONST && prev_ops[-1].opcode == OPCODE_CONST)
606 {
607 TernaryOpFunc func = funcptr;
608
609 /* volatile because some compilers overly aggressive optimize this call out.
610 * see D6012 for details. */
611 volatile double result = func(
612 prev_ops[-3].arg.dval, prev_ops[-2].arg.dval, prev_ops[-1].arg.dval);
613
614 if (fetestexcept(FE_DIVBYZERO | FE_INVALID) == 0) {
615 prev_ops[-3].arg.dval = result;
616 state->ops_count -= 2;
617 state->stack_ptr -= 2;
618 return true;
619 }
620 }
621 break;
622
623 default:
624 BLI_assert(false);
625 return false;
626 }
627
628 parse_add_op(state, code, 1 - args)->arg.ptr = funcptr;
629 return true;
630}
631
632/* Extract the next token from raw characters. */
634{
635 /* Skip white-space. */
636 while (isspace(*state->cur)) {
637 state->cur++;
638 }
639
640 /* End of string. */
641 if (*state->cur == 0) {
642 state->token = 0;
643 return true;
644 }
645
646 /* Floating point numbers. */
647 if (isdigit(*state->cur) || (state->cur[0] == '.' && isdigit(state->cur[1]))) {
648 char *end, *out = state->tokenbuf;
649 bool is_float = false;
650
651 while (isdigit(*state->cur)) {
652 *out++ = *state->cur++;
653 }
654
655 if (*state->cur == '.') {
656 is_float = true;
657 *out++ = *state->cur++;
658
659 while (isdigit(*state->cur)) {
660 *out++ = *state->cur++;
661 }
662 }
663
664 if (ELEM(*state->cur, 'e', 'E')) {
665 is_float = true;
666 *out++ = *state->cur++;
667
668 if (ELEM(*state->cur, '+', '-')) {
669 *out++ = *state->cur++;
670 }
671
672 CHECK_ERROR(isdigit(*state->cur));
673
674 while (isdigit(*state->cur)) {
675 *out++ = *state->cur++;
676 }
677 }
678
679 *out = 0;
680
681 /* Forbid C-style octal constants. */
682 if (!is_float && state->tokenbuf[0] == '0') {
683 for (char *p = state->tokenbuf + 1; *p; p++) {
684 if (*p != '0') {
685 return false;
686 }
687 }
688 }
689
690 state->token = TOKEN_NUMBER;
691 state->tokenval = strtod(state->tokenbuf, &end);
692 return (end == out);
693 }
694
695 /* ?= tokens */
696 if (state->cur[1] == '=' && strchr(token_eq_characters, state->cur[0])) {
697 state->token = MAKE_CHAR2(state->cur[0], state->cur[1]);
698 state->cur += 2;
699 return true;
700 }
701
702 /* Special characters (single character tokens) */
703 if (strchr(token_characters, *state->cur)) {
704 state->token = *state->cur++;
705 return true;
706 }
707
708 /* Identifiers */
709 if (isalpha(*state->cur) || ELEM(*state->cur, '_')) {
710 char *out = state->tokenbuf;
711
712 while (isalnum(*state->cur) || ELEM(*state->cur, '_')) {
713 *out++ = *state->cur++;
714 }
715
716 *out = 0;
717
718 for (int i = 0; keyword_list[i].name; i++) {
719 if (STREQ(state->tokenbuf, keyword_list[i].name)) {
720 state->token = keyword_list[i].token;
721 return true;
722 }
723 }
724
725 state->token = TOKEN_ID;
726 return true;
727 }
728
729 return false;
730}
731
734/* -------------------------------------------------------------------- */
738static bool parse_expr(ExprParseState *state);
739
741{
742 if (!parse_next_token(state) || state->token != '(' || !parse_next_token(state)) {
743 return -1;
744 }
745
746 int arg_count = 0;
747
748 for (;;) {
749 if (!parse_expr(state)) {
750 return -1;
751 }
752
753 arg_count++;
754
755 switch (state->token) {
756 case ',':
757 if (!parse_next_token(state)) {
758 return -1;
759 }
760 break;
761
762 case ')':
763 if (!parse_next_token(state)) {
764 return -1;
765 }
766 return arg_count;
767
768 default:
769 return -1;
770 }
771 }
772}
773
775{
776 int i;
777
778 switch (state->token) {
779 case '+':
781
782 case '-':
785 return true;
786
787 case '(':
788 return parse_next_token(state) && parse_expr(state) && state->token == ')' &&
790
791 case TOKEN_NUMBER:
792 parse_add_op(state, OPCODE_CONST, 1)->arg.dval = state->tokenval;
793 return parse_next_token(state);
794
795 case TOKEN_ID:
796 /* Parameters: search in reverse order in case of duplicate names -
797 * the last one should win. */
798 for (i = state->param_names_len - 1; i >= 0; i--) {
799 if (STREQ(state->tokenbuf, state->param_names[i])) {
801 return parse_next_token(state);
802 }
803 }
804
805 /* Ordinary builtin constants. */
806 for (i = 0; builtin_consts[i].name; i++) {
807 if (STREQ(state->tokenbuf, builtin_consts[i].name)) {
809 return parse_next_token(state);
810 }
811 }
812
813 /* Ordinary builtin functions. */
814 for (i = 0; builtin_ops[i].name; i++) {
815 if (STREQ(state->tokenbuf, builtin_ops[i].name)) {
816 int args = parse_function_args(state);
817
818 /* Search for other arg count versions if necessary. */
819 if (args != opcode_arg_count(builtin_ops[i].op)) {
820 for (int j = i + 1; builtin_ops[j].name; j++) {
821 if (opcode_arg_count(builtin_ops[j].op) == args &&
822 STREQ(builtin_ops[j].name, builtin_ops[i].name))
823 {
824 i = j;
825 break;
826 }
827 }
828 }
829
830 return parse_add_func(state, builtin_ops[i].op, args, builtin_ops[i].funcptr);
831 }
832 }
833
834 /* Specially supported functions. */
835 if (STREQ(state->tokenbuf, "min")) {
837 CHECK_ERROR(count > 0);
838
840 return true;
841 }
842
843 if (STREQ(state->tokenbuf, "max")) {
845 CHECK_ERROR(count > 0);
846
848 return true;
849 }
850
851 return false;
852
853 default:
854 return false;
855 }
856}
857
859{
861
862 for (;;) {
863 switch (state->token) {
864 case '*':
867 break;
868
869 case '/':
872 break;
873
874 default:
875 return true;
876 }
877 }
878}
879
881{
883
884 for (;;) {
885 switch (state->token) {
886 case '+':
889 break;
890
891 case '-':
894 break;
895
896 default:
897 return true;
898 }
899 }
900}
901
903{
904 switch (token) {
905 case TOKEN_EQ:
906 return op_eq;
907 case TOKEN_NE:
908 return op_ne;
909 case '>':
910 return op_gt;
911 case TOKEN_GE:
912 return op_ge;
913 case '<':
914 return op_lt;
915 case TOKEN_LE:
916 return op_le;
917 default:
918 return NULL;
919 }
920}
921
923{
924 BinaryOpFunc next_func = parse_get_cmp_func(state->token);
925
926 if (next_func) {
927 parse_add_op(state, OPCODE_CMP_CHAIN, -1)->arg.func2 = cur_func;
928 int jump = state->last_jmp = state->ops_count;
929
931 CHECK_ERROR(parse_cmp_chain(state, next_func));
932
934 }
935 else {
936 parse_add_func(state, OPCODE_FUNC2, 2, cur_func);
937 }
938
939 return true;
940}
941
943{
945
947
948 if (func) {
950
951 return parse_cmp_chain(state, func);
952 }
953
954 return true;
955}
956
958{
959 if (state->token == TOKEN_NOT) {
962 return true;
963 }
964
965 return parse_cmp(state);
966}
967
969{
971
972 if (state->token == TOKEN_AND) {
974
976
978 }
979
980 return true;
981}
982
984{
986
987 if (state->token == TOKEN_OR) {
989
991
993 }
994
995 return true;
996}
997
999{
1000 /* Temporarily set the constant expression evaluation barrier */
1001 int prev_last_jmp = state->last_jmp;
1002 int start = state->last_jmp = state->ops_count;
1003
1005
1006 if (state->token == TOKEN_IF) {
1007 /* Ternary IF expression in python requires swapping the
1008 * main body with condition, so stash the body opcodes. */
1009 int size = state->ops_count - start;
1010 int bytes = size * sizeof(ExprOp);
1011
1012 ExprOp *body = MEM_mallocN(bytes, "driver if body");
1013 memcpy(body, state->ops + start, bytes);
1014
1015 state->last_jmp = state->ops_count = start;
1016 state->stack_ptr--;
1017
1018 /* Parse condition. */
1019 if (!parse_next_token(state) || !parse_or(state) || state->token != TOKEN_ELSE ||
1021 {
1022 MEM_freeN(body);
1023 return false;
1024 }
1025
1026 int jmp_else = parse_add_jump(state, OPCODE_JMP_ELSE);
1027
1028 /* Add body back. */
1029 memcpy(parse_alloc_ops(state, size), body, bytes);
1030 MEM_freeN(body);
1031
1032 state->stack_ptr++;
1033
1034 int jmp_end = parse_add_jump(state, OPCODE_JMP);
1035
1036 /* Parse the else block. */
1037 parse_set_jump(state, jmp_else);
1038
1040
1041 parse_set_jump(state, jmp_end);
1042 }
1043 /* If no actual jumps happened, restore previous barrier */
1044 else if (state->last_jmp == start) {
1045 state->last_jmp = prev_last_jmp;
1046 }
1047
1048 return true;
1049}
1050
1053/* -------------------------------------------------------------------- */
1058 const char **param_names,
1059 int param_names_len)
1060{
1061 /* Prepare the parser state. */
1063 memset(&state, 0, sizeof(state));
1064
1065 state.cur = state.expr = expression;
1066
1067 state.param_names_len = param_names_len;
1068 state.param_names = param_names;
1069
1070 state.tokenbuf = MEM_mallocN(strlen(expression) + 1, __func__);
1071
1072 state.max_ops = 16;
1073 state.ops = MEM_mallocN(state.max_ops * sizeof(ExprOp), __func__);
1074
1075 /* Parse the expression. */
1076 ExprPyLike_Parsed *expr;
1077
1078 if (parse_next_token(&state) && parse_expr(&state) && state.token == 0) {
1079 BLI_assert(state.stack_ptr == 1);
1080
1081 int bytesize = sizeof(ExprPyLike_Parsed) + state.ops_count * sizeof(ExprOp);
1082
1083 expr = MEM_mallocN(bytesize, "ExprPyLike_Parsed");
1084 expr->ops_count = state.ops_count;
1085 expr->max_stack = state.max_stack;
1086
1087 memcpy(expr->ops, state.ops, state.ops_count * sizeof(ExprOp));
1088 }
1089 else {
1090 /* Always return a non-NULL object so that parse failure can be cached. */
1091 expr = MEM_callocN(sizeof(ExprPyLike_Parsed), "ExprPyLike_Parsed(empty)");
1092 }
1093
1094 MEM_freeN(state.tokenbuf);
1095 MEM_freeN(state.ops);
1096 return expr;
1097}
1098
#define BLI_array_alloca(arr, realsize)
Definition BLI_alloca.h:25
#define BLI_assert(a)
Definition BLI_assert.h:50
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:57
eExprPyLike_EvalStatus
@ EXPR_PYLIKE_FATAL_ERROR
@ EXPR_PYLIKE_SUCCESS
@ EXPR_PYLIKE_DIV_BY_ZERO
@ EXPR_PYLIKE_MATH_ERROR
@ EXPR_PYLIKE_INVALID
struct ExprPyLike_Parsed ExprPyLike_Parsed
MINLINE int power_of_2_max_i(int n)
#define M_PI
#define CLAMP(a, b, c)
#define CLAMP_MAX(a, c)
#define ELEM(...)
#define STREQ(a, b)
#define CLAMP_MIN(a, b)
typedef double(DMatrix)[4][4]
Read Guarded memory(de)allocation.
#define MEM_reallocN(vmemh, len)
void jump(const btVector3 &v=btVector3(0, 0, 0))
local_group_size(16, 16) .push_constant(Type b
pow(value.r - subtrahend, 2.0)") .do_static_compilation(true)
#define NULL
static double op_negate(double arg)
static bool parse_next_token(ExprParseState *state)
static double op_clamp3(double arg, double minv, double maxv)
struct KeywordTokenDef KeywordTokenDef
static int parse_add_jump(ExprParseState *state, eOpCode code)
struct ExprOp ExprOp
double(* TernaryOpFunc)(double, double, double)
static double op_lt(double a, double b)
static int parse_function_args(ExprParseState *state)
static BinaryOpFunc parse_get_cmp_func(short token)
static bool parse_mul(ExprParseState *state)
static double op_smoothstep(double a, double b, double x)
static double op_mul(double a, double b)
static BuiltinOpDef builtin_ops[]
#define MAKE_CHAR2(a, b)
static bool parse_and(ExprParseState *state)
void BLI_expr_pylike_free(ExprPyLike_Parsed *expr)
static bool parse_expr(ExprParseState *state)
#define TOKEN_ID
static const char * token_characters
eExprPyLike_EvalStatus BLI_expr_pylike_eval(ExprPyLike_Parsed *expr, const double *param_values, int param_values_len, double *r_result)
static bool parse_add(ExprParseState *state)
static double op_gt(double a, double b)
#define FAIL_IF(condition)
struct ExprParseState ExprParseState
struct BuiltinOpDef BuiltinOpDef
#define TOKEN_GE
static double op_ne(double a, double b)
#define TOKEN_NOT
#define CHECK_ERROR(condition)
static BuiltinConstDef builtin_consts[]
static double op_div(double a, double b)
static double op_eq(double a, double b)
#define TOKEN_ELSE
static void parse_set_jump(ExprParseState *state, int jump)
#define TOKEN_OR
bool BLI_expr_pylike_is_constant(const ExprPyLike_Parsed *expr)
static double op_lerp(double a, double b, double x)
static ExprOp * parse_add_op(ExprParseState *state, eOpCode code, int stack_delta)
@ OPCODE_FUNC3
@ OPCODE_FUNC2
@ OPCODE_JMP
@ OPCODE_PARAMETER
@ OPCODE_CMP_CHAIN
@ OPCODE_CONST
@ OPCODE_FUNC1
@ OPCODE_JMP_OR
@ OPCODE_JMP_ELSE
@ OPCODE_MAX
@ OPCODE_MIN
@ OPCODE_JMP_AND
static bool parse_add_func(ExprParseState *state, eOpCode code, int args, void *funcptr)
static bool parse_unary(ExprParseState *state)
static bool parse_not(ExprParseState *state)
static double op_le(double a, double b)
static const char * token_eq_characters
double(* BinaryOpFunc)(double, double)
#define TOKEN_LE
double(* UnaryOpFunc)(double)
static double op_ge(double a, double b)
static double op_add(double a, double b)
static double op_not(double a)
bool BLI_expr_pylike_is_using_param(const ExprPyLike_Parsed *expr, int index)
bool BLI_expr_pylike_is_valid(const ExprPyLike_Parsed *expr)
struct BuiltinConstDef BuiltinConstDef
static double op_sub(double a, double b)
ExprPyLike_Parsed * BLI_expr_pylike_parse(const char *expression, const char **param_names, int param_names_len)
#define TOKEN_IF
static double op_degrees(double arg)
static double op_log2(double a, double b)
#define TOKEN_EQ
#define TOKEN_AND
static bool parse_cmp_chain(ExprParseState *state, BinaryOpFunc cur_func)
static KeywordTokenDef keyword_list[]
static double op_clamp(double arg)
static ExprOp * parse_alloc_ops(ExprParseState *state, int count)
static double op_radians(double arg)
static bool parse_cmp(ExprParseState *state)
static bool parse_or(ExprParseState *state)
#define TOKEN_NUMBER
#define TOKEN_NE
static int opcode_arg_count(eOpCode code)
int count
void *(* MEM_mallocN)(size_t len, const char *str)
Definition mallocn.cc:44
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
void *(* MEM_callocN)(size_t len, const char *str)
Definition mallocn.cc:42
ccl_device_inline float2 fmod(const float2 a, const float b)
ccl_device_inline float2 floor(const float2 a)
ccl_device_inline float2 fabs(const float2 a)
ccl_device_inline float3 exp(float3 v)
ccl_device_inline float3 ceil(const float3 a)
ccl_device_inline float3 cos(float3 v)
ccl_device_inline float3 log(float3 v)
static ulong state[N]
const char * name
UnaryOpFunc func1
eOpCode opcode
double dval
BinaryOpFunc func2
union ExprOp::@118 arg
TernaryOpFunc func3
const char * expr
const char ** param_names