PyDecisionGraph 0.3.2.post1__cp315-cp315t-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (148) hide show
  1. decision_graph/__infra__.pxd +1 -0
  2. decision_graph/__init__.pxd +1 -0
  3. decision_graph/__init__.py +39 -0
  4. decision_graph/decision_tree/__init__.pxd +2 -0
  5. decision_graph/decision_tree/__init__.py +85 -0
  6. decision_graph/decision_tree/bake/__infra__.pxd +502 -0
  7. decision_graph/decision_tree/bake/__init__.pxd +502 -0
  8. decision_graph/decision_tree/bake/__init__.py +29 -0
  9. decision_graph/decision_tree/bake/c_action.cp315t-win_amd64.pyd +0 -0
  10. decision_graph/decision_tree/bake/c_action.h +324 -0
  11. decision_graph/decision_tree/bake/c_action.pxd +54 -0
  12. decision_graph/decision_tree/bake/c_action.pyi +126 -0
  13. decision_graph/decision_tree/bake/c_action.pyx +65 -0
  14. decision_graph/decision_tree/bake/c_allocator_protocol.cp315t-win_amd64.pyd +0 -0
  15. decision_graph/decision_tree/bake/c_allocator_protocol.pxd +24 -0
  16. decision_graph/decision_tree/bake/c_allocator_protocol.pyi +61 -0
  17. decision_graph/decision_tree/bake/c_allocator_protocol.pyx +109 -0
  18. decision_graph/decision_tree/bake/c_bake.cp315t-win_amd64.pyd +0 -0
  19. decision_graph/decision_tree/bake/c_bake.h +629 -0
  20. decision_graph/decision_tree/bake/c_bake.pxd +52 -0
  21. decision_graph/decision_tree/bake/c_bake.pyi +151 -0
  22. decision_graph/decision_tree/bake/c_bake.pyx +142 -0
  23. decision_graph/decision_tree/bake/c_collections.cp315t-win_amd64.pyd +0 -0
  24. decision_graph/decision_tree/bake/c_collections.h +692 -0
  25. decision_graph/decision_tree/bake/c_collections.pxd +52 -0
  26. decision_graph/decision_tree/bake/c_collections.pyi +193 -0
  27. decision_graph/decision_tree/bake/c_collections.pyx +161 -0
  28. decision_graph/decision_tree/bake/c_const.cp315t-win_amd64.pyd +0 -0
  29. decision_graph/decision_tree/bake/c_const.h +395 -0
  30. decision_graph/decision_tree/bake/c_const.pxd +56 -0
  31. decision_graph/decision_tree/bake/c_const.pyi +305 -0
  32. decision_graph/decision_tree/bake/c_const.pyx +278 -0
  33. decision_graph/decision_tree/bake/c_edge.cp315t-win_amd64.pyd +0 -0
  34. decision_graph/decision_tree/bake/c_edge.h +511 -0
  35. decision_graph/decision_tree/bake/c_edge.pxd +117 -0
  36. decision_graph/decision_tree/bake/c_edge.pyi +192 -0
  37. decision_graph/decision_tree/bake/c_edge.pyx +270 -0
  38. decision_graph/decision_tree/bake/c_eval.h +848 -0
  39. decision_graph/decision_tree/bake/c_eval.pxd +40 -0
  40. decision_graph/decision_tree/bake/c_expr.cp315t-win_amd64.pyd +0 -0
  41. decision_graph/decision_tree/bake/c_expr.h +1900 -0
  42. decision_graph/decision_tree/bake/c_expr.pxd +129 -0
  43. decision_graph/decision_tree/bake/c_expr.pyi +280 -0
  44. decision_graph/decision_tree/bake/c_expr.pyx +272 -0
  45. decision_graph/decision_tree/bake/c_hierarchy.cp315t-win_amd64.pyd +0 -0
  46. decision_graph/decision_tree/bake/c_hierarchy.h +731 -0
  47. decision_graph/decision_tree/bake/c_hierarchy.pxd +71 -0
  48. decision_graph/decision_tree/bake/c_hierarchy.pyi +409 -0
  49. decision_graph/decision_tree/bake/c_hierarchy.pyx +277 -0
  50. decision_graph/decision_tree/bake/c_logic_group.cp315t-win_amd64.pyd +0 -0
  51. decision_graph/decision_tree/bake/c_logic_group.h +1226 -0
  52. decision_graph/decision_tree/bake/c_logic_group.pxd +162 -0
  53. decision_graph/decision_tree/bake/c_logic_group.pyi +264 -0
  54. decision_graph/decision_tree/bake/c_logic_group.pyx +308 -0
  55. decision_graph/decision_tree/bake/c_node.cp315t-win_amd64.pyd +0 -0
  56. decision_graph/decision_tree/bake/c_node.h +2660 -0
  57. decision_graph/decision_tree/bake/c_node.pxd +381 -0
  58. decision_graph/decision_tree/bake/c_node.pyi +438 -0
  59. decision_graph/decision_tree/bake/c_node.pyx +639 -0
  60. decision_graph/decision_tree/bake/c_reconstruct.cp315t-win_amd64.pyd +0 -0
  61. decision_graph/decision_tree/bake/c_reconstruct.pxd +63 -0
  62. decision_graph/decision_tree/bake/c_reconstruct.pyi +60 -0
  63. decision_graph/decision_tree/bake/c_reconstruct.pyx +252 -0
  64. decision_graph/decision_tree/bake/c_var.cp315t-win_amd64.pyd +0 -0
  65. decision_graph/decision_tree/bake/c_var.h +2054 -0
  66. decision_graph/decision_tree/bake/c_var.pxd +205 -0
  67. decision_graph/decision_tree/bake/c_var.pyi +271 -0
  68. decision_graph/decision_tree/bake/c_var.pyx +228 -0
  69. decision_graph/decision_tree/capi/__infra__.pxd +33 -0
  70. decision_graph/decision_tree/capi/__init__.pxd +33 -0
  71. decision_graph/decision_tree/capi/__init__.py +57 -0
  72. decision_graph/decision_tree/capi/c_abc.cp315t-win_amd64.pyd +0 -0
  73. decision_graph/decision_tree/capi/c_abc.pxd +246 -0
  74. decision_graph/decision_tree/capi/c_abc.pyi +770 -0
  75. decision_graph/decision_tree/capi/c_abc.pyx +1532 -0
  76. decision_graph/decision_tree/capi/c_collection.cp315t-win_amd64.pyd +0 -0
  77. decision_graph/decision_tree/capi/c_collection.pxd +22 -0
  78. decision_graph/decision_tree/capi/c_collection.pyi +245 -0
  79. decision_graph/decision_tree/capi/c_collection.pyx +138 -0
  80. decision_graph/decision_tree/capi/c_node.cp315t-win_amd64.pyd +0 -0
  81. decision_graph/decision_tree/capi/c_node.pxd +95 -0
  82. decision_graph/decision_tree/capi/c_node.pyi +492 -0
  83. decision_graph/decision_tree/capi/c_node.pyx +551 -0
  84. decision_graph/decision_tree/exc.py +189 -0
  85. decision_graph/decision_tree/native/__init__.py +57 -0
  86. decision_graph/decision_tree/native/abc.py +1167 -0
  87. decision_graph/decision_tree/native/collection.py +134 -0
  88. decision_graph/decision_tree/native/node.py +521 -0
  89. decision_graph/includes/decision_graph/decision_tree/bake/c_action.c +16320 -0
  90. decision_graph/includes/decision_graph/decision_tree/bake/c_action.h +324 -0
  91. decision_graph/includes/decision_graph/decision_tree/bake/c_allocator_protocol.c +12473 -0
  92. decision_graph/includes/decision_graph/decision_tree/bake/c_bake.c +16485 -0
  93. decision_graph/includes/decision_graph/decision_tree/bake/c_bake.h +629 -0
  94. decision_graph/includes/decision_graph/decision_tree/bake/c_collections.c +17381 -0
  95. decision_graph/includes/decision_graph/decision_tree/bake/c_collections.h +692 -0
  96. decision_graph/includes/decision_graph/decision_tree/bake/c_const.c +20815 -0
  97. decision_graph/includes/decision_graph/decision_tree/bake/c_const.h +395 -0
  98. decision_graph/includes/decision_graph/decision_tree/bake/c_edge.c +20696 -0
  99. decision_graph/includes/decision_graph/decision_tree/bake/c_edge.h +511 -0
  100. decision_graph/includes/decision_graph/decision_tree/bake/c_eval.h +848 -0
  101. decision_graph/includes/decision_graph/decision_tree/bake/c_expr.c +22065 -0
  102. decision_graph/includes/decision_graph/decision_tree/bake/c_expr.h +1900 -0
  103. decision_graph/includes/decision_graph/decision_tree/bake/c_hierarchy.c +23267 -0
  104. decision_graph/includes/decision_graph/decision_tree/bake/c_hierarchy.h +731 -0
  105. decision_graph/includes/decision_graph/decision_tree/bake/c_logic_group.c +22434 -0
  106. decision_graph/includes/decision_graph/decision_tree/bake/c_logic_group.h +1226 -0
  107. decision_graph/includes/decision_graph/decision_tree/bake/c_node.c +28385 -0
  108. decision_graph/includes/decision_graph/decision_tree/bake/c_node.h +2660 -0
  109. decision_graph/includes/decision_graph/decision_tree/bake/c_reconstruct.c +15955 -0
  110. decision_graph/includes/decision_graph/decision_tree/bake/c_var.c +16881 -0
  111. decision_graph/includes/decision_graph/decision_tree/bake/c_var.h +2054 -0
  112. decision_graph/includes/decision_graph/decision_tree/capi/c_abc.c +59196 -0
  113. decision_graph/includes/decision_graph/decision_tree/capi/c_collection.c +19967 -0
  114. decision_graph/includes/decision_graph/decision_tree/capi/c_node.c +39229 -0
  115. decision_graph/logic_group/__init__.py +25 -0
  116. decision_graph/logic_group/base.py +100 -0
  117. decision_graph/logic_group/pending_request.py +384 -0
  118. decision_graph/webui/__init__.py +24 -0
  119. decision_graph/webui/bake/__init__.py +65 -0
  120. decision_graph/webui/bake/app.py +488 -0
  121. decision_graph/webui/bake/static/d3.v7.min.js +2 -0
  122. decision_graph/webui/bake/static/script.js +1440 -0
  123. decision_graph/webui/bake/static/style.css +920 -0
  124. decision_graph/webui/bake/templates/index.html +183 -0
  125. decision_graph/webui/bake/templates/offline.html +184 -0
  126. decision_graph/webui/capi/__init__.py +54 -0
  127. decision_graph/webui/capi/app.py +366 -0
  128. decision_graph/webui/capi/main.py +53 -0
  129. decision_graph/webui/capi/static/d3.v7.min.js +2 -0
  130. decision_graph/webui/capi/static/script.js +1026 -0
  131. decision_graph/webui/capi/static/style.css +656 -0
  132. decision_graph/webui/capi/templates/index.html +93 -0
  133. decision_graph/webui/capi/templates/offline.html +94 -0
  134. decision_graph/webui/native/__init__.py +54 -0
  135. decision_graph/webui/native/app.py +366 -0
  136. decision_graph/webui/native/main.py +53 -0
  137. decision_graph/webui/native/static/d3.v7.min.js +2 -0
  138. decision_graph/webui/native/static/script.js +1026 -0
  139. decision_graph/webui/native/static/style.css +656 -0
  140. decision_graph/webui/native/templates/index.html +93 -0
  141. decision_graph/webui/native/templates/offline.html +94 -0
  142. pydecisiongraph-0.3.2.post1.dist-info/METADATA +136 -0
  143. pydecisiongraph-0.3.2.post1.dist-info/RECORD +148 -0
  144. pydecisiongraph-0.3.2.post1.dist-info/WHEEL +5 -0
  145. pydecisiongraph-0.3.2.post1.dist-info/licenses/LICENSE +373 -0
  146. pydecisiongraph-0.3.2.post1.dist-info/top_level.txt +2 -0
  147. tests/bake_c_evaluator.cp315t-win_amd64.pyd +0 -0
  148. tests/bake_eval_probe.cp315t-win_amd64.pyd +0 -0
