Blender V5.0
expr_pylike_eval.cc
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
28
29#include <cctype>
30#include <cfenv>
31#include <cmath>
32#include <cstddef>
33#include <cstdlib>
34#include <cstring>
35#include <variant>
36
37#include "MEM_guardedalloc.h"
38
39#include "BLI_alloca.h"
41#include "BLI_math_base.h"
42#include "BLI_utildefines.h"
43#include "BLI_vector.hh"
44
45#ifdef _MSC_VER
46# pragma fenv_access(on)
47#endif
48
49/* -------------------------------------------------------------------- */
52
79
80using UnaryOpFunc = double (*)(double);
81using BinaryOpFunc = double (*)(double, double);
82using TernaryOpFunc = double (*)(double, double, double);
83
98
103
105
106/* -------------------------------------------------------------------- */
109
111{
112 MEM_delete(expr);
113}
114
116{
117 return expr != nullptr && expr->ops.size() > 0;
118}
119
121{
122 return expr != nullptr && expr->ops.size() == 1 && expr->ops[0].opcode == OPCODE_CONST;
123}
124
126{
127 int i;
128
129 if (expr == nullptr) {
130 return false;
131 }
132
133 for (i = 0; i < expr->ops.size(); i++) {
134 if (expr->ops[i].opcode == OPCODE_PARAMETER && expr->ops[i].arg.ival == index) {
135 return true;
136 }
137 }
138
139 return false;
140}
141
143
144/* -------------------------------------------------------------------- */
147
149 const double *param_values,
150 int param_values_len,
151 double *r_result)
152{
153 *r_result = 0.0;
154
155 if (!BLI_expr_pylike_is_valid(expr)) {
156 return EXPR_PYLIKE_INVALID;
157 }
158
159#define FAIL_IF(condition) \
160 if (condition) { \
161 return EXPR_PYLIKE_FATAL_ERROR; \
162 } \
163 ((void)0)
164
165 /* Check the stack requirement is at least remotely sane and allocate on the actual stack. */
166 FAIL_IF(expr->max_stack <= 0 || expr->max_stack > 1000);
167
168 double *stack = BLI_array_alloca(stack, expr->max_stack);
169
170 /* Evaluate expression. */
171 ExprOp *ops = expr->ops.data();
172 int sp = 0, pc;
173
174 feclearexcept(FE_ALL_EXCEPT);
175
176 for (pc = 0; pc >= 0 && pc < expr->ops.size(); pc++) {
177 switch (ops[pc].opcode) {
178 /* Arithmetic */
179 case OPCODE_CONST:
180 FAIL_IF(sp >= expr->max_stack);
181 stack[sp++] = ops[pc].arg.dval;
182 break;
183 case OPCODE_PARAMETER:
184 FAIL_IF(sp >= expr->max_stack || ops[pc].arg.ival >= param_values_len);
185 stack[sp++] = param_values[ops[pc].arg.ival];
186 break;
187 case OPCODE_FUNC1:
188 FAIL_IF(sp < 1);
189 stack[sp - 1] = ops[pc].arg.func1(stack[sp - 1]);
190 break;
191 case OPCODE_FUNC2:
192 FAIL_IF(sp < 2);
193 stack[sp - 2] = ops[pc].arg.func2(stack[sp - 2], stack[sp - 1]);
194 sp--;
195 break;
196 case OPCODE_FUNC3:
197 FAIL_IF(sp < 3);
198 stack[sp - 3] = ops[pc].arg.func3(stack[sp - 3], stack[sp - 2], stack[sp - 1]);
199 sp -= 2;
200 break;
201 case OPCODE_MIN:
202 FAIL_IF(sp < ops[pc].arg.ival);
203 for (int j = 1; j < ops[pc].arg.ival; j++, sp--) {
204 CLAMP_MAX(stack[sp - 2], stack[sp - 1]);
205 }
206 break;
207 case OPCODE_MAX:
208 FAIL_IF(sp < ops[pc].arg.ival);
209 for (int j = 1; j < ops[pc].arg.ival; j++, sp--) {
210 CLAMP_MIN(stack[sp - 2], stack[sp - 1]);
211 }
212 break;
213
214 /* Jumps */
215 case OPCODE_JMP:
216 pc += ops[pc].jmp_offset;
217 break;
218 case OPCODE_JMP_ELSE:
219 FAIL_IF(sp < 1);
220 if (!stack[--sp]) {
221 pc += ops[pc].jmp_offset;
222 }
223 break;
224 case OPCODE_JMP_OR:
225 case OPCODE_JMP_AND:
226 FAIL_IF(sp < 1);
227 if (!stack[sp - 1] == !(ops[pc].opcode == OPCODE_JMP_OR)) {
228 pc += ops[pc].jmp_offset;
229 }
230 else {
231 sp--;
232 }
233 break;
234
235 /* For chaining comparisons, i.e. "a < b < c" as "a < b and b < c" */
236 case OPCODE_CMP_CHAIN:
237 FAIL_IF(sp < 2);
238 /* If comparison fails, return 0 and jump to end. */
239 if (!ops[pc].arg.func2(stack[sp - 2], stack[sp - 1])) {
240 stack[sp - 2] = 0.0;
241 pc += ops[pc].jmp_offset;
242 }
243 /* Otherwise keep b on the stack and proceed. */
244 else {
245 stack[sp - 2] = stack[sp - 1];
246 }
247 sp--;
248 break;
249
250 default:
252 }
253 }
254
255 FAIL_IF(sp != 1 || pc != expr->ops.size());
256
257#undef FAIL_IF
258
259 *r_result = stack[0];
260
261 /* Detect floating point evaluation errors. */
262 int flags = fetestexcept(FE_DIVBYZERO | FE_INVALID);
263 if (flags) {
264 return (flags & FE_INVALID) ? EXPR_PYLIKE_MATH_ERROR : EXPR_PYLIKE_DIV_BY_ZERO;
265 }
266
267 return EXPR_PYLIKE_SUCCESS;
268}
269
271
272/* -------------------------------------------------------------------- */
275
276static double op_negate(double arg)
277{
278 return -arg;
279}
280
281static double op_mul(double a, double b)
282{
283 return a * b;
284}
285
286static double op_div(double a, double b)
287{
288 return a / b;
289}
290
291static double op_add(double a, double b)
292{
293 return a + b;
294}
295
296static double op_sub(double a, double b)
297{
298 return a - b;
299}
300
301static double op_radians(double arg)
302{
303 return arg * M_PI / 180.0;
304}
305
306static double op_degrees(double arg)
307{
308 return arg * 180.0 / M_PI;
309}
310
311static double op_log2(double a, double b)
312{
313 return log(a) / log(b);
314}
315
316static double op_lerp(double a, double b, double x)
317{
318 return a * (1.0 - x) + b * x;
319}
320
321static double op_clamp(double arg)
322{
323 CLAMP(arg, 0.0, 1.0);
324 return arg;
325}
326
327static double op_clamp3(double arg, double minv, double maxv)
328{
329 CLAMP(arg, minv, maxv);
330 return arg;
331}
332
333static double op_smoothstep(double a, double b, double x)
334{
335 double t = (x - a) / (b - a);
336 CLAMP(t, 0.0, 1.0);
337 return t * t * (3.0 - 2.0 * t);
338}
339
340static double op_not(double a)
341{
342 return a ? 0.0 : 1.0;
343}
344
345static double op_eq(double a, double b)
346{
347 return a == b ? 1.0 : 0.0;
348}
349
350static double op_ne(double a, double b)
351{
352 return a != b ? 1.0 : 0.0;
353}
354
355static double op_lt(double a, double b)
356{
357 return a < b ? 1.0 : 0.0;
358}
359
360static double op_le(double a, double b)
361{
362 return a <= b ? 1.0 : 0.0;
363}
364
365static double op_gt(double a, double b)
366{
367 return a > b ? 1.0 : 0.0;
368}
369
370static double op_ge(double a, double b)
371{
372 return a >= b ? 1.0 : 0.0;
373}
374
376 const char *name;
377 double value;
378};
379
381 {"pi", M_PI}, {"True", 1.0}, {"False", 0.0}, {nullptr, 0.0}};
382
384 const char *name;
385 std::variant<UnaryOpFunc, BinaryOpFunc, TernaryOpFunc> funcptr;
386
387 /* Returns the required argument count of the given function call code. */
389 {
390 if (std::holds_alternative<UnaryOpFunc>(funcptr)) {
391 return 1;
392 }
393 if (std::holds_alternative<BinaryOpFunc>(funcptr)) {
394 return 2;
395 }
396 if (std::holds_alternative<TernaryOpFunc>(funcptr)) {
397 return 3;
398 }
399
400 BLI_assert_msg(0, "unexpected function pointer");
401 return -1;
402 }
403};
404
405#ifdef _MSC_VER
406/* Prevent MSVC from inlining calls to ceil/floor so the table below can get a function pointer to
407 * them. */
408# pragma function(ceil)
409# pragma function(floor)
410#endif
411
413 {"radians", UnaryOpFunc(op_radians)},
414 {"degrees", UnaryOpFunc(op_degrees)},
415 {"abs", UnaryOpFunc(fabs)},
416 {"fabs", UnaryOpFunc(fabs)},
417 {"floor", UnaryOpFunc(floor)},
418 {"ceil", UnaryOpFunc(ceil)},
419 {"trunc", UnaryOpFunc(trunc)},
420 {"round", UnaryOpFunc(round)},
421 {"int", UnaryOpFunc(trunc)},
422 {"sin", UnaryOpFunc(sin)},
423 {"cos", UnaryOpFunc(cos)},
424 {"tan", UnaryOpFunc(tan)},
425 {"asin", UnaryOpFunc(asin)},
426 {"acos", UnaryOpFunc(acos)},
427 {"atan", UnaryOpFunc(atan)},
428 {"atan2", BinaryOpFunc(atan2)},
429 {"exp", UnaryOpFunc(exp)},
430 {"log", UnaryOpFunc(log)},
431 {"log", BinaryOpFunc(op_log2)},
432 {"sqrt", UnaryOpFunc(sqrt)},
433 {"pow", BinaryOpFunc(pow)},
434 {"fmod", BinaryOpFunc(fmod)},
435 {"lerp", TernaryOpFunc(op_lerp)},
436 {"clamp", UnaryOpFunc(op_clamp)},
437 {"clamp", TernaryOpFunc(op_clamp3)},
438 {"smoothstep", TernaryOpFunc(op_smoothstep)},
439 {nullptr},
440};
441
443
444/* -------------------------------------------------------------------- */
447
448#define MAKE_CHAR2(a, b) (((a) << 8) | (b))
449
450#define CHECK_ERROR(condition) \
451 if (!(condition)) { \
452 return false; \
453 } \
454 ((void)0)
455
456/* For simplicity simple token types are represented by their own character;
457 * these are special identifiers for multi-character tokens. */
458#define TOKEN_ID MAKE_CHAR2('I', 'D')
459#define TOKEN_NUMBER MAKE_CHAR2('0', '0')
460#define TOKEN_GE MAKE_CHAR2('>', '=')
461#define TOKEN_LE MAKE_CHAR2('<', '=')
462#define TOKEN_NE MAKE_CHAR2('!', '=')
463#define TOKEN_EQ MAKE_CHAR2('=', '=')
464#define TOKEN_AND MAKE_CHAR2('A', 'N')
465#define TOKEN_OR MAKE_CHAR2('O', 'R')
466#define TOKEN_NOT MAKE_CHAR2('N', 'O')
467#define TOKEN_IF MAKE_CHAR2('I', 'F')
468#define TOKEN_ELSE MAKE_CHAR2('E', 'L')
469
470static const char *token_eq_characters = "!=><";
471static const char *token_characters = "~`!@#$%^&*+-=/\\?:;<>(){}[]|.,\"'";
472
474 const char *name;
475 short token;
476};
477
479 {"and", TOKEN_AND},
480 {"or", TOKEN_OR},
481 {"not", TOKEN_NOT},
482 {"if", TOKEN_IF},
483 {"else", TOKEN_ELSE},
484 {nullptr, TOKEN_ID},
485};
486
489 const char **param_names = nullptr;
490
491 /* Original expression */
492 const char *expr = nullptr;
493 const char *cur = nullptr;
494
495 /* Current token */
496 short token = 0;
498 double tokenval = 0.0;
499
500 /* Opcode buffer */
501 int last_jmp = 0;
503
504 /* Stack space requirement tracking */
505 int stack_ptr = 0;
506 int max_stack = 0;
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{code};
519 state->ops.append(op);
520 return &state->ops.last();
521}
522
523/* Add one jump operation and return an index for parse_set_jump. */
525{
526 parse_add_op(state, code, -1);
527 return state->last_jmp = state->ops.size();
528}
529
530/* Set the jump offset in a previously added jump operation. */
532{
533 state->last_jmp = state->ops.size();
534 state->ops[jump - 1].jmp_offset = state->ops.size() - jump;
535}
536
537/* Add a function call operation, applying constant folding when possible. */
539 std::variant<UnaryOpFunc, BinaryOpFunc, TernaryOpFunc> funcptr)
540{
541 ExprOp *prev_ops = state->ops.end();
542 int jmp_gap = state->ops.size() - state->last_jmp;
543
544 feclearexcept(FE_ALL_EXCEPT);
545
546 if (std::holds_alternative<UnaryOpFunc>(funcptr)) {
547 UnaryOpFunc func = std::get<UnaryOpFunc>(funcptr);
548
549 if (jmp_gap >= 1 && prev_ops[-1].opcode == OPCODE_CONST) {
550 /* volatile because some compilers overly aggressive optimize this call out.
551 * see D6012 for details. */
552 volatile double result = func(prev_ops[-1].arg.dval);
553
554 if (fetestexcept(FE_DIVBYZERO | FE_INVALID) == 0) {
555 prev_ops[-1].arg.dval = result;
556 return true;
557 }
558 }
559
561 }
562 else if (std::holds_alternative<BinaryOpFunc>(funcptr)) {
563 BinaryOpFunc func = std::get<BinaryOpFunc>(funcptr);
564
565 if (jmp_gap >= 2 && prev_ops[-2].opcode == OPCODE_CONST && prev_ops[-1].opcode == OPCODE_CONST)
566 {
567 /* volatile because some compilers overly aggressive optimize this call out.
568 * see D6012 for details. */
569 volatile double result = func(prev_ops[-2].arg.dval, prev_ops[-1].arg.dval);
570
571 if (fetestexcept(FE_DIVBYZERO | FE_INVALID) == 0) {
572 prev_ops[-2].arg.dval = result;
573 state->ops.resize(state->ops.size() - 1);
574 state->stack_ptr--;
575 return true;
576 }
577 }
578
580 }
581 else if (std::holds_alternative<TernaryOpFunc>(funcptr)) {
582 TernaryOpFunc func = std::get<TernaryOpFunc>(funcptr);
583
584 if (jmp_gap >= 3 && prev_ops[-3].opcode == OPCODE_CONST &&
585 prev_ops[-2].opcode == OPCODE_CONST && prev_ops[-1].opcode == OPCODE_CONST)
586 {
587 /* volatile because some compilers overly aggressive optimize this call out.
588 * see D6012 for details. */
589 volatile double result = func(
590 prev_ops[-3].arg.dval, prev_ops[-2].arg.dval, prev_ops[-1].arg.dval);
591
592 if (fetestexcept(FE_DIVBYZERO | FE_INVALID) == 0) {
593 prev_ops[-3].arg.dval = result;
594 state->ops.resize(state->ops.size() - 2);
595 state->stack_ptr -= 2;
596 return true;
597 }
598 }
599
601 }
602 else {
603 BLI_assert(false);
604 return false;
605 }
606
607 return true;
608}
609
610/* Extract the next token from raw characters. */
612{
613 /* Skip white-space. */
614 while (isspace(*state->cur)) {
615 state->cur++;
616 }
617
618 /* End of string. */
619 if (*state->cur == 0) {
620 state->token = 0;
621 return true;
622 }
623
624 /* Floating point numbers. */
625 if (isdigit(*state->cur) || (state->cur[0] == '.' && isdigit(state->cur[1]))) {
626 char *end, *out = state->tokenbuf.data();
627 bool is_float = false;
628
629 while (isdigit(*state->cur)) {
630 *out++ = *state->cur++;
631 }
632
633 if (*state->cur == '.') {
634 is_float = true;
635 *out++ = *state->cur++;
636
637 while (isdigit(*state->cur)) {
638 *out++ = *state->cur++;
639 }
640 }
641
642 if (ELEM(*state->cur, 'e', 'E')) {
643 is_float = true;
644 *out++ = *state->cur++;
645
646 if (ELEM(*state->cur, '+', '-')) {
647 *out++ = *state->cur++;
648 }
649
650 CHECK_ERROR(isdigit(*state->cur));
651
652 while (isdigit(*state->cur)) {
653 *out++ = *state->cur++;
654 }
655 }
656
657 *out = 0;
658
659 /* Forbid C-style octal constants. */
660 if (!is_float && state->tokenbuf[0] == '0') {
661 for (char *p = state->tokenbuf.data() + 1; *p; p++) {
662 if (*p != '0') {
663 return false;
664 }
665 }
666 }
667
668 state->token = TOKEN_NUMBER;
669 state->tokenval = strtod(state->tokenbuf.data(), &end);
670 return (end == out);
671 }
672
673 /* ?= tokens */
674 if (state->cur[1] == '=' && strchr(token_eq_characters, state->cur[0])) {
675 state->token = MAKE_CHAR2(state->cur[0], state->cur[1]);
676 state->cur += 2;
677 return true;
678 }
679
680 /* Special characters (single character tokens) */
681 if (strchr(token_characters, *state->cur)) {
682 state->token = *state->cur++;
683 return true;
684 }
685
686 /* Identifiers */
687 if (isalpha(*state->cur) || ELEM(*state->cur, '_')) {
688 char *out = state->tokenbuf.data();
689
690 while (isalnum(*state->cur) || ELEM(*state->cur, '_')) {
691 *out++ = *state->cur++;
692 }
693
694 *out = 0;
695
696 for (int i = 0; keyword_list[i].name; i++) {
697 if (STREQ(state->tokenbuf.data(), keyword_list[i].name)) {
698 state->token = keyword_list[i].token;
699 return true;
700 }
701 }
702
703 state->token = TOKEN_ID;
704 return true;
705 }
706
707 return false;
708}
709
711
712/* -------------------------------------------------------------------- */
715
716static bool parse_expr(ExprParseState *state);
717
719{
720 if (!parse_next_token(state) || state->token != '(' || !parse_next_token(state)) {
721 return -1;
722 }
723
724 int arg_count = 0;
725
726 for (;;) {
727 if (!parse_expr(state)) {
728 return -1;
729 }
730
731 arg_count++;
732
733 switch (state->token) {
734 case ',':
735 if (!parse_next_token(state)) {
736 return -1;
737 }
738 break;
739
740 case ')':
741 if (!parse_next_token(state)) {
742 return -1;
743 }
744 return arg_count;
745
746 default:
747 return -1;
748 }
749 }
750}
751
753{
754 int i;
755
756 switch (state->token) {
757 case '+':
759
760 case '-':
763 return true;
764
765 case '(':
766 return parse_next_token(state) && parse_expr(state) && state->token == ')' &&
768
769 case TOKEN_NUMBER:
770 parse_add_op(state, OPCODE_CONST, 1)->arg.dval = state->tokenval;
771 return parse_next_token(state);
772
773 case TOKEN_ID:
774 /* Parameters: search in reverse order in case of duplicate names -
775 * the last one should win. */
776 for (i = state->param_names_len - 1; i >= 0; i--) {
777 if (STREQ(state->tokenbuf.data(), state->param_names[i])) {
779 return parse_next_token(state);
780 }
781 }
782
783 /* Ordinary builtin constants. */
784 for (i = 0; builtin_consts[i].name; i++) {
785 if (STREQ(state->tokenbuf.data(), builtin_consts[i].name)) {
787 return parse_next_token(state);
788 }
789 }
790
791 /* Ordinary builtin functions. */
792 for (i = 0; builtin_ops[i].name; i++) {
793 if (STREQ(state->tokenbuf.data(), builtin_ops[i].name)) {
794 int args = parse_function_args(state);
795
796 /* Search for other arg count versions if necessary. */
797 if (args != builtin_ops[i].arg_count()) {
798 for (int j = i + 1; builtin_ops[j].name; j++) {
799 if (builtin_ops[j].arg_count() == args &&
801 {
802 i = j;
803 break;
804 }
805 }
806 }
807
808 CHECK_ERROR(builtin_ops[i].name && builtin_ops[i].arg_count() == args);
809
810 return parse_add_func(state, builtin_ops[i].funcptr);
811 }
812 }
813
814 /* Specially supported functions. */
815 if (STREQ(state->tokenbuf.data(), "min")) {
817 CHECK_ERROR(count > 0);
818
820 return true;
821 }
822
823 if (STREQ(state->tokenbuf.data(), "max")) {
825 CHECK_ERROR(count > 0);
826
828 return true;
829 }
830
831 return false;
832
833 default:
834 return false;
835 }
836}
837
839{
841
842 for (;;) {
843 switch (state->token) {
844 case '*':
847 break;
848
849 case '/':
852 break;
853
854 default:
855 return true;
856 }
857 }
858}
859
861{
863
864 for (;;) {
865 switch (state->token) {
866 case '+':
869 break;
870
871 case '-':
874 break;
875
876 default:
877 return true;
878 }
879 }
880}
881
883{
884 switch (token) {
885 case TOKEN_EQ:
886 return op_eq;
887 case TOKEN_NE:
888 return op_ne;
889 case '>':
890 return op_gt;
891 case TOKEN_GE:
892 return op_ge;
893 case '<':
894 return op_lt;
895 case TOKEN_LE:
896 return op_le;
897 default:
898 return nullptr;
899 }
900}
901
903{
904 BinaryOpFunc next_func = parse_get_cmp_func(state->token);
905
906 if (next_func) {
907 parse_add_op(state, OPCODE_CMP_CHAIN, -1)->arg.func2 = cur_func;
908 int jump = state->last_jmp = state->ops.size();
909
911 CHECK_ERROR(parse_cmp_chain(state, next_func));
912
914 }
915 else {
916 parse_add_func(state, cur_func);
917 }
918
919 return true;
920}
921
923{
925
927
928 if (func) {
930
931 return parse_cmp_chain(state, func);
932 }
933
934 return true;
935}
936
938{
939 if (state->token == TOKEN_NOT) {
942 return true;
943 }
944
945 return parse_cmp(state);
946}
947
949{
951
952 if (state->token == TOKEN_AND) {
954
956
958 }
959
960 return true;
961}
962
964{
966
967 if (state->token == TOKEN_OR) {
969
971
973 }
974
975 return true;
976}
977
979{
980 /* Temporarily set the constant expression evaluation barrier */
981 int prev_last_jmp = state->last_jmp;
982 int start = state->last_jmp = state->ops.size();
983
985
986 if (state->token == TOKEN_IF) {
987 /* Ternary IF expression in python requires swapping the
988 * main body with condition, so stash the body opcodes. */
989 const int size = state->ops.size() - start;
990
992 std::copy_n(state->ops.data() + start, size, body.data());
993
994 state->ops.resize(start);
995 state->last_jmp = start;
996 state->stack_ptr--;
997
998 /* Parse condition. */
999 if (!parse_next_token(state) || !parse_or(state) || state->token != TOKEN_ELSE ||
1001 {
1002 return false;
1003 }
1004
1005 int jmp_else = parse_add_jump(state, OPCODE_JMP_ELSE);
1006
1007 /* Add body back. */
1008 const size_t body_offset = state->ops.size();
1009 state->ops.resize(body_offset + size);
1010 std::copy_n(body.data(), size, state->ops.data() + body_offset);
1011 body.clear_and_shrink();
1012
1013 state->stack_ptr++;
1014
1015 int jmp_end = parse_add_jump(state, OPCODE_JMP);
1016
1017 /* Parse the `else` block. */
1018 parse_set_jump(state, jmp_else);
1019
1021
1022 parse_set_jump(state, jmp_end);
1023 }
1024 /* If no actual jumps happened, restore previous barrier */
1025 else if (state->last_jmp == start) {
1026 state->last_jmp = prev_last_jmp;
1027 }
1028
1029 return true;
1030}
1031
1033
1034/* -------------------------------------------------------------------- */
1037
1039 const char **param_names,
1040 int param_names_len)
1041{
1042 /* Prepare the parser state. */
1044
1045 state.cur = state.expr = expression;
1046
1047 state.param_names_len = param_names_len;
1048 state.param_names = param_names;
1049
1050 state.tokenbuf.resize(strlen(expression) + 1);
1051
1052 /* Parse the expression. */
1053 ExprPyLike_Parsed *expr = MEM_new<ExprPyLike_Parsed>("ExprPyLike_Parsed(empty)");
1054
1055 if (parse_next_token(&state) && parse_expr(&state) && state.token == 0) {
1056 BLI_assert(state.stack_ptr == 1);
1057
1058 expr->max_stack = state.max_stack;
1059 expr->ops = std::move(state.ops);
1060 }
1061 else {
1062 /* Always return a non-nullptr object so that parse failure can be cached. */
1063 }
1064
1065 return expr;
1066}
1067
#define BLI_array_alloca(arr, realsize)
Definition BLI_alloca.h:18
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
eExprPyLike_EvalStatus
@ EXPR_PYLIKE_FATAL_ERROR
@ EXPR_PYLIKE_SUCCESS
@ EXPR_PYLIKE_DIV_BY_ZERO
@ EXPR_PYLIKE_MATH_ERROR
@ EXPR_PYLIKE_INVALID
#define M_PI
#define CLAMP(a, b, c)
#define CLAMP_MAX(a, c)
#define ELEM(...)
#define STREQ(a, b)
#define CLAMP_MIN(a, b)
Read Guarded memory(de)allocation.
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
void jump(const btVector3 &v=btVector3(0, 0, 0))
int64_t size() const
T * data()
void clear_and_shrink()
double(*)(double, double, double) TernaryOpFunc
static double op_negate(double arg)
static bool parse_next_token(ExprParseState *state)
static double op_clamp3(double arg, double minv, double maxv)
static int parse_add_jump(ExprParseState *state, eOpCode code)
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_add_func(ExprParseState *state, std::variant< UnaryOpFunc, BinaryOpFunc, TernaryOpFunc > funcptr)
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)
#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_unary(ExprParseState *state)
static bool parse_not(ExprParseState *state)
static double op_le(double a, double b)
static const char * token_eq_characters
#define TOKEN_LE
static double op_ge(double a, double b)
double(*)(double, double) BinaryOpFunc
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)
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)
double(*)(double) UnaryOpFunc
static double op_radians(double arg)
static bool parse_cmp(ExprParseState *state)
static bool parse_or(ExprParseState *state)
#define TOKEN_NUMBER
#define TOKEN_NE
#define out
#define atan
#define tan
#define log
#define sin
#define round
#define pow
#define exp
#define cos
#define trunc
#define floor
#define ceil
#define sqrt
#define asin
#define acos
int count
ccl_device_inline float2 fmod(const float2 a, const float b)
ccl_device_inline float2 fabs(const float2 a)
ccl_device_inline float3 atan2(const float3 y, const float3 x)
static ulong state[N]
const char * name
std::variant< UnaryOpFunc, BinaryOpFunc, TernaryOpFunc > funcptr
const char * name
UnaryOpFunc func1
union ExprOp::@022142020106211234351036361136331370136343206132 arg
eOpCode opcode
BinaryOpFunc func2
TernaryOpFunc func3
blender::Vector< ExprOp > ops
blender::Vector< char > tokenbuf
const char ** param_names
blender::Vector< ExprOp > ops
i
Definition text_draw.cc:230