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,639 @@
|
|
|
1
|
+
from uuid import UUID
|
|
2
|
+
|
|
3
|
+
from cpython.bytes cimport PyBytes_AsString, PyBytes_FromStringAndSize
|
|
4
|
+
from cpython.dict cimport PyDict_Contains
|
|
5
|
+
from cpython.object cimport PyObject
|
|
6
|
+
from cpython.unicode cimport PyUnicode_AsUTF8, PyUnicode_FromString, PyUnicode_FromStringAndSize
|
|
7
|
+
from libc.stdint cimport uintptr_t
|
|
8
|
+
from libc.stdio cimport fprintf, stderr
|
|
9
|
+
|
|
10
|
+
from cbase.bytemap cimport c_bytemap_gen_seq_id
|
|
11
|
+
|
|
12
|
+
from .c_allocator_protocol cimport DCG_DEFAULT_ALLOCATOR
|
|
13
|
+
from .c_edge cimport AUTO_CONDITION, C_AUTO_CONDITION, EDGE_REGISTRY, NO_CONDITION, NodeEdgeCondition, dcg_node_edge_condition
|
|
14
|
+
from .c_var cimport VarView, c_dcg_ret_code_name, c_dcg_var_dealloc, c_dcg_var_init, c_dcg_var_pyunpack, dcg_ret_code
|
|
15
|
+
from ..exc import EvalFailureError, NodeTypeError
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
cdef inline int c_eval_hook_code(object exc):
|
|
19
|
+
"""The code a raised hook ends its node with: the one it carries, or the hook's own error.
|
|
20
|
+
|
|
21
|
+
A hook hands the protocol a code two ways - returning it (a Cython hook, and a
|
|
22
|
+
Python one that returns an int) or raising an EvalFailureError that carries it.
|
|
23
|
+
Raising anything else is the generic hook failure, and the exception itself
|
|
24
|
+
travels as the cause of what the evaluation reports.
|
|
25
|
+
"""
|
|
26
|
+
if isinstance(exc, EvalFailureError) and exc.code:
|
|
27
|
+
return <int> exc.code
|
|
28
|
+
return dcg_ret_code.DCG_ERR_HOOK
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
cdef const size_t DCG_RENDER_BUFSIZE = 1 << 16
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
cdef class LogicNode:
|
|
35
|
+
def __cinit__(self, *args, **kwargs):
|
|
36
|
+
self.children = {}
|
|
37
|
+
self.condition_to_parent = NO_CONDITION
|
|
38
|
+
# self.callback_id = 0
|
|
39
|
+
|
|
40
|
+
def __init__(self, *, str repr=None, object uid=None, **kwargs):
|
|
41
|
+
raise NodeTypeError(
|
|
42
|
+
f'{self.__class__.__name__} is the node base: bake has no type for a bare node yet. '
|
|
43
|
+
'Build an action leaf (NoAction, LongAction, ...), a root, or an expression node.'
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
def __dealloc__(self):
|
|
47
|
+
if not self.owner:
|
|
48
|
+
return
|
|
49
|
+
|
|
50
|
+
if self.header:
|
|
51
|
+
if self.callback_id:
|
|
52
|
+
c_dcg_node_unregister_callback(self.header, self.callback_id)
|
|
53
|
+
c_dcg_node_free_generic(self.header)
|
|
54
|
+
|
|
55
|
+
@staticmethod
|
|
56
|
+
cdef inline LogicNode c_from_header(dcg_node* header, bint owner=False):
|
|
57
|
+
cdef LogicNode instance = LogicNode.__new__(LogicNode)
|
|
58
|
+
instance.header = header
|
|
59
|
+
instance.owner = owner
|
|
60
|
+
|
|
61
|
+
# The registry is keyed by the header address, so the parent is looked
|
|
62
|
+
# up by ITS header - a wrapper is not an address, and a parentless node
|
|
63
|
+
# has none to look up.
|
|
64
|
+
cdef uintptr_t parent_addr = 0
|
|
65
|
+
instance.parent = None
|
|
66
|
+
if header.parent:
|
|
67
|
+
parent_addr = <uintptr_t> header.parent
|
|
68
|
+
if parent_addr in NODE_REGISTRY:
|
|
69
|
+
instance.parent = NODE_REGISTRY[parent_addr]
|
|
70
|
+
return instance
|
|
71
|
+
|
|
72
|
+
@staticmethod
|
|
73
|
+
cdef inline dcg_logic_group_manager* c_get_manager():
|
|
74
|
+
if not C_LGM:
|
|
75
|
+
from .c_logic_group import LGM as _LGM
|
|
76
|
+
global C_LGM, LGM
|
|
77
|
+
C_LGM = <dcg_logic_group_manager*> <uintptr_t> _LGM.address
|
|
78
|
+
LGM = _LGM
|
|
79
|
+
return C_LGM
|
|
80
|
+
|
|
81
|
+
@staticmethod
|
|
82
|
+
cdef LogicNode c_dcg_node_reconstruct(dcg_node* header, bint owner=False):
|
|
83
|
+
from .c_reconstruct import c_dcg_node_reconstruct_from_address
|
|
84
|
+
return c_dcg_node_reconstruct_from_address(<uintptr_t> header, owner)
|
|
85
|
+
|
|
86
|
+
@staticmethod
|
|
87
|
+
def get_manager():
|
|
88
|
+
if not LGM:
|
|
89
|
+
LogicNode.c_get_manager()
|
|
90
|
+
return LGM
|
|
91
|
+
|
|
92
|
+
cdef inline void c_register_node(self):
|
|
93
|
+
NODE_REGISTRY[<uintptr_t> self.header] = self
|
|
94
|
+
|
|
95
|
+
cdef int ret_code = c_dcg_node_register_callback(self.header, LogicNode.c_node_callback_event_adaptor, <void*> <PyObject*> self, &self.callback_id)
|
|
96
|
+
if ret_code != dcg_ret_code.DCG_OK:
|
|
97
|
+
raise RuntimeError(f'c_dcg_node_register_callback failed with err code: {ret_code}')
|
|
98
|
+
|
|
99
|
+
self.c_bind_eval_callback()
|
|
100
|
+
|
|
101
|
+
# === Cython Internal Eval Binding ===
|
|
102
|
+
|
|
103
|
+
@staticmethod
|
|
104
|
+
cdef inline int c_pre_eval_callback_adaptor(dcg_node* node, void* user_data) noexcept:
|
|
105
|
+
cdef LogicNode wrapper = <LogicNode> <PyObject*> user_data
|
|
106
|
+
cdef uint32_t flags = wrapper.eval_hook_flags
|
|
107
|
+
cdef int ret_code = dcg_ret_code.DCG_OK
|
|
108
|
+
cdef object result = None
|
|
109
|
+
|
|
110
|
+
if flags & (dcg_eval_override_flag.DCG_EVAL_CDEF_OVERRIDE << dcg_node_hook_type.DCG_HOOK_PRE_EVAL):
|
|
111
|
+
try:
|
|
112
|
+
ret_code = wrapper.c_pre_eval_fn()
|
|
113
|
+
except BaseException as exc:
|
|
114
|
+
wrapper.c_eval_exception = exc
|
|
115
|
+
return c_eval_hook_code(exc)
|
|
116
|
+
if ret_code != dcg_ret_code.DCG_OK:
|
|
117
|
+
return ret_code
|
|
118
|
+
if flags & (dcg_eval_override_flag.DCG_EVAL_PY_OVERRIDE << dcg_node_hook_type.DCG_HOOK_PRE_EVAL):
|
|
119
|
+
try:
|
|
120
|
+
result = wrapper.pre_eval_fn()
|
|
121
|
+
except BaseException as exc:
|
|
122
|
+
wrapper.c_eval_exception = exc
|
|
123
|
+
return c_eval_hook_code(exc)
|
|
124
|
+
return dcg_ret_code.DCG_OK if result is None else <int> result
|
|
125
|
+
return dcg_ret_code.DCG_OK
|
|
126
|
+
|
|
127
|
+
@staticmethod
|
|
128
|
+
cdef inline int c_eval_fn_callback_adaptor(dcg_node* node, void* user_data) noexcept:
|
|
129
|
+
cdef LogicNode wrapper = <LogicNode> <PyObject*> user_data
|
|
130
|
+
cdef uint32_t flags = wrapper.eval_hook_flags
|
|
131
|
+
cdef int ret_code = dcg_ret_code.DCG_OK
|
|
132
|
+
cdef object result = None
|
|
133
|
+
|
|
134
|
+
if flags & (dcg_eval_override_flag.DCG_EVAL_CDEF_OVERRIDE << dcg_node_hook_type.DCG_HOOK_EVAL):
|
|
135
|
+
try:
|
|
136
|
+
ret_code = wrapper.c_eval_fn()
|
|
137
|
+
except BaseException as exc:
|
|
138
|
+
wrapper.c_eval_exception = exc
|
|
139
|
+
return c_eval_hook_code(exc)
|
|
140
|
+
if ret_code != dcg_ret_code.DCG_OK:
|
|
141
|
+
return ret_code
|
|
142
|
+
if flags & (dcg_eval_override_flag.DCG_EVAL_PY_OVERRIDE << dcg_node_hook_type.DCG_HOOK_EVAL):
|
|
143
|
+
try:
|
|
144
|
+
result = wrapper.eval_fn()
|
|
145
|
+
except BaseException as exc:
|
|
146
|
+
wrapper.c_eval_exception = exc
|
|
147
|
+
return c_eval_hook_code(exc)
|
|
148
|
+
return dcg_ret_code.DCG_OK if result is None else <int> result
|
|
149
|
+
return dcg_ret_code.DCG_OK
|
|
150
|
+
|
|
151
|
+
@staticmethod
|
|
152
|
+
cdef inline int c_post_eval_callback_adaptor(dcg_node* node, void* user_data) noexcept:
|
|
153
|
+
cdef LogicNode wrapper = <LogicNode> <PyObject*> user_data
|
|
154
|
+
cdef uint32_t flags = wrapper.eval_hook_flags
|
|
155
|
+
cdef int ret_code = dcg_ret_code.DCG_OK
|
|
156
|
+
cdef object result = None
|
|
157
|
+
|
|
158
|
+
if flags & (dcg_eval_override_flag.DCG_EVAL_CDEF_OVERRIDE << dcg_node_hook_type.DCG_HOOK_POST_EVAL):
|
|
159
|
+
try:
|
|
160
|
+
ret_code = wrapper.c_post_eval_fn()
|
|
161
|
+
except BaseException as exc:
|
|
162
|
+
wrapper.c_eval_exception = exc
|
|
163
|
+
return c_eval_hook_code(exc)
|
|
164
|
+
if ret_code != dcg_ret_code.DCG_OK:
|
|
165
|
+
return ret_code
|
|
166
|
+
if flags & (dcg_eval_override_flag.DCG_EVAL_PY_OVERRIDE << dcg_node_hook_type.DCG_HOOK_POST_EVAL):
|
|
167
|
+
try:
|
|
168
|
+
result = wrapper.post_eval_fn()
|
|
169
|
+
except BaseException as exc:
|
|
170
|
+
wrapper.c_eval_exception = exc
|
|
171
|
+
return c_eval_hook_code(exc)
|
|
172
|
+
return dcg_ret_code.DCG_OK if result is None else <int> result
|
|
173
|
+
return dcg_ret_code.DCG_OK
|
|
174
|
+
|
|
175
|
+
cdef int c_pre_eval_fn(self):
|
|
176
|
+
return dcg_ret_code.DCG_OK
|
|
177
|
+
|
|
178
|
+
cdef int c_eval_fn(self):
|
|
179
|
+
return dcg_ret_code.DCG_OK
|
|
180
|
+
|
|
181
|
+
cdef int c_post_eval_fn(self):
|
|
182
|
+
return dcg_ret_code.DCG_OK
|
|
183
|
+
|
|
184
|
+
cdef void c_bind_eval_callback(self):
|
|
185
|
+
if not self.header:
|
|
186
|
+
return
|
|
187
|
+
|
|
188
|
+
cdef bytes class_name = type(self).__name__.encode()
|
|
189
|
+
cdef uint32_t flags = 0
|
|
190
|
+
if <void*> self.c_pre_eval_fn != <void*> LogicNode.c_pre_eval_fn:
|
|
191
|
+
flags |= dcg_eval_override_flag.DCG_EVAL_CDEF_OVERRIDE << dcg_node_hook_type.DCG_HOOK_PRE_EVAL
|
|
192
|
+
if <void*> self.c_eval_fn != <void*> LogicNode.c_eval_fn:
|
|
193
|
+
flags |= dcg_eval_override_flag.DCG_EVAL_CDEF_OVERRIDE << dcg_node_hook_type.DCG_HOOK_EVAL
|
|
194
|
+
if <void*> self.c_post_eval_fn != <void*> LogicNode.c_post_eval_fn:
|
|
195
|
+
flags |= dcg_eval_override_flag.DCG_EVAL_CDEF_OVERRIDE << dcg_node_hook_type.DCG_HOOK_POST_EVAL
|
|
196
|
+
|
|
197
|
+
if type(self).pre_eval_fn is not getattr(LogicNode, 'pre_eval_fn'):
|
|
198
|
+
flags |= dcg_eval_override_flag.DCG_EVAL_PY_OVERRIDE << dcg_node_hook_type.DCG_HOOK_PRE_EVAL
|
|
199
|
+
if type(self).eval_fn is not getattr(LogicNode, 'eval_fn'):
|
|
200
|
+
flags |= dcg_eval_override_flag.DCG_EVAL_PY_OVERRIDE << dcg_node_hook_type.DCG_HOOK_EVAL
|
|
201
|
+
if type(self).post_eval_fn is not getattr(LogicNode, 'post_eval_fn'):
|
|
202
|
+
flags |= dcg_eval_override_flag.DCG_EVAL_PY_OVERRIDE << dcg_node_hook_type.DCG_HOOK_POST_EVAL
|
|
203
|
+
|
|
204
|
+
if not flags:
|
|
205
|
+
return
|
|
206
|
+
|
|
207
|
+
if not self.owner:
|
|
208
|
+
fprintf(stderr, 'c_bind_eval_callback: %s at %p overrides an eval hook but does not own its node - the hook is not installed\n', <const char*> class_name, <const void*> self.header)
|
|
209
|
+
return
|
|
210
|
+
|
|
211
|
+
self.eval_hook_flags = flags
|
|
212
|
+
cdef int ret_code = 0
|
|
213
|
+
|
|
214
|
+
if flags & ((dcg_eval_override_flag.DCG_EVAL_CDEF_OVERRIDE | dcg_eval_override_flag.DCG_EVAL_PY_OVERRIDE) << dcg_node_hook_type.DCG_HOOK_PRE_EVAL):
|
|
215
|
+
ret_code = c_dcg_node_register_eval_hook(self.header, dcg_node_hook_type.DCG_HOOK_PRE_EVAL, LogicNode.c_pre_eval_callback_adaptor, <void*> <PyObject*> self)
|
|
216
|
+
if ret_code != dcg_ret_code.DCG_OK:
|
|
217
|
+
raise RuntimeError(f'c_dcg_node_register_eval_hook failed with err code: {ret_code}')
|
|
218
|
+
if flags & ((dcg_eval_override_flag.DCG_EVAL_CDEF_OVERRIDE | dcg_eval_override_flag.DCG_EVAL_PY_OVERRIDE) << dcg_node_hook_type.DCG_HOOK_EVAL):
|
|
219
|
+
ret_code = c_dcg_node_register_eval_hook(self.header, dcg_node_hook_type.DCG_HOOK_EVAL, LogicNode.c_eval_fn_callback_adaptor, <void*> <PyObject*> self)
|
|
220
|
+
if ret_code != dcg_ret_code.DCG_OK:
|
|
221
|
+
raise RuntimeError(f'c_dcg_node_register_eval_hook failed with err code: {ret_code}')
|
|
222
|
+
if flags & ((dcg_eval_override_flag.DCG_EVAL_CDEF_OVERRIDE | dcg_eval_override_flag.DCG_EVAL_PY_OVERRIDE) << dcg_node_hook_type.DCG_HOOK_POST_EVAL):
|
|
223
|
+
ret_code = c_dcg_node_register_eval_hook(self.header, dcg_node_hook_type.DCG_HOOK_POST_EVAL, LogicNode.c_post_eval_callback_adaptor, <void*> <PyObject*> self)
|
|
224
|
+
if ret_code != dcg_ret_code.DCG_OK:
|
|
225
|
+
raise RuntimeError(f'c_dcg_node_register_eval_hook failed with err code: {ret_code}')
|
|
226
|
+
|
|
227
|
+
@staticmethod
|
|
228
|
+
cdef list c_eval_stage_name_list(dcg_eval_stage stage):
|
|
229
|
+
cdef list names = []
|
|
230
|
+
if stage & dcg_eval_stage.DCG_EVAL_STAGE_PRE_EVAL:
|
|
231
|
+
names.append('pre')
|
|
232
|
+
if stage & dcg_eval_stage.DCG_EVAL_STAGE_EVAL:
|
|
233
|
+
names.append('eval')
|
|
234
|
+
if stage & dcg_eval_stage.DCG_EVAL_STAGE_POST_EVAL:
|
|
235
|
+
names.append('post')
|
|
236
|
+
if stage & dcg_eval_stage.DCG_EVAL_STAGE_DONE:
|
|
237
|
+
names.append('done')
|
|
238
|
+
return names
|
|
239
|
+
|
|
240
|
+
@staticmethod
|
|
241
|
+
cdef str c_eval_stage_names(dcg_eval_stage stage):
|
|
242
|
+
cdef list names = LogicNode.c_eval_stage_name_list(stage)
|
|
243
|
+
return ', '.join(names) if names else 'none'
|
|
244
|
+
|
|
245
|
+
@staticmethod
|
|
246
|
+
cdef str c_eval_failed_stage_name(dcg_eval_stage stage):
|
|
247
|
+
"""The stage a failure stopped at, read off the mask of what completed.
|
|
248
|
+
|
|
249
|
+
A failure that happened AT a stage is a stage that did not complete, and
|
|
250
|
+
the protocol walks them in order - so the first one missing from the mask
|
|
251
|
+
is where it stopped. None when the mask says the node got through all of
|
|
252
|
+
them, which is not a failure a node reports.
|
|
253
|
+
"""
|
|
254
|
+
if not (stage & dcg_eval_stage.DCG_EVAL_STAGE_PRE_EVAL):
|
|
255
|
+
return 'pre'
|
|
256
|
+
if not (stage & dcg_eval_stage.DCG_EVAL_STAGE_EVAL):
|
|
257
|
+
return 'eval'
|
|
258
|
+
if not (stage & dcg_eval_stage.DCG_EVAL_STAGE_POST_EVAL):
|
|
259
|
+
return 'post'
|
|
260
|
+
return None
|
|
261
|
+
|
|
262
|
+
@staticmethod
|
|
263
|
+
cdef str c_eval_source_name(dcg_eval_stage stage):
|
|
264
|
+
"""What was running, read off the same mask that says how far it got.
|
|
265
|
+
|
|
266
|
+
The two questions a failure asks - which stage, and what was running - are
|
|
267
|
+
read from one word: the producer bits are the eval stage's own, and a node
|
|
268
|
+
that never got that far has the stage itself for an answer, because the pre
|
|
269
|
+
and post hooks produce no value - what ran there IS the hook.
|
|
270
|
+
"""
|
|
271
|
+
cdef str failed_at = LogicNode.c_eval_failed_stage_name(stage)
|
|
272
|
+
if failed_at == 'pre':
|
|
273
|
+
return 'pre_hook'
|
|
274
|
+
if failed_at == 'post':
|
|
275
|
+
return 'post_hook'
|
|
276
|
+
if stage & dcg_eval_stage.DCG_EVAL_STAGE_HOOK:
|
|
277
|
+
return 'hook'
|
|
278
|
+
if stage & dcg_eval_stage.DCG_EVAL_STAGE_TYPE_RULE:
|
|
279
|
+
return 'type_rule'
|
|
280
|
+
if stage & dcg_eval_stage.DCG_EVAL_STAGE_BUILTIN:
|
|
281
|
+
return 'builtin'
|
|
282
|
+
return None
|
|
283
|
+
|
|
284
|
+
cdef void c_check_eval_code(self, int ret_code, dcg_node* subject=NULL):
|
|
285
|
+
"""Report a failed evaluation as an EvalFailureError - the one place it is built.
|
|
286
|
+
|
|
287
|
+
Every door that evaluates a node comes through here, so a failure reports
|
|
288
|
+
the same things whichever one asked: the node that refused, the code it
|
|
289
|
+
ended with, the stage that failed, what was running (the built-in rule, the
|
|
290
|
+
rule the node's type installed, or a hook), and the run it happened in.
|
|
291
|
+
|
|
292
|
+
WHERE it stopped is the failing node's own state, not this wrapper's: in a
|
|
293
|
+
walk the node that reported the code is the one whose stages say how far it
|
|
294
|
+
got, and a root that reached it is not the node that refused.
|
|
295
|
+
|
|
296
|
+
A hook's own exception - kept by the adapter that caught it - becomes the
|
|
297
|
+
CAUSE of this one, so nothing the hook said is lost and the code the node
|
|
298
|
+
ends with is still the protocol's to name.
|
|
299
|
+
"""
|
|
300
|
+
if ret_code == dcg_ret_code.DCG_OK:
|
|
301
|
+
return
|
|
302
|
+
|
|
303
|
+
cdef object cause = self.c_eval_exception
|
|
304
|
+
self.c_eval_exception = None
|
|
305
|
+
|
|
306
|
+
cdef dcg_node* failed = subject if subject else self.header
|
|
307
|
+
cdef dcg_node_eval_ctx* ctx = &failed.eval_ctx
|
|
308
|
+
cdef str code = PyUnicode_FromString(c_dcg_ret_code_name(<dcg_ret_code> ret_code))
|
|
309
|
+
cdef str run = f'run {ctx.eval_seq_id:#0x}' if ctx.eval_seq_id else 'no run'
|
|
310
|
+
cdef str text = PyUnicode_FromString(failed.repr) if failed.repr else 'unrepr'
|
|
311
|
+
cdef str type_str = PyUnicode_FromString(c_dcg_node_type_name(failed.ntype))
|
|
312
|
+
cdef object failed_at = LogicNode.c_eval_failed_stage_name(ctx.stage)
|
|
313
|
+
cdef object source = LogicNode.c_eval_source_name(ctx.stage)
|
|
314
|
+
cdef str stopped = f'; stopped at {failed_at} in {source}' if failed_at and source else ''
|
|
315
|
+
|
|
316
|
+
raise EvalFailureError(
|
|
317
|
+
f'{type_str}({text!r}) at {<uintptr_t> failed:#0x} failed to evaluate: {code} ({ret_code}); '
|
|
318
|
+
f'stages completed: {LogicNode.c_eval_stage_names(ctx.stage)}; {run}{stopped}',
|
|
319
|
+
code=ret_code,
|
|
320
|
+
code_name=code,
|
|
321
|
+
node=NODE_REGISTRY[<uintptr_t> failed],
|
|
322
|
+
node_type=type_str,
|
|
323
|
+
node_repr=text,
|
|
324
|
+
address=<uintptr_t> failed,
|
|
325
|
+
source=source,
|
|
326
|
+
failed_at=failed_at,
|
|
327
|
+
stages=tuple(LogicNode.c_eval_stage_name_list(ctx.stage)),
|
|
328
|
+
run_id=ctx.eval_seq_id,
|
|
329
|
+
) from cause
|
|
330
|
+
|
|
331
|
+
# === Cython Internal Binding ===
|
|
332
|
+
|
|
333
|
+
@staticmethod
|
|
334
|
+
cdef void c_node_callback_event_adaptor(dcg_node_event event, dcg_node* node, dcg_node* subject, uint64_t seq_id, void* user_data) noexcept:
|
|
335
|
+
if not node or not user_data:
|
|
336
|
+
return
|
|
337
|
+
|
|
338
|
+
cdef LogicNode wrapper = <LogicNode> <PyObject*> user_data
|
|
339
|
+
cdef LogicNode child
|
|
340
|
+
cdef NodeEdgeCondition condition
|
|
341
|
+
|
|
342
|
+
if event == DCG_NODE_EVENT_CHILD_ADDED or event == DCG_NODE_EVENT_CHILD_UPDATED:
|
|
343
|
+
child = NODE_REGISTRY[<uintptr_t> subject]
|
|
344
|
+
condition = EDGE_REGISTRY[<uintptr_t> subject.condition_to_parent]
|
|
345
|
+
wrapper.children[condition] = child
|
|
346
|
+
child.parent = wrapper
|
|
347
|
+
child.condition_to_parent = condition
|
|
348
|
+
elif event == DCG_NODE_EVENT_CHILD_REMOVED:
|
|
349
|
+
for condition, child in wrapper.children.items():
|
|
350
|
+
if <uintptr_t> child.header == <uintptr_t> subject:
|
|
351
|
+
break
|
|
352
|
+
else:
|
|
353
|
+
raise BufferError(f'Node {<uintptr_t> node:#0x} not found!')
|
|
354
|
+
wrapper.children.pop(condition)
|
|
355
|
+
child = NODE_REGISTRY[<uintptr_t> subject]
|
|
356
|
+
child.parent = None
|
|
357
|
+
child.condition_to_parent = NO_CONDITION
|
|
358
|
+
elif event == DCG_NODE_EVENT_CHILD_CLEARED:
|
|
359
|
+
wrapper.children.clear()
|
|
360
|
+
|
|
361
|
+
cdef void c_append(self, dcg_node* child, dcg_node_edge_condition* condition):
|
|
362
|
+
cdef int ret_code = c_dcg_node_append(self.header, child, condition)
|
|
363
|
+
if ret_code != dcg_ret_code.DCG_OK:
|
|
364
|
+
raise RuntimeError(f'c_dcg_node_append failed with err code: {ret_code}')
|
|
365
|
+
|
|
366
|
+
cdef void c_replace(self, dcg_node* old_node, dcg_node* new_node):
|
|
367
|
+
cdef int ret_code = c_dcg_node_replace(old_node, new_node)
|
|
368
|
+
if ret_code != dcg_ret_code.DCG_OK:
|
|
369
|
+
raise RuntimeError(f'c_dcg_node_replace failed with err code: {ret_code}')
|
|
370
|
+
|
|
371
|
+
cdef void c_detach(self):
|
|
372
|
+
cdef int ret_code = c_dcg_node_detach(self.header)
|
|
373
|
+
if ret_code != dcg_ret_code.DCG_OK:
|
|
374
|
+
raise RuntimeError(f'c_dcg_node_detach failed with err code: {ret_code}')
|
|
375
|
+
|
|
376
|
+
cdef str c_render(self, int max_depth, bint show_labels, bint show_out, str style):
|
|
377
|
+
cdef dcg_render_opts opts
|
|
378
|
+
cdef size_t cap = DCG_RENDER_BUFSIZE
|
|
379
|
+
cdef int ret
|
|
380
|
+
cdef bytes buffer
|
|
381
|
+
cdef char* out
|
|
382
|
+
|
|
383
|
+
c_dcg_render_opts_default(&opts)
|
|
384
|
+
opts.max_depth = max_depth
|
|
385
|
+
opts.show_labels = show_labels
|
|
386
|
+
opts.show_out = show_out
|
|
387
|
+
opts.style = DCG_RENDER_UNICODE if style == 'unicode' else DCG_RENDER_ASCII
|
|
388
|
+
|
|
389
|
+
buffer = PyBytes_FromStringAndSize(NULL, <Py_ssize_t> cap)
|
|
390
|
+
out = PyBytes_AsString(buffer)
|
|
391
|
+
ret = c_dcg_node_render_to_string(self.header, out, cap, &opts)
|
|
392
|
+
if ret == dcg_ret_code.DCG_ERR_FULL:
|
|
393
|
+
raise BufferError(f'The render does not fit the {cap}-byte buffer.')
|
|
394
|
+
if ret < 0:
|
|
395
|
+
raise RuntimeError(f'c_dcg_node_render_to_string failed with err code: {ret}')
|
|
396
|
+
return PyUnicode_FromStringAndSize(out, ret)
|
|
397
|
+
|
|
398
|
+
# === Python Dunders ===
|
|
399
|
+
|
|
400
|
+
def __repr__(self):
|
|
401
|
+
if not self.header:
|
|
402
|
+
return f'<{self.__class__.__name__} (Uninitialized)>'
|
|
403
|
+
return f'<{self.__class__.__name__}({self.repr!r})>'
|
|
404
|
+
|
|
405
|
+
def __rshift__(self, LogicNode other):
|
|
406
|
+
self.c_append(other.header, C_AUTO_CONDITION)
|
|
407
|
+
return other
|
|
408
|
+
|
|
409
|
+
def __enter__(self):
|
|
410
|
+
cdef int ret_code = c_dcg_lgm_enter_node(LogicNode.c_get_manager(), self.header)
|
|
411
|
+
if ret_code != dcg_ret_code.DCG_OK:
|
|
412
|
+
raise RuntimeError(f'c_dcg_lgm_enter_node failed with err code: {ret_code}')
|
|
413
|
+
self.get_manager().node_stack_append(self)
|
|
414
|
+
return self
|
|
415
|
+
|
|
416
|
+
def __exit__(self, exc_type, exc_value, traceback):
|
|
417
|
+
cdef int ret_code = c_dcg_lgm_exit_node(LogicNode.c_get_manager(), self.header)
|
|
418
|
+
if ret_code != dcg_ret_code.DCG_OK:
|
|
419
|
+
raise RuntimeError(f'c_dcg_lgm_exit_node failed with err code: {ret_code}')
|
|
420
|
+
self.get_manager().node_stack_pop(self)
|
|
421
|
+
return False
|
|
422
|
+
|
|
423
|
+
# === Python Interfaces ===
|
|
424
|
+
|
|
425
|
+
def append(self, LogicNode child, NodeEdgeCondition condition=AUTO_CONDITION):
|
|
426
|
+
self.c_append(child.header, condition.header if condition is not None else C_AUTO_CONDITION)
|
|
427
|
+
|
|
428
|
+
def overwrite(self, LogicNode new_node, NodeEdgeCondition condition):
|
|
429
|
+
cdef LogicNode original = self.children.get(condition)
|
|
430
|
+
if original is None:
|
|
431
|
+
raise KeyError(f'Edge {condition} not registered, cannot overwrite.')
|
|
432
|
+
self.c_replace(original.header, new_node.header)
|
|
433
|
+
|
|
434
|
+
def replace(self, LogicNode original_node, LogicNode new_node):
|
|
435
|
+
self.c_replace(original_node.header, new_node.header)
|
|
436
|
+
|
|
437
|
+
def detach(self):
|
|
438
|
+
self.c_detach()
|
|
439
|
+
|
|
440
|
+
def label(self, str name):
|
|
441
|
+
cdef int ret_code = c_dcg_node_add_label(self.header, PyUnicode_AsUTF8(name))
|
|
442
|
+
if ret_code != dcg_ret_code.DCG_OK:
|
|
443
|
+
raise RuntimeError(f'c_dcg_node_add_label failed with err code: {ret_code}')
|
|
444
|
+
|
|
445
|
+
def unlabel(self, str name):
|
|
446
|
+
cdef int ret_code = c_dcg_node_remove_label(self.header, PyUnicode_AsUTF8(name))
|
|
447
|
+
if ret_code != dcg_ret_code.DCG_OK:
|
|
448
|
+
raise RuntimeError(f'c_dcg_node_remove_label failed with err code: {ret_code}')
|
|
449
|
+
|
|
450
|
+
def has_label(self, str name):
|
|
451
|
+
return bool(c_dcg_node_has_label(self.header, PyUnicode_AsUTF8(name)))
|
|
452
|
+
|
|
453
|
+
def render(self, int max_depth=0, bint show_labels=True, bint show_out=False, str style='unicode'):
|
|
454
|
+
return self.c_render(max_depth, show_labels, show_out, style)
|
|
455
|
+
|
|
456
|
+
def validate(self):
|
|
457
|
+
cdef dcg_validate_report report
|
|
458
|
+
if c_dcg_node_validate(self.header, &report):
|
|
459
|
+
return None
|
|
460
|
+
return report.code, report.errors, report.nodes, report.depth
|
|
461
|
+
|
|
462
|
+
# === Eval Protocol ===
|
|
463
|
+
|
|
464
|
+
def pre_eval_fn(self):
|
|
465
|
+
pass
|
|
466
|
+
|
|
467
|
+
def eval_fn(self):
|
|
468
|
+
pass
|
|
469
|
+
|
|
470
|
+
def post_eval_fn(self):
|
|
471
|
+
pass
|
|
472
|
+
|
|
473
|
+
cdef void c_eval(self):
|
|
474
|
+
if not self.header:
|
|
475
|
+
raise RuntimeError(f'<{self.__class__.__name__}> not initialized!')
|
|
476
|
+
self.c_check_eval_code(c_dcg_node_eval(self.header))
|
|
477
|
+
|
|
478
|
+
def eval(self):
|
|
479
|
+
self.c_eval()
|
|
480
|
+
return c_dcg_var_pyunpack(&self.header.out)
|
|
481
|
+
|
|
482
|
+
def dry_run(self):
|
|
483
|
+
if not self.header:
|
|
484
|
+
raise RuntimeError(f'<{self.__class__.__name__}> not initialized!')
|
|
485
|
+
|
|
486
|
+
cdef dcg_var_t value
|
|
487
|
+
c_dcg_var_init(&value)
|
|
488
|
+
|
|
489
|
+
cdef int ret_code = c_dcg_node_dryrun(self.header, &value)
|
|
490
|
+
cdef object answer = c_dcg_var_pyunpack(&value)
|
|
491
|
+
c_dcg_var_dealloc(&value)
|
|
492
|
+
|
|
493
|
+
if ret_code != dcg_ret_code.DCG_OK:
|
|
494
|
+
self.c_check_eval_code(ret_code)
|
|
495
|
+
return answer
|
|
496
|
+
|
|
497
|
+
property eval_hooks:
|
|
498
|
+
def __get__(self):
|
|
499
|
+
if not self.header:
|
|
500
|
+
raise RuntimeError(f'<{self.__class__.__name__}> not initialized!')
|
|
501
|
+
|
|
502
|
+
cdef uint32_t flags = self.eval_hook_flags
|
|
503
|
+
cdef list installed = []
|
|
504
|
+
|
|
505
|
+
if flags & (dcg_eval_override_flag.DCG_EVAL_CDEF_OVERRIDE << dcg_node_hook_type.DCG_HOOK_PRE_EVAL):
|
|
506
|
+
installed.append('c_pre_eval_fn')
|
|
507
|
+
if flags & (dcg_eval_override_flag.DCG_EVAL_PY_OVERRIDE << dcg_node_hook_type.DCG_HOOK_PRE_EVAL):
|
|
508
|
+
installed.append('pre_eval_fn')
|
|
509
|
+
if flags & (dcg_eval_override_flag.DCG_EVAL_CDEF_OVERRIDE << dcg_node_hook_type.DCG_HOOK_EVAL):
|
|
510
|
+
installed.append('c_eval_fn')
|
|
511
|
+
if flags & (dcg_eval_override_flag.DCG_EVAL_PY_OVERRIDE << dcg_node_hook_type.DCG_HOOK_EVAL):
|
|
512
|
+
installed.append('eval_fn')
|
|
513
|
+
if flags & (dcg_eval_override_flag.DCG_EVAL_CDEF_OVERRIDE << dcg_node_hook_type.DCG_HOOK_POST_EVAL):
|
|
514
|
+
installed.append('c_post_eval_fn')
|
|
515
|
+
if flags & (dcg_eval_override_flag.DCG_EVAL_PY_OVERRIDE << dcg_node_hook_type.DCG_HOOK_POST_EVAL):
|
|
516
|
+
installed.append('post_eval_fn')
|
|
517
|
+
return tuple(installed)
|
|
518
|
+
|
|
519
|
+
# === Python Properties ===
|
|
520
|
+
|
|
521
|
+
property repr:
|
|
522
|
+
def __get__(self):
|
|
523
|
+
if not self.header:
|
|
524
|
+
raise RuntimeError(f'<{self.__class__.__name__}> not initialized!')
|
|
525
|
+
if not self.header.repr:
|
|
526
|
+
return None
|
|
527
|
+
return PyUnicode_FromString(self.header.repr)
|
|
528
|
+
|
|
529
|
+
property type:
|
|
530
|
+
def __get__(self):
|
|
531
|
+
if not self.header:
|
|
532
|
+
raise RuntimeError(f'<{self.__class__.__name__}> not initialized!')
|
|
533
|
+
return PyUnicode_FromString(c_dcg_node_type_name(self.header.ntype))
|
|
534
|
+
|
|
535
|
+
property uuid:
|
|
536
|
+
def __get__(self):
|
|
537
|
+
if not self.header:
|
|
538
|
+
raise RuntimeError(f'<{self.__class__.__name__}> not initialized!')
|
|
539
|
+
return UUID(bytes=self.header.uid[:16])
|
|
540
|
+
|
|
541
|
+
property autogen:
|
|
542
|
+
def __get__(self):
|
|
543
|
+
if not self.header:
|
|
544
|
+
raise RuntimeError(f'<{self.__class__.__name__}> not initialized!')
|
|
545
|
+
return bool(self.header.autogen)
|
|
546
|
+
|
|
547
|
+
property is_leaf:
|
|
548
|
+
def __get__(self):
|
|
549
|
+
if not self.header:
|
|
550
|
+
raise RuntimeError(f'<{self.__class__.__name__}> not initialized!')
|
|
551
|
+
return c_dcg_node_is_leaf(self.header)
|
|
552
|
+
|
|
553
|
+
property labels:
|
|
554
|
+
def __get__(self):
|
|
555
|
+
cdef list out = []
|
|
556
|
+
if not self.header:
|
|
557
|
+
raise RuntimeError(f'<{self.__class__.__name__}> not initialized!')
|
|
558
|
+
cdef dcg_node_label* entry = self.header.labels
|
|
559
|
+
while entry != NULL:
|
|
560
|
+
if entry.label != NULL:
|
|
561
|
+
out.append(PyUnicode_FromString(entry.label))
|
|
562
|
+
entry = entry.next
|
|
563
|
+
return out
|
|
564
|
+
|
|
565
|
+
property size:
|
|
566
|
+
def __get__(self):
|
|
567
|
+
if not self.header:
|
|
568
|
+
raise RuntimeError(f'<{self.__class__.__name__}> not initialized!')
|
|
569
|
+
return c_dcg_node_subtree_size(self.header)
|
|
570
|
+
|
|
571
|
+
property out:
|
|
572
|
+
def __get__(self):
|
|
573
|
+
if not self.header:
|
|
574
|
+
raise RuntimeError(f'<{self.__class__.__name__}> not initialized!')
|
|
575
|
+
return VarView.c_from_header(&self.header.out)
|
|
576
|
+
|
|
577
|
+
property address:
|
|
578
|
+
def __get__(self):
|
|
579
|
+
if not self.header:
|
|
580
|
+
raise RuntimeError(f'<{self.__class__.__name__}> not initialized!')
|
|
581
|
+
return <uintptr_t> self.header
|
|
582
|
+
|
|
583
|
+
|
|
584
|
+
cdef class PlaceholderNode(LogicNode):
|
|
585
|
+
def __init__(self, **kwargs):
|
|
586
|
+
cdef dcg_node* node = c_dcg_node_new_placeholder(DCG_DEFAULT_ALLOCATOR)
|
|
587
|
+
if not node:
|
|
588
|
+
raise MemoryError('Failed to allocate a PlaceholderNode.')
|
|
589
|
+
|
|
590
|
+
self.header = node
|
|
591
|
+
self.owner = True
|
|
592
|
+
self.c_register_node()
|
|
593
|
+
|
|
594
|
+
|
|
595
|
+
cdef class LogicNodeRegistry(BoundByteMap):
|
|
596
|
+
@staticmethod
|
|
597
|
+
cdef LogicNodeRegistry c_from_header(bytemap* header, bint owner=False):
|
|
598
|
+
cdef LogicNodeRegistry instance = LogicNodeRegistry.__new__(LogicNodeRegistry)
|
|
599
|
+
instance.owner = owner
|
|
600
|
+
instance.seq_id = c_bytemap_gen_seq_id(<void*> instance)
|
|
601
|
+
instance.c_bind(header)
|
|
602
|
+
return instance
|
|
603
|
+
|
|
604
|
+
cdef const char* c_serialize_key(self, object obj, size_t* key_len):
|
|
605
|
+
# A header address is provided in format of int (uintptr_t)
|
|
606
|
+
if isinstance(obj, int):
|
|
607
|
+
self._ws_key_buf = <void*> <uintptr_t> obj
|
|
608
|
+
if key_len:
|
|
609
|
+
key_len[0] = sizeof(void*)
|
|
610
|
+
return <const char*> &self._ws_key_buf
|
|
611
|
+
return BoundByteMap.c_serialize_key(self, obj, key_len)
|
|
612
|
+
|
|
613
|
+
cdef object c_deserialize_key(self, const char* key, size_t key_len):
|
|
614
|
+
return <uintptr_t> <void*> key
|
|
615
|
+
|
|
616
|
+
cdef const char* c_serialize_value(self, object obj, size_t* value_len):
|
|
617
|
+
cdef LogicNode node = <LogicNode> obj
|
|
618
|
+
self._ws_ptr = <void*> node.header
|
|
619
|
+
if value_len:
|
|
620
|
+
value_len[0] = sizeof(void*)
|
|
621
|
+
return <const char*> &self._ws_ptr
|
|
622
|
+
|
|
623
|
+
cdef object c_deserialize_value(self, const char* value, size_t value_len):
|
|
624
|
+
cdef void** vp = <void**> value
|
|
625
|
+
cdef dcg_node* node = <dcg_node*> vp[0]
|
|
626
|
+
return LogicNode.c_dcg_node_reconstruct(node)
|
|
627
|
+
|
|
628
|
+
def __getitem__(self, object key):
|
|
629
|
+
cdef int ret_code = PyDict_Contains(<dict> self, key)
|
|
630
|
+
if ret_code == 0:
|
|
631
|
+
return LogicNode.c_dcg_node_reconstruct(<dcg_node*> <uintptr_t> key)
|
|
632
|
+
return super().__getitem__(key)
|
|
633
|
+
|
|
634
|
+
# Local Cached LGM Pointer
|
|
635
|
+
cdef dcg_logic_group_manager* C_LGM = NULL
|
|
636
|
+
cdef object LGM = None
|
|
637
|
+
|
|
638
|
+
cdef LogicNodeRegistry NODE_REGISTRY = LogicNodeRegistry()
|
|
639
|
+
globals()['NODE_REGISTRY'] = NODE_REGISTRY
|
|
Binary file
|