@@ -0,0 +1,1900 @@
1
+ #ifndef C_DCG_BAKE_EXPR_H
2
+ #define C_DCG_BAKE_EXPR_H
3
+
4
+ #include <math.h>
5
+ #include <stdbool.h>
6
+ #include <stddef.h>
7
+ #include <stdint.h>
8
+ #include <stdio.h>
9
+ #include <string.h>
10
+
11
+ #include <cbase/allocator_protocol/c_allocator_protocol.h>
12
+
13
+ #include <decision_graph/decision_tree/bake/c_node.h>
14
+
15
+ /*
16
+ * The operator family: a node that computes a value from the values of other
17
+ * nodes. The operands are the interesting part - they are not child edges, they
18
+ * are VALUE SLOTS inside the expression, so evaluating one is a loop over a flat
19
+ * array rather than a walk of the graph:
20
+ *
21
+ * - an operand bound to another node is a REFERENCE to that node's out slot,
22
+ * read when the expression is evaluated (see c_dcg_var_init_ref), so an
23
+ * operand always sees what its input holds at that moment;
24
+ * - an operand bound to a constant is the value itself, folded in at bake
25
+ * time, because a literal needs no indirection to be read.
26
+ *
27
+ * Strings are the one thing a folded operand owns: the copy is nested under the
28
+ * expression, so freeing the expression releases it. References own nothing.
29
+ */
30
+
31
+ // ========== Constants ==========
32
+
33
+ /** Operand slots a call node is allocated with when the count is not given. */
34
+ #ifndef DCG_EXPR_DEFAULT_ARGS
35
+ #define DCG_EXPR_DEFAULT_ARGS 2U
36
+ #endif
37
+
38
+ /**
39
+ * @brief Operator payload of an operator node.
40
+ *
41
+ * The family nibble mirrors the capi operator classes one-to-one
42
+ * (arithmetic / comparison / logical / access), so a Python operator maps
43
+ * straight onto an op code at bake time. Comparison and logical values are
44
+ * deliberately identical to the capi int_enum values, which is what lets
45
+ * the Cython layer cast instead of translate.
46
+ */
47
+ typedef enum dcg_op_code {
48
+ DCG_OP_NONE = 0x0000, // No operator (non-operator nodes).
49
+ // === Arithmetic (capi MathExpressionOperator) ===
50
+ DCG_OP_ARITH = 0x0100, // <- Mask and Generics
51
+ DCG_OP_ADD = 0x0101, // a + b
52
+ DCG_OP_SUB = 0x0102, // a - b
53
+ DCG_OP_MUL = 0x0103, // a * b
54
+ DCG_OP_DIV = 0x0104, // a / b
55
+ DCG_OP_FLOORDIV = 0x0105, // a // b
56
+ DCG_OP_POW = 0x0106, // a ** b
57
+ DCG_OP_NEG = 0x0107, // -a (unary)
58
+ // === Comparison (capi ComparisonExpressionOperator int_enum) ===
59
+ DCG_OP_COMPARE = 0x0200, // <- Mask and Generics
60
+ DCG_OP_EQ = 0x0201, // a == b
61
+ DCG_OP_NE = 0x0202, // a != b
62
+ DCG_OP_GT = 0x0203, // a > b
63
+ DCG_OP_GE = 0x0204, // a >= b
64
+ DCG_OP_LT = 0x0205, // a < b
65
+ DCG_OP_LE = 0x0206, // a <= b
66
+ // === Logical (capi LogicalExpressionOperator int_enum) ===
67
+ DCG_OP_LOGIC = 0x0300, // <- Mask and Generics
68
+ DCG_OP_AND = 0x0301, // a and b
69
+ DCG_OP_OR = 0x0302, // a or b
70
+ DCG_OP_NOT = 0x0303, // not a (unary)
71
+ // === Access (capi Attr / Getter expressions) ===
72
+ DCG_OP_ACCESS = 0x0400, // <- Mask and Generics
73
+ DCG_OP_ATTR = 0x0401, // a.attr
74
+ DCG_OP_GETITEM = 0x0402 // a[key]
75
+ } dcg_op_code;
76
+
77
+ /**
78
+ * @brief Bit masks over an operator code: family nibble and variant number.
79
+ */
80
+ typedef enum dcg_op_mask {
81
+ DCG_OP_FAMILY_MASK = 0x0F00, // Extracts the family nibble of a dcg_op_code.
82
+ DCG_OP_VARIANT_MASK = 0x00FF // Extracts the variant number of a dcg_op_code.
83
+ } dcg_op_mask;
84
+
85
+ /*
86
+ * The shape of the flat rule list (DCG_EXPR_EVAL_FNS): one slot per family, and
87
+ * one family's worth of slots for the variants. Operator codes number their
88
+ * variants from 1, so a family needs a slot for the family itself (variant 0)
89
+ * and room for the highest variant it writes - 7, which is arithmetic's NEG.
90
+ */
91
+ #ifndef DCG_OP_FAMILIES
92
+ #define DCG_OP_FAMILIES 5U
93
+ #endif
94
+
95
+ #ifndef DCG_OP_FAMILY_SLOTS
96
+ #define DCG_OP_FAMILY_SLOTS 8U
97
+ #endif
98
+
99
+ #if (DCG_OP_NEG & DCG_OP_VARIANT_MASK) >= DCG_OP_FAMILY_SLOTS
100
+ #error "DCG_OP_FAMILY_SLOTS must cover the operator variants (see DCG_OP_NEG)"
101
+ #endif
102
+
103
+ #if ((DCG_OP_ACCESS & DCG_OP_FAMILY_MASK) >> 8) >= DCG_OP_FAMILIES
104
+ #error "DCG_OP_FAMILIES must cover the operator families (see DCG_OP_ACCESS)"
105
+ #endif
106
+
107
+ // ========== Structs ==========
108
+
109
+ /**
110
+ * @brief Applying one operator: the arithmetic of a single op, in the one shape
111
+ * they all share.
112
+ *
113
+ * This is the KERNEL, not the rule. It takes the operands and produces the
114
+ * value, and its callers are the operator's own evaluation
115
+ * (c_dcg_node_expr_eval_<op>) and the two runtime dispatchers a caller with a
116
+ * code and no node reaches (c_dcg_node_expr_apply_unary /
117
+ * c_dcg_node_expr_apply_binary). Which kernel applies an operator is settled
118
+ * where the operator is known, never searched for later - see
119
+ * c_dcg_node_expr_set_op and c_dcg_node_expr_apply_fn_of.
120
+ *
121
+ * `b` is the second operand for a binary and untouched for a unary - the two
122
+ * arities share one signature so one kernel serves either, and a unary kernel is
123
+ * the only thing that may ignore it.
124
+ *
125
+ * @param out Receives the value.
126
+ * @param a Left operand (the only operand for a unary).
127
+ * @param b Right operand, or NULL to a unary kernel.
128
+ * @return DCG_OK, or a DCG_ERR_* code.
129
+ */
130
+ typedef int (*dcg_expr_apply_fn)(dcg_var_t* out, const dcg_var_t* a, const dcg_var_t* b);
131
+
132
+ /**
133
+ * @brief An expression node: the base node plus its operator and its operands.
134
+ *
135
+ * The operands are VALUES, not child nodes - they sit inline in the block,
136
+ * which is what makes evaluating an expression a loop over a flat array rather
137
+ * than a walk of the graph. A graph keeps its edges for control flow; an
138
+ * expression keeps its operands here.
139
+ *
140
+ * `args` is a flexible array member, so the block is allocated with exactly as
141
+ * many operands as it needs and the node is never an embedded field. Each slot is
142
+ * filled by binding an input node (c_dcg_node_expr_bind): a reference to the
143
+ * input's out slot, or the folded value of a constant input; a string value
144
+ * folded in is a copy nested under the node, and a reference owns nothing.
145
+ *
146
+ * At RUN time those slots are a WORKSPACE: the walk evaluates each component,
147
+ * dereferences what it reads as, and writes the result over the slot before the
148
+ * operator is applied. What a binding left there is overwritten on every
149
+ * evaluation, so nothing may read the slots as a record of what the node was
150
+ * built from - the components array is that.
151
+ *
152
+ * `components` is the node's hold on the operands it was given, one per slot and
153
+ * one reference each - no exceptions, so there is no case to reason about and
154
+ * the array answers "what was this operand made from?" for every slot alike.
155
+ * `c_dcg_node_expr_bind` takes the reference and teardown gives it back
156
+ * (c_ap_decref), so a caller that lets go of an operand it bound leaves the
157
+ * expression working, and one that bound a live read leaves it something to ask
158
+ * when the entry's type arrives. The array is n_args entries - exactly as many
159
+ * as there are slots, which is why there is no capacity field - and it sits in
160
+ * the node's OWN block, right behind the operand slots, so the node has no
161
+ * separate block to release and the array goes where the node goes.
162
+ *
163
+ * That is the C half of the hold. The Python wrapper keeps the other half, on
164
+ * the wrapper objects rather than on the blocks, and both are needed: this one
165
+ * keeps the BLOCK alive, the wrapper's keeps the WRAPPER alive.
166
+ *
167
+ * The repr is composed when the node is built, from the operator and the texts of
168
+ * the nodes it was given: "-12" for a negation of 12, "12 - 3" for a difference,
169
+ * "my_call(a, b)" for a call. Both renderings stay available afterwards -
170
+ * c_dcg_node_expr_op_style() and c_dcg_node_expr_func_style() - which is what the
171
+ * capi calls its op-style and func-style repr.
172
+ *
173
+ * The base node must stay the FIRST member: a dcg_expression_node* is therefore
174
+ * a valid dcg_node*.
175
+ */
176
+ typedef struct dcg_expression_node {
177
+ dcg_node base; // The common node header. Must stay first.
178
+ dcg_op_code op; // The operator applied to the operands.
179
+ size_t n_args; // Number of operands in `args`, and in `components`.
180
+ dcg_node** components; // Operand pointers, n_args of them (see below).
181
+ dcg_var_t args[]; // Operand slots, n_args of them, then the pointers.
182
+ } dcg_expression_node;
183
+
184
+ /*
185
+ * Where `components` points: the operand pointers are the second of the two
186
+ * arrays the block is sized for, immediately behind the slots. Two arrays and
187
+ * one allocation, because a flexible array member has to be last - and because
188
+ * an array of pointers needs no more alignment than a var slot provides, the
189
+ * slots being sizeof(dcg_var_t) apart.
190
+ */
191
+ static inline dcg_node** c_dcg_node_expr_components(dcg_expression_node* node) {
192
+ return (dcg_node**) (node->args + node->n_args);
193
+ }
194
+
195
+ // ========== Forward Declarations ==========
196
+
197
+ // Lifecycle
198
+ static inline dcg_expression_node* c_dcg_node_new_expr(size_t n_args, dcg_node_type ntype, allocator_protocol* allocator);
199
+ static inline void c_dcg_node_free_expr(dcg_expression_node* node);
200
+
201
+ // Typed constructors (the inputs are bound as operands, in order)
202
+ static inline dcg_expression_node* c_dcg_node_new_expr_unary(dcg_op_code op, dcg_node* src, allocator_protocol* allocator);
203
+ static inline dcg_expression_node* c_dcg_node_new_expr_binary(dcg_op_code op, dcg_node* var_0, dcg_node* var_1, allocator_protocol* allocator);
204
+ static inline dcg_expression_node* c_dcg_node_new_expr_ternary(dcg_op_code op, dcg_node* var_0, dcg_node* var_1, dcg_node* var_2, allocator_protocol* allocator);
205
+ static inline dcg_expression_node* c_dcg_node_new_expr_call(dcg_op_code op, dcg_node** vars, size_t n_vars, const char* repr, allocator_protocol* allocator);
206
+
207
+ // Operands
208
+ static inline int c_dcg_node_expr_bind(dcg_expression_node* node, size_t index, dcg_node* input);
209
+
210
+ // Evaluation - what this family's nodes evaluate to
211
+ static inline int c_dcg_node_expr_set_op(dcg_expression_node* node, dcg_op_code op);
212
+ static inline size_t c_dcg_op_code_index(dcg_op_code op);
213
+ static inline dcg_expr_apply_fn c_dcg_node_expr_apply_fn_of(dcg_node_type ntype, dcg_op_code op);
214
+ static inline int c_dcg_node_expr_apply_unary(dcg_var_t* out, dcg_op_code op, const dcg_var_t* a);
215
+ static inline int c_dcg_node_expr_apply_binary(dcg_var_t* out, dcg_op_code op, const dcg_var_t* a, const dcg_var_t* b);
216
+
217
+ // Running a node - the protocol entry the operand step drives (defined in c_eval.h,
218
+ // which is above this family: the declaration is what makes the cycle safe)
219
+ static inline int c_dcg_node_eval(dcg_node* node);
220
+
221
+ // Operands at run time - the workspace, filled from this node's own components
222
+ static inline int c_dcg_node_expr_eval_operand(dcg_expression_node* node, size_t index);
223
+
224
+ // Applying - the arithmetic of ONE operator, one kernel each
225
+ static inline int c_dcg_node_expr_apply_refuse(dcg_var_t* out, const dcg_var_t* a, const dcg_var_t* b);
226
+ static inline int c_dcg_node_expr_apply_neg(dcg_var_t* out, const dcg_var_t* a, const dcg_var_t* b);
227
+ static inline int c_dcg_node_expr_apply_not(dcg_var_t* out, const dcg_var_t* a, const dcg_var_t* b);
228
+ static inline int c_dcg_node_expr_apply_add(dcg_var_t* out, const dcg_var_t* a, const dcg_var_t* b);
229
+ static inline int c_dcg_node_expr_apply_sub(dcg_var_t* out, const dcg_var_t* a, const dcg_var_t* b);
230
+ static inline int c_dcg_node_expr_apply_mul(dcg_var_t* out, const dcg_var_t* a, const dcg_var_t* b);
231
+ static inline int c_dcg_node_expr_apply_div(dcg_var_t* out, const dcg_var_t* a, const dcg_var_t* b);
232
+ static inline int c_dcg_node_expr_apply_floordiv(dcg_var_t* out, const dcg_var_t* a, const dcg_var_t* b);
233
+ static inline int c_dcg_node_expr_apply_pow(dcg_var_t* out, const dcg_var_t* a, const dcg_var_t* b);
234
+ static inline int c_dcg_node_expr_apply_eq(dcg_var_t* out, const dcg_var_t* a, const dcg_var_t* b);
235
+ static inline int c_dcg_node_expr_apply_ne(dcg_var_t* out, const dcg_var_t* a, const dcg_var_t* b);
236
+ static inline int c_dcg_node_expr_apply_gt(dcg_var_t* out, const dcg_var_t* a, const dcg_var_t* b);
237
+ static inline int c_dcg_node_expr_apply_ge(dcg_var_t* out, const dcg_var_t* a, const dcg_var_t* b);
238
+ static inline int c_dcg_node_expr_apply_lt(dcg_var_t* out, const dcg_var_t* a, const dcg_var_t* b);
239
+ static inline int c_dcg_node_expr_apply_le(dcg_var_t* out, const dcg_var_t* a, const dcg_var_t* b);
240
+ static inline int c_dcg_node_expr_apply_and(dcg_var_t* out, const dcg_var_t* a, const dcg_var_t* b);
241
+ static inline int c_dcg_node_expr_apply_or(dcg_var_t* out, const dcg_var_t* a, const dcg_var_t* b);
242
+
243
+ // Evaluating - the rule of ONE operator, one function each (the flat list of
244
+ // them is DCG_EXPR_EVAL_FNS), and the entry that finds one from the code
245
+ static inline dcg_node_hook_fn c_dcg_node_expr_eval_of_arity(dcg_node_type ntype);
246
+ static inline int c_dcg_node_expr_eval(dcg_node* node, void* user_data);
247
+ static inline int c_dcg_node_expr_eval_refuse(dcg_node* node, void* user_data);
248
+ static inline int c_dcg_node_expr_eval_unknown(dcg_node* node, void* user_data);
249
+ static inline int c_dcg_node_expr_eval_ternary(dcg_node* node, void* user_data);
250
+ static inline int c_dcg_node_expr_eval_call(dcg_node* node, void* user_data);
251
+ static inline int c_dcg_node_expr_eval_neg(dcg_node* node, void* user_data);
252
+ static inline int c_dcg_node_expr_eval_not(dcg_node* node, void* user_data);
253
+ static inline int c_dcg_node_expr_eval_add(dcg_node* node, void* user_data);
254
+ static inline int c_dcg_node_expr_eval_sub(dcg_node* node, void* user_data);
255
+ static inline int c_dcg_node_expr_eval_mul(dcg_node* node, void* user_data);
256
+ static inline int c_dcg_node_expr_eval_div(dcg_node* node, void* user_data);
257
+ static inline int c_dcg_node_expr_eval_floordiv(dcg_node* node, void* user_data);
258
+ static inline int c_dcg_node_expr_eval_pow(dcg_node* node, void* user_data);
259
+ static inline int c_dcg_node_expr_eval_eq(dcg_node* node, void* user_data);
260
+ static inline int c_dcg_node_expr_eval_ne(dcg_node* node, void* user_data);
261
+ static inline int c_dcg_node_expr_eval_gt(dcg_node* node, void* user_data);
262
+ static inline int c_dcg_node_expr_eval_ge(dcg_node* node, void* user_data);
263
+ static inline int c_dcg_node_expr_eval_lt(dcg_node* node, void* user_data);
264
+ static inline int c_dcg_node_expr_eval_le(dcg_node* node, void* user_data);
265
+ static inline int c_dcg_node_expr_eval_and(dcg_node* node, void* user_data);
266
+ static inline int c_dcg_node_expr_eval_or(dcg_node* node, void* user_data);
267
+
268
+ // Composing the repr
269
+ static inline int c_dcg_node_expr_alias(const dcg_node* input, char* out, size_t cap);
270
+ static inline int c_dcg_node_expr_op_style(dcg_node* const* inputs, size_t n_inputs, dcg_op_code op, char* out, size_t cap);
271
+ static inline int c_dcg_node_expr_func_style(dcg_node* const* inputs, size_t n_inputs, dcg_op_code op, const char* name, char* out, size_t cap);
272
+
273
+ /*
274
+ * The protocol, for ONE thing only: an expression's operands are other NODES, so
275
+ * producing their values means running them, and running a node is the
276
+ * evaluator's (c_dcg_node_eval). This family owns the operand loop - the slots
277
+ * and the pointers are its own fields - and the protocol owns what running a node
278
+ * means.
279
+ *
280
+ * c_eval.h includes every family, so this is a cycle in the include graph. It is
281
+ * a CONTAINED one - the protocol needs nothing from this family but the entry
282
+ * below, and this header needs nothing from the protocol but c_dcg_node_eval -
283
+ * and it is safe because of where this include sits: BELOW this family's
284
+ * declarations, so that a translation unit entering through this header has them
285
+ * by the time the protocol parses, and a translation unit entering through the
286
+ * protocol gets them when it reaches this header from the other side. The
287
+ * forward declaration above is what makes the second order work.
288
+ */
289
+ #include <decision_graph/decision_tree/bake/c_eval.h>
290
+
291
+ // ========== Operator Utilities ==========
292
+
293
+ /*
294
+ * Operator names, one table per family. The variant numbers start at 1, so the
295
+ * table index is the variant minus one.
296
+ */
297
+ static const char* const DCG_OP_ARITH_NAMES[] = {"ADD", "SUB", "MUL", "DIV", "FLOORDIV", "POW", "NEG"};
298
+ static const char* const DCG_OP_COMPARE_NAMES[] = {"EQ", "NE", "GT", "GE", "LT", "LE"};
299
+ static const char* const DCG_OP_LOGIC_NAMES[] = {"AND", "OR", "NOT"};
300
+ static const char* const DCG_OP_ACCESS_NAMES[] = {"ATTR", "GETITEM"};
301
+
302
+ /*
303
+ * Operator symbols, one table per family, mirroring the capi's op_repr - the
304
+ * form a node's repr is built from.
305
+ */
306
+ static const char* const DCG_OP_ARITH_SYMBOLS[] = {"+", "-", "*", "/", "//", "**", "-"};
307
+ static const char* const DCG_OP_COMPARE_SYMBOLS[] = {"==", "!=", ">", ">=", "<", "<="};
308
+ static const char* const DCG_OP_LOGIC_SYMBOLS[] = {"&", "|", "~"};
309
+ static const char* const DCG_OP_ACCESS_SYMBOLS[] = {".", "[]"};
310
+
311
+ /**
312
+ * @brief The symbol of an operator ("+", "==", ...) as an arity writes it.
313
+ *
314
+ * One operand writes it in front (-12), two write it between (12 - 3), and three
315
+ * are the if-form, which writes no symbol at all. Which of the two a caller gets
316
+ * is the operand count's business, not the operator's: the same "-" is a
317
+ * negation for one operand and a subtraction for two.
318
+ *
319
+ * @param op Operator code.
320
+ * @param n_args Operands the operator applies to.
321
+ * @return Static string; "" for DCG_OP_NONE, an unknown operator, or an arity
322
+ * that writes nothing.
323
+ */
324
+ static inline const char* c_dcg_op_code_symbol(dcg_op_code op, size_t n_args) {
325
+ if (op == DCG_OP_NONE) return "";
326
+ if (n_args < 1 || n_args > 2) return ""; /* the if-form and the calls write none */
327
+
328
+ size_t variant = (size_t) ((int) op & DCG_OP_VARIANT_MASK);
329
+ if (variant == 0) return "";
330
+ variant--;
331
+
332
+ switch ((int) op & DCG_OP_FAMILY_MASK) {
333
+ case DCG_OP_ARITH:
334
+ return c_dcg_name_at(DCG_OP_ARITH_SYMBOLS, sizeof(DCG_OP_ARITH_SYMBOLS) / sizeof(char*), variant);
335
+ case DCG_OP_COMPARE:
336
+ return c_dcg_name_at(DCG_OP_COMPARE_SYMBOLS, sizeof(DCG_OP_COMPARE_SYMBOLS) / sizeof(char*), variant);
337
+ case DCG_OP_LOGIC:
338
+ return c_dcg_name_at(DCG_OP_LOGIC_SYMBOLS, sizeof(DCG_OP_LOGIC_SYMBOLS) / sizeof(char*), variant);
339
+ case DCG_OP_ACCESS:
340
+ return c_dcg_name_at(DCG_OP_ACCESS_SYMBOLS, sizeof(DCG_OP_ACCESS_SYMBOLS) / sizeof(char*), variant);
341
+ default:
342
+ return "";
343
+ }
344
+ }
345
+
346
+ /**
347
+ * @brief Stable display name of an operator code.
348
+ *
349
+ * @param op Operator code.
350
+ * @return Static string; "UNKNOWN" for an out-of-range operator.
351
+ */
352
+ static inline const char* c_dcg_op_code_name(dcg_op_code op) {
353
+ if (op == DCG_OP_NONE) return "NONE";
354
+
355
+ size_t variant = (size_t) ((int) op & DCG_OP_VARIANT_MASK);
356
+ if (variant == 0) return "UNKNOWN";
357
+ variant--; // operator variants are numbered from 1
358
+
359
+ switch ((int) op & DCG_OP_FAMILY_MASK) {
360
+ case DCG_OP_ARITH:
361
+ return c_dcg_name_at(DCG_OP_ARITH_NAMES, sizeof(DCG_OP_ARITH_NAMES) / sizeof(char*), variant);
362
+ case DCG_OP_COMPARE:
363
+ return c_dcg_name_at(DCG_OP_COMPARE_NAMES, sizeof(DCG_OP_COMPARE_NAMES) / sizeof(char*), variant);
364
+ case DCG_OP_LOGIC:
365
+ return c_dcg_name_at(DCG_OP_LOGIC_NAMES, sizeof(DCG_OP_LOGIC_NAMES) / sizeof(char*), variant);
366
+ case DCG_OP_ACCESS:
367
+ return c_dcg_name_at(DCG_OP_ACCESS_NAMES, sizeof(DCG_OP_ACCESS_NAMES) / sizeof(char*), variant);
368
+ default:
369
+ return "UNKNOWN";
370
+ }
371
+ }
372
+ /**
373
+ * @brief The node type an operator code belongs to.
374
+ *
375
+ * @param op Operator code.
376
+ * @return UNARY / BINARY / TERNARY / CALL as implied by the operator, or
377
+ * DCG_NODE_OP when the operator is unknown.
378
+ */
379
+ static inline dcg_node_type c_dcg_op_code_node_type(dcg_op_code op) {
380
+ switch (op) {
381
+ case DCG_OP_NEG:
382
+ case DCG_OP_NOT:
383
+ return DCG_NODE_UNARY;
384
+ case DCG_OP_ADD:
385
+ case DCG_OP_SUB:
386
+ case DCG_OP_MUL:
387
+ case DCG_OP_DIV:
388
+ case DCG_OP_FLOORDIV:
389
+ case DCG_OP_POW:
390
+ case DCG_OP_EQ:
391
+ case DCG_OP_NE:
392
+ case DCG_OP_GT:
393
+ case DCG_OP_GE:
394
+ case DCG_OP_LT:
395
+ case DCG_OP_LE:
396
+ case DCG_OP_AND:
397
+ case DCG_OP_OR:
398
+ case DCG_OP_ATTR:
399
+ case DCG_OP_GETITEM:
400
+ return DCG_NODE_BINARY;
401
+ default:
402
+ return DCG_NODE_OP;
403
+ }
404
+ }
405
+
406
+ // ========== Composing the Repr ==========
407
+
408
+ /**
409
+ * @brief The text an input node is written as: its repr, or its value's format.
410
+ *
411
+ * A node carries its repr from the moment it is built, so the fallback is only
412
+ * for a node somebody assembled by hand without one. An expression is written in
413
+ * parentheses: the composed text then reads the way it evaluates.
414
+ *
415
+ * @param input Input node to write (NULL-safe).
416
+ * @param out Destination buffer.
417
+ * @param cap Capacity of out.
418
+ * @return Number of characters written (excluding NUL), or a DCG_ERR_* code.
419
+ */
420
+ static inline int c_dcg_node_expr_alias(const dcg_node* input, char* out, size_t cap) {
421
+ if (!input || !out || cap == 0) return DCG_ERR_INVALID_ARG;
422
+ if (!input->repr) return c_dcg_var_format(&input->out, out, cap);
423
+
424
+ /* An input that is an expression is written in parentheses, so the composed
425
+ * text reads the way it evaluates: a + (b * c), not a + b * c. */
426
+ int n = c_dcg_node_type_is_op(input->ntype) ? snprintf(out, cap, "(%s)", input->repr) : snprintf(out, cap, "%s", input->repr);
427
+ return n < 0 ? DCG_ERR_FORMAT : n;
428
+ }
429
+
430
+ /**
431
+ * @brief Render an expression the way its operator writes it - the op style.
432
+ *
433
+ * One operand is written after the symbol (-12), two around it (12 - 3), three
434
+ * as the if-form (cond ? then : else), and any other count comma-separated. This
435
+ * is the repr an operator node is given when it is built; a call, which writes a
436
+ * name rather than a symbol, takes c_dcg_node_expr_func_style() instead.
437
+ *
438
+ * @param inputs Input nodes, in operand order.
439
+ * @param n_inputs Number of inputs.
440
+ * @param op Operator code.
441
+ * @param out Destination buffer.
442
+ * @param cap Capacity of out.
443
+ * @return Number of characters written (excluding NUL), or a DCG_ERR_* code -
444
+ * a positive answer, so a caller checks for a negative one.
445
+ */
446
+ static inline int c_dcg_node_expr_op_style(dcg_node* const* inputs, size_t n_inputs, dcg_op_code op, char* out, size_t cap) {
447
+ if (!inputs || !out || cap == 0 || n_inputs == 0) return DCG_ERR_INVALID_ARG;
448
+
449
+ const char* token = c_dcg_op_code_symbol(op, n_inputs);
450
+ char alias[DCG_NODE_STRING_MAXLEN];
451
+ dcg_strbuf buf;
452
+
453
+ c_dcg_sb_init(&buf, out, cap);
454
+
455
+ for (size_t i = 0; i < n_inputs; i++) {
456
+ if (i == 0) {
457
+ if (n_inputs == 1) c_dcg_sb_puts(&buf, token); /* -12: the token in front */
458
+ }
459
+ else if (n_inputs == 2) c_dcg_sb_printf(&buf, " %s ", token); /* 12 - 3: between */
460
+ else if (n_inputs == 3 && i == 1) c_dcg_sb_puts(&buf, " ? ");
461
+ else if (n_inputs == 3 && i == 2) c_dcg_sb_puts(&buf, " : ");
462
+ else c_dcg_sb_puts(&buf, ", ");
463
+
464
+ if (c_dcg_node_expr_alias(inputs[i], alias, sizeof(alias)) < 0) return DCG_ERR_FORMAT;
465
+ c_dcg_sb_puts(&buf, alias);
466
+ }
467
+
468
+ return buf.used < buf.cap ? (int) buf.used : DCG_ERR_FULL;
469
+ }
470
+
471
+ /**
472
+ * @brief Render an expression the way a function writes it - the func style.
473
+ *
474
+ * NAME(a, b): the name is the caller's - a call is written as the callee it calls
475
+ * - and falls back to the operator's own name. This is the repr a call node is
476
+ * given when it is built, and the one to ask for when a name reads better than a
477
+ * symbol.
478
+ *
479
+ * @param inputs Input nodes, in operand order.
480
+ * @param n_inputs Number of inputs.
481
+ * @param op Operator code (named when `name` is NULL).
482
+ * @param name The function name to write (may be NULL).
483
+ * @param out Destination buffer.
484
+ * @param cap Capacity of out.
485
+ * @return Number of characters written (excluding NUL), or a DCG_ERR_* code -
486
+ * a positive answer, so a caller checks for a negative one.
487
+ */
488
+ static inline int c_dcg_node_expr_func_style(dcg_node* const* inputs, size_t n_inputs, dcg_op_code op, const char* name, char* out, size_t cap) {
489
+ if (!inputs || !out || cap == 0 || n_inputs == 0) return DCG_ERR_INVALID_ARG;
490
+
491
+ char alias[DCG_NODE_STRING_MAXLEN];
492
+ dcg_strbuf buf;
493
+
494
+ c_dcg_sb_init(&buf, out, cap);
495
+ c_dcg_sb_printf(&buf, "%s(", name ? name : c_dcg_op_code_name(op));
496
+
497
+ for (size_t i = 0; i < n_inputs; i++) {
498
+ if (c_dcg_node_expr_alias(inputs[i], alias, sizeof(alias)) < 0) return DCG_ERR_FORMAT;
499
+ if (i > 0) c_dcg_sb_puts(&buf, ", ");
500
+ c_dcg_sb_puts(&buf, alias);
501
+ }
502
+ c_dcg_sb_puts(&buf, ")");
503
+
504
+ return buf.used < buf.cap ? (int) buf.used : DCG_ERR_FULL;
505
+ }
506
+
507
+ // ========== Lifecycle Methods ==========
508
+
509
+ /**
510
+ * @brief Allocate an expression node with room for its operands.
511
+ *
512
+ * The block is sized for the operands it will hold, so the count is fixed for
513
+ * the lifetime of the node - which is what a baked graph wants: the operands
514
+ * are known when the node is built. The operand slots start empty; bind them
515
+ * with c_dcg_node_expr_bind() or use a typed constructor, which fills them.
516
+ *
517
+ * @param n_args Number of operands (clamped to DCG_EXPR_DEFAULT_ARGS if 0).
518
+ * @param ntype An operator type (UNARY / BINARY / TERNARY / CALL, or the head).
519
+ * @param allocator Allocator for the block; NULL falls back to the plain heap.
520
+ * @return The node, or NULL on OOM / invalid type.
521
+ */
522
+ static inline dcg_expression_node* c_dcg_node_new_expr(size_t n_args, dcg_node_type ntype, allocator_protocol* allocator) {
523
+ if (!c_dcg_node_type_is_op(ntype)) return NULL;
524
+ if (n_args == 0) n_args = DCG_EXPR_DEFAULT_ARGS;
525
+
526
+ dcg_expression_node* node = (dcg_expression_node*) c_ap_alloc(sizeof(dcg_expression_node) + (n_args * (sizeof(dcg_var_t) + sizeof(dcg_node*))), allocator);
527
+ if (!node) return NULL;
528
+
529
+ if (c_dcg_node_init(&node->base, ntype, NULL) != DCG_OK) {
530
+ c_ap_free_owned(node);
531
+ return NULL;
532
+ }
533
+
534
+ /* Every expression node is built here, so this is the one place the family's
535
+ * rule is injected - the same way every other family's constructor injects
536
+ * its own (see DCG_EVAL_DIRECT_HOOKS): the arity's rule, since no operator is
537
+ * set yet. The constructors that take an operator hand it over a moment
538
+ * later, and the rule goes with it (c_dcg_node_expr_set_op). A build with the
539
+ * injection off installs nothing and the dispatch finds the rule instead. */
540
+ #if DCG_EVAL_DIRECT_HOOKS
541
+ node->base.eval_ctx.type_eval_fn = c_dcg_node_expr_eval_of_arity(ntype);
542
+ #endif
543
+ (void) c_dcg_node_expr_set_op(node, DCG_OP_NONE);
544
+ node->n_args = n_args;
545
+ node->components = c_dcg_node_expr_components(node);
546
+
547
+ for (size_t i = 0; i < n_args; i++) (void) c_dcg_var_init(&node->args[i]);
548
+ return node;
549
+ }
550
+
551
+ /**
552
+ * @brief Tear down an expression node and free its buf.
553
+ *
554
+ * The operand slots and the pointer array both sit in blocks of the node's own,
555
+ * so they go with it. What has to be given back first is the hold the node took
556
+ * on its operands: an operand nothing else holds is released here, and one that
557
+ * outlives the expression is simply left with one fewer reference.
558
+ *
559
+ * The reference each operand holds is given back HERE, once per slot, and that is
560
+ * the whole of it: the hold was taken by c_dcg_node_expr_bind() and this is the
561
+ * one place it is returned, so the count a bind raised is the count a free
562
+ * lowers and nothing double-counts. When it was the last reference the block is
563
+ * released by the allocator itself, which takes the ownership tree with it.
564
+ *
565
+ * @param node Node to free (NULL-safe).
566
+ */
567
+ static inline void c_dcg_node_free_expr(dcg_expression_node* node) {
568
+ if (!node) return;
569
+
570
+ for (size_t i = 0; i < node->n_args; i++) {
571
+ if (node->components[i]) c_ap_decref(node->components[i]);
572
+ }
573
+ c_dcg_node_free(&node->base);
574
+ }
575
+
576
+ // ========== Applying One Operator ==========
577
+
578
+ /*
579
+ * The kernels: one operator's arithmetic, taking the operands and producing the
580
+ * value. They live here, with the operator tables they are named for, and they
581
+ * are reached two ways - by the operator's own evaluation rule
582
+ * (c_dcg_node_expr_eval_<op>, the way a node is evaluated) and by the runtime
583
+ * dispatchers a caller with a code and no node uses
584
+ * (c_dcg_node_expr_apply_unary / c_dcg_node_expr_apply_binary). So a kernel
585
+ * cannot be right by one way and wrong by the other: there is one kernel each,
586
+ * and both ways call it.
587
+ */
588
+
589
+ /**
590
+ * @brief The kernel of an operator this arity has no kernel for: saying so is
591
+ * the kernel.
592
+ *
593
+ * Every op has a function in each arity - the ones that can apply it and this
594
+ * one, which refuses it - so the apply-side lookup never answers NULL for an
595
+ * arity that has operators, and its callers never have to ask.
596
+ *
597
+ * @param out Receives nothing: left as it was.
598
+ * @return DCG_ERR_TYPE.
599
+ */
600
+ static inline int c_dcg_node_expr_apply_refuse(dcg_var_t* out, const dcg_var_t* a, const dcg_var_t* b) {
601
+ (void) a;
602
+ (void) b;
603
+ (void) c_dcg_var_init(out);
604
+ return DCG_ERR_TYPE;
605
+ }
606
+
607
+ /**
608
+ * @brief Negation: a number, negated.
609
+ *
610
+ * @param out Receives the value.
611
+ * @param a The operand.
612
+ * @param b Unused (see dcg_expr_apply_fn).
613
+ * @return DCG_OK, or DCG_ERR_MATH for an operand that is not a number.
614
+ */
615
+ static inline int c_dcg_node_expr_apply_neg(dcg_var_t* out, const dcg_var_t* a, const dcg_var_t* b) {
616
+ (void) b;
617
+ (void) c_dcg_var_init(out);
618
+
619
+ switch (c_dcg_var_numeric_of(a)) {
620
+ case VAR_NUMERIC_INT:
621
+ return c_dcg_var_init_int(out, -c_dcg_var_as_int(a));
622
+ case VAR_NUMERIC_DOUBLE:
623
+ return c_dcg_var_init_double(out, -c_dcg_var_as_double(a));
624
+ default:
625
+ return DCG_ERR_MATH;
626
+ }
627
+ }
628
+
629
+ /**
630
+ * @brief Logical not: the operand's truth, negated.
631
+ *
632
+ * @param out Receives the value.
633
+ * @param a The operand.
634
+ * @param b Unused.
635
+ * @return DCG_OK.
636
+ */
637
+ static inline int c_dcg_node_expr_apply_not(dcg_var_t* out, const dcg_var_t* a, const dcg_var_t* b) {
638
+ (void) b;
639
+ (void) c_dcg_var_init(out);
640
+ return c_dcg_var_init_bool(out, !c_dcg_var_is_truthy(a));
641
+ }
642
+
643
+ /**
644
+ * @brief Addition: two numbers, added.
645
+ *
646
+ * The result follows the operands: a whole-number pair stays whole, and one
647
+ * double in the pair makes the result a double. Integer arithmetic is 64-bit and
648
+ * wraps the way the C it is written in does; Python's arbitrary-precision ints
649
+ * are not mirrored.
650
+ *
651
+ * @param out Receives the value.
652
+ * @param a Left operand.
653
+ * @param b Right operand.
654
+ * @return DCG_OK, or DCG_ERR_MATH for an operand that is not a number.
655
+ */
656
+ static inline int c_dcg_node_expr_apply_add(dcg_var_t* out, const dcg_var_t* a, const dcg_var_t* b) {
657
+ (void) c_dcg_var_init(out);
658
+
659
+ dcg_var_numeric left = c_dcg_var_numeric_of(a);
660
+ dcg_var_numeric right = c_dcg_var_numeric_of(b);
661
+ if (left == VAR_NUMERIC_NONE || right == VAR_NUMERIC_NONE) return DCG_ERR_MATH;
662
+
663
+ if (left == VAR_NUMERIC_DOUBLE || right == VAR_NUMERIC_DOUBLE) return c_dcg_var_init_double(out, c_dcg_var_as_double(a) + c_dcg_var_as_double(b));
664
+ return c_dcg_var_init_int(out, c_dcg_var_as_int(a) + c_dcg_var_as_int(b));
665
+ }
666
+
667
+ /**
668
+ * @brief Subtraction: two numbers, subtracted.
669
+ *
670
+ * @param out Receives the value.
671
+ * @param a Left operand.
672
+ * @param b Right operand.
673
+ * @return DCG_OK, or DCG_ERR_MATH for an operand that is not a number.
674
+ */
675
+ static inline int c_dcg_node_expr_apply_sub(dcg_var_t* out, const dcg_var_t* a, const dcg_var_t* b) {
676
+ (void) c_dcg_var_init(out);
677
+
678
+ dcg_var_numeric left = c_dcg_var_numeric_of(a);
679
+ dcg_var_numeric right = c_dcg_var_numeric_of(b);
680
+ if (left == VAR_NUMERIC_NONE || right == VAR_NUMERIC_NONE) return DCG_ERR_MATH;
681
+
682
+ if (left == VAR_NUMERIC_DOUBLE || right == VAR_NUMERIC_DOUBLE) return c_dcg_var_init_double(out, c_dcg_var_as_double(a) - c_dcg_var_as_double(b));
683
+ return c_dcg_var_init_int(out, c_dcg_var_as_int(a) - c_dcg_var_as_int(b));
684
+ }
685
+
686
+ /**
687
+ * @brief Multiplication: two numbers, multiplied.
688
+ *
689
+ * @param out Receives the value.
690
+ * @param a Left operand.
691
+ * @param b Right operand.
692
+ * @return DCG_OK, or DCG_ERR_MATH for an operand that is not a number.
693
+ */
694
+ static inline int c_dcg_node_expr_apply_mul(dcg_var_t* out, const dcg_var_t* a, const dcg_var_t* b) {
695
+ (void) c_dcg_var_init(out);
696
+
697
+ dcg_var_numeric left = c_dcg_var_numeric_of(a);
698
+ dcg_var_numeric right = c_dcg_var_numeric_of(b);
699
+ if (left == VAR_NUMERIC_NONE || right == VAR_NUMERIC_NONE) return DCG_ERR_MATH;
700
+
701
+ if (left == VAR_NUMERIC_DOUBLE || right == VAR_NUMERIC_DOUBLE) return c_dcg_var_init_double(out, c_dcg_var_as_double(a) * c_dcg_var_as_double(b));
702
+ return c_dcg_var_init_int(out, c_dcg_var_as_int(a) * c_dcg_var_as_int(b));
703
+ }
704
+
705
+ /**
706
+ * @brief Division: two numbers, divided as floats - including a zero divisor,
707
+ * which is refused.
708
+ *
709
+ * Python's `/` is a float division and refuses a zero divisor, so this one is a
710
+ * double and refuses it too: an infinity travels on through the graph as if it
711
+ * were a price.
712
+ *
713
+ * @param out Receives the value.
714
+ * @param a Left operand (the dividend).
715
+ * @param b Right operand (the divisor).
716
+ * @return DCG_OK, or DCG_ERR_MATH.
717
+ */
718
+ static inline int c_dcg_node_expr_apply_div(dcg_var_t* out, const dcg_var_t* a, const dcg_var_t* b) {
719
+ (void) c_dcg_var_init(out);
720
+
721
+ if (c_dcg_var_numeric_of(a) == VAR_NUMERIC_NONE || c_dcg_var_numeric_of(b) == VAR_NUMERIC_NONE) return DCG_ERR_MATH;
722
+
723
+ double y = c_dcg_var_as_double(b);
724
+ if (y == 0.0) return DCG_ERR_MATH;
725
+ return c_dcg_var_init_double(out, c_dcg_var_as_double(a) / y);
726
+ }
727
+
728
+ /**
729
+ * @brief Floor division: the quotient floored toward minus infinity, as Python's
730
+ * `//` is - and a zero divisor is refused.
731
+ *
732
+ * @param out Receives the value.
733
+ * @param a Left operand (the dividend).
734
+ * @param b Right operand (the divisor).
735
+ * @return DCG_OK, or DCG_ERR_MATH.
736
+ */
737
+ static inline int c_dcg_node_expr_apply_floordiv(dcg_var_t* out, const dcg_var_t* a, const dcg_var_t* b) {
738
+ (void) c_dcg_var_init(out);
739
+
740
+ dcg_var_numeric left = c_dcg_var_numeric_of(a);
741
+ dcg_var_numeric right = c_dcg_var_numeric_of(b);
742
+ if (left == VAR_NUMERIC_NONE || right == VAR_NUMERIC_NONE) return DCG_ERR_MATH;
743
+
744
+ if (left == VAR_NUMERIC_DOUBLE || right == VAR_NUMERIC_DOUBLE) {
745
+ double y = c_dcg_var_as_double(b);
746
+ if (y == 0.0) return DCG_ERR_MATH;
747
+ return c_dcg_var_init_double(out, floor(c_dcg_var_as_double(a) / y));
748
+ }
749
+
750
+ ssize_t y = c_dcg_var_as_int(b);
751
+ if (y == 0) return DCG_ERR_MATH;
752
+
753
+ ssize_t x = c_dcg_var_as_int(a);
754
+ ssize_t quotient = x / y;
755
+ ssize_t remainder = x % y;
756
+ // Python floors toward minus infinity; C truncates toward zero.
757
+ if (remainder != 0 && ((remainder < 0) != (y < 0))) quotient--;
758
+ return c_dcg_var_init_int(out, quotient);
759
+ }
760
+
761
+ /**
762
+ * @brief Exponentiation: a whole exponent over whole numbers stays whole.
763
+ *
764
+ * A whole exponent over whole numbers is raised by squaring, which keeps an
765
+ * integer to the power of an integer an integer - as Python does. Everything
766
+ * else - a fractional exponent, a double operand - is a real power, and comes
767
+ * from the platform's own. A zero base with a negative exponent is the one real
768
+ * power Python refuses, and this refuses it too.
769
+ *
770
+ * @param out Receives the value.
771
+ * @param a Left operand (the base).
772
+ * @param b Right operand (the exponent).
773
+ * @return DCG_OK, or DCG_ERR_MATH.
774
+ */
775
+ static inline int c_dcg_node_expr_apply_pow(dcg_var_t* out, const dcg_var_t* a, const dcg_var_t* b) {
776
+ (void) c_dcg_var_init(out);
777
+
778
+ dcg_var_numeric left = c_dcg_var_numeric_of(a);
779
+ dcg_var_numeric right = c_dcg_var_numeric_of(b);
780
+ if (left == VAR_NUMERIC_NONE || right == VAR_NUMERIC_NONE) return DCG_ERR_MATH;
781
+
782
+ double x = c_dcg_var_as_double(a);
783
+ double y = c_dcg_var_as_double(b);
784
+ ssize_t exponent = (ssize_t) y;
785
+
786
+ if (c_dcg_var_ref_base(b->dtype) != VAR_TYPE_DOUBLE && y == (double) exponent && left == VAR_NUMERIC_INT) {
787
+ if (exponent >= 0) {
788
+ ssize_t result = 1;
789
+ ssize_t base = c_dcg_var_as_int(a);
790
+ for (ssize_t e = exponent; e > 0; e >>= 1) {
791
+ if (e & 1) result *= base;
792
+ if (e > 1) base *= base;
793
+ }
794
+ return c_dcg_var_init_int(out, result);
795
+ }
796
+ return c_dcg_var_init_double(out, 1.0 / pow(x, (double) -exponent));
797
+ }
798
+
799
+ if (x == 0.0 && y < 0.0) return DCG_ERR_MATH;
800
+ return c_dcg_var_init_double(out, pow(x, y));
801
+ }
802
+
803
+ /**
804
+ * @brief Equality: the operands compared by VALUE and not by tag.
805
+ *
806
+ * `2 == 2.0` holds here, as it does in Python, where an edge's own match does not
807
+ * (see c_dcg_var_equals - an edge is identified by what its value IS).
808
+ *
809
+ * @param out Receives the value.
810
+ * @param a Left operand.
811
+ * @param b Right operand.
812
+ * @return DCG_OK.
813
+ */
814
+ static inline int c_dcg_node_expr_apply_eq(dcg_var_t* out, const dcg_var_t* a, const dcg_var_t* b) {
815
+ (void) c_dcg_var_init(out);
816
+
817
+ dcg_var_numeric left = c_dcg_var_numeric_of(a);
818
+ dcg_var_numeric right = c_dcg_var_numeric_of(b);
819
+ bool equal;
820
+
821
+ if (left != VAR_NUMERIC_NONE && right != VAR_NUMERIC_NONE) {
822
+ equal = (left == VAR_NUMERIC_DOUBLE || right == VAR_NUMERIC_DOUBLE) ? c_dcg_var_as_double(a) == c_dcg_var_as_double(b) : c_dcg_var_as_int(a) == c_dcg_var_as_int(b);
823
+ }
824
+ else {
825
+ equal = left == right && c_dcg_var_equals(a, b);
826
+ }
827
+ return c_dcg_var_init_bool(out, equal);
828
+ }
829
+
830
+ /**
831
+ * @brief Inequality: the negation of what c_dcg_node_expr_apply_eq() holds.
832
+ *
833
+ * @param out Receives the value.
834
+ * @param a Left operand.
835
+ * @param b Right operand.
836
+ * @return DCG_OK.
837
+ */
838
+ static inline int c_dcg_node_expr_apply_ne(dcg_var_t* out, const dcg_var_t* a, const dcg_var_t* b) {
839
+ (void) c_dcg_var_init(out);
840
+
841
+ dcg_var_numeric left = c_dcg_var_numeric_of(a);
842
+ dcg_var_numeric right = c_dcg_var_numeric_of(b);
843
+ bool equal;
844
+
845
+ if (left != VAR_NUMERIC_NONE && right != VAR_NUMERIC_NONE) {
846
+ equal = (left == VAR_NUMERIC_DOUBLE || right == VAR_NUMERIC_DOUBLE) ? c_dcg_var_as_double(a) == c_dcg_var_as_double(b) : c_dcg_var_as_int(a) == c_dcg_var_as_int(b);
847
+ }
848
+ else {
849
+ equal = left == right && c_dcg_var_equals(a, b);
850
+ }
851
+ return c_dcg_var_init_bool(out, !equal);
852
+ }
853
+
854
+ /**
855
+ * @brief The ordering of two operands: numbers by value, strings by text, and
856
+ * anything else refused.
857
+ *
858
+ * The four ordering operators differ only in the comparison they make of this,
859
+ * which is why they read it here.
860
+ *
861
+ * @param out Receives -1, 0 or 1 as `a` orders before, with, or after `b`.
862
+ * @param a Left operand.
863
+ * @param b Right operand.
864
+ * @return DCG_OK, or DCG_ERR_MATH for operands with no order.
865
+ */
866
+ static inline int c_dcg_node_expr_order(int* out, const dcg_var_t* a, const dcg_var_t* b) {
867
+ if (c_dcg_var_numeric_of(a) != VAR_NUMERIC_NONE && c_dcg_var_numeric_of(b) != VAR_NUMERIC_NONE) {
868
+ double x = c_dcg_var_as_double(a);
869
+ double y = c_dcg_var_as_double(b);
870
+ *out = x < y ? -1 : (x > y ? 1 : 0);
871
+ return DCG_OK;
872
+ }
873
+ if (c_dcg_var_ref_base(a->dtype) == VAR_TYPE_STRING && c_dcg_var_ref_base(b->dtype) == VAR_TYPE_STRING) {
874
+ const char* x = c_dcg_var_as_string(a);
875
+ const char* y = c_dcg_var_as_string(b);
876
+ *out = strcmp(x ? x : "", y ? y : "");
877
+ return DCG_OK;
878
+ }
879
+ return DCG_ERR_MATH;
880
+ }
881
+
882
+ /**
883
+ * @brief Greater than, by the order c_dcg_node_expr_order() reads.
884
+ *
885
+ * @param out Receives the value.
886
+ * @param a Left operand.
887
+ * @param b Right operand.
888
+ * @return DCG_OK, or DCG_ERR_MATH for operands with no order.
889
+ */
890
+ static inline int c_dcg_node_expr_apply_gt(dcg_var_t* out, const dcg_var_t* a, const dcg_var_t* b) {
891
+ (void) c_dcg_var_init(out);
892
+ int order = 0;
893
+ int ret_code = c_dcg_node_expr_order(&order, a, b);
894
+ if (ret_code != DCG_OK) return ret_code;
895
+ return c_dcg_var_init_bool(out, order > 0);
896
+ }
897
+
898
+ /**
899
+ * @brief Greater than or equal, by the order c_dcg_node_expr_order() reads.
900
+ *
901
+ * @param out Receives the value.
902
+ * @param a Left operand.
903
+ * @param b Right operand.
904
+ * @return DCG_OK, or DCG_ERR_MATH for operands with no order.
905
+ */
906
+ static inline int c_dcg_node_expr_apply_ge(dcg_var_t* out, const dcg_var_t* a, const dcg_var_t* b) {
907
+ (void) c_dcg_var_init(out);
908
+ int order = 0;
909
+ int ret_code = c_dcg_node_expr_order(&order, a, b);
910
+ if (ret_code != DCG_OK) return ret_code;
911
+ return c_dcg_var_init_bool(out, order >= 0);
912
+ }
913
+
914
+ /**
915
+ * @brief Less than, by the order c_dcg_node_expr_order() reads.
916
+ *
917
+ * @param out Receives the value.
918
+ * @param a Left operand.
919
+ * @param b Right operand.
920
+ * @return DCG_OK, or DCG_ERR_MATH for operands with no order.
921
+ */
922
+ static inline int c_dcg_node_expr_apply_lt(dcg_var_t* out, const dcg_var_t* a, const dcg_var_t* b) {
923
+ (void) c_dcg_var_init(out);
924
+ int order = 0;
925
+ int ret_code = c_dcg_node_expr_order(&order, a, b);
926
+ if (ret_code != DCG_OK) return ret_code;
927
+ return c_dcg_var_init_bool(out, order < 0);
928
+ }
929
+
930
+ /**
931
+ * @brief Less than or equal, by the order c_dcg_node_expr_order() reads.
932
+ *
933
+ * @param out Receives the value.
934
+ * @param a Left operand.
935
+ * @param b Right operand.
936
+ * @return DCG_OK, or DCG_ERR_MATH for operands with no order.
937
+ */
938
+ static inline int c_dcg_node_expr_apply_le(dcg_var_t* out, const dcg_var_t* a, const dcg_var_t* b) {
939
+ (void) c_dcg_var_init(out);
940
+ int order = 0;
941
+ int ret_code = c_dcg_node_expr_order(&order, a, b);
942
+ if (ret_code != DCG_OK) return ret_code;
943
+ return c_dcg_var_init_bool(out, order <= 0);
944
+ }
945
+
946
+ /**
947
+ * @brief Logical and: an OPERAND rather than a bool.
948
+ *
949
+ * Python's `and` answers with one of its operands, and so does this: the left one
950
+ * decides, and the right one is the answer only when the left one did not.
951
+ *
952
+ * @param out Receives the value.
953
+ * @param a Left operand.
954
+ * @param b Right operand.
955
+ * @return DCG_OK.
956
+ */
957
+ static inline int c_dcg_node_expr_apply_and(dcg_var_t* out, const dcg_var_t* a, const dcg_var_t* b) {
958
+ (void) c_dcg_var_init(out);
959
+ *out = c_dcg_var_is_truthy(a) ? *b : *a;
960
+ return DCG_OK;
961
+ }
962
+
963
+ /**
964
+ * @brief Logical or: the other operand, chosen the other way round.
965
+ *
966
+ * @param out Receives the value.
967
+ * @param a Left operand.
968
+ * @param b Right operand.
969
+ * @return DCG_OK.
970
+ */
971
+ static inline int c_dcg_node_expr_apply_or(dcg_var_t* out, const dcg_var_t* a, const dcg_var_t* b) {
972
+ (void) c_dcg_var_init(out);
973
+ *out = c_dcg_var_is_truthy(a) ? *a : *b;
974
+ return DCG_OK;
975
+ }
976
+
977
+ // ========== The Rules, One Per Operator ==========
978
+
979
+ /*
980
+ * What this family's nodes evaluate to: the operator applied to the operands the
981
+ * node binds. The rules live HERE, with the operator and the operands they are
982
+ * about, ONE PER OPERATOR - and the rule is the node's own.
983
+ *
984
+ * A rule BEGINS by producing its own operands: the components are the node's own
985
+ * fields, so the rule runs them and writes what they are worth into the
986
+ * workspace itself (c_dcg_node_expr_eval_operand, one slot at a time, as many as
987
+ * its operator takes), and only then applies its kernel to the slots. So a rule
988
+ * is a pass-through - operands produced, one kernel, the value into the node's own
989
+ * out slot - with nothing to check, nothing to count, nothing to copy and nothing
990
+ * to follow: what an operand refers to has been settled by the time the kernel
991
+ * sees it.
992
+ *
993
+ * WHICH rule runs is settled once, from the flat list (DCG_EXPR_EVAL_FNS), when
994
+ * the node's operator is set - and where that answer is KEPT is what
995
+ * DCG_EVAL_DIRECT_HOOKS decides: injected onto the node as its type-eval rule
996
+ * (dcg_node_eval_ctx.type_eval_fn), so an evaluation is a call through that
997
+ * pointer and nothing else; or left to be found again at evaluation time by
998
+ * c_dcg_node_expr_eval, which asks the same two questions. One rule, reached two
999
+ * ways - so the two builds cannot compute different values.
1000
+ */
1001
+
1002
+ /**
1003
+ * @brief The rule of a node that has no operator to apply: saying so is the rule.
1004
+ *
1005
+ * What an operator node is born with (see c_dcg_node_expr_eval_of_arity), and
1006
+ * what an operator of another arity leaves standing - a comparison handed to a
1007
+ * unary node, a family head, an operator code no rule exists for (see
1008
+ * c_dcg_node_expr_set_op). Refusing is the whole of it: no operand is read and
1009
+ * the node's slot is left as it was.
1010
+ *
1011
+ * @param node The operator node (unused).
1012
+ * @param user_data Unused: a node's own rule carries no context.
1013
+ * @return DCG_ERR_TYPE.
1014
+ */
1015
+ static inline int c_dcg_node_expr_eval_refuse(dcg_node* node, void* user_data) {
1016
+ (void) node;
1017
+ (void) user_data;
1018
+ return DCG_ERR_TYPE;
1019
+ }
1020
+
1021
+ /**
1022
+ * @brief The rule of an operator node of no arity: saying so is the rule.
1023
+ *
1024
+ * Reached by a node built with the operator family's head type (DCG_NODE_OP)
1025
+ * rather than one of the four arities - a node nobody can evaluate, which
1026
+ * reports rather than guessing at an arity.
1027
+ *
1028
+ * @param node The operator node.
1029
+ * @param user_data Unused.
1030
+ * @return DCG_ERR_TYPE.
1031
+ */
1032
+ static inline int c_dcg_node_expr_eval_unknown(dcg_node* node, void* user_data) {
1033
+ (void) user_data;
1034
+
1035
+ (void) fprintf(stderr, "c_dcg_node_expr_eval_unknown: no arity to evaluate operator node type %s (0x%04x) at %p by\n", c_dcg_node_type_name(node->ntype), (unsigned) node->ntype, (const void*) node);
1036
+ return DCG_ERR_TYPE;
1037
+ }
1038
+
1039
+ /**
1040
+ * @brief The rule of an if-expression: the arm its condition picks.
1041
+ *
1042
+ * The condition is read FIRST, and then only the arm it selects, so an operand
1043
+ * that cannot produce a value on the side not taken is not this node's problem.
1044
+ *
1045
+ * @param node The ternary node (a dcg_expression_node).
1046
+ * @param user_data Unused: a node's own rule carries no context.
1047
+ * @return DCG_OK, or the code that stopped it.
1048
+ */
1049
+ static inline int c_dcg_node_expr_eval_ternary(dcg_node* node, void* user_data) {
1050
+ (void) user_data;
1051
+
1052
+ dcg_expression_node* expr = (dcg_expression_node*) node;
1053
+
1054
+ /* The condition FIRST, and then only the arm it picks: an operand on the side
1055
+ * not taken is not this node's to produce. */
1056
+ int ret_code = c_dcg_node_expr_eval_operand(expr, 0);
1057
+ if (ret_code != DCG_OK) return ret_code;
1058
+
1059
+ size_t arm = c_dcg_var_is_truthy(&expr->args[0]) ? 1 : 2;
1060
+ ret_code = c_dcg_node_expr_eval_operand(expr, arm);
1061
+ if (ret_code != DCG_OK) return ret_code;
1062
+
1063
+ node->out = expr->args[arm];
1064
+ return DCG_OK;
1065
+ }
1066
+
1067
+ /**
1068
+ * @brief The rule of a call: there is none, and saying so is the rule.
1069
+ *
1070
+ * A call's callee is composed into its display text and stored nowhere, so
1071
+ * there is nothing to apply; it reports rather than producing a value nothing
1072
+ * can read.
1073
+ *
1074
+ * @param node The call node.
1075
+ * @param user_data Unused.
1076
+ * @return DCG_ERR_TYPE.
1077
+ */
1078
+ static inline int c_dcg_node_expr_eval_call(dcg_node* node, void* user_data) {
1079
+ (void) user_data;
1080
+
1081
+ (void) fprintf(stderr, "c_dcg_node_expr_eval_call: a call node at %p has no evaluation: its callee is composed into its repr and stored nowhere\n", (const void*) node);
1082
+ return DCG_ERR_TYPE;
1083
+ }
1084
+
1085
+ /**
1086
+ * @brief `-a`: the node's one operand, negated (c_dcg_node_expr_apply_neg).
1087
+ *
1088
+ * @param node The operator node (a dcg_expression_node).
1089
+ * @param user_data Unused: a node's own rule carries no context.
1090
+ * @return DCG_OK, or a DCG_ERR_* code.
1091
+ */
1092
+ static inline int c_dcg_node_expr_eval_neg(dcg_node* node, void* user_data) {
1093
+ (void) user_data;
1094
+
1095
+ dcg_expression_node* expr = (dcg_expression_node*) node;
1096
+ int ret_code = c_dcg_node_expr_eval_operand(expr, 0); /* the operand, first */
1097
+ if (ret_code != DCG_OK) return ret_code;
1098
+ return c_dcg_node_expr_apply_neg(&node->out, &expr->args[0], NULL);
1099
+ }
1100
+
1101
+ /**
1102
+ * @brief `not a`: the node's one operand, negated as a truth (c_dcg_node_expr_apply_not).
1103
+ *
1104
+ * @param node The operator node (a dcg_expression_node).
1105
+ * @param user_data Unused: a node's own rule carries no context.
1106
+ * @return DCG_OK, or a DCG_ERR_* code.
1107
+ */
1108
+ static inline int c_dcg_node_expr_eval_not(dcg_node* node, void* user_data) {
1109
+ (void) user_data;
1110
+
1111
+ dcg_expression_node* expr = (dcg_expression_node*) node;
1112
+ int ret_code = c_dcg_node_expr_eval_operand(expr, 0); /* the operand, first */
1113
+ if (ret_code != DCG_OK) return ret_code;
1114
+ return c_dcg_node_expr_apply_not(&node->out, &expr->args[0], NULL);
1115
+ }
1116
+
1117
+ /**
1118
+ * @brief `a + b`: the node's two operands, added (c_dcg_node_expr_apply_add).
1119
+ *
1120
+ * @param node The operator node (a dcg_expression_node).
1121
+ * @param user_data Unused: a node's own rule carries no context.
1122
+ * @return DCG_OK, or a DCG_ERR_* code.
1123
+ */
1124
+ static inline int c_dcg_node_expr_eval_add(dcg_node* node, void* user_data) {
1125
+ (void) user_data;
1126
+
1127
+ dcg_expression_node* expr = (dcg_expression_node*) node;
1128
+ int ret_code = c_dcg_node_expr_eval_operand(expr, 0); /* the operands, left first */
1129
+ if (ret_code == DCG_OK) ret_code = c_dcg_node_expr_eval_operand(expr, 1);
1130
+ if (ret_code != DCG_OK) return ret_code;
1131
+ return c_dcg_node_expr_apply_add(&node->out, &expr->args[0], &expr->args[1]);
1132
+ }
1133
+
1134
+ /**
1135
+ * @brief `a - b`: the node's two operands, subtracted (c_dcg_node_expr_apply_sub).
1136
+ *
1137
+ * @param node The operator node (a dcg_expression_node).
1138
+ * @param user_data Unused: a node's own rule carries no context.
1139
+ * @return DCG_OK, or a DCG_ERR_* code.
1140
+ */
1141
+ static inline int c_dcg_node_expr_eval_sub(dcg_node* node, void* user_data) {
1142
+ (void) user_data;
1143
+
1144
+ dcg_expression_node* expr = (dcg_expression_node*) node;
1145
+ int ret_code = c_dcg_node_expr_eval_operand(expr, 0); /* the operands, left first */
1146
+ if (ret_code == DCG_OK) ret_code = c_dcg_node_expr_eval_operand(expr, 1);
1147
+ if (ret_code != DCG_OK) return ret_code;
1148
+ return c_dcg_node_expr_apply_sub(&node->out, &expr->args[0], &expr->args[1]);
1149
+ }
1150
+
1151
+ /**
1152
+ * @brief `a * b`: the node's two operands, multiplied (c_dcg_node_expr_apply_mul).
1153
+ *
1154
+ * @param node The operator node (a dcg_expression_node).
1155
+ * @param user_data Unused: a node's own rule carries no context.
1156
+ * @return DCG_OK, or a DCG_ERR_* code.
1157
+ */
1158
+ static inline int c_dcg_node_expr_eval_mul(dcg_node* node, void* user_data) {
1159
+ (void) user_data;
1160
+
1161
+ dcg_expression_node* expr = (dcg_expression_node*) node;
1162
+ int ret_code = c_dcg_node_expr_eval_operand(expr, 0); /* the operands, left first */
1163
+ if (ret_code == DCG_OK) ret_code = c_dcg_node_expr_eval_operand(expr, 1);
1164
+ if (ret_code != DCG_OK) return ret_code;
1165
+ return c_dcg_node_expr_apply_mul(&node->out, &expr->args[0], &expr->args[1]);
1166
+ }
1167
+
1168
+ /**
1169
+ * @brief `a / b`: the node's two operands, divided (c_dcg_node_expr_apply_div).
1170
+ *
1171
+ * @param node The operator node (a dcg_expression_node).
1172
+ * @param user_data Unused: a node's own rule carries no context.
1173
+ * @return DCG_OK, or a DCG_ERR_* code.
1174
+ */
1175
+ static inline int c_dcg_node_expr_eval_div(dcg_node* node, void* user_data) {
1176
+ (void) user_data;
1177
+
1178
+ dcg_expression_node* expr = (dcg_expression_node*) node;
1179
+ int ret_code = c_dcg_node_expr_eval_operand(expr, 0); /* the operands, left first */
1180
+ if (ret_code == DCG_OK) ret_code = c_dcg_node_expr_eval_operand(expr, 1);
1181
+ if (ret_code != DCG_OK) return ret_code;
1182
+ return c_dcg_node_expr_apply_div(&node->out, &expr->args[0], &expr->args[1]);
1183
+ }
1184
+
1185
+ /**
1186
+ * @brief `a // b`: the node's two operands, floor-divided (c_dcg_node_expr_apply_floordiv).
1187
+ *
1188
+ * @param node The operator node (a dcg_expression_node).
1189
+ * @param user_data Unused: a node's own rule carries no context.
1190
+ * @return DCG_OK, or a DCG_ERR_* code.
1191
+ */
1192
+ static inline int c_dcg_node_expr_eval_floordiv(dcg_node* node, void* user_data) {
1193
+ (void) user_data;
1194
+
1195
+ dcg_expression_node* expr = (dcg_expression_node*) node;
1196
+ int ret_code = c_dcg_node_expr_eval_operand(expr, 0); /* the operands, left first */
1197
+ if (ret_code == DCG_OK) ret_code = c_dcg_node_expr_eval_operand(expr, 1);
1198
+ if (ret_code != DCG_OK) return ret_code;
1199
+ return c_dcg_node_expr_apply_floordiv(&node->out, &expr->args[0], &expr->args[1]);
1200
+ }
1201
+
1202
+ /**
1203
+ * @brief `a ** b`: the node's two operands, raised (c_dcg_node_expr_apply_pow).
1204
+ *
1205
+ * @param node The operator node (a dcg_expression_node).
1206
+ * @param user_data Unused: a node's own rule carries no context.
1207
+ * @return DCG_OK, or a DCG_ERR_* code.
1208
+ */
1209
+ static inline int c_dcg_node_expr_eval_pow(dcg_node* node, void* user_data) {
1210
+ (void) user_data;
1211
+
1212
+ dcg_expression_node* expr = (dcg_expression_node*) node;
1213
+ int ret_code = c_dcg_node_expr_eval_operand(expr, 0); /* the operands, left first */
1214
+ if (ret_code == DCG_OK) ret_code = c_dcg_node_expr_eval_operand(expr, 1);
1215
+ if (ret_code != DCG_OK) return ret_code;
1216
+ return c_dcg_node_expr_apply_pow(&node->out, &expr->args[0], &expr->args[1]);
1217
+ }
1218
+
1219
+ /**
1220
+ * @brief `a == b`: the node's two operands, compared (c_dcg_node_expr_apply_eq).
1221
+ *
1222
+ * @param node The operator node (a dcg_expression_node).
1223
+ * @param user_data Unused: a node's own rule carries no context.
1224
+ * @return DCG_OK, or a DCG_ERR_* code.
1225
+ */
1226
+ static inline int c_dcg_node_expr_eval_eq(dcg_node* node, void* user_data) {
1227
+ (void) user_data;
1228
+
1229
+ dcg_expression_node* expr = (dcg_expression_node*) node;
1230
+ int ret_code = c_dcg_node_expr_eval_operand(expr, 0); /* the operands, left first */
1231
+ if (ret_code == DCG_OK) ret_code = c_dcg_node_expr_eval_operand(expr, 1);
1232
+ if (ret_code != DCG_OK) return ret_code;
1233
+ return c_dcg_node_expr_apply_eq(&node->out, &expr->args[0], &expr->args[1]);
1234
+ }
1235
+
1236
+ /**
1237
+ * @brief `a != b`: the node's two operands, compared (c_dcg_node_expr_apply_ne).
1238
+ *
1239
+ * @param node The operator node (a dcg_expression_node).
1240
+ * @param user_data Unused: a node's own rule carries no context.
1241
+ * @return DCG_OK, or a DCG_ERR_* code.
1242
+ */
1243
+ static inline int c_dcg_node_expr_eval_ne(dcg_node* node, void* user_data) {
1244
+ (void) user_data;
1245
+
1246
+ dcg_expression_node* expr = (dcg_expression_node*) node;
1247
+ int ret_code = c_dcg_node_expr_eval_operand(expr, 0); /* the operands, left first */
1248
+ if (ret_code == DCG_OK) ret_code = c_dcg_node_expr_eval_operand(expr, 1);
1249
+ if (ret_code != DCG_OK) return ret_code;
1250
+ return c_dcg_node_expr_apply_ne(&node->out, &expr->args[0], &expr->args[1]);
1251
+ }
1252
+
1253
+ /**
1254
+ * @brief `a > b`: the node's two operands, ordered (c_dcg_node_expr_apply_gt).
1255
+ *
1256
+ * @param node The operator node (a dcg_expression_node).
1257
+ * @param user_data Unused: a node's own rule carries no context.
1258
+ * @return DCG_OK, or a DCG_ERR_* code.
1259
+ */
1260
+ static inline int c_dcg_node_expr_eval_gt(dcg_node* node, void* user_data) {
1261
+ (void) user_data;
1262
+
1263
+ dcg_expression_node* expr = (dcg_expression_node*) node;
1264
+ int ret_code = c_dcg_node_expr_eval_operand(expr, 0); /* the operands, left first */
1265
+ if (ret_code == DCG_OK) ret_code = c_dcg_node_expr_eval_operand(expr, 1);
1266
+ if (ret_code != DCG_OK) return ret_code;
1267
+ return c_dcg_node_expr_apply_gt(&node->out, &expr->args[0], &expr->args[1]);
1268
+ }
1269
+
1270
+ /**
1271
+ * @brief `a >= b`: the node's two operands, ordered (c_dcg_node_expr_apply_ge).
1272
+ *
1273
+ * @param node The operator node (a dcg_expression_node).
1274
+ * @param user_data Unused: a node's own rule carries no context.
1275
+ * @return DCG_OK, or a DCG_ERR_* code.
1276
+ */
1277
+ static inline int c_dcg_node_expr_eval_ge(dcg_node* node, void* user_data) {
1278
+ (void) user_data;
1279
+
1280
+ dcg_expression_node* expr = (dcg_expression_node*) node;
1281
+ int ret_code = c_dcg_node_expr_eval_operand(expr, 0); /* the operands, left first */
1282
+ if (ret_code == DCG_OK) ret_code = c_dcg_node_expr_eval_operand(expr, 1);
1283
+ if (ret_code != DCG_OK) return ret_code;
1284
+ return c_dcg_node_expr_apply_ge(&node->out, &expr->args[0], &expr->args[1]);
1285
+ }
1286
+
1287
+ /**
1288
+ * @brief `a < b`: the node's two operands, ordered (c_dcg_node_expr_apply_lt).
1289
+ *
1290
+ * @param node The operator node (a dcg_expression_node).
1291
+ * @param user_data Unused: a node's own rule carries no context.
1292
+ * @return DCG_OK, or a DCG_ERR_* code.
1293
+ */
1294
+ static inline int c_dcg_node_expr_eval_lt(dcg_node* node, void* user_data) {
1295
+ (void) user_data;
1296
+
1297
+ dcg_expression_node* expr = (dcg_expression_node*) node;
1298
+ int ret_code = c_dcg_node_expr_eval_operand(expr, 0); /* the operands, left first */
1299
+ if (ret_code == DCG_OK) ret_code = c_dcg_node_expr_eval_operand(expr, 1);
1300
+ if (ret_code != DCG_OK) return ret_code;
1301
+ return c_dcg_node_expr_apply_lt(&node->out, &expr->args[0], &expr->args[1]);
1302
+ }
1303
+
1304
+ /**
1305
+ * @brief `a <= b`: the node's two operands, ordered (c_dcg_node_expr_apply_le).
1306
+ *
1307
+ * @param node The operator node (a dcg_expression_node).
1308
+ * @param user_data Unused: a node's own rule carries no context.
1309
+ * @return DCG_OK, or a DCG_ERR_* code.
1310
+ */
1311
+ static inline int c_dcg_node_expr_eval_le(dcg_node* node, void* user_data) {
1312
+ (void) user_data;
1313
+
1314
+ dcg_expression_node* expr = (dcg_expression_node*) node;
1315
+ int ret_code = c_dcg_node_expr_eval_operand(expr, 0); /* the operands, left first */
1316
+ if (ret_code == DCG_OK) ret_code = c_dcg_node_expr_eval_operand(expr, 1);
1317
+ if (ret_code != DCG_OK) return ret_code;
1318
+ return c_dcg_node_expr_apply_le(&node->out, &expr->args[0], &expr->args[1]);
1319
+ }
1320
+
1321
+ /**
1322
+ * @brief `a and b`: the node's two operands, the operand one of them picks
1323
+ * (c_dcg_node_expr_apply_and).
1324
+ *
1325
+ * @param node The operator node (a dcg_expression_node).
1326
+ * @param user_data Unused: a node's own rule carries no context.
1327
+ * @return DCG_OK, or a DCG_ERR_* code.
1328
+ */
1329
+ static inline int c_dcg_node_expr_eval_and(dcg_node* node, void* user_data) {
1330
+ (void) user_data;
1331
+
1332
+ dcg_expression_node* expr = (dcg_expression_node*) node;
1333
+ int ret_code = c_dcg_node_expr_eval_operand(expr, 0); /* the operands, left first */
1334
+ if (ret_code == DCG_OK) ret_code = c_dcg_node_expr_eval_operand(expr, 1);
1335
+ if (ret_code != DCG_OK) return ret_code;
1336
+ return c_dcg_node_expr_apply_and(&node->out, &expr->args[0], &expr->args[1]);
1337
+ }
1338
+
1339
+ /**
1340
+ * @brief `a or b`: the node's two operands, the operand one of them picks
1341
+ * (c_dcg_node_expr_apply_or).
1342
+ *
1343
+ * @param node The operator node (a dcg_expression_node).
1344
+ * @param user_data Unused: a node's own rule carries no context.
1345
+ * @return DCG_OK, or a DCG_ERR_* code.
1346
+ */
1347
+ static inline int c_dcg_node_expr_eval_or(dcg_node* node, void* user_data) {
1348
+ (void) user_data;
1349
+
1350
+ dcg_expression_node* expr = (dcg_expression_node*) node;
1351
+ int ret_code = c_dcg_node_expr_eval_operand(expr, 0); /* the operands, left first */
1352
+ if (ret_code == DCG_OK) ret_code = c_dcg_node_expr_eval_operand(expr, 1);
1353
+ if (ret_code != DCG_OK) return ret_code;
1354
+ return c_dcg_node_expr_apply_or(&node->out, &expr->args[0], &expr->args[1]);
1355
+ }
1356
+
1357
+ // ========== The Flat List of Rules ==========
1358
+
1359
+ /**
1360
+ * @brief Where an operator's rule sits in the flat list of type-eval rules.
1361
+ *
1362
+ * The operator code carries its own two dimensions - the family nibble and the
1363
+ * variant number - and the list is those two flattened into one: the family's
1364
+ * slots first, the variant within them. A rule is therefore found by ARITHMETIC
1365
+ * on the code, never by a search of it.
1366
+ *
1367
+ * @param op Operator code.
1368
+ * @return The operator's slot in DCG_EXPR_EVAL_FNS.
1369
+ */
1370
+ static inline size_t c_dcg_op_code_index(dcg_op_code op) {
1371
+ return (size_t) (((((int) op & DCG_OP_FAMILY_MASK) >> 8) * DCG_OP_FAMILY_SLOTS) + ((int) op & DCG_OP_VARIANT_MASK));
1372
+ }
1373
+
1374
+ /**
1375
+ * @brief The flat list of the family's type-eval rules - one per operator.
1376
+ *
1377
+ * THE LIST THE ONE-TIME CHOICE READS (c_dcg_node_expr_set_op): a node's rule is
1378
+ * its operator's entry here, taken once, when the operator is set, and kept on
1379
+ * the node from then on. Evaluating the node is a call through that pointer, so
1380
+ * nothing searches this list at run time.
1381
+ *
1382
+ * The slots follow the operator codes themselves - the family's slots first,
1383
+ * then the variant number within the family - so the index is arithmetic on the
1384
+ * code (c_dcg_op_code_index) and a reader can see where each rule sits. A slot
1385
+ * that names no operator with a rule of its own holds the rule that says so,
1386
+ * which is what lets a node be given ANY code and still evaluate to an answer
1387
+ * rather than to a crash.
1388
+ *
1389
+ * The rules that belong to an ARITY rather than to an operator - the
1390
+ * if-expression and the call - are not here: they are chosen by the node's type
1391
+ * as it is built (c_dcg_node_expr_eval_of_arity), because those nodes carry no
1392
+ * operator to be indexed with.
1393
+ */
1394
+ static const dcg_node_hook_fn DCG_EXPR_EVAL_FNS[DCG_OP_FAMILIES * DCG_OP_FAMILY_SLOTS] = {
1395
+ /* 0x00__: no operator */
1396
+ c_dcg_node_expr_eval_unknown, // 0x0000 DCG_OP_NONE
1397
+ c_dcg_node_expr_eval_unknown, // 0x0001
1398
+ c_dcg_node_expr_eval_unknown, // 0x0002
1399
+ c_dcg_node_expr_eval_unknown, // 0x0003
1400
+ c_dcg_node_expr_eval_unknown, // 0x0004
1401
+ c_dcg_node_expr_eval_unknown, // 0x0005
1402
+ c_dcg_node_expr_eval_unknown, // 0x0006
1403
+ c_dcg_node_expr_eval_unknown, // 0x0007
1404
+ /* 0x01__: arithmetic - the head, then six binary operators and one unary */
1405
+ c_dcg_node_expr_eval_unknown, // 0x0100 DCG_OP_ARITH (the family head)
1406
+ c_dcg_node_expr_eval_add, // 0x0101 DCG_OP_ADD
1407
+ c_dcg_node_expr_eval_sub, // 0x0102 DCG_OP_SUB
1408
+ c_dcg_node_expr_eval_mul, // 0x0103 DCG_OP_MUL
1409
+ c_dcg_node_expr_eval_div, // 0x0104 DCG_OP_DIV
1410
+ c_dcg_node_expr_eval_floordiv, // 0x0105 DCG_OP_FLOORDIV
1411
+ c_dcg_node_expr_eval_pow, // 0x0106 DCG_OP_POW
1412
+ c_dcg_node_expr_eval_neg, // 0x0107 DCG_OP_NEG (unary)
1413
+ /* 0x02__: comparison */
1414
+ c_dcg_node_expr_eval_unknown, // 0x0200 DCG_OP_COMPARE (the family head)
1415
+ c_dcg_node_expr_eval_eq, // 0x0201 DCG_OP_EQ
1416
+ c_dcg_node_expr_eval_ne, // 0x0202 DCG_OP_NE
1417
+ c_dcg_node_expr_eval_gt, // 0x0203 DCG_OP_GT
1418
+ c_dcg_node_expr_eval_ge, // 0x0204 DCG_OP_GE
1419
+ c_dcg_node_expr_eval_lt, // 0x0205 DCG_OP_LT
1420
+ c_dcg_node_expr_eval_le, // 0x0206 DCG_OP_LE
1421
+ c_dcg_node_expr_eval_unknown, // 0x0207
1422
+ /* 0x03__: logical */
1423
+ c_dcg_node_expr_eval_unknown, // 0x0300 DCG_OP_LOGIC (the family head)
1424
+ c_dcg_node_expr_eval_and, // 0x0301 DCG_OP_AND
1425
+ c_dcg_node_expr_eval_or, // 0x0302 DCG_OP_OR
1426
+ c_dcg_node_expr_eval_not, // 0x0303 DCG_OP_NOT (unary)
1427
+ c_dcg_node_expr_eval_unknown, // 0x0304
1428
+ c_dcg_node_expr_eval_unknown, // 0x0305
1429
+ c_dcg_node_expr_eval_unknown, // 0x0306
1430
+ c_dcg_node_expr_eval_unknown, // 0x0307
1431
+ /* 0x04__: access - an attribute or an item read is not computed here, so its
1432
+ * slots refuse rather than evaluate */
1433
+ c_dcg_node_expr_eval_unknown, // 0x0400 DCG_OP_ACCESS (the family head)
1434
+ c_dcg_node_expr_eval_refuse, // 0x0401 DCG_OP_ATTR
1435
+ c_dcg_node_expr_eval_refuse, // 0x0402 DCG_OP_GETITEM
1436
+ c_dcg_node_expr_eval_unknown, // 0x0403
1437
+ c_dcg_node_expr_eval_unknown, // 0x0404
1438
+ c_dcg_node_expr_eval_unknown, // 0x0405
1439
+ c_dcg_node_expr_eval_unknown, // 0x0406
1440
+ c_dcg_node_expr_eval_unknown // 0x0407
1441
+ };
1442
+
1443
+ /**
1444
+ * @brief The rule a node is born with: its arity's own.
1445
+ *
1446
+ * Every operator node is built here (c_dcg_node_new_expr), before any operator
1447
+ * is set: an operator node is given the refusing rule until its operator
1448
+ * arrives, and a node whose arity has no operator to apply - the if-expression,
1449
+ * the call - is given the rule that IS its evaluation. Those two cannot come
1450
+ * from the flat list: an if-expression and a call carry no operator for the list
1451
+ * to be indexed with, so their rule is the arity's, chosen once, here.
1452
+ *
1453
+ * @param ntype The node's type.
1454
+ * @return The rule to install - the refusing one for a node awaiting an
1455
+ * operator, and the reporting one for a type this family has no arity
1456
+ * to evaluate.
1457
+ */
1458
+ static inline dcg_node_hook_fn c_dcg_node_expr_eval_of_arity(dcg_node_type ntype) {
1459
+ switch (ntype) {
1460
+ case DCG_NODE_TERNARY:
1461
+ return c_dcg_node_expr_eval_ternary;
1462
+ case DCG_NODE_CALL:
1463
+ return c_dcg_node_expr_eval_call;
1464
+ case DCG_NODE_UNARY:
1465
+ case DCG_NODE_BINARY:
1466
+ return c_dcg_node_expr_eval_refuse;
1467
+ default:
1468
+ return c_dcg_node_expr_eval_unknown;
1469
+ }
1470
+ }
1471
+
1472
+ /**
1473
+ * @brief The family's evaluation of a node that carries no rule of its own: the
1474
+ * rule found from the code - the DISPATCH.
1475
+ *
1476
+ * What a build with DCG_EVAL_DIRECT_HOOKS OFF reaches, through the protocol's
1477
+ * dispatch (c_dcg_node_eval_default): those nodes were taught nothing as they
1478
+ * were built, so the rule is looked up here instead. It finds the same rule the
1479
+ * injected build keeps ON the node - one rule, reached two ways, so the two
1480
+ * builds cannot compute different values - by the same two questions the
1481
+ * injection asks once: the operator, when it belongs to this node's arity, and
1482
+ * the arity itself when it does not (an if-expression and a call carry no
1483
+ * operator to look one up with).
1484
+ *
1485
+ * @param node Expression node to evaluate.
1486
+ * @param user_data Unused: the rule it finds carries no context.
1487
+ * @return DCG_OK, or a DCG_ERR_* code.
1488
+ */
1489
+ static inline int c_dcg_node_expr_eval(dcg_node* node, void* user_data) {
1490
+ dcg_expression_node* expr = (dcg_expression_node*) node;
1491
+ if (c_dcg_op_code_node_type(expr->op) == node->ntype) return DCG_EXPR_EVAL_FNS[c_dcg_op_code_index(expr->op)](node, user_data);
1492
+ return c_dcg_node_expr_eval_of_arity(node->ntype)(node, user_data);
1493
+ }
1494
+
1495
+ /**
1496
+ * @brief Which kernel applies an operator, in one arity - the APPLY-SIDE lookup.
1497
+ *
1498
+ * For a caller that has an operator CODE and no node (c_dcg_node_expr_apply_unary
1499
+ * / c_dcg_node_expr_apply_binary). A node is not this function's business any
1500
+ * more: which rule EVALUATES a node was settled when its operator was set, and
1501
+ * the rule reads the kernel it needs directly.
1502
+ *
1503
+ * An op this arity has no kernel for - a comparison applied as a unary, AND as a
1504
+ * unary, the access ops, a family mask - answers with
1505
+ * c_dcg_node_expr_apply_refuse(), so a caller never has to ask whether it has an
1506
+ * answer. A TERNARY or a CALL has no operator to apply at all and answers NULL:
1507
+ * the ternary reads the arm its condition picks, and a call's callee is composed
1508
+ * into its display text and stored nowhere.
1509
+ *
1510
+ * @param ntype The arity whose kernels to search.
1511
+ * @param op The operator code.
1512
+ * @return The kernel for (arity, op), or NULL for an arity with no operator.
1513
+ */
1514
+ static inline dcg_expr_apply_fn c_dcg_node_expr_apply_fn_of(dcg_node_type ntype, dcg_op_code op) {
1515
+ if (ntype == DCG_NODE_UNARY) {
1516
+ switch (op) {
1517
+ case DCG_OP_NEG:
1518
+ return c_dcg_node_expr_apply_neg;
1519
+ case DCG_OP_NOT:
1520
+ return c_dcg_node_expr_apply_not;
1521
+ default:
1522
+ return c_dcg_node_expr_apply_refuse;
1523
+ }
1524
+ }
1525
+ if (ntype == DCG_NODE_BINARY) {
1526
+ switch (op) {
1527
+ case DCG_OP_ADD:
1528
+ return c_dcg_node_expr_apply_add;
1529
+ case DCG_OP_SUB:
1530
+ return c_dcg_node_expr_apply_sub;
1531
+ case DCG_OP_MUL:
1532
+ return c_dcg_node_expr_apply_mul;
1533
+ case DCG_OP_DIV:
1534
+ return c_dcg_node_expr_apply_div;
1535
+ case DCG_OP_FLOORDIV:
1536
+ return c_dcg_node_expr_apply_floordiv;
1537
+ case DCG_OP_POW:
1538
+ return c_dcg_node_expr_apply_pow;
1539
+ case DCG_OP_EQ:
1540
+ return c_dcg_node_expr_apply_eq;
1541
+ case DCG_OP_NE:
1542
+ return c_dcg_node_expr_apply_ne;
1543
+ case DCG_OP_GT:
1544
+ return c_dcg_node_expr_apply_gt;
1545
+ case DCG_OP_GE:
1546
+ return c_dcg_node_expr_apply_ge;
1547
+ case DCG_OP_LT:
1548
+ return c_dcg_node_expr_apply_lt;
1549
+ case DCG_OP_LE:
1550
+ return c_dcg_node_expr_apply_le;
1551
+ case DCG_OP_AND:
1552
+ return c_dcg_node_expr_apply_and;
1553
+ case DCG_OP_OR:
1554
+ return c_dcg_node_expr_apply_or;
1555
+ default:
1556
+ return c_dcg_node_expr_apply_refuse;
1557
+ }
1558
+ }
1559
+ return NULL;
1560
+ }
1561
+
1562
+ /**
1563
+ * @brief Set a node's operator, and - when the build injects - the rule that
1564
+ * evaluates it.
1565
+ *
1566
+ * The operator is known when the node is built - the constructors take the code
1567
+ * - so the ONE-TIME choice is here, out of the flat list (DCG_EXPR_EVAL_FNS).
1568
+ * With DCG_EVAL_DIRECT_HOOKS on, the rule is installed on the node as its
1569
+ * type-eval rule (dcg_node_eval_ctx.type_eval_fn) and every evaluation after that
1570
+ * is a call through that pointer; with it off, nothing is installed and the rule
1571
+ * is found again at evaluation time (c_dcg_node_expr_eval) - the same rule, the
1572
+ * same two questions, asked at the other end.
1573
+ *
1574
+ * Only an operator that BELONGS to the node's arity installs anything. A node
1575
+ * whose arity has no operator to apply keeps the rule it was born with - the
1576
+ * if-expression, the call - and an operator of another arity (a comparison
1577
+ * handed to a unary node, a family head, a code with no rule at all) leaves the
1578
+ * refusing rule standing. The two never disagree, because the code and the rule
1579
+ * are written together here and nowhere else.
1580
+ *
1581
+ * @param node Expression node to set it on.
1582
+ * @param op Operator code.
1583
+ * @return DCG_OK, DCG_ERR_INVALID_ARG, or DCG_ERR_BUSY (a frozen node).
1584
+ */
1585
+ static inline int c_dcg_node_expr_set_op(dcg_expression_node* node, dcg_op_code op) {
1586
+ if (!node) return DCG_ERR_INVALID_ARG;
1587
+ if (node->base.flags & DCG_NODE_FLAG_FROZEN) return DCG_ERR_BUSY; /* baked: the operator is what it is */
1588
+
1589
+ node->op = op;
1590
+ #if DCG_EVAL_DIRECT_HOOKS
1591
+ if (c_dcg_op_code_node_type(op) == node->base.ntype) node->base.eval_ctx.type_eval_fn = DCG_EXPR_EVAL_FNS[c_dcg_op_code_index(op)];
1592
+ #endif
1593
+ return DCG_OK;
1594
+ }
1595
+
1596
+ /**
1597
+ * @brief The value of a unary operator over one operand - by operator code.
1598
+ *
1599
+ * The runtime dispatch, for a caller that has a code and no node: the arity's
1600
+ * kernel for the operator is looked up and called, so the answer is the one the
1601
+ * operator's own evaluation reaches - c_dcg_node_expr_eval_neg or
1602
+ * c_dcg_node_expr_eval_not read their operand and call the same kernel.
1603
+ *
1604
+ * @param out Receives the value.
1605
+ * @param op Operator (DCG_OP_NEG, DCG_OP_NOT).
1606
+ * @param a The operand.
1607
+ * @return DCG_OK, DCG_ERR_TYPE (an operator the unary arity has no meaning for)
1608
+ * or DCG_ERR_MATH when the operand is outside the operator's domain.
1609
+ */
1610
+ static inline int c_dcg_node_expr_apply_unary(dcg_var_t* out, dcg_op_code op, const dcg_var_t* a) {
1611
+ return c_dcg_node_expr_apply_fn_of(DCG_NODE_UNARY, op)(out, a, NULL);
1612
+ }
1613
+
1614
+ /**
1615
+ * @brief The value of a binary operator over two operands - by operator code.
1616
+ *
1617
+ * The runtime dispatch, for a caller that has a code and no node: the arity's
1618
+ * kernel for the operator is looked up and called, so the answer is the one the
1619
+ * operator's own evaluation reaches - c_dcg_node_expr_eval_add and its siblings
1620
+ * read their two operands and call the same kernel.
1621
+ *
1622
+ * @param out Receives the value.
1623
+ * @param op Operator.
1624
+ * @param a Left operand.
1625
+ * @param b Right operand.
1626
+ * @return DCG_OK, DCG_ERR_TYPE (an operator this arity has no kernel for) or
1627
+ * DCG_ERR_MATH.
1628
+ */
1629
+ static inline int c_dcg_node_expr_apply_binary(dcg_var_t* out, dcg_op_code op, const dcg_var_t* a, const dcg_var_t* b) {
1630
+ return c_dcg_node_expr_apply_fn_of(DCG_NODE_BINARY, op)(out, a, b);
1631
+ }
1632
+
1633
+ // ========== The Workspace, at Run Time ==========
1634
+
1635
+ /*
1636
+ * The slot array is a WORKSPACE: an operand's value is produced by RUNNING the
1637
+ * node it was bound to, and what comes back is written into the slot as a
1638
+ * LITERAL - a reference is read through here, once, so the rule that runs
1639
+ * afterwards finds plain values and never has to ask where an operand came from
1640
+ * or follow anything.
1641
+ *
1642
+ * Producing them is the FAMILY's business, over its own fields: `components` and
1643
+ * `args` are the expression node's, and the rule that needs its operands asks for
1644
+ * them itself, slot by slot - it knows how many its operator takes, so nothing
1645
+ * counts anything at run time. What running a node MEANS is still the protocol's
1646
+ * - the entry is c_dcg_node_eval, declared above - and the walk knows nothing
1647
+ * about operands at all.
1648
+ *
1649
+ * ONE SLOT AT A TIME, and that is the contract's shape rather than a convenience:
1650
+ * a component is run and its slot filled before the next one is touched, because
1651
+ * two operands may share storage and what one evaluation writes may be what the
1652
+ * other reads.
1653
+ *
1654
+ * What a rule does NOT do here is check its node over: a slot with no component
1655
+ * bound is a malformed node, refused by c_dcg_node_eval() itself, and holding
1656
+ * every rule to a shape that the bake pass is what establishes would be a test
1657
+ * paid on every evaluation.
1658
+ */
1659
+
1660
+ /**
1661
+ * @brief Run ONE component and write what it is worth into its operand slot.
1662
+ *
1663
+ * The operand's value of the moment, fully dereferenced: the component's own
1664
+ * evaluation produces its value into its out slot, and what lands here is that
1665
+ * value as a literal - not a way back to it.
1666
+ *
1667
+ * @param node Expression node whose operand to produce.
1668
+ * @param index Which operand.
1669
+ * @return DCG_OK, or the code the component's own evaluation stopped with
1670
+ * (DCG_ERR_INVALID_ARG included: a slot with no component bound has
1671
+ * nothing to run).
1672
+ */
1673
+ static inline int c_dcg_node_expr_eval_operand(dcg_expression_node* node, size_t index) {
1674
+ dcg_node* component = node->components[index];
1675
+
1676
+ int ret_code = c_dcg_node_eval(component);
1677
+ if (ret_code != DCG_OK) return ret_code;
1678
+
1679
+ c_dcg_var_snapshot(&node->args[index], &component->out);
1680
+ return DCG_OK;
1681
+ }
1682
+
1683
+ // ========== Public APIs - Typed Constructors ==========
1684
+
1685
+ /**
1686
+ * @brief Allocate an expression over one input node.
1687
+ *
1688
+ * @param op Operator code (e.g. DCG_OP_NEG, DCG_OP_NOT).
1689
+ * @param src Input node the operand refers to.
1690
+ * @param allocator Allocator for the block; NULL falls back to the plain heap.
1691
+ * @return The node, or NULL on OOM / invalid type / a missing input.
1692
+ */
1693
+ static inline dcg_expression_node* c_dcg_node_new_expr_unary(dcg_op_code op, dcg_node* src, allocator_protocol* allocator) {
1694
+ if (!src) return NULL;
1695
+
1696
+ dcg_expression_node* node = c_dcg_node_new_expr(1, DCG_NODE_UNARY, allocator);
1697
+ if (!node) return NULL;
1698
+
1699
+ (void) c_dcg_node_expr_set_op(node, op);
1700
+ if (c_dcg_node_expr_bind(node, 0, src) != DCG_OK) {
1701
+ c_ap_free_owned(node);
1702
+ return NULL;
1703
+ }
1704
+
1705
+ dcg_node* inputs[1] = {src};
1706
+ char repr[DCG_NODE_STRING_MAXLEN];
1707
+
1708
+ if (c_dcg_node_expr_op_style(inputs, 1, op, repr, sizeof(repr)) < 0 || c_dcg_node_set_repr(&node->base, repr) != DCG_OK) {
1709
+ c_ap_free_owned(node);
1710
+ return NULL;
1711
+ }
1712
+ return node;
1713
+ }
1714
+
1715
+ /**
1716
+ * @brief Allocate an expression over two input nodes, the left one first.
1717
+ *
1718
+ * @param op Operator code (arithmetic, comparison, logical or access).
1719
+ * @param var_0 Left input node.
1720
+ * @param var_1 Right input node.
1721
+ * @param allocator Allocator for the block; NULL falls back to the plain heap.
1722
+ * @return The node, or NULL on OOM / invalid type / a missing input.
1723
+ */
1724
+ static inline dcg_expression_node* c_dcg_node_new_expr_binary(dcg_op_code op, dcg_node* var_0, dcg_node* var_1, allocator_protocol* allocator) {
1725
+ if (!var_0 || !var_1) return NULL;
1726
+
1727
+ dcg_expression_node* node = c_dcg_node_new_expr(2, DCG_NODE_BINARY, allocator);
1728
+ if (!node) return NULL;
1729
+
1730
+ (void) c_dcg_node_expr_set_op(node, op);
1731
+ if (c_dcg_node_expr_bind(node, 0, var_0) != DCG_OK || c_dcg_node_expr_bind(node, 1, var_1) != DCG_OK) {
1732
+ c_ap_free_owned(node);
1733
+ return NULL;
1734
+ }
1735
+
1736
+ dcg_node* inputs[2] = {var_0, var_1};
1737
+ char repr[DCG_NODE_STRING_MAXLEN];
1738
+
1739
+ if (c_dcg_node_expr_op_style(inputs, 2, op, repr, sizeof(repr)) < 0 || c_dcg_node_set_repr(&node->base, repr) != DCG_OK) {
1740
+ c_ap_free_owned(node);
1741
+ return NULL;
1742
+ }
1743
+ return node;
1744
+ }
1745
+
1746
+ /**
1747
+ * @brief Allocate an expression over three input nodes (cond, then, else).
1748
+ *
1749
+ * @param op Operator code (DCG_OP_NONE for the plain if-expression).
1750
+ * @param var_0 Condition input node.
1751
+ * @param var_1 Input node taken when the condition holds.
1752
+ * @param var_2 Input node taken when it does not.
1753
+ * @param allocator Allocator for the block; NULL falls back to the plain heap.
1754
+ * @return The node, or NULL on OOM / invalid type / a missing input.
1755
+ */
1756
+ static inline dcg_expression_node* c_dcg_node_new_expr_ternary(dcg_op_code op, dcg_node* var_0, dcg_node* var_1, dcg_node* var_2, allocator_protocol* allocator) {
1757
+ if (!var_0 || !var_1 || !var_2) return NULL;
1758
+
1759
+ dcg_expression_node* node = c_dcg_node_new_expr(3, DCG_NODE_TERNARY, allocator);
1760
+ if (!node) return NULL;
1761
+
1762
+ (void) c_dcg_node_expr_set_op(node, op);
1763
+ for (size_t i = 0; i < 3; i++) {
1764
+ dcg_node* input = i == 0 ? var_0 : (i == 1 ? var_1 : var_2);
1765
+ if (c_dcg_node_expr_bind(node, i, input) != DCG_OK) {
1766
+ c_ap_free_owned(node);
1767
+ return NULL;
1768
+ }
1769
+ }
1770
+
1771
+ dcg_node* inputs[3] = {var_0, var_1, var_2};
1772
+ char repr[DCG_NODE_STRING_MAXLEN];
1773
+
1774
+ if (c_dcg_node_expr_op_style(inputs, 3, op, repr, sizeof(repr)) < 0 || c_dcg_node_set_repr(&node->base, repr) != DCG_OK) {
1775
+ c_ap_free_owned(node);
1776
+ return NULL;
1777
+ }
1778
+ return node;
1779
+ }
1780
+
1781
+ /**
1782
+ * @brief Allocate a variadic call over an array of input nodes.
1783
+ *
1784
+ * A call has no symbol of its own, so its repr is given rather than inferred.
1785
+ *
1786
+ * @param op Operator code.
1787
+ * @param vars Input nodes, in order (at least one).
1788
+ * @param n_vars Number of input nodes.
1789
+ * @param repr The callee's name; the repr is composed from it (may be NULL).
1790
+ * @param allocator Allocator for the block; NULL falls back to the plain heap.
1791
+ * @return The node, or NULL on OOM / invalid type / a missing input.
1792
+ */
1793
+ static inline dcg_expression_node* c_dcg_node_new_expr_call(dcg_op_code op, dcg_node** vars, size_t n_vars, const char* repr, allocator_protocol* allocator) {
1794
+ if (!vars || n_vars == 0) return NULL;
1795
+
1796
+ dcg_expression_node* node = c_dcg_node_new_expr(n_vars, DCG_NODE_CALL, allocator);
1797
+ if (!node) return NULL;
1798
+
1799
+ (void) c_dcg_node_expr_set_op(node, op);
1800
+ for (size_t i = 0; i < n_vars; i++) {
1801
+ if (c_dcg_node_expr_bind(node, i, vars[i]) != DCG_OK) {
1802
+ c_ap_free_owned(node);
1803
+ return NULL;
1804
+ }
1805
+ }
1806
+ /* A call is written as the function it calls: the repr the caller gives is
1807
+ * the callee's name, and the operands are written as its arguments. */
1808
+ char composed[DCG_NODE_STRING_MAXLEN];
1809
+
1810
+ if (c_dcg_node_expr_func_style(vars, n_vars, op, repr, composed, sizeof(composed)) < 0 || c_dcg_node_set_repr(&node->base, composed) != DCG_OK) {
1811
+ c_ap_free_owned(node);
1812
+ return NULL;
1813
+ }
1814
+ return node;
1815
+ }
1816
+
1817
+ // ========== Public APIs - Operands ==========
1818
+
1819
+ /**
1820
+ * @brief Bind an input node to one operand slot.
1821
+ *
1822
+ * What lands in the slot depends on what the input is:
1823
+ *
1824
+ * - a variable node already refers to the value it reflects, so its out is
1825
+ * taken as it is: the operand is that same reference, one hop from the value;
1826
+ * - a constant is known when the graph is baked, so its VALUE is folded in -
1827
+ * a string is copied into a block nested under the expression, and a scalar,
1828
+ * pointer or container is stored as it is;
1829
+ * - anything else is a node whose value is produced at evaluation time, so the
1830
+ * slot becomes a reference to its out slot and reads it when read.
1831
+ *
1832
+ * The input must outlive the expression in the second case, as any reference
1833
+ * requires; in the first case the expression is self-contained.
1834
+ *
1835
+ * What is written here is the graph AS BUILT, and an evaluation overwrites it:
1836
+ * before the operator runs, the node's own rule re-fills every slot with the
1837
+ * value of the moment (see c_dcg_node_expr_eval_operand). So a populated slot is
1838
+ * a starting point for reading the graph without running it - which is what
1839
+ * static analysis and the bake pass want - and never a promise about what an
1840
+ * evaluation will use. The components array is the record that stays true.
1841
+ *
1842
+ * @param node Expression to modify.
1843
+ * @param index Operand index.
1844
+ * @param input Node whose value the operand takes.
1845
+ * @return DCG_OK, DCG_ERR_INVALID_ARG, DCG_ERR_RANGE, DCG_ERR_BUSY (a frozen
1846
+ * node), or DCG_ERR_OOM.
1847
+ */
1848
+ static inline int c_dcg_node_expr_bind(dcg_expression_node* node, size_t index, dcg_node* input) {
1849
+ if (!node || !input) return DCG_ERR_INVALID_ARG;
1850
+ if (index >= node->n_args) return DCG_ERR_RANGE;
1851
+ if (node->base.flags & DCG_NODE_FLAG_FROZEN) return DCG_ERR_BUSY; /* baked: the operands are what they are */
1852
+
1853
+ dcg_var_t* slot = &node->args[index];
1854
+ int ret_code = DCG_OK;
1855
+
1856
+ /* A variable node holds no value of its own: its out is already a reference
1857
+ * to the value it reflects. Taking it as it is keeps the operand one hop
1858
+ * from the value - wrapping it would make a reference to a reference and
1859
+ * charge every read an indirection that reads nothing. */
1860
+ if (input->ntype == DCG_NODE_VARIABLE) {
1861
+ *slot = input->out;
1862
+ }
1863
+ else if (c_dcg_node_type_is_input(input->ntype)) {
1864
+ if (input->out.dtype != VAR_TYPE_STRING || !input->out.value.as_string) {
1865
+ *slot = input->out; /* a value, borrowed as the constant holds it */
1866
+ }
1867
+ else {
1868
+ size_t len = strlen(input->out.value.as_string);
1869
+ char* copy = (char*) c_ap_alloc_child(len + 1, NULL, node);
1870
+ if (!copy) return DCG_ERR_OOM;
1871
+ memcpy(copy, input->out.value.as_string, len + 1);
1872
+ ret_code = c_dcg_var_init_string(slot, copy);
1873
+ }
1874
+ }
1875
+ else {
1876
+ ret_code = c_dcg_var_init_ref(slot, &input->out);
1877
+ }
1878
+ if (ret_code != DCG_OK) return ret_code;
1879
+
1880
+ /* The hold goes on last, so a slot that could not be filled leaves the
1881
+ * previous operand standing. It goes on EVERY operand, whatever the slot
1882
+ * took: a slot filled with a value is self-contained as far as reading goes,
1883
+ * but the operand is still what the slot was made from, and an expression is
1884
+ * read back by whoever built it - a debugger, a renderer, a second bind -
1885
+ * through exactly this array. One rule, one reference count, no case that
1886
+ * has to be reasoned about twice.
1887
+ *
1888
+ * The new reference is taken before the old one is given back: rebinding a
1889
+ * slot to the operand it already holds, or two expressions trading one, must
1890
+ * not drop the last reference between the two steps. */
1891
+ dcg_node* previous = node->components[index];
1892
+ if (previous == input) return DCG_OK;
1893
+
1894
+ c_ap_incref(input);
1895
+ if (previous) c_ap_decref(previous);
1896
+ node->components[index] = input;
1897
+ return DCG_OK;
1898
+ }
1899
+
1900
+ #endif // C_DCG_BAKE_EXPR_H