PyDecisionGraph 0.3.2.post1__cp315-cp315-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.cp315-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.cp315-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.cp315-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.cp315-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.cp315-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.cp315-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.cp315-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.cp315-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.cp315-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.cp315-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.cp315-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.cp315-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.cp315-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.cp315-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.cp315-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.cp315-win_amd64.pyd +0 -0
  148. tests/bake_eval_probe.cp315-win_amd64.pyd +0 -0
@@ -0,0 +1,511 @@
1
+ #ifndef C_DCG_BAKE_EDGE_H
2
+ #define C_DCG_BAKE_EDGE_H
3
+
4
+ #include <stdbool.h>
5
+ #include <stdint.h>
6
+ #include <stdio.h>
7
+ #include <string.h>
8
+
9
+ #include <cbase/allocator_protocol/c_allocator_protocol.h>
10
+
11
+ #include <decision_graph/decision_tree/bake/c_var.h>
12
+
13
+ // ========== Constants ==========
14
+
15
+ /** Capacity of a condition's inline display buffer. */
16
+ #ifndef DCG_EDGE_REPR_MAXLEN
17
+ #define DCG_EDGE_REPR_MAXLEN 128
18
+ #endif
19
+
20
+ // ========== Structs ==========
21
+
22
+ // clang-format off
23
+
24
+ typedef enum dcg_node_edge_type {
25
+ DCG_NODE_EDGE_VALUE = 0, // A caller-owned condition: `value` is what selects the edge. Also what a zeroed condition reads as.
26
+ DCG_NODE_EDGE_NONE = 1, // The unconditional edge.
27
+ DCG_NODE_EDGE_ELSE = 2, // The fallback: always taken last.
28
+ DCG_NODE_EDGE_AUTO = 3, // Unresolved - the parent infers which arm the edge belongs to.
29
+ DCG_NODE_EDGE_TRUE = 4, // The true arm of a two-way branch.
30
+ DCG_NODE_EDGE_FALSE = 5 // The false arm of a two-way branch.
31
+ } dcg_node_edge_type;
32
+
33
+ /**
34
+ * @brief The condition carried by an edge (i.e. by a child, about its parent).
35
+ *
36
+ * Identity is the `type`: the five built-ins are the static objects below,
37
+ * handed out by the DCG_*_CONDITION macros, and every comparison in the bake
38
+ * layer is a test of that field (c_dcg_condition_is_else,
39
+ * c_dcg_condition_matches, ...) - never of the object's address. Any other
40
+ * condition is a caller-owned object of type DCG_NODE_EDGE_VALUE, whose
41
+ * `value` is what the parent's evaluated value gets compared against.
42
+ *
43
+ * The field rather than the address is what makes identity survive the
44
+ * header being included by more than one translation unit: each unit gets its
45
+ * own copy of the built-ins, and the copies agree because they carry the same
46
+ * type - which an address comparison could never see.
47
+ *
48
+ * The repr is an inline buffer naming the condition and its address, so a
49
+ * condition owns no memory at all: the built-ins are static objects and a
50
+ * user condition is one flat block.
51
+ */
52
+ typedef struct dcg_node_edge_condition {
53
+ dcg_node_edge_type type;
54
+ dcg_var_t value; // Condition payload (bools for the TRUE/FALSE built-ins).
55
+ char repr[DCG_EDGE_REPR_MAXLEN]; // Display text, always present.
56
+ } dcg_node_edge_condition;
57
+
58
+ // ========== Built-in Conditions ==========
59
+
60
+ /*
61
+ * The five built-ins live in static storage: no heap, so nothing can leak
62
+ * and there is nothing to free.
63
+ *
64
+ * They are file-scope objects in a header, so every translation unit that
65
+ * includes this header gets its own copy. That is harmless: each copy is born
66
+ * carrying its type, and every comparison reads the type - a graph built in
67
+ * one unit and inspected in another still agrees on which edge is which. Only
68
+ * the repr is unit-local (it names the copy's own address, which is there for
69
+ * debugging and is never compared).
70
+ *
71
+ * Static storage is writable by default, which is what lets the repr carry
72
+ * each condition's own address - that address only exists once the object
73
+ * does, so it is written by the one-time c_dcg_condition_init_globals(),
74
+ * guarded by __DCG_CONDITION_INITIALIZED and triggered from the macros below.
75
+ */
76
+
77
+ static dcg_node_edge_condition __DCG_NO_CONDITION = {.type = DCG_NODE_EDGE_NONE};
78
+ static dcg_node_edge_condition __DCG_ELSE_CONDITION = {.type = DCG_NODE_EDGE_ELSE};
79
+ static dcg_node_edge_condition __DCG_AUTO_CONDITION = {.type = DCG_NODE_EDGE_AUTO};
80
+ static dcg_node_edge_condition __DCG_TRUE_CONDITION = {.type = DCG_NODE_EDGE_TRUE};
81
+ static dcg_node_edge_condition __DCG_FALSE_CONDITION = {.type = DCG_NODE_EDGE_FALSE};
82
+
83
+ static bool __DCG_CONDITION_INITIALIZED = false;
84
+
85
+ // clang-format on
86
+
87
+ // ========== Forward Declarations ==========
88
+
89
+ static inline void c_dcg_condition_init_globals(void);
90
+
91
+ // Identity (address comparison - the way conditions are told apart)
92
+ static inline bool c_dcg_condition_is_sentinel(const dcg_node_edge_condition* cond);
93
+ static inline bool c_dcg_condition_is_none(const dcg_node_edge_condition* cond);
94
+ static inline bool c_dcg_condition_is_else(const dcg_node_edge_condition* cond);
95
+ static inline bool c_dcg_condition_is_auto(const dcg_node_edge_condition* cond);
96
+ static inline bool c_dcg_condition_is_true(const dcg_node_edge_condition* cond);
97
+ static inline bool c_dcg_condition_is_false(const dcg_node_edge_condition* cond);
98
+ static inline bool c_dcg_condition_is_binary(const dcg_node_edge_condition* cond);
99
+
100
+ // Comparison
101
+ static inline bool c_dcg_condition_equals(const dcg_node_edge_condition* lhs, const dcg_node_edge_condition* rhs);
102
+ static inline bool c_dcg_condition_matches(const dcg_node_edge_condition* cond, const dcg_var_t* value);
103
+
104
+ // Lifecycle
105
+ static inline int c_dcg_condition_init(dcg_node_edge_condition* cond, dcg_var_t value, const char* repr);
106
+ static inline dcg_node_edge_condition* c_dcg_edge_new(dcg_var_t value, const char* repr, allocator_protocol* allocator);
107
+ static inline void c_dcg_condition_dealloc(dcg_node_edge_condition* cond);
108
+ static inline void c_dcg_condition_free(dcg_node_edge_condition* cond);
109
+
110
+ // Output
111
+ static inline const char* c_dcg_condition_repr(const dcg_node_edge_condition* cond);
112
+ static inline int c_dcg_condition_format(const dcg_node_edge_condition* cond, const char* fallback_prefix, char* out, size_t cap);
113
+ static inline int c_dcg_condition_print(const dcg_node_edge_condition* cond, FILE* stream);
114
+
115
+ // ========== Utility Functions ==========
116
+
117
+ /**
118
+ * @brief Write the built-in conditions, once per translation unit.
119
+ *
120
+ * Fills the five static objects: their payloads, and the repr naming each one
121
+ * together with its own address. The flag is raised last, so the macros below
122
+ * only ever hand out a fully written condition.
123
+ *
124
+ * Not synchronized: the bake layer initializes its built-ins while a graph is
125
+ * being built, which is single-threaded work. A caller that builds graphs
126
+ * from several threads should touch one of the DCG_*_CONDITION macros once,
127
+ * up front, from the thread that starts up.
128
+ */
129
+ static inline void c_dcg_condition_init_globals(void) {
130
+ if (__DCG_CONDITION_INITIALIZED) return;
131
+
132
+ dcg_node_edge_condition* no_condition = &__DCG_NO_CONDITION;
133
+ dcg_node_edge_condition* else_condition = &__DCG_ELSE_CONDITION;
134
+ dcg_node_edge_condition* auto_condition = &__DCG_AUTO_CONDITION;
135
+ dcg_node_edge_condition* true_condition = &__DCG_TRUE_CONDITION;
136
+ dcg_node_edge_condition* false_condition = &__DCG_FALSE_CONDITION;
137
+
138
+ /* --------------------------------------------------------
139
+ * NO_CONDITION
140
+ * -------------------------------------------------------- */
141
+
142
+ (void) snprintf(
143
+ no_condition->repr,
144
+ sizeof(no_condition->repr),
145
+ "<CONDITION 0x%zx>(Unconditional)",
146
+ (size_t) (uintptr_t) no_condition
147
+ );
148
+
149
+ no_condition->value = (dcg_var_t) {0};
150
+
151
+ /* --------------------------------------------------------
152
+ * ELSE_CONDITION
153
+ * -------------------------------------------------------- */
154
+
155
+ (void) snprintf(
156
+ else_condition->repr,
157
+ sizeof(else_condition->repr),
158
+ "<CONDITION Internal 0x%zx>(Else)",
159
+ (size_t) (uintptr_t) else_condition
160
+ );
161
+
162
+ else_condition->value = (dcg_var_t) {0};
163
+
164
+ /* --------------------------------------------------------
165
+ * AUTO_CONDITION
166
+ * -------------------------------------------------------- */
167
+
168
+ (void) snprintf(
169
+ auto_condition->repr,
170
+ sizeof(auto_condition->repr),
171
+ "<CONDITION Internal 0x%zx>(Auto)",
172
+ (size_t) (uintptr_t) auto_condition
173
+ );
174
+
175
+ auto_condition->value = (dcg_var_t) {0};
176
+
177
+ /* --------------------------------------------------------
178
+ * TRUE_CONDITION
179
+ * -------------------------------------------------------- */
180
+
181
+ (void) snprintf(
182
+ true_condition->repr,
183
+ sizeof(true_condition->repr),
184
+ "<CONDITION 0x%zx>(True)",
185
+ (size_t) (uintptr_t) true_condition
186
+ );
187
+
188
+ true_condition->value = (dcg_var_t) {
189
+ .dtype = VAR_TYPE_BOOL,
190
+ .value = {
191
+ .as_bool = true,
192
+ },
193
+ };
194
+
195
+ /* --------------------------------------------------------
196
+ * FALSE_CONDITION
197
+ * -------------------------------------------------------- */
198
+
199
+ (void) snprintf(
200
+ false_condition->repr,
201
+ sizeof(false_condition->repr),
202
+ "<CONDITION 0x%zx>(False)",
203
+ (size_t) (uintptr_t) false_condition
204
+ );
205
+
206
+ false_condition->value = (dcg_var_t) {
207
+ .dtype = VAR_TYPE_BOOL,
208
+ .value = {
209
+ .as_bool = false,
210
+ },
211
+ };
212
+
213
+ __DCG_CONDITION_INITIALIZED = true;
214
+ }
215
+
216
+ // ========== Public APIs ==========
217
+
218
+ /*
219
+ * The identity predicates read the condition's type, which every copy of a
220
+ * built-in carries from its first byte - so they answer correctly whether or
221
+ * not the one-time init has run, and whichever translation unit built the
222
+ * condition and whichever one is inspecting it.
223
+ */
224
+
225
+ /**
226
+ * @brief Predicate: is this one of the five built-in conditions?
227
+ *
228
+ * @param cond Condition to inspect (NULL-safe).
229
+ * @return true for the built-ins, false for NULL or a caller-owned condition.
230
+ */
231
+ static inline bool c_dcg_condition_is_sentinel(const dcg_node_edge_condition* cond) {
232
+ if (!cond) return false;
233
+ return cond->type != DCG_NODE_EDGE_VALUE;
234
+ }
235
+
236
+ /**
237
+ * @brief Predicate: is the condition the unconditional one?
238
+ *
239
+ * A NULL condition means the same thing, which is what lets a detached node
240
+ * and a root's child both read as "no condition".
241
+ *
242
+ * @param cond Condition to inspect (NULL-safe).
243
+ * @return true for NULL or DCG_NO_CONDITION.
244
+ */
245
+ static inline bool c_dcg_condition_is_none(const dcg_node_edge_condition* cond) {
246
+ return cond == NULL || cond->type == DCG_NODE_EDGE_NONE;
247
+ }
248
+
249
+ /**
250
+ * @brief Predicate: is the condition the else fallback?
251
+ *
252
+ * @param cond Condition to inspect (NULL-safe).
253
+ * @return true for DCG_ELSE_CONDITION.
254
+ */
255
+ static inline bool c_dcg_condition_is_else(const dcg_node_edge_condition* cond) {
256
+ return cond && cond->type == DCG_NODE_EDGE_ELSE;
257
+ }
258
+
259
+ /**
260
+ * @brief Predicate: is the condition an unresolved AUTO?
261
+ *
262
+ * @param cond Condition to inspect (NULL-safe).
263
+ * @return true for DCG_AUTO_CONDITION.
264
+ */
265
+ static inline bool c_dcg_condition_is_auto(const dcg_node_edge_condition* cond) {
266
+ return cond && cond->type == DCG_NODE_EDGE_AUTO;
267
+ }
268
+
269
+ /**
270
+ * @brief Predicate: is the condition the true branch?
271
+ *
272
+ * @param cond Condition to inspect (NULL-safe).
273
+ * @return true for DCG_TRUE_CONDITION.
274
+ */
275
+ static inline bool c_dcg_condition_is_true(const dcg_node_edge_condition* cond) {
276
+ return cond && cond->type == DCG_NODE_EDGE_TRUE;
277
+ }
278
+
279
+ /**
280
+ * @brief Predicate: is the condition the false branch?
281
+ *
282
+ * @param cond Condition to inspect (NULL-safe).
283
+ * @return true for DCG_FALSE_CONDITION.
284
+ */
285
+ static inline bool c_dcg_condition_is_false(const dcg_node_edge_condition* cond) {
286
+ return cond && cond->type == DCG_NODE_EDGE_FALSE;
287
+ }
288
+
289
+ /**
290
+ * @brief Predicate: is the condition a two-way branch (true or false)?
291
+ *
292
+ * @param cond Condition to inspect (NULL-safe).
293
+ * @return true for DCG_TRUE_CONDITION / DCG_FALSE_CONDITION.
294
+ */
295
+ static inline bool c_dcg_condition_is_binary(const dcg_node_edge_condition* cond) {
296
+ return cond && (cond->type == DCG_NODE_EDGE_TRUE || cond->type == DCG_NODE_EDGE_FALSE);
297
+ }
298
+
299
+ /**
300
+ * @brief Equality of two conditions: the type, then the payload.
301
+ *
302
+ * The built-ins compare by type, so any copy of DCG_TRUE_CONDITION equals any
303
+ * other - which is what makes a condition built in one translation unit
304
+ * recognisable in another. A NULL condition equals DCG_NO_CONDITION. Two
305
+ * value conditions compare by payload, which is what an evaluator uses to
306
+ * pick a branch: two user conditions carrying the same value are the same
307
+ * edge as far as the graph is concerned.
308
+ *
309
+ * @param lhs Left condition (NULL-safe: NULL is DCG_NO_CONDITION).
310
+ * @param rhs Right condition (NULL-safe: NULL is DCG_NO_CONDITION).
311
+ * @return true when the conditions identify the same edge.
312
+ */
313
+ static inline bool c_dcg_condition_equals(const dcg_node_edge_condition* lhs, const dcg_node_edge_condition* rhs) {
314
+ if (lhs == rhs) return true;
315
+ if (c_dcg_condition_is_none(lhs) && c_dcg_condition_is_none(rhs)) return true;
316
+ if (!lhs || !rhs) return false;
317
+ if (lhs->type != rhs->type) return false;
318
+ if (lhs->type != DCG_NODE_EDGE_VALUE) return true;
319
+ return c_dcg_var_equals(&lhs->value, &rhs->value);
320
+ }
321
+
322
+ /**
323
+ * @brief Does the parent's evaluated value select this edge?
324
+ *
325
+ * The branch-selection predicate of the evaluator:
326
+ * - NULL / NO_CONDITION / AUTO always select,
327
+ * - ELSE always selects (the caller tries it last),
328
+ * - TRUE / FALSE select on the value's truthiness,
329
+ * - anything else selects on value equality.
330
+ *
331
+ * @param cond Edge condition (NULL-safe: NULL behaves as NO_CONDITION).
332
+ * @param value The parent's evaluated value (NULL-safe: NULL is falsy).
333
+ * @return true when the edge is taken.
334
+ */
335
+ static inline bool c_dcg_condition_matches(const dcg_node_edge_condition* cond, const dcg_var_t* value) {
336
+ if (c_dcg_condition_is_none(cond) || c_dcg_condition_is_else(cond) || c_dcg_condition_is_auto(cond)) return true;
337
+ if (c_dcg_condition_is_true(cond)) return c_dcg_var_is_truthy(value);
338
+ if (c_dcg_condition_is_false(cond)) return !c_dcg_var_is_truthy(value);
339
+ return c_dcg_var_equals(&cond->value, value);
340
+ }
341
+
342
+ /**
343
+ * @brief Initialize a caller-owned condition.
344
+ *
345
+ * The repr is COPIED into the condition's inline buffer (truncated to
346
+ * DCG_EDGE_REPR_MAXLEN - 1 characters when it does not fit), so the caller
347
+ * keeps nothing alive on its behalf.
348
+ *
349
+ * @param cond Condition to initialize.
350
+ * @param value Payload the parent's value is compared against.
351
+ * @param repr Display text (may be NULL - the buffer then starts empty and
352
+ * formatting falls back to the payload).
353
+ * @return DCG_OK, or DCG_ERR_INVALID_ARG when cond is NULL.
354
+ */
355
+ static inline int c_dcg_condition_init(dcg_node_edge_condition* cond, dcg_var_t value, const char* repr) {
356
+ if (!cond) return DCG_ERR_INVALID_ARG;
357
+
358
+ memset(cond, 0, sizeof(*cond));
359
+ cond->type = DCG_NODE_EDGE_VALUE; // stated rather than left to the zeroing: the type is what identity reads
360
+ cond->value = value;
361
+
362
+ if (repr) {
363
+ size_t len = strlen(repr);
364
+ if (len >= sizeof(cond->repr)) len = sizeof(cond->repr) - 1;
365
+ memcpy(cond->repr, repr, len);
366
+ cond->repr[len] = '\0';
367
+ }
368
+
369
+ return DCG_OK;
370
+ }
371
+
372
+ /**
373
+ * @brief Allocate and initialize a standalone condition.
374
+ *
375
+ * For the conditions a builder has to create at run time - mapping keys and
376
+ * other value conditions. The built-ins never need this: they already exist
377
+ * as static objects.
378
+ *
379
+ * @param value Payload the parent's value is compared against.
380
+ * @param repr Display text (may be NULL).
381
+ * @param allocator Allocator for the block; NULL falls back to the plain heap.
382
+ * @return The condition, or NULL on OOM.
383
+ */
384
+ static inline dcg_node_edge_condition* c_dcg_edge_new(dcg_var_t value, const char* repr, allocator_protocol* allocator) {
385
+ dcg_node_edge_condition* cond = (dcg_node_edge_condition*) c_ap_alloc(sizeof(dcg_node_edge_condition), allocator);
386
+ if (!cond) return NULL;
387
+
388
+ c_dcg_condition_init(cond, value, repr);
389
+ return cond;
390
+ }
391
+
392
+ /**
393
+ * @brief Tear down a condition - leaves a zeroed buf.
394
+ *
395
+ * A condition owns nothing, so this only zeroes it. The built-ins must not be
396
+ * passed here: they are static objects, not a caller's buffer.
397
+ *
398
+ * @param cond Condition to tear down (NULL-safe).
399
+ */
400
+ static inline void c_dcg_condition_dealloc(dcg_node_edge_condition* cond) {
401
+ if (!cond) return;
402
+ memset(cond, 0, sizeof(*cond));
403
+ }
404
+
405
+ /**
406
+ * @brief Tear down a condition and free its buf.
407
+ *
408
+ * @param cond Condition to free (NULL-safe). Must be an ap-allocated
409
+ * standalone condition, NOT one of the built-ins.
410
+ */
411
+ static inline void c_dcg_condition_free(dcg_node_edge_condition* cond) {
412
+ if (!cond) return;
413
+ c_dcg_condition_dealloc(cond);
414
+ c_ap_free_owned(cond); // a condition may carry nested blocks of its own
415
+ }
416
+
417
+ /**
418
+ * @brief The condition's display text.
419
+ *
420
+ * @param cond Condition to inspect (NULL-safe).
421
+ * @return Its inline repr, or "" for NULL.
422
+ */
423
+ static inline const char* c_dcg_condition_repr(const dcg_node_edge_condition* cond) {
424
+ if (!cond) return "";
425
+ return cond->repr;
426
+ }
427
+
428
+ /**
429
+ * @brief Render a condition into a caller-provided buffer.
430
+ *
431
+ * Uses the inline repr; a caller-owned condition created without one falls
432
+ * back to its payload, prefixed by `fallback_prefix` when given.
433
+ *
434
+ * @param cond Condition to render (NULL-safe).
435
+ * @param fallback_prefix Text placed before the payload when the repr is
436
+ * empty (may be NULL).
437
+ * @param out Destination buffer.
438
+ * @param cap Capacity of out.
439
+ * @return Number of characters written (excluding NUL), or DCG_ERR_*.
440
+ */
441
+ static inline int c_dcg_condition_format(const dcg_node_edge_condition* cond, const char* fallback_prefix, char* out, size_t cap) {
442
+ if (!out || cap == 0) return DCG_ERR_INVALID_ARG;
443
+ if (!cond) {
444
+ int n = snprintf(out, cap, "NO_CONDITION");
445
+ return n < 0 ? DCG_ERR_FORMAT : n;
446
+ }
447
+ if (cond->repr[0] != '\0') {
448
+ int n = snprintf(out, cap, "%s", cond->repr);
449
+ return n < 0 ? DCG_ERR_FORMAT : n;
450
+ }
451
+
452
+ int written = snprintf(out, cap, "%s", fallback_prefix ? fallback_prefix : "");
453
+ if (written < 0) return DCG_ERR_FORMAT;
454
+ if ((size_t) written >= cap) return DCG_ERR_FULL;
455
+
456
+ int n = c_dcg_var_format(&cond->value, out + written, cap - (size_t) written);
457
+ if (n < 0) return n;
458
+ return written + n;
459
+ }
460
+
461
+ /**
462
+ * @brief Print a condition to a stream.
463
+ *
464
+ * @param cond Condition to print (NULL-safe).
465
+ * @param stream Destination stream.
466
+ * @return Number of characters written, or DCG_ERR_*.
467
+ */
468
+ static inline int c_dcg_condition_print(const dcg_node_edge_condition* cond, FILE* stream) {
469
+ if (!stream) return DCG_ERR_INVALID_ARG;
470
+ char buf[DCG_EDGE_REPR_MAXLEN];
471
+ int n = c_dcg_condition_format(cond, "", buf, sizeof(buf));
472
+ if (n < 0) return n;
473
+ return fprintf(stream, "%s", buf);
474
+ }
475
+
476
+ /**
477
+ * The five built-in edge conditions. Each expands to a const pointer to its
478
+ * static object, initializing the built-ins on first use; the pointer is
479
+ * stable for the lifetime of the translation unit, so it can be stored in a
480
+ * static node table and never needs freeing.
481
+ *
482
+ * Two pointers to the same built-in from two translation units are different
483
+ * addresses - what identifies the edge is the type the object carries, so
484
+ * compare through c_dcg_condition_* rather than with ==.
485
+ */
486
+ #define DCG_NO_CONDITION \
487
+ (__DCG_CONDITION_INITIALIZED \
488
+ ? (&__DCG_NO_CONDITION) \
489
+ : (c_dcg_condition_init_globals(), &__DCG_NO_CONDITION))
490
+
491
+ #define DCG_ELSE_CONDITION \
492
+ (__DCG_CONDITION_INITIALIZED \
493
+ ? (&__DCG_ELSE_CONDITION) \
494
+ : (c_dcg_condition_init_globals(), &__DCG_ELSE_CONDITION))
495
+
496
+ #define DCG_AUTO_CONDITION \
497
+ (__DCG_CONDITION_INITIALIZED \
498
+ ? (&__DCG_AUTO_CONDITION) \
499
+ : (c_dcg_condition_init_globals(), &__DCG_AUTO_CONDITION))
500
+
501
+ #define DCG_TRUE_CONDITION \
502
+ (__DCG_CONDITION_INITIALIZED \
503
+ ? (&__DCG_TRUE_CONDITION) \
504
+ : (c_dcg_condition_init_globals(), &__DCG_TRUE_CONDITION))
505
+
506
+ #define DCG_FALSE_CONDITION \
507
+ (__DCG_CONDITION_INITIALIZED \
508
+ ? (&__DCG_FALSE_CONDITION) \
509
+ : (c_dcg_condition_init_globals(), &__DCG_FALSE_CONDITION))
510
+
511
+ #endif // C_DCG_BAKE_EDGE_H