PyDecisionGraph 0.3.1.post2__cp313-cp313-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 (144) hide show
  1. decision_graph/__init__.py +26 -0
  2. decision_graph/decision_tree/__init__.py +85 -0
  3. decision_graph/decision_tree/bake/__infra__.pxd +502 -0
  4. decision_graph/decision_tree/bake/__init__.pxd +502 -0
  5. decision_graph/decision_tree/bake/__init__.py +29 -0
  6. decision_graph/decision_tree/bake/c_action.cp313-win_amd64.pyd +0 -0
  7. decision_graph/decision_tree/bake/c_action.h +324 -0
  8. decision_graph/decision_tree/bake/c_action.pxd +54 -0
  9. decision_graph/decision_tree/bake/c_action.pyi +126 -0
  10. decision_graph/decision_tree/bake/c_action.pyx +65 -0
  11. decision_graph/decision_tree/bake/c_allocator_protocol.cp313-win_amd64.pyd +0 -0
  12. decision_graph/decision_tree/bake/c_allocator_protocol.pxd +24 -0
  13. decision_graph/decision_tree/bake/c_allocator_protocol.pyi +61 -0
  14. decision_graph/decision_tree/bake/c_allocator_protocol.pyx +109 -0
  15. decision_graph/decision_tree/bake/c_bake.cp313-win_amd64.pyd +0 -0
  16. decision_graph/decision_tree/bake/c_bake.h +629 -0
  17. decision_graph/decision_tree/bake/c_bake.pxd +52 -0
  18. decision_graph/decision_tree/bake/c_bake.pyi +151 -0
  19. decision_graph/decision_tree/bake/c_bake.pyx +142 -0
  20. decision_graph/decision_tree/bake/c_collections.cp313-win_amd64.pyd +0 -0
  21. decision_graph/decision_tree/bake/c_collections.h +692 -0
  22. decision_graph/decision_tree/bake/c_collections.pxd +52 -0
  23. decision_graph/decision_tree/bake/c_collections.pyi +193 -0
  24. decision_graph/decision_tree/bake/c_collections.pyx +161 -0
  25. decision_graph/decision_tree/bake/c_const.cp313-win_amd64.pyd +0 -0
  26. decision_graph/decision_tree/bake/c_const.h +395 -0
  27. decision_graph/decision_tree/bake/c_const.pxd +56 -0
  28. decision_graph/decision_tree/bake/c_const.pyi +305 -0
  29. decision_graph/decision_tree/bake/c_const.pyx +278 -0
  30. decision_graph/decision_tree/bake/c_edge.cp313-win_amd64.pyd +0 -0
  31. decision_graph/decision_tree/bake/c_edge.h +511 -0
  32. decision_graph/decision_tree/bake/c_edge.pxd +117 -0
  33. decision_graph/decision_tree/bake/c_edge.pyi +192 -0
  34. decision_graph/decision_tree/bake/c_edge.pyx +270 -0
  35. decision_graph/decision_tree/bake/c_eval.h +848 -0
  36. decision_graph/decision_tree/bake/c_eval.pxd +40 -0
  37. decision_graph/decision_tree/bake/c_expr.cp313-win_amd64.pyd +0 -0
  38. decision_graph/decision_tree/bake/c_expr.h +1900 -0
  39. decision_graph/decision_tree/bake/c_expr.pxd +129 -0
  40. decision_graph/decision_tree/bake/c_expr.pyi +280 -0
  41. decision_graph/decision_tree/bake/c_expr.pyx +272 -0
  42. decision_graph/decision_tree/bake/c_hierarchy.cp313-win_amd64.pyd +0 -0
  43. decision_graph/decision_tree/bake/c_hierarchy.h +731 -0
  44. decision_graph/decision_tree/bake/c_hierarchy.pxd +71 -0
  45. decision_graph/decision_tree/bake/c_hierarchy.pyi +409 -0
  46. decision_graph/decision_tree/bake/c_hierarchy.pyx +277 -0
  47. decision_graph/decision_tree/bake/c_logic_group.cp313-win_amd64.pyd +0 -0
  48. decision_graph/decision_tree/bake/c_logic_group.h +1226 -0
  49. decision_graph/decision_tree/bake/c_logic_group.pxd +162 -0
  50. decision_graph/decision_tree/bake/c_logic_group.pyi +264 -0
  51. decision_graph/decision_tree/bake/c_logic_group.pyx +308 -0
  52. decision_graph/decision_tree/bake/c_node.cp313-win_amd64.pyd +0 -0
  53. decision_graph/decision_tree/bake/c_node.h +2660 -0
  54. decision_graph/decision_tree/bake/c_node.pxd +381 -0
  55. decision_graph/decision_tree/bake/c_node.pyi +438 -0
  56. decision_graph/decision_tree/bake/c_node.pyx +639 -0
  57. decision_graph/decision_tree/bake/c_reconstruct.cp313-win_amd64.pyd +0 -0
  58. decision_graph/decision_tree/bake/c_reconstruct.pxd +63 -0
  59. decision_graph/decision_tree/bake/c_reconstruct.pyi +60 -0
  60. decision_graph/decision_tree/bake/c_reconstruct.pyx +252 -0
  61. decision_graph/decision_tree/bake/c_var.cp313-win_amd64.pyd +0 -0
  62. decision_graph/decision_tree/bake/c_var.h +2054 -0
  63. decision_graph/decision_tree/bake/c_var.pxd +205 -0
  64. decision_graph/decision_tree/bake/c_var.pyi +271 -0
  65. decision_graph/decision_tree/bake/c_var.pyx +228 -0
  66. decision_graph/decision_tree/capi/__init__.pxd +33 -0
  67. decision_graph/decision_tree/capi/__init__.py +57 -0
  68. decision_graph/decision_tree/capi/c_abc.cp313-win_amd64.pyd +0 -0
  69. decision_graph/decision_tree/capi/c_abc.pxd +246 -0
  70. decision_graph/decision_tree/capi/c_abc.pyi +770 -0
  71. decision_graph/decision_tree/capi/c_abc.pyx +1529 -0
  72. decision_graph/decision_tree/capi/c_collection.cp313-win_amd64.pyd +0 -0
  73. decision_graph/decision_tree/capi/c_collection.pxd +22 -0
  74. decision_graph/decision_tree/capi/c_collection.pyi +245 -0
  75. decision_graph/decision_tree/capi/c_collection.pyx +138 -0
  76. decision_graph/decision_tree/capi/c_node.cp313-win_amd64.pyd +0 -0
  77. decision_graph/decision_tree/capi/c_node.pxd +95 -0
  78. decision_graph/decision_tree/capi/c_node.pyi +492 -0
  79. decision_graph/decision_tree/capi/c_node.pyx +551 -0
  80. decision_graph/decision_tree/exc.py +189 -0
  81. decision_graph/decision_tree/native/__init__.py +57 -0
  82. decision_graph/decision_tree/native/abc.py +1164 -0
  83. decision_graph/decision_tree/native/collection.py +134 -0
  84. decision_graph/decision_tree/native/node.py +521 -0
  85. decision_graph/includes/decision_graph/decision_tree/bake/c_action.c +16320 -0
  86. decision_graph/includes/decision_graph/decision_tree/bake/c_action.h +324 -0
  87. decision_graph/includes/decision_graph/decision_tree/bake/c_allocator_protocol.c +12473 -0
  88. decision_graph/includes/decision_graph/decision_tree/bake/c_bake.c +16485 -0
  89. decision_graph/includes/decision_graph/decision_tree/bake/c_bake.h +629 -0
  90. decision_graph/includes/decision_graph/decision_tree/bake/c_collections.c +17381 -0
  91. decision_graph/includes/decision_graph/decision_tree/bake/c_collections.h +692 -0
  92. decision_graph/includes/decision_graph/decision_tree/bake/c_const.c +20815 -0
  93. decision_graph/includes/decision_graph/decision_tree/bake/c_const.h +395 -0
  94. decision_graph/includes/decision_graph/decision_tree/bake/c_edge.c +20696 -0
  95. decision_graph/includes/decision_graph/decision_tree/bake/c_edge.h +511 -0
  96. decision_graph/includes/decision_graph/decision_tree/bake/c_eval.h +848 -0
  97. decision_graph/includes/decision_graph/decision_tree/bake/c_expr.c +22065 -0
  98. decision_graph/includes/decision_graph/decision_tree/bake/c_expr.h +1900 -0
  99. decision_graph/includes/decision_graph/decision_tree/bake/c_hierarchy.c +23267 -0
  100. decision_graph/includes/decision_graph/decision_tree/bake/c_hierarchy.h +731 -0
  101. decision_graph/includes/decision_graph/decision_tree/bake/c_logic_group.c +22434 -0
  102. decision_graph/includes/decision_graph/decision_tree/bake/c_logic_group.h +1226 -0
  103. decision_graph/includes/decision_graph/decision_tree/bake/c_node.c +28385 -0
  104. decision_graph/includes/decision_graph/decision_tree/bake/c_node.h +2660 -0
  105. decision_graph/includes/decision_graph/decision_tree/bake/c_reconstruct.c +15955 -0
  106. decision_graph/includes/decision_graph/decision_tree/bake/c_var.c +16881 -0
  107. decision_graph/includes/decision_graph/decision_tree/bake/c_var.h +2054 -0
  108. decision_graph/includes/decision_graph/decision_tree/capi/c_abc.c +59143 -0
  109. decision_graph/includes/decision_graph/decision_tree/capi/c_collection.c +19967 -0
  110. decision_graph/includes/decision_graph/decision_tree/capi/c_node.c +39220 -0
  111. decision_graph/logic_group/__init__.py +25 -0
  112. decision_graph/logic_group/base.py +100 -0
  113. decision_graph/logic_group/pending_request.py +384 -0
  114. decision_graph/webui/__init__.py +24 -0
  115. decision_graph/webui/bake/__init__.py +65 -0
  116. decision_graph/webui/bake/app.py +488 -0
  117. decision_graph/webui/bake/static/d3.v7.min.js +2 -0
  118. decision_graph/webui/bake/static/script.js +1440 -0
  119. decision_graph/webui/bake/static/style.css +920 -0
  120. decision_graph/webui/bake/templates/index.html +183 -0
  121. decision_graph/webui/bake/templates/offline.html +184 -0
  122. decision_graph/webui/capi/__init__.py +54 -0
  123. decision_graph/webui/capi/app.py +366 -0
  124. decision_graph/webui/capi/main.py +53 -0
  125. decision_graph/webui/capi/static/d3.v7.min.js +2 -0
  126. decision_graph/webui/capi/static/script.js +1026 -0
  127. decision_graph/webui/capi/static/style.css +656 -0
  128. decision_graph/webui/capi/templates/index.html +93 -0
  129. decision_graph/webui/capi/templates/offline.html +94 -0
  130. decision_graph/webui/native/__init__.py +54 -0
  131. decision_graph/webui/native/app.py +366 -0
  132. decision_graph/webui/native/main.py +53 -0
  133. decision_graph/webui/native/static/d3.v7.min.js +2 -0
  134. decision_graph/webui/native/static/script.js +1026 -0
  135. decision_graph/webui/native/static/style.css +656 -0
  136. decision_graph/webui/native/templates/index.html +93 -0
  137. decision_graph/webui/native/templates/offline.html +94 -0
  138. pydecisiongraph-0.3.1.post2.dist-info/METADATA +130 -0
  139. pydecisiongraph-0.3.1.post2.dist-info/RECORD +144 -0
  140. pydecisiongraph-0.3.1.post2.dist-info/WHEEL +5 -0
  141. pydecisiongraph-0.3.1.post2.dist-info/licenses/LICENSE +373 -0
  142. pydecisiongraph-0.3.1.post2.dist-info/top_level.txt +2 -0
  143. tests/bake_c_evaluator.cp313-win_amd64.pyd +0 -0
  144. tests/bake_eval_probe.cp313-win_amd64.pyd +0 -0
