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.
- decision_graph/__infra__.pxd +1 -0
- decision_graph/__init__.pxd +1 -0
- decision_graph/__init__.py +39 -0
- decision_graph/decision_tree/__init__.pxd +2 -0
- decision_graph/decision_tree/__init__.py +85 -0
- decision_graph/decision_tree/bake/__infra__.pxd +502 -0
- decision_graph/decision_tree/bake/__init__.pxd +502 -0
- decision_graph/decision_tree/bake/__init__.py +29 -0
- decision_graph/decision_tree/bake/c_action.cp315-win_amd64.pyd +0 -0
- decision_graph/decision_tree/bake/c_action.h +324 -0
- decision_graph/decision_tree/bake/c_action.pxd +54 -0
- decision_graph/decision_tree/bake/c_action.pyi +126 -0
- decision_graph/decision_tree/bake/c_action.pyx +65 -0
- decision_graph/decision_tree/bake/c_allocator_protocol.cp315-win_amd64.pyd +0 -0
- decision_graph/decision_tree/bake/c_allocator_protocol.pxd +24 -0
- decision_graph/decision_tree/bake/c_allocator_protocol.pyi +61 -0
- decision_graph/decision_tree/bake/c_allocator_protocol.pyx +109 -0
- decision_graph/decision_tree/bake/c_bake.cp315-win_amd64.pyd +0 -0
- decision_graph/decision_tree/bake/c_bake.h +629 -0
- decision_graph/decision_tree/bake/c_bake.pxd +52 -0
- decision_graph/decision_tree/bake/c_bake.pyi +151 -0
- decision_graph/decision_tree/bake/c_bake.pyx +142 -0
- decision_graph/decision_tree/bake/c_collections.cp315-win_amd64.pyd +0 -0
- decision_graph/decision_tree/bake/c_collections.h +692 -0
- decision_graph/decision_tree/bake/c_collections.pxd +52 -0
- decision_graph/decision_tree/bake/c_collections.pyi +193 -0
- decision_graph/decision_tree/bake/c_collections.pyx +161 -0
- decision_graph/decision_tree/bake/c_const.cp315-win_amd64.pyd +0 -0
- decision_graph/decision_tree/bake/c_const.h +395 -0
- decision_graph/decision_tree/bake/c_const.pxd +56 -0
- decision_graph/decision_tree/bake/c_const.pyi +305 -0
- decision_graph/decision_tree/bake/c_const.pyx +278 -0
- decision_graph/decision_tree/bake/c_edge.cp315-win_amd64.pyd +0 -0
- decision_graph/decision_tree/bake/c_edge.h +511 -0
- decision_graph/decision_tree/bake/c_edge.pxd +117 -0
- decision_graph/decision_tree/bake/c_edge.pyi +192 -0
- decision_graph/decision_tree/bake/c_edge.pyx +270 -0
- decision_graph/decision_tree/bake/c_eval.h +848 -0
- decision_graph/decision_tree/bake/c_eval.pxd +40 -0
- decision_graph/decision_tree/bake/c_expr.cp315-win_amd64.pyd +0 -0
- decision_graph/decision_tree/bake/c_expr.h +1900 -0
- decision_graph/decision_tree/bake/c_expr.pxd +129 -0
- decision_graph/decision_tree/bake/c_expr.pyi +280 -0
- decision_graph/decision_tree/bake/c_expr.pyx +272 -0
- decision_graph/decision_tree/bake/c_hierarchy.cp315-win_amd64.pyd +0 -0
- decision_graph/decision_tree/bake/c_hierarchy.h +731 -0
- decision_graph/decision_tree/bake/c_hierarchy.pxd +71 -0
- decision_graph/decision_tree/bake/c_hierarchy.pyi +409 -0
- decision_graph/decision_tree/bake/c_hierarchy.pyx +277 -0
- decision_graph/decision_tree/bake/c_logic_group.cp315-win_amd64.pyd +0 -0
- decision_graph/decision_tree/bake/c_logic_group.h +1226 -0
- decision_graph/decision_tree/bake/c_logic_group.pxd +162 -0
- decision_graph/decision_tree/bake/c_logic_group.pyi +264 -0
- decision_graph/decision_tree/bake/c_logic_group.pyx +308 -0
- decision_graph/decision_tree/bake/c_node.cp315-win_amd64.pyd +0 -0
- decision_graph/decision_tree/bake/c_node.h +2660 -0
- decision_graph/decision_tree/bake/c_node.pxd +381 -0
- decision_graph/decision_tree/bake/c_node.pyi +438 -0
- decision_graph/decision_tree/bake/c_node.pyx +639 -0
- decision_graph/decision_tree/bake/c_reconstruct.cp315-win_amd64.pyd +0 -0
- decision_graph/decision_tree/bake/c_reconstruct.pxd +63 -0
- decision_graph/decision_tree/bake/c_reconstruct.pyi +60 -0
- decision_graph/decision_tree/bake/c_reconstruct.pyx +252 -0
- decision_graph/decision_tree/bake/c_var.cp315-win_amd64.pyd +0 -0
- decision_graph/decision_tree/bake/c_var.h +2054 -0
- decision_graph/decision_tree/bake/c_var.pxd +205 -0
- decision_graph/decision_tree/bake/c_var.pyi +271 -0
- decision_graph/decision_tree/bake/c_var.pyx +228 -0
- decision_graph/decision_tree/capi/__infra__.pxd +33 -0
- decision_graph/decision_tree/capi/__init__.pxd +33 -0
- decision_graph/decision_tree/capi/__init__.py +57 -0
- decision_graph/decision_tree/capi/c_abc.cp315-win_amd64.pyd +0 -0
- decision_graph/decision_tree/capi/c_abc.pxd +246 -0
- decision_graph/decision_tree/capi/c_abc.pyi +770 -0
- decision_graph/decision_tree/capi/c_abc.pyx +1532 -0
- decision_graph/decision_tree/capi/c_collection.cp315-win_amd64.pyd +0 -0
- decision_graph/decision_tree/capi/c_collection.pxd +22 -0
- decision_graph/decision_tree/capi/c_collection.pyi +245 -0
- decision_graph/decision_tree/capi/c_collection.pyx +138 -0
- decision_graph/decision_tree/capi/c_node.cp315-win_amd64.pyd +0 -0
- decision_graph/decision_tree/capi/c_node.pxd +95 -0
- decision_graph/decision_tree/capi/c_node.pyi +492 -0
- decision_graph/decision_tree/capi/c_node.pyx +551 -0
- decision_graph/decision_tree/exc.py +189 -0
- decision_graph/decision_tree/native/__init__.py +57 -0
- decision_graph/decision_tree/native/abc.py +1167 -0
- decision_graph/decision_tree/native/collection.py +134 -0
- decision_graph/decision_tree/native/node.py +521 -0
- decision_graph/includes/decision_graph/decision_tree/bake/c_action.c +16320 -0
- decision_graph/includes/decision_graph/decision_tree/bake/c_action.h +324 -0
- decision_graph/includes/decision_graph/decision_tree/bake/c_allocator_protocol.c +12473 -0
- decision_graph/includes/decision_graph/decision_tree/bake/c_bake.c +16485 -0
- decision_graph/includes/decision_graph/decision_tree/bake/c_bake.h +629 -0
- decision_graph/includes/decision_graph/decision_tree/bake/c_collections.c +17381 -0
- decision_graph/includes/decision_graph/decision_tree/bake/c_collections.h +692 -0
- decision_graph/includes/decision_graph/decision_tree/bake/c_const.c +20815 -0
- decision_graph/includes/decision_graph/decision_tree/bake/c_const.h +395 -0
- decision_graph/includes/decision_graph/decision_tree/bake/c_edge.c +20696 -0
- decision_graph/includes/decision_graph/decision_tree/bake/c_edge.h +511 -0
- decision_graph/includes/decision_graph/decision_tree/bake/c_eval.h +848 -0
- decision_graph/includes/decision_graph/decision_tree/bake/c_expr.c +22065 -0
- decision_graph/includes/decision_graph/decision_tree/bake/c_expr.h +1900 -0
- decision_graph/includes/decision_graph/decision_tree/bake/c_hierarchy.c +23267 -0
- decision_graph/includes/decision_graph/decision_tree/bake/c_hierarchy.h +731 -0
- decision_graph/includes/decision_graph/decision_tree/bake/c_logic_group.c +22434 -0
- decision_graph/includes/decision_graph/decision_tree/bake/c_logic_group.h +1226 -0
- decision_graph/includes/decision_graph/decision_tree/bake/c_node.c +28385 -0
- decision_graph/includes/decision_graph/decision_tree/bake/c_node.h +2660 -0
- decision_graph/includes/decision_graph/decision_tree/bake/c_reconstruct.c +15955 -0
- decision_graph/includes/decision_graph/decision_tree/bake/c_var.c +16881 -0
- decision_graph/includes/decision_graph/decision_tree/bake/c_var.h +2054 -0
- decision_graph/includes/decision_graph/decision_tree/capi/c_abc.c +59196 -0
- decision_graph/includes/decision_graph/decision_tree/capi/c_collection.c +19967 -0
- decision_graph/includes/decision_graph/decision_tree/capi/c_node.c +39229 -0
- decision_graph/logic_group/__init__.py +25 -0
- decision_graph/logic_group/base.py +100 -0
- decision_graph/logic_group/pending_request.py +384 -0
- decision_graph/webui/__init__.py +24 -0
- decision_graph/webui/bake/__init__.py +65 -0
- decision_graph/webui/bake/app.py +488 -0
- decision_graph/webui/bake/static/d3.v7.min.js +2 -0
- decision_graph/webui/bake/static/script.js +1440 -0
- decision_graph/webui/bake/static/style.css +920 -0
- decision_graph/webui/bake/templates/index.html +183 -0
- decision_graph/webui/bake/templates/offline.html +184 -0
- decision_graph/webui/capi/__init__.py +54 -0
- decision_graph/webui/capi/app.py +366 -0
- decision_graph/webui/capi/main.py +53 -0
- decision_graph/webui/capi/static/d3.v7.min.js +2 -0
- decision_graph/webui/capi/static/script.js +1026 -0
- decision_graph/webui/capi/static/style.css +656 -0
- decision_graph/webui/capi/templates/index.html +93 -0
- decision_graph/webui/capi/templates/offline.html +94 -0
- decision_graph/webui/native/__init__.py +54 -0
- decision_graph/webui/native/app.py +366 -0
- decision_graph/webui/native/main.py +53 -0
- decision_graph/webui/native/static/d3.v7.min.js +2 -0
- decision_graph/webui/native/static/script.js +1026 -0
- decision_graph/webui/native/static/style.css +656 -0
- decision_graph/webui/native/templates/index.html +93 -0
- decision_graph/webui/native/templates/offline.html +94 -0
- pydecisiongraph-0.3.2.post1.dist-info/METADATA +136 -0
- pydecisiongraph-0.3.2.post1.dist-info/RECORD +148 -0
- pydecisiongraph-0.3.2.post1.dist-info/WHEEL +5 -0
- pydecisiongraph-0.3.2.post1.dist-info/licenses/LICENSE +373 -0
- pydecisiongraph-0.3.2.post1.dist-info/top_level.txt +2 -0
- tests/bake_c_evaluator.cp315-win_amd64.pyd +0 -0
- 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
|