| 768 | /* ── Expression tree helpers ────────────────────────────────────── */ |
| 769 | |
| 770 | static void expr_free(cbm_expr_t *e) { |
| 771 | enum { EXPR_FREE_STACK = 128 }; |
| 772 | cbm_expr_t *stack[EXPR_FREE_STACK]; |
| 773 | int top = 0; |
| 774 | if (e) { |
| 775 | stack[top++] = e; |
| 776 | } |
| 777 | while (top > 0) { |
| 778 | cbm_expr_t *cur = stack[--top]; |
| 779 | if (cur->type == EXPR_CONDITION) { |
| 780 | safe_str_free(&cur->cond.variable); |
| 781 | safe_str_free(&cur->cond.property); |
| 782 | safe_str_free(&cur->cond.op); |
| 783 | safe_str_free(&cur->cond.value); |
| 784 | safe_str_free(&cur->cond.coalesce_default); |
| 785 | for (int i = 0; i < cur->cond.in_value_count; i++) { |
| 786 | safe_str_free(&cur->cond.in_values[i]); |
| 787 | } |
| 788 | free(cur->cond.in_values); |
| 789 | safe_str_free(&cur->cond.func); |
| 790 | for (int i = 0; i < cur->cond.arg_count; i++) { |
| 791 | safe_str_free(&cur->cond.args[i].variable); |
| 792 | safe_str_free(&cur->cond.args[i].property); |
| 793 | safe_str_free(&cur->cond.args[i].literal); |
| 794 | } |
| 795 | free(cur->cond.args); |
| 796 | } |
| 797 | if (cur->right) { |
| 798 | if (top < EXPR_FREE_STACK) { |
| 799 | stack[top++] = cur->right; |
| 800 | } else { |
| 801 | expr_free(cur->right); /* recurse when stack overflows */ |
| 802 | } |
| 803 | } |
| 804 | if (cur->left) { |
| 805 | if (top < EXPR_FREE_STACK) { |
| 806 | stack[top++] = cur->left; |
| 807 | } else { |
| 808 | expr_free(cur->left); /* recurse when stack overflows */ |
| 809 | } |
| 810 | } |
| 811 | free(cur); |
| 812 | } |
| 813 | } |
| 814 | |
| 815 | static cbm_expr_t *expr_leaf(cbm_condition_t c) { |
| 816 | cbm_expr_t *e = calloc(CBM_ALLOC_ONE, sizeof(cbm_expr_t)); |
no test coverage detected