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,629 @@
1
+ #ifndef C_DCG_BAKE_BAKE_H
2
+ #define C_DCG_BAKE_BAKE_H
3
+
4
+ #include <stdbool.h>
5
+ #include <stddef.h>
6
+ #include <stdint.h>
7
+ #include <stdio.h>
8
+ #include <string.h>
9
+
10
+ #include <cbase/allocator_protocol/c_allocator_protocol.h>
11
+
12
+ #include <decision_graph/decision_tree/bake/c_collections.h>
13
+ #include <decision_graph/decision_tree/bake/c_eval.h>
14
+ #include <decision_graph/decision_tree/bake/c_expr.h>
15
+ #include <decision_graph/decision_tree/bake/c_hierarchy.h>
16
+ #include <decision_graph/decision_tree/bake/c_node.h>
17
+ #include <decision_graph/decision_tree/bake/c_var.h>
18
+
19
+ /*
20
+ * THE BAKE PROTOCOL - the pass that makes a built graph ready to be walked.
21
+ *
22
+ * A build says what a graph IS and an evaluation says what it DOES. Between the
23
+ * two sits this: the last thing done to a graph before it is evaluated, and the
24
+ * pass that an evaluation is written assuming has run. It is asked of the ROOT
25
+ * and of nothing else, because a graph is entered from one place.
26
+ *
27
+ * THREE THINGS, and the order is the order they can be done in:
28
+ *
29
+ * 1. VERIFY. The shape an evaluation ASSUMES is established here and nowhere
30
+ * else - the hot path carries no check for a malformed node, because this
31
+ * is the check (see c_dcg_node_expr_eval_operand, and the rules above it).
32
+ * Two halves:
33
+ *
34
+ * - the GRAPH's structure, which the node layer already knows how to
35
+ * check (c_dcg_node_validate: one parentless root, arms in reading
36
+ * order, an else last, an action below nothing, no cycles);
37
+ * - what an OPERATOR node needs before its rule can run at all, which is
38
+ * this header's own:
39
+ * . every operand its arity takes must have a component bound. The
40
+ * rule runs its operands and reads the workspace they filled, one
41
+ * slot at a time, with nothing checked in between: a slot with no
42
+ * component behind it is a read of nothing, not a value;
43
+ * . its operator must be one its own arity applies. An operator of
44
+ * another arity leaves the node carrying the rule that refuses to
45
+ * evaluate (c_dcg_node_expr_set_op), so the graph has a node that
46
+ * can never produce a value - refused here, before a walk
47
+ * discovers it one evaluation at a time;
48
+ * . its operand array must be exactly its arity long. A block sized
49
+ * for fewer operands than its rule reads is out of bounds, not
50
+ * merely wrong.
51
+ *
52
+ * A CALL is refused by the same reading, and so is a node of the operator
53
+ * family's head type: neither has an evaluation at all (see
54
+ * c_dcg_node_expr_eval_call and c_dcg_node_expr_eval_unknown), and a graph
55
+ * holding one cannot be evaluated - a build defect, and this is the last
56
+ * moment it can be reported as one.
57
+ *
58
+ * 2. SEAL. What the pass verified, it then holds. Every node it walked is
59
+ * FROZEN, so the graph's structure is what it was: the node layer refuses
60
+ * an append, a detach, a removal and a clear under a frozen parent, and an
61
+ * expression refuses a rebind and an operator change under one too (see
62
+ * c_dcg_node_expr_bind and c_dcg_node_expr_set_op). And every STORE the
63
+ * graph reads - the group behind a read - is frozen with it, so no entry
64
+ * can appear in it afterwards.
65
+ *
66
+ * The store is the half an evaluation depends on: a resolved read holds a
67
+ * reference INTO its store's block, and that block moves only when the
68
+ * store grows (see c_dcg_mapping_lgroup_get_node). A frozen store cannot
69
+ * grow, which is what keeps a resolved read's reference good.
70
+ *
71
+ * What sealing does NOT stop is a VALUE. A store is sealed in SHAPE - the
72
+ * entries it has are the entries it will have - and a caller goes on
73
+ * writing what it already holds. That is the whole point of a store: a
74
+ * graph is baked once and fed many times.
75
+ *
76
+ * 3. PREPARE. A walk records what it visits into the root's own path record,
77
+ * and a record with no room takes room as it goes - an allocation inside
78
+ * the walk. So the room is made here instead, sized for what a walk from
79
+ * this root can need: one entry per level, and no walk goes deeper than
80
+ * the graph (see c_dcg_root_node_eval_path_reserve). A baked graph's walk
81
+ * allocates nothing.
82
+ *
83
+ * A BAKE IS ASKED FOR, AND IT IS ALL OR NOTHING: a graph that fails either half
84
+ * of the verification is NOT locked and NOT prepared, so a caller that ignores
85
+ * the report is left with a graph that does not work rather than with one that
86
+ * half works. A bake that succeeds is IDEMPOTENT - asked again it locks
87
+ * nothing, seals nothing and prepares nothing, and says so in the report.
88
+ *
89
+ * THE REPORT is what the pass found and what it affected, and it is the
90
+ * caller's to hand in; a caller that wants only the code passes NULL. The
91
+ * verification's findings ride in the same three fields as the operands' -
92
+ * `code`, `node` and `errors` name the FIRST problem, in the order the pass asks
93
+ * its questions - and what the pass CHANGED is counted beside them: how many
94
+ * nodes it locked, how many stores it sealed, and how much room it made.
95
+ *
96
+ * The header is C only: it is above every family (it includes them all, and the
97
+ * protocol they are evaluated by) and nothing includes it. A Cython module that
98
+ * binds it does so with a textual `cdef extern from`, so it creates no pxd edge
99
+ * and no cycle - see DEPENDENCY.md section 3.2.
100
+ */
101
+
102
+ // ========== Constants ==========
103
+
104
+ /**
105
+ * @brief What a bake is asked for.
106
+ *
107
+ * A bake that is asked for nothing in particular does the whole of it, which is
108
+ * why the default is NONE rather than a flag with the work in it: the flags name
109
+ * the departures from a bake, and there is one - asking for the verdict without
110
+ * the lockdown.
111
+ */
112
+ typedef enum dcg_bake_flag {
113
+ DCG_BAKE_FLAG_NONE = 0, // Verify, lock and prepare - the whole of a bake.
114
+ DCG_BAKE_FLAG_VALIDATE_ONLY = 1 << 0 // Verify and report; lock nothing, prepare nothing.
115
+ } dcg_bake_flag;
116
+
117
+ // ========== Structs ==========
118
+
119
+ /**
120
+ * @brief What a bake is asked WITH.
121
+ *
122
+ * The flags are the whole of it today, and the struct is here rather than a
123
+ * plain argument for the reason a C API has one: what a pass is asked grows,
124
+ * and growing a struct leaves every call site that already exists alone.
125
+ *
126
+ * Zero-initialize it, or call c_dcg_bake_input_init(): a bake asked with no
127
+ * flags set is the whole of a bake.
128
+ */
129
+ typedef struct dcg_bake_input {
130
+ uint64_t flags; // dcg_bake_flag bits.
131
+ } dcg_bake_input;
132
+
133
+ /**
134
+ * @brief What a bake found, and what it affected.
135
+ *
136
+ * The first five fields are the shape `dcg_validate_report` has, because the
137
+ * pass runs that check: `code` is the first problem (DCG_OK when the graph is
138
+ * baked), `node` the node it was found on, `errors` how many were found, and
139
+ * `nodes` and `depth` what the walk that ran covered.
140
+ *
141
+ * The last three are the other half of the pass - what it CHANGED, which a
142
+ * re-bake reports as nothing:
143
+ *
144
+ * - `locked`: nodes this pass froze. A graph in the graph's own size means
145
+ * nothing was locked and the graph was already baked;
146
+ * - `sealed`: stores this pass froze. Each one is counted once, however many
147
+ * reads name it;
148
+ * - `capacity`: entries the root's record was prepared with. 0 when the pass
149
+ * did not prepare one - a validate-only bake, or one that failed.
150
+ */
151
+ typedef struct dcg_bake_report {
152
+ dcg_ret_code code; // DCG_OK when the graph is baked, or the first problem that stopped the pass.
153
+ dcg_node* node; // The node that produced it; NULL when it was not a node's.
154
+ size_t errors; // Problems found: the graph's shape, then its operators' operands.
155
+ size_t nodes; // Nodes the pass walked - the branches, and the operands they read.
156
+ size_t depth; // The deepest level the walk reached.
157
+ size_t locked; // Nodes this pass locked.
158
+ size_t sealed; // Stores this pass sealed.
159
+ size_t capacity; // Entries the root's record was prepared with.
160
+ } dcg_bake_report;
161
+
162
+ // ========== Forward Declarations ==========
163
+
164
+ // The input, and the report a caller wants of its own
165
+ static inline void c_dcg_bake_input_init(dcg_bake_input* input);
166
+ static inline dcg_bake_report* c_dcg_bake_report_new(allocator_protocol* allocator);
167
+ static inline void c_dcg_bake_report_free(dcg_bake_report* report);
168
+
169
+ // The node's own halves
170
+ static inline void c_dcg_bake_fail(dcg_bake_report* report, const dcg_node* node, dcg_ret_code err, bool* valid);
171
+ static inline void c_dcg_bake_node_check(const dcg_node* node, dcg_bake_report* report, bool* valid);
172
+ static inline void c_dcg_bake_node_lock(dcg_node* node, dcg_bake_report* report);
173
+
174
+ // The walk
175
+ static inline void c_dcg_bake_walk(dcg_node* node, dcg_bake_report* report, size_t depth, bool* valid);
176
+ static inline void c_dcg_bake_resolve_walk(dcg_node* node);
177
+ static inline void c_dcg_bake_lock_walk(dcg_node* node, dcg_bake_report* report);
178
+ static inline void c_dcg_bake_unmark_walk(dcg_node* node);
179
+ static inline int c_dcg_bake_prepare_record(dcg_root_node* root, dcg_bake_report* report);
180
+ static inline int c_dcg_root_node_bake(dcg_root_node* root, const dcg_bake_input* input, dcg_bake_report* report);
181
+
182
+ // ========== The Input ==========
183
+
184
+ /**
185
+ * @brief What a bake is asked with when the caller does not say.
186
+ *
187
+ * A bake asked for nothing is the whole of a bake: verify, lock, prepare. The
188
+ * function is here so a caller does not have to know what "nothing" is spelled
189
+ * as, which is what keeps a field added later from becoming an uninitialized
190
+ * one at every call site.
191
+ *
192
+ * @param input Input to fill (NULL-safe: nothing to fill).
193
+ */
194
+ static inline void c_dcg_bake_input_init(dcg_bake_input* input) {
195
+ if (!input) return;
196
+ memset(input, 0, sizeof(*input));
197
+ }
198
+
199
+ // ========== The Report's Own Lifecycle ==========
200
+
201
+ /**
202
+ * @brief A report of its own, so a caller can ask for one and keep it.
203
+ *
204
+ * The door writes a report the caller hands in, which is what the layer's own
205
+ * callers want: a bake is asked once and read where it happened. A report of its
206
+ * own is the other half of the same API - what a binding layer hands back to a
207
+ * caller who asked for the answer rather than for the graph to be baked - so the
208
+ * type carries the two functions every owning type has, and they are the two it
209
+ * needs: a report owns nothing behind its block (the node it may name belongs to
210
+ * the graph), so there is no payload for a `_dealloc` to release and it goes with
211
+ * `_free` alone.
212
+ *
213
+ * A fresh report says what a bake that never ran says: DCG_OK, no node, no
214
+ * counts. It is a report to be FILLED, and the door fills it in place.
215
+ *
216
+ * @param allocator Allocator for the block; NULL falls back to the plain heap.
217
+ * @return The report, or NULL on OOM.
218
+ */
219
+ static inline dcg_bake_report* c_dcg_bake_report_new(allocator_protocol* allocator) {
220
+ dcg_bake_report* report = (dcg_bake_report*) c_ap_alloc(sizeof(dcg_bake_report), allocator);
221
+ if (!report) return NULL;
222
+
223
+ memset(report, 0, sizeof(*report));
224
+ return report;
225
+ }
226
+
227
+ /**
228
+ * @brief Release a report of its own.
229
+ *
230
+ * Only for a report from c_dcg_bake_report_new(). The report a caller hands the
231
+ * door is that caller's - a local, a field of a larger struct - and nothing here
232
+ * frees it.
233
+ *
234
+ * @param report Report to free (NULL-safe).
235
+ */
236
+ static inline void c_dcg_bake_report_free(dcg_bake_report* report) {
237
+ if (!report) return;
238
+ c_ap_free_owned(report);
239
+ }
240
+
241
+ // ========== The Node's Own Halves ==========
242
+
243
+ /**
244
+ * @brief Record a problem the bake found, and say the graph is not bakeable.
245
+ *
246
+ * The sibling of c_dcg_validate_fail, over the pass's own report: the first
247
+ * problem is the one a caller is told about - it is the one the pass stopped
248
+ * asking questions after - and the rest are counted.
249
+ *
250
+ * @param report Report being filled (NULL-safe: counting is then skipped).
251
+ * @param node Node the problem was found on.
252
+ * @param err Error code.
253
+ * @param valid Bakeability accumulator (NULL-safe).
254
+ */
255
+ static inline void c_dcg_bake_fail(dcg_bake_report* report, const dcg_node* node, dcg_ret_code err, bool* valid) {
256
+ if (report) {
257
+ report->errors++;
258
+ if (report->code == DCG_OK) {
259
+ report->code = err;
260
+ report->node = (dcg_node*) node;
261
+ }
262
+ }
263
+ if (valid) *valid = false;
264
+ }
265
+
266
+ /**
267
+ * @brief What an evaluation of ONE node assumes, and what the bake owes it.
268
+ *
269
+ * Only an operator node has anything to check here: every other family settles
270
+ * its value at birth (a literal is its value, an action is the node itself, a
271
+ * root is true) or carries its own rule from the store that built it, and none
272
+ * of them reads a field this pass could find missing.
273
+ *
274
+ * An operator node is the one that does: its rule runs each operand and applies
275
+ * the operator, and the three things it takes for granted are exactly the three
276
+ * checked below. Each is refused with the code the layer would otherwise report
277
+ * one evaluation at a time - DCG_ERR_TYPE for a node that has no evaluation and
278
+ * for an operator its arity does not apply, and DCG_ERR_UNBOUND for an operand
279
+ * slot with nothing bound to it, because the value that rule would have read is
280
+ * the one that never landed.
281
+ *
282
+ * @param node Node to check.
283
+ * @param report Report to record a problem in.
284
+ * @param valid Bakeability accumulator.
285
+ */
286
+ static inline void c_dcg_bake_node_check(const dcg_node* node, dcg_bake_report* report, bool* valid) {
287
+ /*
288
+ * A breakpoint is build-time scaffolding, and the bake takes it down (see
289
+ * c_dcg_node_resolve_breakpoint). What it has to have, for that to be
290
+ * possible, is the node it resumed into: the breakpoint's one arm is an ELSE
291
+ * holding the rest of the branch, and a breakpoint without it is a branch
292
+ * that broke and never carried on anywhere - nothing to splice in, and
293
+ * nothing for a walk to reach if it stayed.
294
+ */
295
+ if (node->ntype == DCG_NODE_BREAKPOINT) {
296
+ if (!c_dcg_node_child_by_condition(node, DCG_ELSE_CONDITION)) {
297
+ c_dcg_bake_fail(report, node, DCG_ERR_UNRESOLVED, valid);
298
+ }
299
+ return;
300
+ }
301
+
302
+ if (!c_dcg_node_type_is_op(node->ntype)) return;
303
+
304
+ const dcg_expression_node* expr = (const dcg_expression_node*) node;
305
+ size_t arity = c_dcg_node_type_arity(node->ntype);
306
+
307
+ /* An arity of none: the family's head type, and a call. Neither has a rule
308
+ * to run - a call's callee is composed into its display text and stored
309
+ * nowhere - so a graph holding one has nothing to evaluate it with. */
310
+ if (arity == 0) {
311
+ c_dcg_bake_fail(report, node, DCG_ERR_TYPE, valid);
312
+ return;
313
+ }
314
+
315
+ /* The operator the node applies, and the arity applying it, are ONE fact
316
+ * settled at build time: an operator of another arity installs nothing and
317
+ * leaves the refusing rule standing (see c_dcg_node_expr_set_op), so a node
318
+ * whose pair does not agree is a node that can never produce a value. An
319
+ * if-expression carries no operator its arity applies - its rule IS the
320
+ * arity's - so a code on it decides nothing and is not asked about. */
321
+ if (node->ntype != DCG_NODE_TERNARY && c_dcg_op_code_node_type(expr->op) != node->ntype) {
322
+ c_dcg_bake_fail(report, node, DCG_ERR_TYPE, valid);
323
+ return;
324
+ }
325
+
326
+ /* The array its rules index by arity: shorter than the arity is a read past
327
+ * the block, and the block is the node's own - so this is checked before a
328
+ * single slot is, and the slots are only read once it holds. */
329
+ if (expr->n_args != arity) {
330
+ c_dcg_bake_fail(report, node, DCG_ERR_TYPE, valid);
331
+ return;
332
+ }
333
+
334
+ for (size_t i = 0; i < expr->n_args; i++) {
335
+ if (!expr->components[i]) c_dcg_bake_fail(report, node, DCG_ERR_UNBOUND, valid);
336
+ }
337
+ }
338
+
339
+ /**
340
+ * @brief Lock one node and seal the store behind it, if it reads one.
341
+ *
342
+ * The two locks a bake applies, in the one place they are applied:
343
+ *
344
+ * - the NODE is frozen, whatever type it is. A node the graph was built from
345
+ * is not a place to hang more from, and the node layer already refuses the
346
+ * operations a frozen parent forbids (see dcg_node_flag);
347
+ * - the STORE is sealed, when the node is a read naming one. The block a
348
+ * resolved read points into stops moving the moment the store can no longer
349
+ * grow, and a store can no longer grow the moment it is frozen (see
350
+ * c_dcg_mapping_lgroup_get_create_slot - the one place an entry is born).
351
+ *
352
+ * Both are counted as they are taken and only when they are taken, which is what
353
+ * makes a re-bake report nothing: a node that was frozen and a store that was
354
+ * sealed are left alone the second time.
355
+ *
356
+ * A read that names no store - one bound by hand to another node's slot, the
357
+ * case c_dcg_node_eval_default answers - has no store to seal; a read naming a
358
+ * group that is not a store has none either.
359
+ *
360
+ * @param node Node to lock.
361
+ * @param report Report the counts go to.
362
+ */
363
+ static inline void c_dcg_bake_node_lock(dcg_node* node, dcg_bake_report* report) {
364
+ if (!(node->flags & DCG_NODE_FLAG_FROZEN)) {
365
+ node->flags |= DCG_NODE_FLAG_FROZEN;
366
+ report->locked++;
367
+ }
368
+ if (node->ntype != DCG_NODE_VARIABLE) return;
369
+
370
+ dcg_logic_group* group = ((dcg_variable_node*) node)->logic_group;
371
+ if (!group || group->lgtype != DCG_LG_MAPPING) return;
372
+
373
+ dcg_mapping_lgroup* mapping = (dcg_mapping_lgroup*) group;
374
+ if (mapping->frozen) return;
375
+
376
+ mapping->frozen = true;
377
+ report->sealed++;
378
+ }
379
+
380
+ // ========== The Walk ==========
381
+
382
+ /**
383
+ * @brief Walk the graph a bake is asked about, checking what it reaches.
384
+ *
385
+ * The edges are TWO, and both are the graph: a node's CHILDREN, which are the
386
+ * branches its value selects between, and - for an operator node - its
387
+ * COMPONENTS, which are the operands its rule runs. An operand is not a child
388
+ * of anything: it is what the node was built from, and the evaluation reaches it
389
+ * through the node rather than through the tree. A walk that followed only
390
+ * children would therefore walk past every read, every literal and every
391
+ * operator composed into one - which is most of the graph.
392
+ *
393
+ * Each node is marked as it is reached and skipped when it is reached again, so
394
+ * one node is checked once however many operands name it - and so a graph whose
395
+ * operands lead back to where they came from is walked to its end rather than
396
+ * forever. The marks are the check walk's, and the pass that follows it - the
397
+ * lockdown, or the unmarking a validate-only bake owes - is what takes them off
398
+ * again.
399
+ *
400
+ * What that terminating walk does NOT do is refuse an operand edge that points
401
+ * back into the graph: what this pass owes a graph is that every node an
402
+ * evaluation will run has what its rule needs, and that it reaches each of them
403
+ * once - an operand graph's SHAPE beyond that is a question about the graph, not
404
+ * about a node, and a build that composes its operands from other operands
405
+ * (which is every build the wrappers allow) cannot make one.
406
+ *
407
+ * @param node Node to walk.
408
+ * @param report Report being filled.
409
+ * @param depth How deep the node sits.
410
+ * @param valid Bakeability accumulator.
411
+ */
412
+ static inline void c_dcg_bake_walk(dcg_node* node, dcg_bake_report* report, size_t depth, bool* valid) {
413
+ if (!node || (node->flags & DCG_NODE_FLAG_VISITED)) return;
414
+
415
+ node->flags |= DCG_NODE_FLAG_VISITED;
416
+ report->nodes++;
417
+ if (depth > report->depth) report->depth = depth;
418
+
419
+ c_dcg_bake_node_check(node, report, valid);
420
+
421
+ for (dcg_node* child = node->children; child; child = child->next_sibling) c_dcg_bake_walk(child, report, depth + 1, valid);
422
+
423
+ if (!c_dcg_node_type_is_op(node->ntype)) return;
424
+
425
+ dcg_expression_node* expr = (dcg_expression_node*) node;
426
+ for (size_t i = 0; i < expr->n_args; i++) c_dcg_bake_walk(expr->components[i], report, depth + 1, valid);
427
+ }
428
+
429
+ /**
430
+ * @brief Take every breakpoint out of the graph, leaving the branch in its place.
431
+ *
432
+ * The pass that makes a built graph the graph an evaluation walks: a breakpoint
433
+ * is how a BUILD carries on somewhere else, and by the time the graph is baked
434
+ * there is no carrying on left to do - the node the break resumed into is what
435
+ * the branch does. So each breakpoint is spliced out and its continuation takes
436
+ * its place, and the graph the walks below see has none in it.
437
+ *
438
+ * It runs only on a graph the check walk passed, and the check is what makes
439
+ * that safe: every breakpoint was verified to HAVE a continuation, so no splice
440
+ * here can fail for want of one. The work itself is the breakpoint's own (see
441
+ * c_dcg_node_resolve_breakpoint) - what is here is where it happens.
442
+ *
443
+ * The marks are LEFT as they are: the check walk put them there and the lock
444
+ * walk is still to consume them, and the node a splice removes has its own mark
445
+ * taken off by the splice, so the lock walk reaches exactly what the check walk
446
+ * verified and nothing that left.
447
+ *
448
+ * @param node Node to resolve breaks below.
449
+ */
450
+ static inline void c_dcg_bake_resolve_walk(dcg_node* node) {
451
+ if (!node) return;
452
+
453
+ for (dcg_node* child = node->children; child; ) {
454
+ /* The successor is read BEFORE anything is spliced: a splice unlinks the
455
+ * breakpoint, and its own sibling link goes with it. */
456
+ dcg_node* next = child->next_sibling;
457
+
458
+ if (child->ntype == DCG_NODE_BREAKPOINT) {
459
+ /* The continuation is read BEFORE the splice: resolving detaches the
460
+ * breakpoint, and what it resumed into goes with it. */
461
+ dcg_node* continuation = c_dcg_node_child_by_condition(child, DCG_ELSE_CONDITION);
462
+ if (c_dcg_node_resolve_breakpoint(child) == DCG_OK && continuation) {
463
+ /* Descend into what took the breakpoint's place: the branch may
464
+ * have broken again further down. */
465
+ c_dcg_bake_resolve_walk(continuation);
466
+ }
467
+ } else {
468
+ c_dcg_bake_resolve_walk(child);
469
+ }
470
+ child = next;
471
+ }
472
+ }
473
+
474
+ /**
475
+ * @brief Apply the lockdown to every node the check walk reached.
476
+ *
477
+ * The marks are CONSUMED here rather than consulted: a node's mark is taken off
478
+ * as the walk enters it, which is what makes this walk reach each node exactly
479
+ * once and leave the graph's scratch bit as it found it. It runs only over a
480
+ * graph the check walk passed, so what it walks is what was verified.
481
+ *
482
+ * @param node Node to lock.
483
+ * @param report Report the counts go to.
484
+ */
485
+ static inline void c_dcg_bake_lock_walk(dcg_node* node, dcg_bake_report* report) {
486
+ if (!node || !(node->flags & DCG_NODE_FLAG_VISITED)) return;
487
+
488
+ node->flags &= ~(uint32_t) DCG_NODE_FLAG_VISITED;
489
+ c_dcg_bake_node_lock(node, report);
490
+
491
+ for (dcg_node* child = node->children; child; child = child->next_sibling) c_dcg_bake_lock_walk(child, report);
492
+
493
+ if (!c_dcg_node_type_is_op(node->ntype)) return;
494
+
495
+ dcg_expression_node* expr = (dcg_expression_node*) node;
496
+ for (size_t i = 0; i < expr->n_args; i++) c_dcg_bake_lock_walk(expr->components[i], report);
497
+ }
498
+
499
+ /**
500
+ * @brief Take the check walk's marks off, changing nothing else.
501
+ *
502
+ * What a pass that decided NOT to lock the graph owes it: the bit is scratch,
503
+ * and the walks that use it for their own bookkeeping later - the teardown of a
504
+ * whole graph above all - take a marked node for one they have already been to.
505
+ *
506
+ * @param node Node to unmark.
507
+ */
508
+ static inline void c_dcg_bake_unmark_walk(dcg_node* node) {
509
+ if (!node || !(node->flags & DCG_NODE_FLAG_VISITED)) return;
510
+
511
+ node->flags &= ~(uint32_t) DCG_NODE_FLAG_VISITED;
512
+
513
+ for (dcg_node* child = node->children; child; child = child->next_sibling) c_dcg_bake_unmark_walk(child);
514
+
515
+ if (!c_dcg_node_type_is_op(node->ntype)) return;
516
+
517
+ dcg_expression_node* expr = (dcg_expression_node*) node;
518
+ for (size_t i = 0; i < expr->n_args; i++) c_dcg_bake_unmark_walk(expr->components[i]);
519
+ }
520
+
521
+ /**
522
+ * @brief Make the room the root's record needs, before a walk needs it.
523
+ *
524
+ * The walk the bake is preparing for records one node per level into the root's
525
+ * own record, and a record that runs out of room grows inside the walk. That is
526
+ * the only allocation an evaluation makes, and it is the one this removes: the
527
+ * room is made here, sized for what a walk from this root can ever use - the
528
+ * height of the graph plus the node it starts from - so the walk that follows a
529
+ * bake never grows anything.
530
+ *
531
+ * A record that already has room is left as it is, and reported with the room
532
+ * it has: a graph whose root was walked before it was baked is prepared already.
533
+ *
534
+ * @param root Root whose record to make room for.
535
+ * @param report Report receiving the room.
536
+ * @return DCG_OK, or DCG_ERR_OOM when the room could not be made.
537
+ */
538
+ static inline int c_dcg_bake_prepare_record(dcg_root_node* root, dcg_bake_report* report) {
539
+ dcg_node_eval_path* path = &root->eval_path;
540
+
541
+ if (!path->node || !path->eval_val) {
542
+ int ret_code = c_dcg_root_node_eval_path_reserve(root, 0); /* 0: the graph's own height, plus 1 */
543
+ if (ret_code != DCG_OK) return ret_code;
544
+ }
545
+
546
+ report->capacity = path->capacity;
547
+ return DCG_OK;
548
+ }
549
+
550
+ // ========== The Door ==========
551
+
552
+ /**
553
+ * @brief Bake a graph from its root: verify it, lock it, and prepare it.
554
+ *
555
+ * The whole of the protocol, in the order its three parts have to happen in:
556
+ *
557
+ * 1. the graph's structure is checked first (c_dcg_node_validate). A graph
558
+ * that fails it is not walked any further: what the operand walk would be
559
+ * reading is a shape nobody has established, and the caller has something
560
+ * to fix before a second opinion is worth anything;
561
+ * 2. every node the walk reaches is checked for what an evaluation of it
562
+ * assumes (c_dcg_bake_node_check);
563
+ * 3. only then is the graph locked and sealed (c_dcg_bake_lock_walk) and its
564
+ * record prepared (c_dcg_bake_prepare_record) - so a graph that fails is
565
+ * left exactly as it was found, and a caller that ignored the report has a
566
+ * graph that does not work rather than one that half works. A
567
+ * validate-only bake stops after the second part, having taken its marks
568
+ * off the graph.
569
+ *
570
+ * @param root Root of the graph to bake. A root is the only entry, so this is
571
+ * the only door.
572
+ * @param input What the bake is asked with; NULL asks for the whole of one.
573
+ * @param report Receives what was found and what was affected (may be NULL: the
574
+ * return code then carries the answer on its own).
575
+ * @return DCG_OK when the graph is baked, or the DCG_ERR_* that stopped it.
576
+ */
577
+ static inline int c_dcg_root_node_bake(dcg_root_node* root, const dcg_bake_input* input, dcg_bake_report* report) {
578
+ if (!root) return DCG_ERR_INVALID_ARG;
579
+
580
+ /* A caller that wants only the verdict gets one: the report is written to
581
+ * the caller's slot when there is one, and to a local when there is not, so
582
+ * everything below reads one report either way. */
583
+ dcg_bake_report local;
584
+ dcg_bake_report* out = report ? report : &local;
585
+ uint64_t flags = input ? input->flags : (uint64_t) DCG_BAKE_FLAG_NONE;
586
+
587
+ memset(out, 0, sizeof(*out));
588
+
589
+ /* The graph's own shape first: it is what every walk below assumes, and the
590
+ * node layer's report is the one a caller reads for it. */
591
+ dcg_validate_report structure;
592
+ if (!c_dcg_node_validate(&root->base, &structure)) {
593
+ out->code = structure.code;
594
+ out->node = structure.node;
595
+ out->errors = structure.errors;
596
+ out->nodes = structure.nodes;
597
+ out->depth = structure.depth;
598
+ return out->code;
599
+ }
600
+
601
+ bool valid = true;
602
+ c_dcg_bake_walk(&root->base, out, 0, &valid);
603
+
604
+ if (!valid || (flags & DCG_BAKE_FLAG_VALIDATE_ONLY)) {
605
+ c_dcg_bake_unmark_walk(&root->base);
606
+ return out->code;
607
+ }
608
+
609
+ /* The breaks come down before anything is locked: what a bake leaves behind
610
+ * is the graph an evaluation walks, and that graph has no breakpoints in it.
611
+ * The check above established that each one HAS a continuation, so this
612
+ * cannot fail - and a validate-only ask has already returned, so asking
613
+ * whether a graph would bake still changes nothing about it. */
614
+ c_dcg_bake_resolve_walk(&root->base);
615
+
616
+ c_dcg_bake_lock_walk(&root->base, out);
617
+
618
+ int ret_code = c_dcg_bake_prepare_record(root, out);
619
+ if (ret_code != DCG_OK) {
620
+ /* The graph is locked - it was verified, and the lockdown is what the
621
+ * verification bought - but it has no room to be walked in, so the pass
622
+ * says what stopped it rather than reporting a bake that is not ready. */
623
+ out->code = ret_code;
624
+ out->node = NULL;
625
+ }
626
+ return ret_code;
627
+ }
628
+
629
+ #endif // C_DCG_BAKE_BAKE_H