wirely 0.1.0__py3-none-any.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.
- wirely/__init__.py +406 -0
- wirely/__main__.py +45 -0
- wirely/ast.py +1135 -0
- wirely/block.py +542 -0
- wirely/block_type.py +342 -0
- wirely/blocks.lark +126 -0
- wirely/compiler.py +125 -0
- wirely/engine.py +418 -0
- wirely/enum.py +15 -0
- wirely/example.py +36 -0
- wirely/graphviz.py +107 -0
- wirely/knx/__init__.py +0 -0
- wirely/knx/connection_adapter.py +90 -0
- wirely/knx/connection_config.py +152 -0
- wirely/lib/abb_jra.wrl +64 -0
- wirely/lib/csv.wrl +32 -0
- wirely/lib/ext_prog.wrl +12 -0
- wirely/lib/gpio.wrl +23 -0
- wirely/lib/knx.wrl +80 -0
- wirely/lib/memory.wrl +45 -0
- wirely/lib/mqtt.wrl +66 -0
- wirely/lib/std.wrl +134 -0
- wirely/lib/time.wrl +93 -0
- wirely/lib/two_n.wrl +111 -0
- wirely/misc/__init__.py +0 -0
- wirely/misc/service.py +48 -0
- wirely/native/__init__.py +74 -0
- wirely/native/abb_jra.py +66 -0
- wirely/native/csv.py +194 -0
- wirely/native/ext_prog.py +77 -0
- wirely/native/gpio.py +125 -0
- wirely/native/knx.py +455 -0
- wirely/native/memory.py +113 -0
- wirely/native/mqtt.py +605 -0
- wirely/native/std.py +334 -0
- wirely/native/test_library.py +14 -0
- wirely/native/time.py +359 -0
- wirely/native/two_n.py +632 -0
- wirely/node.py +125 -0
- wirely/parser.py +175 -0
- wirely/program.py +155 -0
- wirely/run.py +143 -0
- wirely/service.py +65 -0
- wirely/test.py +569 -0
- wirely/time.py +172 -0
- wirely/two_n/__init__.py +0 -0
- wirely/two_n/cert.py +126 -0
- wirely/two_n/client.py +402 -0
- wirely/two_n/fake_server.py +415 -0
- wirely/two_n/grab_images.py +146 -0
- wirely/types.py +522 -0
- wirely/util.py +111 -0
- wirely/value.py +163 -0
- wirely-0.1.0.dist-info/METADATA +94 -0
- wirely-0.1.0.dist-info/RECORD +59 -0
- wirely-0.1.0.dist-info/WHEEL +5 -0
- wirely-0.1.0.dist-info/entry_points.txt +2 -0
- wirely-0.1.0.dist-info/licenses/LICENSE +21 -0
- wirely-0.1.0.dist-info/top_level.txt +1 -0
wirely/block.py
ADDED
|
@@ -0,0 +1,542 @@
|
|
|
1
|
+
import abc
|
|
2
|
+
|
|
3
|
+
import wirely as blocks
|
|
4
|
+
from wirely.util import check_parameters
|
|
5
|
+
|
|
6
|
+
# TODO: Rename this to BlockInstantiation or BlockUsage?
|
|
7
|
+
# TODO: Probably Input, Output, StateVariable etc. should be moved out of this and only exist in BlockType
|
|
8
|
+
class Block(blocks.Node):
|
|
9
|
+
def __init__(self, graph, *, name, block_type, par_values, instantiation):
|
|
10
|
+
super().__init__(graph=graph, name=name)
|
|
11
|
+
|
|
12
|
+
self.block_type = block_type
|
|
13
|
+
self.par_values = par_values
|
|
14
|
+
self.instantiation = instantiation
|
|
15
|
+
# TODO: Clean up
|
|
16
|
+
for par in self.par_values:
|
|
17
|
+
val = self.par_values[par]
|
|
18
|
+
if not isinstance(val, blocks.ValueParameter) and not isinstance(val, blocks.Value) and not isinstance(val, blocks.Type):
|
|
19
|
+
raise Exception(f'Got invalid parameter value: {val}')
|
|
20
|
+
# TODO: Also check for contains_parameters? When should this be done?
|
|
21
|
+
# if val.contains_parameters:
|
|
22
|
+
# raise Exception(f'Got parameter value {val} contains parameters')
|
|
23
|
+
|
|
24
|
+
# TODO: A lot of this stuff should be moved to BlockType, instead the Block instance should only store the connections to the BlockType's inputs and from the BlockType's outputs
|
|
25
|
+
self.block_inputs = []
|
|
26
|
+
self.block_outputs = []
|
|
27
|
+
self.block_inputs_byname = {}
|
|
28
|
+
self.block_outputs_byname = {}
|
|
29
|
+
self.block_temporaries = []
|
|
30
|
+
self.block_statevars = []
|
|
31
|
+
self.block_procedures = []
|
|
32
|
+
self.last_proc_id = 0
|
|
33
|
+
|
|
34
|
+
if self.name in self.graph.blocks:
|
|
35
|
+
raise Exception(f'Duplicate block name {self.name!r}')
|
|
36
|
+
self.graph.blocks[self.name] = self
|
|
37
|
+
|
|
38
|
+
def __repr__(self):
|
|
39
|
+
# return f"{type(self).__name__}{{{self.graph}.{self.name}}}"
|
|
40
|
+
return f"{self.graph}.{self.name}[{type(self).__name__}]"
|
|
41
|
+
|
|
42
|
+
def __str__(self):
|
|
43
|
+
return f"{self.graph}.{self.name}"
|
|
44
|
+
|
|
45
|
+
def validate(self):
|
|
46
|
+
for input in self.all_block_inputs:
|
|
47
|
+
if input.connected_source is None:
|
|
48
|
+
# TODO: Hack
|
|
49
|
+
if isinstance(input.block, blocks.BlockOutput):
|
|
50
|
+
continue
|
|
51
|
+
raise Exception(f'Input {self.graph}.{self.name}.{input.name} is not connected')
|
|
52
|
+
|
|
53
|
+
for output in self.block_outputs:
|
|
54
|
+
if output.temporary.producer is None:
|
|
55
|
+
# TODO: Hack
|
|
56
|
+
if isinstance(output.temporary.block, blocks.BlockInput):
|
|
57
|
+
continue
|
|
58
|
+
raise Exception(f'Output {self.graph}.{self.name}.{output.name} has no producer')
|
|
59
|
+
|
|
60
|
+
def create_native_state(self, path, engine, static_block_context):
|
|
61
|
+
if path[-1] != self:
|
|
62
|
+
raise Exception('path[-1] != self')
|
|
63
|
+
# TODO
|
|
64
|
+
if hasattr(self, 'block_type') and hasattr(self.block_type, 'is_native'):
|
|
65
|
+
if self.block_type.is_native:
|
|
66
|
+
# print('QQQ', self.name)
|
|
67
|
+
return self.native_class(path, engine, static_block_context=static_block_context)
|
|
68
|
+
else:
|
|
69
|
+
# print('WWW', self.name)
|
|
70
|
+
pass
|
|
71
|
+
|
|
72
|
+
return None
|
|
73
|
+
|
|
74
|
+
# TODO: Clean up
|
|
75
|
+
@property
|
|
76
|
+
def all_block_inputs(self):
|
|
77
|
+
res = []
|
|
78
|
+
for input in self.block_inputs:
|
|
79
|
+
if input.is_list:
|
|
80
|
+
# TODO: Currently the individual entries of the list are already in block_inputs
|
|
81
|
+
# res += input.members
|
|
82
|
+
pass
|
|
83
|
+
else:
|
|
84
|
+
res.append(input)
|
|
85
|
+
return res
|
|
86
|
+
|
|
87
|
+
# TODO: Rename
|
|
88
|
+
# TODO: Must probably be cleaned up to allow multiple instantiations of the same block
|
|
89
|
+
# path: A tuple which contains the name of the block being instantiated
|
|
90
|
+
def instantiate(self, *, singletons, add_var, variable_map, state_map_new, all_variables, state_map_changed, state_map, state_variables, instances, path, parent_instance):
|
|
91
|
+
if self.block_type.is_singleton:
|
|
92
|
+
if self.block_type in singletons:
|
|
93
|
+
return
|
|
94
|
+
|
|
95
|
+
# TODO: This probably should not need an instance of Block here
|
|
96
|
+
singletons[self.block_type] = self
|
|
97
|
+
|
|
98
|
+
# TODO: This is an ugly hack, remove
|
|
99
|
+
if hasattr(self.block_type, 'singleton_instance'):
|
|
100
|
+
raise Exception("hasattr(self.block_type, 'singleton_instance')")
|
|
101
|
+
# print(f'Using {self} as instance for {self.block_type}')
|
|
102
|
+
self.block_type.singleton_instance = self
|
|
103
|
+
|
|
104
|
+
path = (self.block_type,)
|
|
105
|
+
|
|
106
|
+
if self.block_type.is_singleton:
|
|
107
|
+
instance = blocks.BlockInstance(self.block_type, parent_instance=None)
|
|
108
|
+
else:
|
|
109
|
+
instance = blocks.BlockInstance(self, parent_instance=parent_instance)
|
|
110
|
+
|
|
111
|
+
if path != instance.path:
|
|
112
|
+
raise Exception(f'path {path} != instance.path {instance.path}')
|
|
113
|
+
|
|
114
|
+
if instance.path in instances:
|
|
115
|
+
raise Exception(f'Duplicate path {path}')
|
|
116
|
+
instances[instance.path] = instance
|
|
117
|
+
|
|
118
|
+
for name, child_block in self.block_type.blocks.items():
|
|
119
|
+
child_path = path + (child_block,)
|
|
120
|
+
child_block.instantiate(singletons=singletons, add_var=add_var, variable_map=variable_map, state_map_new=state_map_new, all_variables=all_variables, state_map_changed=state_map_changed, state_map=state_map, state_variables=state_variables, instances=instances, path=child_path, parent_instance=instance)
|
|
121
|
+
|
|
122
|
+
# TODO: Clean up
|
|
123
|
+
# path_prefix = '.'.join([block.name for block in path])
|
|
124
|
+
path_prefix = '.'.join([(block.name if isinstance(block, blocks.Block) else block.name.id_str) for block in path])
|
|
125
|
+
for state_var in self.block_statevars:
|
|
126
|
+
v = blocks.Variable(path_prefix + '.' + state_var.name + '.new', state_var.var_type)
|
|
127
|
+
state_map_new[path, state_var] = v
|
|
128
|
+
all_variables.append(v)
|
|
129
|
+
|
|
130
|
+
v = blocks.Variable(path_prefix + '.' + state_var.name + '.changed', blocks.types.boolean)
|
|
131
|
+
state_map_changed[path, state_var] = v
|
|
132
|
+
all_variables.append(v)
|
|
133
|
+
|
|
134
|
+
v = blocks.Variable(path_prefix + '.' + state_var.name, state_var.var_type)
|
|
135
|
+
state_map[path, state_var] = v
|
|
136
|
+
state_variables.append(v)
|
|
137
|
+
for var in self.all_block_inputs:
|
|
138
|
+
if var.var_type is None:
|
|
139
|
+
continue
|
|
140
|
+
variable_map[path, var.var_identifier] = add_var(*blocks.resolve_variable(path, var))
|
|
141
|
+
for var in self.block_outputs:
|
|
142
|
+
if var.var_type is None:
|
|
143
|
+
continue
|
|
144
|
+
variable_map[path, var.var_identifier] = add_var(*blocks.resolve_variable(path, var))
|
|
145
|
+
for var in self.block_temporaries:
|
|
146
|
+
variable_map[path, var.var_identifier] = add_var(*blocks.resolve_variable(path, var))
|
|
147
|
+
|
|
148
|
+
# TODO: Probably should be merged into instantiate()
|
|
149
|
+
def get_block_procedures_paths(self, prefix):
|
|
150
|
+
# TODO: Handle singletons properly?
|
|
151
|
+
res = [(blocks.path_resolve_singleton(prefix + (self,)), proc) for proc in self.block_procedures]
|
|
152
|
+
for child_block in self.block_type.blocks.values():
|
|
153
|
+
res += child_block.get_block_procedures_paths(prefix + (self,))
|
|
154
|
+
return res
|
|
155
|
+
|
|
156
|
+
# TODO: Probably should be merged into instantiate()
|
|
157
|
+
def get_all_block_paths(self, prefix):
|
|
158
|
+
# TODO: Handle singletons properly?
|
|
159
|
+
res = [blocks.path_resolve_singleton(prefix + (self,))]
|
|
160
|
+
for child_block in self.block_type.blocks.values():
|
|
161
|
+
res += child_block.get_all_block_paths(prefix + (self,))
|
|
162
|
+
return res
|
|
163
|
+
|
|
164
|
+
class Member(abc.ABC):
|
|
165
|
+
def __init__(self, block: 'Block', name: str):
|
|
166
|
+
if not isinstance(block, Block):
|
|
167
|
+
raise Exception('block parameter is not a Block')
|
|
168
|
+
if not isinstance(name, str):
|
|
169
|
+
raise Exception('name parameter is not a str')
|
|
170
|
+
name = str(name)
|
|
171
|
+
|
|
172
|
+
self.block = block
|
|
173
|
+
self.name = name
|
|
174
|
+
|
|
175
|
+
def __repr__(self):
|
|
176
|
+
return f"{self.block}.{self.name}"
|
|
177
|
+
|
|
178
|
+
# TODO: Move out of block class / Remove Member parent?
|
|
179
|
+
class Source(Member, abc.ABC):
|
|
180
|
+
@property
|
|
181
|
+
@abc.abstractmethod
|
|
182
|
+
def temporary(self):
|
|
183
|
+
...
|
|
184
|
+
|
|
185
|
+
# TODO: Move out of block class / Remove Member parent?
|
|
186
|
+
class Target(Member, abc.ABC):
|
|
187
|
+
pass
|
|
188
|
+
|
|
189
|
+
# TODO: Handle var_type == None better
|
|
190
|
+
# TODO: Clean up, move more functionality to BlockInput
|
|
191
|
+
class Input(Source):
|
|
192
|
+
def __init__(self, block: 'Block', name: str, is_list: bool, var_type: blocks.types.Type, *, block_input: blocks.BlockInput):
|
|
193
|
+
Block.Member.__init__(self, block, name)
|
|
194
|
+
if not isinstance(is_list, bool):
|
|
195
|
+
raise Exception('is_list parameter is not of type bool')
|
|
196
|
+
if var_type is not None and not isinstance(var_type, blocks.types.Type):
|
|
197
|
+
raise Exception('var_type parameter is not a Type')
|
|
198
|
+
|
|
199
|
+
self.is_list = bool(is_list)
|
|
200
|
+
self.var_type = var_type
|
|
201
|
+
self.block_input = block_input
|
|
202
|
+
|
|
203
|
+
self.conn_spec = self.block_input.conn_spec
|
|
204
|
+
|
|
205
|
+
if isinstance(self.conn_spec, blocks.ConnectorSpecificationData):
|
|
206
|
+
if self.conn_spec.kind_spec.kind == blocks.ConnectionKind.Value:
|
|
207
|
+
self.impl_var_type = self.var_type
|
|
208
|
+
elif self.conn_spec.kind_spec.kind == blocks.ConnectionKind.Event:
|
|
209
|
+
self.impl_var_type = blocks.types.TypeOption(self.var_type)
|
|
210
|
+
else:
|
|
211
|
+
raise Exception(f'Unknown ConnectionKind: {self.conn_spec.kind_spec.kind}')
|
|
212
|
+
else:
|
|
213
|
+
# TODO: ?
|
|
214
|
+
self.impl_var_type = None
|
|
215
|
+
|
|
216
|
+
if self.is_list:
|
|
217
|
+
self.members = []
|
|
218
|
+
else:
|
|
219
|
+
self.connected_source = None
|
|
220
|
+
|
|
221
|
+
self.block.block_inputs.append(self)
|
|
222
|
+
if self.name in self.block.block_inputs_byname:
|
|
223
|
+
raise Exception(f'Duplicate input name {name!r}')
|
|
224
|
+
self.block.block_inputs_byname[self.name] = self
|
|
225
|
+
|
|
226
|
+
def __repr__(self):
|
|
227
|
+
return f"{self.block}.{self.name}"
|
|
228
|
+
|
|
229
|
+
@property
|
|
230
|
+
def temporary(self):
|
|
231
|
+
if self.is_list:
|
|
232
|
+
raise Exception('Trying to get temporary for list')
|
|
233
|
+
if self.connected_source is None:
|
|
234
|
+
raise Exception(f'{self} is not connected')
|
|
235
|
+
# TODO: Cache multiple follows?
|
|
236
|
+
return self.connected_source.temporary
|
|
237
|
+
|
|
238
|
+
def connect(self, output):
|
|
239
|
+
# TODO: Clean up class structure for value sources / targets
|
|
240
|
+
if not isinstance(output, Block.Output) and not isinstance(output, blocks.BlockInput) and not isinstance(output, blocks.BlockStateVariable) and not isinstance(output, blocks.BlockTemporaryVariable):
|
|
241
|
+
raise Exception('output parameter value is not an Output instance and not a BlockInput, BlockStateVariable or BlockTemporaryVariable')
|
|
242
|
+
if self.is_list:
|
|
243
|
+
# TODO: Clean up
|
|
244
|
+
# TODO: What should be used for block_input here?
|
|
245
|
+
input = Block.Input(self.block, f'{self.name}.{len(self.members)}', False, self.var_type, block_input=self)
|
|
246
|
+
input.connect(output)
|
|
247
|
+
self.members.append(input)
|
|
248
|
+
else:
|
|
249
|
+
if self.connected_source is not None:
|
|
250
|
+
raise Exception(f'{self} is already connected to {self.connected_source}, trying to connect to {output}')
|
|
251
|
+
|
|
252
|
+
self.connected_source = output
|
|
253
|
+
|
|
254
|
+
# TODO: Remove and replace with self, Input should contains a reference to BlockType, not to Block
|
|
255
|
+
@property
|
|
256
|
+
def var_identifier(self):
|
|
257
|
+
return 'INP ' + self.block.block_type.name.id_str + '.' + self.name
|
|
258
|
+
|
|
259
|
+
# TODO: Handle var_type == None better
|
|
260
|
+
# TODO: Clean up, move more functionality to BlockOutput
|
|
261
|
+
class Output(Target):
|
|
262
|
+
# TODO: Allow creating Output without Temporary?
|
|
263
|
+
def __init__(self, block: 'Block', name: str, var_type: blocks.types.Type, input_dependencies=[], *, block_output: blocks.BlockOutput):
|
|
264
|
+
Block.Member.__init__(self, block, name)
|
|
265
|
+
if var_type is not None and not isinstance(var_type, blocks.types.Type):
|
|
266
|
+
raise Exception('var_type parameter is not a Type')
|
|
267
|
+
|
|
268
|
+
self.var_type = var_type
|
|
269
|
+
self.block_output = block_output
|
|
270
|
+
|
|
271
|
+
self.conn_spec = self.block_output.conn_spec
|
|
272
|
+
|
|
273
|
+
if isinstance(self.conn_spec, blocks.ConnectorSpecificationData):
|
|
274
|
+
if self.conn_spec.kind_spec.kind == blocks.ConnectionKind.Value:
|
|
275
|
+
self.impl_var_type = self.var_type
|
|
276
|
+
elif self.conn_spec.kind_spec.kind == blocks.ConnectionKind.Event:
|
|
277
|
+
self.impl_var_type = blocks.types.TypeOption(self.var_type)
|
|
278
|
+
else:
|
|
279
|
+
raise Exception(f'Unknown ConnectionKind: {self.conn_spec.kind_spec.kind}')
|
|
280
|
+
else:
|
|
281
|
+
# TODO: ?
|
|
282
|
+
self.impl_var_type = None
|
|
283
|
+
|
|
284
|
+
self.input_dependencies = []
|
|
285
|
+
for dependency in input_dependencies:
|
|
286
|
+
if not isinstance(dependency, Block.Input):
|
|
287
|
+
raise Exception('input_dependencies parameter value is not an Input instance')
|
|
288
|
+
if dependency.block != self.block:
|
|
289
|
+
raise Exception('input_dependencies parameter value is for another block')
|
|
290
|
+
self.input_dependencies.append(dependency)
|
|
291
|
+
|
|
292
|
+
if self.var_type is not None:
|
|
293
|
+
self.temporary = Block.Temporary(block, name + '_val', var_type, impl_var_type=self.impl_var_type)
|
|
294
|
+
# TODO: Hack
|
|
295
|
+
self.orig_temporary = self.temporary
|
|
296
|
+
|
|
297
|
+
self.block.block_outputs.append(self)
|
|
298
|
+
if self.name in self.block.block_outputs_byname:
|
|
299
|
+
raise Exception(f'Duplicate output name {name!r}')
|
|
300
|
+
self.block.block_outputs_byname[self.name] = self
|
|
301
|
+
|
|
302
|
+
@property
|
|
303
|
+
def name_desc(self):
|
|
304
|
+
if self.name == '':
|
|
305
|
+
return 'implicit output'
|
|
306
|
+
else:
|
|
307
|
+
return self.name
|
|
308
|
+
|
|
309
|
+
def __repr__(self):
|
|
310
|
+
return f"{self.block}.{self.name}"
|
|
311
|
+
|
|
312
|
+
# TODO: Remove and replace with self, Output should contains a reference to BlockType, not to Block
|
|
313
|
+
@property
|
|
314
|
+
def var_identifier(self):
|
|
315
|
+
return 'OUT ' + self.block.block_type.name.id_str + '.' + self.name
|
|
316
|
+
|
|
317
|
+
# TODO: Allow both temporaries and output values to be references to other values
|
|
318
|
+
class Temporary(Source, Target):
|
|
319
|
+
def __init__(self, block: 'Block', name: str, var_type: blocks.types.Type, *, impl_var_type):
|
|
320
|
+
Block.Member.__init__(self, block, name)
|
|
321
|
+
if not isinstance(var_type, blocks.types.Type):
|
|
322
|
+
raise Exception('var_type parameter is not a Type')
|
|
323
|
+
|
|
324
|
+
self.var_type = var_type
|
|
325
|
+
|
|
326
|
+
self.impl_var_type = impl_var_type
|
|
327
|
+
|
|
328
|
+
self.producer = None
|
|
329
|
+
|
|
330
|
+
self.block.block_temporaries.append(self)
|
|
331
|
+
|
|
332
|
+
def __repr__(self):
|
|
333
|
+
return f"{self.block}.{self.name}"
|
|
334
|
+
|
|
335
|
+
@property
|
|
336
|
+
def temporary(self):
|
|
337
|
+
return self
|
|
338
|
+
|
|
339
|
+
# TODO: Remove and replace with self, Temporary should contains a reference to BlockType, not to Block
|
|
340
|
+
@property
|
|
341
|
+
def var_identifier(self):
|
|
342
|
+
return 'TEMP ' + self.block.block_type.name.id_str + '.' + self.name
|
|
343
|
+
|
|
344
|
+
class StateVariable(Source, Target):
|
|
345
|
+
def __init__(self, block: 'Block', name: str, var_type: blocks.types.Type, initial_value):
|
|
346
|
+
Block.Member.__init__(self, block, name)
|
|
347
|
+
if not isinstance(var_type, blocks.types.Type):
|
|
348
|
+
raise Exception('var_type parameter is not a Type')
|
|
349
|
+
|
|
350
|
+
self.var_type = var_type
|
|
351
|
+
self.initial_value = self.var_type.verify_value(initial_value)
|
|
352
|
+
|
|
353
|
+
self.impl_var_type = self.var_type
|
|
354
|
+
|
|
355
|
+
self.producer = None
|
|
356
|
+
|
|
357
|
+
self.block.block_statevars.append(self)
|
|
358
|
+
|
|
359
|
+
def __repr__(self):
|
|
360
|
+
return f"StateVariable[{self}]"
|
|
361
|
+
|
|
362
|
+
def __str__(self):
|
|
363
|
+
return f"{self.block}.{self.name}"
|
|
364
|
+
|
|
365
|
+
# TODO
|
|
366
|
+
@property
|
|
367
|
+
def temporary(self):
|
|
368
|
+
return self
|
|
369
|
+
|
|
370
|
+
class Procedure:
|
|
371
|
+
# TODO: Make name mandatory
|
|
372
|
+
def __init__(self, block: 'Block', dependencies, results, impl, can_trigger=False, name=None, *, static_context, declaration_procedure):
|
|
373
|
+
if not isinstance(block, Block):
|
|
374
|
+
raise Exception('block parameter is not a Block')
|
|
375
|
+
|
|
376
|
+
self.block = block
|
|
377
|
+
self.name = name
|
|
378
|
+
self.declaration_procedure = declaration_procedure
|
|
379
|
+
# TODO: Is this a complete static context? Probably not because the context might also depend on the parameters to the current block. Move this somewhere else?
|
|
380
|
+
self.static_context = static_context
|
|
381
|
+
|
|
382
|
+
self.block.last_proc_id += 1
|
|
383
|
+
self.id = self.block.last_proc_id
|
|
384
|
+
|
|
385
|
+
self.dependencies = []
|
|
386
|
+
for dependency in dependencies:
|
|
387
|
+
if not isinstance(dependency, Block.Source):
|
|
388
|
+
raise Exception('dependencies parameter value is not an Input or Temporary instance')
|
|
389
|
+
# TODO: Clean up inheritance
|
|
390
|
+
# TODO: Allow depending on the new value of the state variable?
|
|
391
|
+
# TODO: Also make dependency on old value explicit?
|
|
392
|
+
if isinstance(dependency, Block.StateVariable):
|
|
393
|
+
raise Exception('dependencies parameter value is a StateVariable instance')
|
|
394
|
+
# TODO: Allow dependencies on other blocks? Probably yes. Maybe check where dependency.block is a child of the current block?
|
|
395
|
+
# if dependency.block != self.block:
|
|
396
|
+
# raise Exception('dependencies parameter value is for another block')
|
|
397
|
+
self.dependencies.append(dependency)
|
|
398
|
+
|
|
399
|
+
self.results = []
|
|
400
|
+
for result in results:
|
|
401
|
+
if not isinstance(result, Block.Target):
|
|
402
|
+
raise Exception(f'results parameter value is not an Output or Temporary instance: {type(result)}')
|
|
403
|
+
if result.block != self.block:
|
|
404
|
+
raise Exception('results parameter value is for another block')
|
|
405
|
+
self.results.append(result)
|
|
406
|
+
|
|
407
|
+
# TODO: whether dependencies of output include required inputs as dependencies
|
|
408
|
+
|
|
409
|
+
# TODO: Clean up temporary / output stuff
|
|
410
|
+
if result.temporary.producer is not None:
|
|
411
|
+
raise Exception(f'{result} already has producer {result.temporary.producer}, got new producer {self}')
|
|
412
|
+
# print(f'Setting result for {result} / {result.temporary}')
|
|
413
|
+
result.temporary.producer = self
|
|
414
|
+
|
|
415
|
+
self.impl = impl
|
|
416
|
+
self.can_trigger = can_trigger
|
|
417
|
+
|
|
418
|
+
self.block.block_procedures.append(self)
|
|
419
|
+
|
|
420
|
+
def __repr__(self):
|
|
421
|
+
if self.name is not None:
|
|
422
|
+
return f"{self.block}.{self.name}"
|
|
423
|
+
else:
|
|
424
|
+
return f"{self.block}.proc{self.id}"
|
|
425
|
+
|
|
426
|
+
# TODO: Remove and replace with self, Procedure should contains a reference to BlockType, not to Block
|
|
427
|
+
@property
|
|
428
|
+
def proc_identifier(self):
|
|
429
|
+
return 'PROC ' + self.block.block_type.name.id_str + '.' + self.name
|
|
430
|
+
|
|
431
|
+
class BlockInstance(blocks.NodeInstance):
|
|
432
|
+
@check_parameters
|
|
433
|
+
def __init__(self, block, parent_instance):
|
|
434
|
+
if isinstance(block, blocks.Block):
|
|
435
|
+
super().__init__(block, parent_instance=parent_instance)
|
|
436
|
+
# Alias for self.node
|
|
437
|
+
self.block = block
|
|
438
|
+
|
|
439
|
+
self.is_singleton = False
|
|
440
|
+
|
|
441
|
+
self.block_type = self.block.block_type
|
|
442
|
+
elif isinstance(block, blocks.BlockType):
|
|
443
|
+
if parent_instance != None:
|
|
444
|
+
raise Exception('parent_instance != None')
|
|
445
|
+
# TODO: Clean up? Create separate type?
|
|
446
|
+
super().__init__(None, parent_instance=None)
|
|
447
|
+
self.block = None
|
|
448
|
+
|
|
449
|
+
self.is_singleton = True
|
|
450
|
+
|
|
451
|
+
self.block_type = block
|
|
452
|
+
else:
|
|
453
|
+
raise Exception(f'Unknown value for block: {block}')
|
|
454
|
+
|
|
455
|
+
if self.is_singleton:
|
|
456
|
+
self.parameter_values = {}
|
|
457
|
+
else:
|
|
458
|
+
self.parameter_values = dict(self.block.par_values)
|
|
459
|
+
|
|
460
|
+
# Resolve parameter values from parent
|
|
461
|
+
parent_parameter_values = None
|
|
462
|
+
if self.parent_instance is not None:
|
|
463
|
+
parent_parameter_values = self.parent_instance.parameter_values
|
|
464
|
+
# TODO: Do this properly, handle nested parameters etc.
|
|
465
|
+
for key in list(self.parameter_values):
|
|
466
|
+
# TODO: Clean up
|
|
467
|
+
val = self.parameter_values[key]
|
|
468
|
+
if isinstance(val, blocks.TypeParameter) or isinstance(val, blocks.ValueParameter):
|
|
469
|
+
par = val.parameter
|
|
470
|
+
if par not in parent_parameter_values:
|
|
471
|
+
if key.is_optional:
|
|
472
|
+
# TODO: Clean this up. Store a value like None in this case?
|
|
473
|
+
del self.parameter_values[key]
|
|
474
|
+
continue
|
|
475
|
+
raise Exception(f'Could not find parameter {par.name} in parent')
|
|
476
|
+
# TODO: Check type etc.
|
|
477
|
+
new_val = parent_parameter_values[par]
|
|
478
|
+
# print('QQQ', val, new_val)
|
|
479
|
+
# TODO: Properly handle type parameters
|
|
480
|
+
if isinstance(val, blocks.TypeParameter):
|
|
481
|
+
if not isinstance(val, blocks.Type):
|
|
482
|
+
# TODO: hints?
|
|
483
|
+
raise blocks.ProgramError(val.parameter, f'[TODO: Error message] Got parameter of type {new_val.value_type} for {val.parameter.name} where type parameter was expected')
|
|
484
|
+
else:
|
|
485
|
+
if val.value_type != new_val.value_type:
|
|
486
|
+
# TODO: Fix error message / hints
|
|
487
|
+
hints = [
|
|
488
|
+
blocks.CompilerHint(val.parameter, 'Parameter is declared here'),
|
|
489
|
+
]
|
|
490
|
+
raise blocks.ProgramError(val.parameter, f'[TODO: Error message] Got parameter of type {new_val.value_type} for {val.parameter.name} where parameter of type {val.value_type} expected', hints=hints)
|
|
491
|
+
# TODO: Check whether this is fully resolved
|
|
492
|
+
self.parameter_values[key] = new_val
|
|
493
|
+
|
|
494
|
+
# TODO: Clean this up. Probably other places should also used the parents' parameters
|
|
495
|
+
# TODO: Also, probably make sure that no parameters have been resolved here? Or should the be resolved as far as possible? Currently they are resolved in a weird way (a direct parameter might be resolved while the same parameter in a function call is not).
|
|
496
|
+
all_par_values = dict(self.parameter_values)
|
|
497
|
+
next_parent = self.parent_instance
|
|
498
|
+
while next_parent is not None:
|
|
499
|
+
for key in next_parent.parameter_values:
|
|
500
|
+
if key not in all_par_values:
|
|
501
|
+
all_par_values[key] = next_parent.parameter_values[key]
|
|
502
|
+
next_parent = next_parent.parent_instance
|
|
503
|
+
|
|
504
|
+
# print(f'Resolving while creating BlockInstance: {self.parameter_values} /// {all_par_values}')
|
|
505
|
+
# TODO: Store this?
|
|
506
|
+
parameter_values_resolved = {key: value.resolve_self(par_values=all_par_values) for key, value in self.parameter_values.items()}
|
|
507
|
+
|
|
508
|
+
parameter_values_bystr = {key.name.id_str: value for key, value in parameter_values_resolved.items()}
|
|
509
|
+
native_class = self.block_type.native_class
|
|
510
|
+
parsed_native_instance = None
|
|
511
|
+
|
|
512
|
+
# TODO: Pass this differently
|
|
513
|
+
parameter_values_bystr[''] = {}
|
|
514
|
+
if self.block is None:
|
|
515
|
+
# Singleton
|
|
516
|
+
parameter_values_bystr['']['BlkFilename'] = None
|
|
517
|
+
else:
|
|
518
|
+
# TODO: Get the filename from somewhere else?
|
|
519
|
+
parameter_values_bystr['']['BlkFilename'] = self.block.instantiation.source_position.filename
|
|
520
|
+
|
|
521
|
+
if native_class is not None:
|
|
522
|
+
# TODO: Make sure that this is executed if a native class exists even if there is no native procedure in the block and the block has no native state?
|
|
523
|
+
data_type = native_class.native_block_data_type
|
|
524
|
+
if data_type is not None:
|
|
525
|
+
try:
|
|
526
|
+
parsed_native_instance = data_type.create(parameter_values=parameter_values_bystr)
|
|
527
|
+
except blocks.ProgramError as e:
|
|
528
|
+
raise
|
|
529
|
+
except Exception as e:
|
|
530
|
+
raise blocks.InternalCompilerError(block.instantiation, e, msg=f'Error while parsing parameters: {e}')
|
|
531
|
+
self.static_context = blocks.StaticBlockContext(parameter_values=parameter_values_bystr, parsed_native_instance=parsed_native_instance)
|
|
532
|
+
|
|
533
|
+
@property
|
|
534
|
+
def path(self):
|
|
535
|
+
if self.is_singleton:
|
|
536
|
+
return (self.block_type,)
|
|
537
|
+
|
|
538
|
+
if self.parent_instance is None:
|
|
539
|
+
base_path = ()
|
|
540
|
+
else:
|
|
541
|
+
base_path = self.parent_instance.path
|
|
542
|
+
return base_path + (self.block,)
|