@@ -0,0 +1,324 @@
1
+ #ifndef C_DCG_BAKE_ACTION_H
2
+ #define C_DCG_BAKE_ACTION_H
3
+
4
+ #include <stdbool.h>
5
+ #include <stddef.h>
6
+ #include <stdint.h>
7
+ #include <stdio.h>
8
+
9
+ #include <cbase/allocator_protocol/c_allocator_protocol.h>
10
+
11
+ #include <decision_graph/decision_tree/bake/c_node.h>
12
+
13
+ /*
14
+ * The action family: the leaves that say what the graph decides (NOACTION /
15
+ * LONGACTION / SHORTACTION / CANCELACTION / CLEARACTION).
16
+ *
17
+ * Every one of them is the same struct - the base node and the two fields the
18
+ * builder needs to place it: whether it should be connected to the active node
19
+ * as it is built, and whatever data the caller hangs off it. The family's _free
20
+ * is the base free today, because nothing it carries goes beyond the block; it
21
+ * stays a function of its own - and the dispatcher in c_hierarchy.h routes the
22
+ * whole family through it - because the action types are the ones the capi keeps
23
+ * growing (a trade's signal, the payload a caller hangs off it), and their
24
+ * teardown is where that growth will land. The graph's own types - the root and
25
+ * the breakpoint - live in c_hierarchy.h too.
26
+ */
27
+
28
+ // ========== Constants ==========
29
+
30
+ /* DCG_DEF_REPR_NOACTION is defined in c_node.h - see the note there. */
31
+ /* DCG_DEF_REPR_PLACEHOLDER is defined in c_node.h too: the placeholder is a
32
+ * plain node rather than an action, and its constructor sits there. */
33
+
34
+ // ========== Structs ==========
35
+
36
+ /**
37
+ * @brief An action node: the base node plus what the builder needs to place it.
38
+ *
39
+ * `auto_connect` records how the node was built, not what it is: the plain
40
+ * constructors leave it false and the connect ones set it to what they were
41
+ * asked for - "join the active node" is what the capi does with it. Nothing
42
+ * acts on the field afterwards; it is there for whoever reads the node back.
43
+ * `action_data` is the caller's opaque pointer and is NOT owned: it is carried
44
+ * to whoever evaluates the action, and stays the caller's to release.
45
+ *
46
+ * `sig` is the signal the action stands for, and it is what the capi's action
47
+ * classes carry in the same field name: +1 for a long, -1 for a short, 0 for
48
+ * everything that decides nothing. The type is the authority the signal follows;
49
+ * the variant constructors, whose types all decide nothing, pass 0.
50
+ *
51
+ * The base node must stay the FIRST member: a dcg_action_node* is therefore a
52
+ * valid dcg_node*.
53
+ */
54
+ typedef struct dcg_action_node {
55
+ dcg_node base; // The common node header. Must stay first.
56
+ bool auto_connect; // Whether the builder should connect it as it is made.
57
+ ssize_t sig; // The action's signal: +1 long, -1 short, 0 for the rest.
58
+ void* action_data; // NOT owned - the caller's own payload.
59
+ } dcg_action_node;
60
+
61
+ // ========== Forward Declarations ==========
62
+
63
+ /*
64
+ * The manager a connect constructor joins through. Only the pointer is named
65
+ * here: connecting needs the manager's stacks, which c_logic_group.h defines,
66
+ * and that header includes this one - so the bodies live there and this header
67
+ * carries the family's shape.
68
+ */
69
+ typedef struct dcg_logic_group_manager dcg_logic_group_manager;
70
+
71
+ // Lifecycle - the action family (a type, its repr, and its two fields)
72
+ static inline dcg_action_node* c_dcg_node_new_action(dcg_node_type action_type, const char* repr, ssize_t sig, void* action_data, allocator_protocol* allocator);
73
+ static inline void c_dcg_node_free_action(dcg_action_node* node);
74
+
75
+ static inline dcg_action_node* c_dcg_node_new_action_trade(dcg_node_type action_type, allocator_protocol* allocator);
76
+ static inline dcg_action_node* c_dcg_node_new_action_clear(allocator_protocol* allocator);
77
+
78
+ static inline int c_dcg_node_auto_fill(dcg_node* node);
79
+
80
+ // ========== Lifecycle Methods ==========
81
+
82
+ /**
83
+ * @brief Allocate an action leaf of an explicit type - standalone.
84
+ *
85
+ * The node joins no graph here: connecting means taking the arm the active
86
+ * node reserved, and that needs a manager to say which node is active. These
87
+ * constructors build a node nobody owns yet; the connect variants below take
88
+ * the manager and do the joining.
89
+ *
90
+ * @param action_type An action type.
91
+ * @param repr Display text to copy (may be NULL: naming the type is the
92
+ * caller's, which is what the variants below do).
93
+ * @param sig The signal to carry (+1 long, -1 short, 0 for the rest).
94
+ * @param action_data The caller's payload (not owned; may be NULL).
95
+ * @param allocator Allocator for the block; NULL falls back to the plain heap.
96
+ * @return The node, or NULL on OOM / invalid type.
97
+ */
98
+ static inline dcg_action_node* c_dcg_node_new_action(dcg_node_type action_type, const char* repr, ssize_t sig, void* action_data, allocator_protocol* allocator) {
99
+ if (!c_dcg_node_type_is_action(action_type)) return NULL;
100
+
101
+ dcg_action_node* node = (dcg_action_node*) c_ap_alloc(sizeof(dcg_action_node), allocator);
102
+ if (!node) return NULL;
103
+
104
+ if (c_dcg_node_init(&node->base, action_type, repr) != DCG_OK) {
105
+ c_ap_free_owned(node);
106
+ return NULL;
107
+ }
108
+
109
+ node->auto_connect = false; /* nothing was connected: the callers that do say so */
110
+ node->sig = sig;
111
+ node->action_data = action_data;
112
+
113
+ /* An action's value is the node itself, and it is written HERE rather than
114
+ * produced by an evaluation: nothing about running an action changes what it
115
+ * stands for, so there is nothing for an evaluation to do - and a node whose
116
+ * slot is the node needs no rule and no dispatch (see c_dcg_node_eval_default).
117
+ * The slot is NODE-shaped rather than an opaque pointer, which is what lets it
118
+ * unpack as the wrapper again (see c_dcg_var_pyunpack). */
119
+ if (c_dcg_var_init_node(&node->base.out, &node->base) != DCG_OK) {
120
+ c_ap_free_owned(node);
121
+ return NULL;
122
+ }
123
+ return node;
124
+ }
125
+
126
+ /**
127
+ * @brief Tear down an action and free its buf.
128
+ *
129
+ * The base free is the whole of it: `auto_connect` and `sig` are plain fields
130
+ * inside the block, and `action_data` is the caller's own pointer, not owned.
131
+ * It is a function of its own anyway - and the dispatcher in c_hierarchy.h
132
+ * routes the whole family through it - because the action types are the ones
133
+ * the capi keeps growing, and this is where the release of whatever they grow
134
+ * will live.
135
+ *
136
+ * @param node Node to free (NULL-safe).
137
+ */
138
+ static inline void c_dcg_node_free_action(dcg_action_node* node) {
139
+ if (!node) return;
140
+ c_dcg_node_free(&node->base);
141
+ }
142
+
143
+ // ========== Public APIs - Variant Constructors ==========
144
+
145
+ /**
146
+ * @brief Allocate a trade action of an explicit type.
147
+ *
148
+ * Only the types that trade are accepted - a long, a short and a cancel - and
149
+ * each brings the signal it stands for with it: +1, -1 and 0, the values the
150
+ * capi's action classes default to. The repr is the type's name, so the caller
151
+ * states the decision and nothing else; anything the type does not already say
152
+ * the generic constructor can carry.
153
+ *
154
+ * @param action_type DCG_NODE_LONGACTION, DCG_NODE_SHORTACTION or DCG_NODE_CANCELACTION.
155
+ * @param allocator Allocator for the block; NULL falls back to the plain heap.
156
+ * @return The node, or NULL on OOM / a type that does not trade.
157
+ */
158
+ static inline dcg_action_node* c_dcg_node_new_action_trade(dcg_node_type action_type, allocator_protocol* allocator) {
159
+ ssize_t sig = 0;
160
+
161
+ switch (action_type) {
162
+ case DCG_NODE_LONGACTION:
163
+ sig = 1;
164
+ break;
165
+ case DCG_NODE_SHORTACTION:
166
+ sig = -1;
167
+ break;
168
+ case DCG_NODE_CANCELACTION:
169
+ break; /* neither direction: a cancel closes what is open */
170
+ default:
171
+ return NULL; /* a clear, a no-op and a placeholder are not trades */
172
+ }
173
+
174
+ return c_dcg_node_new_action(action_type, c_dcg_node_type_name(action_type), sig, NULL, allocator);
175
+ }
176
+
177
+ /**
178
+ * @brief Allocate an action leaf that flattens the position.
179
+ *
180
+ * @param allocator Allocator for the block; NULL falls back to the plain heap.
181
+ * @return The node, or NULL on OOM.
182
+ */
183
+ static inline dcg_action_node* c_dcg_node_new_action_clear(allocator_protocol* allocator) {
184
+ return c_dcg_node_new_action(DCG_NODE_CLEARACTION, c_dcg_node_type_name(DCG_NODE_CLEARACTION), 0, NULL, allocator);
185
+ }
186
+
187
+ // ========== Public APIs - Closing a Branch ==========
188
+
189
+ /**
190
+ * @brief Close a node's branches: fill the arm it never got, add the fallback.
191
+ *
192
+ * The step a build closes a node with, and the second half of the placeholder
193
+ * discipline - a node is entered with its arms reserved (c_dcg_node_get_placeholder),
194
+ * a build fills the arms it means to, and this supplies the ones it did not.
195
+ * It runs on exit, before the placeholders are consolidated.
196
+ *
197
+ * Every arm it fills is filled with an auto-generated no-action, so a later
198
+ * consolidation can tell it from an action the caller wrote. The rules read the
199
+ * node's EXISTING branches, in the capi's order:
200
+ *
201
+ * - no branch at all -> an auto no-action on the unconditioned edge;
202
+ * - one branch -> its opposite when it is a binary, nothing when it is
203
+ * unconditioned, and the TRUE arm an ELSE-only node
204
+ * was waiting for (inserted in front, so the fallback
205
+ * stays last) - a value-keyed arm stands alone;
206
+ * - two branches -> two binaries stand, an ELSE stands as the fallback,
207
+ * two value-keyed arms gain a protective ELSE, and any
208
+ * other pairing is contradictory (DCG_ERR_EDGE);
209
+ * - three or more -> binaries and unconditioned edges are illegal among
210
+ * them (DCG_ERR_TYPE or DCG_ERR_EDGE), and a set with
211
+ * no fallback gains a trailing ELSE.
212
+ *
213
+ * Nothing is added when the node already says all it can: the call is
214
+ * idempotent, and a failure changes nothing - the auto no-action is released
215
+ * rather than left half-linked.
216
+ *
217
+ * @param node Node to close (NULL-safe).
218
+ * @return DCG_OK, or a DCG_ERR_* code: DCG_ERR_INVALID_ARG (NULL), DCG_ERR_EDGE
219
+ * (contradictory branches), DCG_ERR_TYPE (too many, or a binary among
220
+ * three) and DCG_ERR_OOM.
221
+ */
222
+ static inline int c_dcg_node_auto_fill(dcg_node* node) {
223
+ if (!node) return DCG_ERR_INVALID_ARG;
224
+
225
+ size_t count = c_dcg_node_child_count(node);
226
+ dcg_node* last = c_dcg_node_last_child(node);
227
+
228
+ /* The arm every branch below is filled with, built on demand and released
229
+ * again if the link it was built for is refused. */
230
+ dcg_action_node* no_action = NULL;
231
+ int ret = DCG_OK;
232
+
233
+ if (count == 0) {
234
+ no_action = c_dcg_node_new_action(DCG_NODE_NOACTION, DCG_DEF_REPR_NOACTION, 0, NULL, c_ap_protocol_from_ptr(node));
235
+ if (!no_action) return DCG_ERR_OOM;
236
+ no_action->base.autogen = true;
237
+
238
+ ret = c_dcg_node_append(node, &no_action->base, DCG_NO_CONDITION);
239
+ if (ret != DCG_OK) c_dcg_node_free_action(no_action);
240
+ return ret;
241
+ }
242
+
243
+ const dcg_node_edge_condition* condition = last->condition_to_parent;
244
+
245
+ if (count == 1) {
246
+ const dcg_node_edge_condition* fill = NULL;
247
+ bool head = false; /* an ELSE waits for its TRUE arm, which goes in front */
248
+
249
+ if (c_dcg_condition_is_none(condition)) return DCG_OK;
250
+ else if (c_dcg_condition_is_binary(condition)) fill = c_dcg_condition_is_true(condition) ? DCG_FALSE_CONDITION : DCG_TRUE_CONDITION;
251
+ else if (c_dcg_condition_is_else(condition)) {
252
+ fill = DCG_TRUE_CONDITION;
253
+ head = true;
254
+ }
255
+ else return DCG_OK; /* a value-keyed arm stands alone */
256
+
257
+ no_action = c_dcg_node_new_action(DCG_NODE_NOACTION, DCG_DEF_REPR_NOACTION, 0, NULL, c_ap_protocol_from_ptr(node));
258
+ if (!no_action) return DCG_ERR_OOM;
259
+ no_action->base.autogen = true;
260
+
261
+ ret = head ? c_dcg_node_append_at(node, &no_action->base, fill, 0) : c_dcg_node_append(node, &no_action->base, fill);
262
+ if (ret != DCG_OK) c_dcg_node_free_action(no_action);
263
+ return ret;
264
+ }
265
+
266
+ if (count == 2) {
267
+ const dcg_node_edge_condition* second_condition = last->prev_sibling->condition_to_parent;
268
+
269
+ if (c_dcg_condition_is_none(condition) || c_dcg_condition_is_none(second_condition)) return DCG_ERR_EDGE; /* an unconditioned arm cannot share a node */
270
+ if (c_dcg_condition_is_else(condition) || c_dcg_condition_is_else(second_condition)) return DCG_OK;
271
+ if (c_dcg_condition_is_binary(condition) && c_dcg_condition_is_binary(second_condition)) return DCG_OK;
272
+ if (c_dcg_condition_is_binary(condition) || c_dcg_condition_is_binary(second_condition)) return DCG_ERR_EDGE; /* a binary beside a value-keyed arm */
273
+ /* Two value-keyed arms: the fallback is what makes the node total. */
274
+ }
275
+ else {
276
+ /* Three or more: only value-keyed arms may share a node. */
277
+ for (dcg_node* child = node->children; child; child = child->next_sibling) {
278
+ const dcg_node_edge_condition* child_condition = child->condition_to_parent;
279
+
280
+ if (c_dcg_condition_is_none(child_condition)) return DCG_ERR_EDGE;
281
+ if (c_dcg_condition_is_binary(child_condition)) return DCG_ERR_TYPE;
282
+ if (c_dcg_condition_is_else(child_condition)) return DCG_OK; /* the fallback is there already, and it is last */
283
+ }
284
+ }
285
+
286
+ no_action = c_dcg_node_new_action(DCG_NODE_NOACTION, DCG_DEF_REPR_NOACTION, 0, NULL, c_ap_protocol_from_ptr(node));
287
+ if (!no_action) return DCG_ERR_OOM;
288
+ no_action->base.autogen = true;
289
+
290
+ ret = c_dcg_node_append(node, &no_action->base, DCG_ELSE_CONDITION);
291
+ if (ret != DCG_OK) c_dcg_node_free_action(no_action);
292
+ return ret;
293
+ }
294
+
295
+ /**
296
+ * @brief Leave a node: fill the branch it never got, then retire the stand-ins.
297
+ *
298
+ * The order is the capi's, and it is the same for every type. `auto_fill`
299
+ * supplies the branches a build did not build, so a node entered and left
300
+ * without one still says something; the consolidation that follows turns
301
+ * whatever is STILL a placeholder into a no-action, so a graph that has closed
302
+ * never contains a stand-in.
303
+ *
304
+ * This is the exit half of every type's context ops - which is why the manager
305
+ * installs it as a node arrives rather than each constructor arming it: the body
306
+ * needs the action family's constructors, and those are above the header that
307
+ * declares the ops.
308
+ *
309
+ * @param node Node being left.
310
+ * @param mgr Manager holding the build (unused: closing is the node's own).
311
+ * @return DCG_OK, or a DCG_ERR_* code.
312
+ */
313
+ static inline int c_dcg_node_ctx_exit_closed(dcg_node* node, dcg_logic_group_manager* mgr) {
314
+ (void) mgr;
315
+ if (!node) return DCG_ERR_INVALID_ARG;
316
+
317
+ int ret = c_dcg_node_auto_fill(node);
318
+ if (ret != DCG_OK) return ret;
319
+
320
+ c_dcg_node_consolidate_placeholder(node);
321
+ return DCG_OK;
322
+ }
323
+
324
+ #endif // C_DCG_BAKE_ACTION_H
@@ -0,0 +1,54 @@
1
+ from libcpp cimport bool as c_bool
2
+
3
+ from cbase.allocator_protocol cimport allocator_protocol
4
+
5
+ from .c_node cimport LogicNode, dcg_node, dcg_node_type
6
+
7
+
8
+ cdef extern from "decision_graph/decision_tree/bake/c_action.h":
9
+ ctypedef struct dcg_action_node:
10
+ dcg_node base
11
+ c_bool auto_connect
12
+ ssize_t sig
13
+ void* action_data
14
+
15
+ dcg_action_node* c_dcg_node_new_action(dcg_node_type action_type, const char* repr, ssize_t sig, void* action_data, allocator_protocol* allocator) noexcept nogil
16
+ void c_dcg_node_free_action(dcg_action_node* node) noexcept nogil
17
+
18
+ dcg_action_node* c_dcg_node_new_action_trade(dcg_node_type action_type, allocator_protocol* allocator) noexcept nogil
19
+ dcg_action_node* c_dcg_node_new_action_clear(allocator_protocol* allocator) noexcept nogil
20
+
21
+ int c_dcg_node_auto_fill(dcg_node* node) noexcept nogil
22
+
23
+
24
+ cdef extern from "decision_graph/decision_tree/bake/c_logic_group.h":
25
+ ctypedef struct dcg_logic_group_manager:
26
+ pass
27
+
28
+ dcg_action_node* c_dcg_node_connect_new_action(dcg_node_type action_type, const char* repr, c_bool auto_connect, ssize_t sig, void* action_data, dcg_logic_group_manager* mgr, allocator_protocol* allocator) noexcept nogil
29
+ dcg_action_node* c_dcg_node_connect_new_action_trade(dcg_node_type action_type, c_bool auto_connect, dcg_logic_group_manager* mgr, allocator_protocol* allocator) noexcept nogil
30
+ dcg_action_node* c_dcg_node_connect_new_action_clear(c_bool auto_connect, dcg_logic_group_manager* mgr, allocator_protocol* allocator) noexcept nogil
31
+
32
+
33
+ cdef class ActionNode(LogicNode):
34
+ pass
35
+
36
+
37
+ cdef class NoAction(ActionNode):
38
+ pass
39
+
40
+
41
+ cdef class LongAction(ActionNode):
42
+ pass
43
+
44
+
45
+ cdef class ShortAction(ActionNode):
46
+ pass
47
+
48
+
49
+ cdef class CancelAction(ActionNode):
50
+ pass
51
+
52
+
53
+ cdef class ClearAction(ActionNode):
54
+ pass
@@ -0,0 +1,126 @@
1
+ """The action layer: the leaves a graph decides with.
2
+
3
+ An action is where a graph stops and answers. It carries a signal - long, short,
4
+ flat - and is a typed node of its own, so a render, an evaluation and a rebuild
5
+ can all tell the leaves apart.
6
+
7
+ A leaf that is built inside a build joins the node being built, unless the caller
8
+ says otherwise; the action that joins nothing stands on its own.
9
+ """
10
+
11
+ from typing import Any
12
+
13
+ from .c_node import LogicNode
14
+
15
+
16
+ class ActionNode(LogicNode):
17
+ """An action leaf: a typed terminal node carrying a signal.
18
+
19
+ The class itself is the family's own; a caller builds the named leaves below
20
+ it, which differ only in the type and the signal they pass.
21
+ """
22
+
23
+ def __init__(self, node_type: int, *, repr: str | None = None, sig: int = 0, auto_connect: bool = True, **kwargs: Any) -> None:
24
+ """Build an action of an explicit node type.
25
+
26
+ Args:
27
+ node_type: The C node type this action is.
28
+ repr: Display text to copy; the type's own text when not given.
29
+ sig: The signal it carries: +1 long, -1 short, 0 for the rest.
30
+ auto_connect: Whether it joins the node being built.
31
+
32
+ Raises:
33
+ MemoryError: When the block cannot be allocated.
34
+ """
35
+ ...
36
+
37
+
38
+ class NoAction(ActionNode):
39
+ """The leaf that decides nothing: what an unfilled arm closes as."""
40
+
41
+ def __init__(self, *, sig: int = 0, repr: str = 'NoAction', auto_connect: bool = True, autogen: bool = False, **kwargs: Any) -> None:
42
+ """Build a no-action leaf.
43
+
44
+ Args:
45
+ sig: The signal it carries, 0 by default.
46
+ repr: Display text to copy.
47
+ auto_connect: Whether it joins the node being built.
48
+ autogen: Whether the builder generated it rather than a caller.
49
+ """
50
+ ...
51
+
52
+ def __int__(self) -> int:
53
+ """The signal the leaf carries."""
54
+ ...
55
+
56
+
57
+ class LongAction(ActionNode):
58
+ """The leaf that signals long."""
59
+
60
+ def __init__(self, *, sig: int = 1, repr: str = 'LongAction', auto_connect: bool = True, **kwargs: Any) -> None:
61
+ """Build a long-action leaf.
62
+
63
+ Args:
64
+ sig: The signal it carries, +1 by default.
65
+ repr: Display text to copy.
66
+ auto_connect: Whether it joins the node being built.
67
+ """
68
+ ...
69
+
70
+ def __int__(self) -> int:
71
+ """The signal the leaf carries."""
72
+ ...
73
+
74
+
75
+ class ShortAction(ActionNode):
76
+ """The leaf that signals short."""
77
+
78
+ def __init__(self, *, sig: int = -1, repr: str = 'ShortAction', auto_connect: bool = True, **kwargs: Any) -> None:
79
+ """Build a short-action leaf.
80
+
81
+ Args:
82
+ sig: The signal it carries, -1 by default.
83
+ repr: Display text to copy.
84
+ auto_connect: Whether it joins the node being built.
85
+ """
86
+ ...
87
+
88
+ def __int__(self) -> int:
89
+ """The signal the leaf carries."""
90
+ ...
91
+
92
+
93
+ class CancelAction(ActionNode):
94
+ """The leaf that cancels an outstanding signal."""
95
+
96
+ def __init__(self, *, sig: int = 0, repr: str = 'CancelAction', auto_connect: bool = True, **kwargs: Any) -> None:
97
+ """Build a cancel-action leaf.
98
+
99
+ Args:
100
+ sig: The signal it carries, 0 by default.
101
+ repr: Display text to copy.
102
+ auto_connect: Whether it joins the node being built.
103
+ """
104
+ ...
105
+
106
+ def __int__(self) -> int:
107
+ """The signal the leaf carries."""
108
+ ...
109
+
110
+
111
+ class ClearAction(ActionNode):
112
+ """The leaf that flattens: it clears whatever position is held."""
113
+
114
+ def __init__(self, *, sig: int = 0, repr: str = 'ClearAction', auto_connect: bool = True, **kwargs: Any) -> None:
115
+ """Build a clear-action leaf.
116
+
117
+ Args:
118
+ sig: The signal it carries, 0 by default.
119
+ repr: Display text to copy.
120
+ auto_connect: Whether it joins the node being built.
121
+ """
122
+ ...
123
+
124
+ def __int__(self) -> int:
125
+ """The signal the leaf carries."""
126
+ ...
@@ -0,0 +1,65 @@
1
+ from cpython.unicode cimport PyUnicode_AsUTF8
2
+
3
+ from .c_allocator_protocol cimport DCG_DEFAULT_ALLOCATOR
4
+
5
+
6
+ cdef class ActionNode(LogicNode):
7
+ def __init__(self, dcg_node_type node_type, *, str repr=None, ssize_t sig=0, bint auto_connect=True, **kwargs):
8
+ cdef dcg_action_node* node = c_dcg_node_connect_new_action(
9
+ node_type,
10
+ PyUnicode_AsUTF8(repr) if repr is not None else NULL,
11
+ auto_connect,
12
+ sig,
13
+ NULL,
14
+ LogicNode.c_get_manager(),
15
+ DCG_DEFAULT_ALLOCATOR,
16
+ )
17
+
18
+ if not node:
19
+ raise MemoryError(f'Failed to allocate {self.__class__.__name__}.')
20
+
21
+ self.header = &node.base
22
+ self.owner = True
23
+
24
+ self.c_register_node()
25
+
26
+
27
+ cdef class NoAction(ActionNode):
28
+ def __init__(self, *, ssize_t sig=0, str repr='NoAction', bint auto_connect=True, bint autogen=False, **kwargs):
29
+ ActionNode.__init__(self, dcg_node_type.DCG_NODE_NOACTION, repr=repr, sig=sig, auto_connect=auto_connect)
30
+ self.header.autogen = autogen
31
+
32
+ def __int__(self):
33
+ return 0
34
+
35
+
36
+ cdef class LongAction(ActionNode):
37
+ def __init__(self, *, ssize_t sig=1, str repr='LongAction', bint auto_connect=True, **kwargs):
38
+ ActionNode.__init__(self, dcg_node_type.DCG_NODE_LONGACTION, repr=repr, sig=sig, auto_connect=auto_connect)
39
+
40
+ def __int__(self):
41
+ return 1
42
+
43
+
44
+ cdef class ShortAction(ActionNode):
45
+ def __init__(self, *, ssize_t sig=-1, str repr='ShortAction', bint auto_connect=True, **kwargs):
46
+ ActionNode.__init__(self, dcg_node_type.DCG_NODE_SHORTACTION, repr=repr, sig=sig, auto_connect=auto_connect)
47
+
48
+ def __int__(self):
49
+ return -1
50
+
51
+
52
+ cdef class CancelAction(ActionNode):
53
+ def __init__(self, *, ssize_t sig=0, str repr='CancelAction', bint auto_connect=True, **kwargs):
54
+ ActionNode.__init__(self, dcg_node_type.DCG_NODE_CANCELACTION, repr=repr, sig=sig, auto_connect=auto_connect)
55
+
56
+ def __int__(self):
57
+ return 0
58
+
59
+
60
+ cdef class ClearAction(ActionNode):
61
+ def __init__(self, *, ssize_t sig=0, str repr='ClearAction', bint auto_connect=True, **kwargs):
62
+ ActionNode.__init__(self, dcg_node_type.DCG_NODE_CLEARACTION, repr=repr, sig=sig, auto_connect=auto_connect)
63
+
64
+ def __int__(self):
65
+ return 0
@@ -0,0 +1,24 @@
1
+ from libcpp cimport bool as c_bool
2
+
3
+ from cbase.allocator_protocol cimport AllocatorConfigContext, allocator_protocol, heap_allocator, shm_allocator_ctx
4
+
5
+ cdef c_bool DCG_CFG_LOCKED
6
+ cdef c_bool DCG_CFG_SHARED
7
+ cdef c_bool DCG_CFG_FREELIST
8
+
9
+
10
+ cdef class ConfigContext(AllocatorConfigContext):
11
+ pass
12
+
13
+
14
+ cdef heap_allocator* HEAP_ALLOCATOR
15
+ cdef shm_allocator_ctx* SHM_ALLOCATOR
16
+
17
+ cdef allocator_protocol* DCG_DEFAULT_ALLOCATOR
18
+ cdef allocator_protocol* DCG_SHM_ALLOCATOR
19
+ cdef allocator_protocol* DCG_HEAP_ALLOCATOR
20
+
21
+ cdef ConfigContext DCG_SHARED
22
+ cdef ConfigContext DCG_LOCKED
23
+ cdef ConfigContext DCG_LOCKFREE
24
+ cdef ConfigContext DCG_FREELIST
@@ -0,0 +1,61 @@
1
+ """The allocator the bake layer allocates through, and the switches it reads.
2
+
3
+ Every block the layer builds - nodes, groups, slots, strings - comes from the
4
+ allocator protocol, and this module owns the layer's instance of it along with
5
+ the switches that decide how it behaves: locking, shared memory, and the free
6
+ list.
7
+
8
+ A switch is read by the C layer as it allocates, so it is set by entering one of
9
+ the contexts below rather than by assignment, and a block's lifetime is the
10
+ protocol's: a child block dies with its parent.
11
+ """
12
+
13
+ from cbase.allocator_protocol import AllocatorConfigContext
14
+
15
+
16
+ class ConfigContext(AllocatorConfigContext):
17
+ """A set of allocator switches, applied on entry and put back on exit.
18
+
19
+ Attributes:
20
+ overrides: The switches this context sets, keyed by name.
21
+ originals: What those switches were before the context was entered.
22
+ """
23
+
24
+ def __init__(self, *, locked: bool = ..., shared: bool = ..., freelist: bool = ...) -> None:
25
+ """Build a context that sets the switches it is given.
26
+
27
+ Args:
28
+ locked: Whether allocations take the protocol's lock.
29
+ shared: Whether blocks come from shared memory.
30
+ freelist: Whether freed blocks are recycled.
31
+ """
32
+ ...
33
+
34
+
35
+ class _RuntimeAllocatorConfig:
36
+ """The live switch values, read through properties rather than copied."""
37
+
38
+ @property
39
+ def DCG_CFG_LOCKED(self) -> bool:
40
+ """Whether allocations currently take the protocol's lock."""
41
+ ...
42
+
43
+ @property
44
+ def DCG_CFG_SHARED(self) -> bool:
45
+ """Whether blocks currently come from shared memory."""
46
+ ...
47
+
48
+ @property
49
+ def DCG_CFG_FREELIST(self) -> bool:
50
+ """Whether freed blocks are currently recycled."""
51
+ ...
52
+
53
+
54
+ # The four switch contexts: shared memory, locking, lock-free, free list.
55
+ DCG_SHARED: ConfigContext
56
+ DCG_LOCKED: ConfigContext
57
+ DCG_LOCKFREE: ConfigContext
58
+ DCG_FREELIST: ConfigContext
59
+
60
+ # The switches as they are right now, for a caller that has to ask.
61
+ RUNTIME_ALLOCATOR_CONFIG: _RuntimeAllocatorConfig