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.
Files changed (59) hide show
  1. wirely/__init__.py +406 -0
  2. wirely/__main__.py +45 -0
  3. wirely/ast.py +1135 -0
  4. wirely/block.py +542 -0
  5. wirely/block_type.py +342 -0
  6. wirely/blocks.lark +126 -0
  7. wirely/compiler.py +125 -0
  8. wirely/engine.py +418 -0
  9. wirely/enum.py +15 -0
  10. wirely/example.py +36 -0
  11. wirely/graphviz.py +107 -0
  12. wirely/knx/__init__.py +0 -0
  13. wirely/knx/connection_adapter.py +90 -0
  14. wirely/knx/connection_config.py +152 -0
  15. wirely/lib/abb_jra.wrl +64 -0
  16. wirely/lib/csv.wrl +32 -0
  17. wirely/lib/ext_prog.wrl +12 -0
  18. wirely/lib/gpio.wrl +23 -0
  19. wirely/lib/knx.wrl +80 -0
  20. wirely/lib/memory.wrl +45 -0
  21. wirely/lib/mqtt.wrl +66 -0
  22. wirely/lib/std.wrl +134 -0
  23. wirely/lib/time.wrl +93 -0
  24. wirely/lib/two_n.wrl +111 -0
  25. wirely/misc/__init__.py +0 -0
  26. wirely/misc/service.py +48 -0
  27. wirely/native/__init__.py +74 -0
  28. wirely/native/abb_jra.py +66 -0
  29. wirely/native/csv.py +194 -0
  30. wirely/native/ext_prog.py +77 -0
  31. wirely/native/gpio.py +125 -0
  32. wirely/native/knx.py +455 -0
  33. wirely/native/memory.py +113 -0
  34. wirely/native/mqtt.py +605 -0
  35. wirely/native/std.py +334 -0
  36. wirely/native/test_library.py +14 -0
  37. wirely/native/time.py +359 -0
  38. wirely/native/two_n.py +632 -0
  39. wirely/node.py +125 -0
  40. wirely/parser.py +175 -0
  41. wirely/program.py +155 -0
  42. wirely/run.py +143 -0
  43. wirely/service.py +65 -0
  44. wirely/test.py +569 -0
  45. wirely/time.py +172 -0
  46. wirely/two_n/__init__.py +0 -0
  47. wirely/two_n/cert.py +126 -0
  48. wirely/two_n/client.py +402 -0
  49. wirely/two_n/fake_server.py +415 -0
  50. wirely/two_n/grab_images.py +146 -0
  51. wirely/types.py +522 -0
  52. wirely/util.py +111 -0
  53. wirely/value.py +163 -0
  54. wirely-0.1.0.dist-info/METADATA +94 -0
  55. wirely-0.1.0.dist-info/RECORD +59 -0
  56. wirely-0.1.0.dist-info/WHEEL +5 -0
  57. wirely-0.1.0.dist-info/entry_points.txt +2 -0
  58. wirely-0.1.0.dist-info/licenses/LICENSE +21 -0
  59. wirely-0.1.0.dist-info/top_level.txt +1 -0
wirely/ast.py ADDED
@@ -0,0 +1,1135 @@
1
+ import enum
2
+ import typing
3
+ import abc
4
+ import contextlib
5
+ import ast
6
+ import re
7
+ import datetime
8
+
9
+ import lark
10
+
11
+ import wirely as blocks
12
+ from wirely.util import check_parameters
13
+
14
+
15
+ class SourcePosition:
16
+ @check_parameters
17
+ def __init__(self, *, filename, meta, input_data):
18
+ self.filename = filename
19
+ self.meta = meta
20
+ self.input_data = input_data
21
+
22
+ @property
23
+ def start_pos(self):
24
+ # TODO: Handle source elements which were created without any token where e.g. start_pos is not set?
25
+ return self.meta.start_pos
26
+
27
+ @property
28
+ def start_line(self):
29
+ return self.meta.line
30
+
31
+
32
+ class SourceElement:
33
+ @check_parameters
34
+ def __init__(self, *, source_position: SourcePosition):
35
+ self.source_position = source_position
36
+
37
+ @property
38
+ @contextlib.contextmanager
39
+ def context(self):
40
+ exc = None
41
+ try:
42
+ yield None
43
+ except Exception as e:
44
+ exc = e
45
+ if isinstance(exc, ProgramError):
46
+ raise
47
+ if exc is not None:
48
+ raise InternalCompilerError(self, exc)
49
+
50
+
51
+ class CompilerHint:
52
+ @check_parameters
53
+ def __init__(self, source_element: SourceElement, msg):
54
+ self.source_element = source_element
55
+ self.msg = msg
56
+ self.fake_ui = lark.exceptions.UnexpectedInput()
57
+ self.fake_ui.pos_in_stream = self.source_element.source_position.start_pos
58
+
59
+ def __str__(self):
60
+ return self.msg
61
+
62
+ def get_context(self):
63
+ return self.fake_ui.get_context(self.source_element.source_position.input_data)
64
+
65
+
66
+ class ProgramError(Exception):
67
+ @check_parameters
68
+ def __init__(self, source_element: typing.Optional[SourceElement], msg, inner_exception=None, *, hints: typing.List[CompilerHint] = []):
69
+ # raise Exception()
70
+ self.source_element = source_element
71
+ self.msg = msg
72
+ self.inner_exception = inner_exception
73
+ self.hints = hints
74
+ if self.source_element is not None:
75
+ self.fake_ui = lark.exceptions.UnexpectedInput()
76
+ self.fake_ui.pos_in_stream = self.source_element.source_position.start_pos
77
+
78
+ if self.source_element is None and not isinstance(self, InternalCompilerError):
79
+ raise Exception('self.source_element is None and not isinstance(self, InternalCompilerError)')
80
+
81
+ def __str__(self):
82
+ return self.msg
83
+
84
+ def get_context(self):
85
+ return self.fake_ui.get_context(self.source_element.source_position.input_data)
86
+
87
+
88
+ class InternalCompilerError(ProgramError):
89
+ @check_parameters
90
+ def __init__(self, source_element: typing.Optional[SourceElement], inner_exception, *, msg=None):
91
+ if msg is None:
92
+ msg = 'Internal Compiler Error: ' + str(inner_exception)
93
+ super().__init__(source_element, msg, inner_exception)
94
+
95
+
96
+ class Identifier(SourceElement):
97
+ @check_parameters
98
+ def __init__(self, s: str, *, source_position: SourcePosition):
99
+ super().__init__(source_position=source_position)
100
+ if s.startswith('r#'):
101
+ s = s[2:]
102
+ self.id_str = s
103
+
104
+ def __repr__(self):
105
+ # return f'{self.id_str} [{self.source_position.start_line}]'
106
+ return self.id_str
107
+
108
+
109
+ class RefType(enum.Enum):
110
+ BlockInput = enum.auto()
111
+ BlockOutput = enum.auto()
112
+
113
+
114
+ # TODO: Merge with RefType?
115
+ class BlockConnectorType(enum.Enum):
116
+ BlockInput = enum.auto()
117
+ BlockOutput = enum.auto()
118
+
119
+
120
+ class RunStage(enum.Enum):
121
+ Input = enum.auto()
122
+ Default = enum.auto()
123
+ Output = enum.auto()
124
+
125
+
126
+ class BlockConnectorTypeSpecifier(SourceElement):
127
+ @check_parameters
128
+ def __init__(self, conn_type: BlockConnectorType, *, source_position: SourcePosition):
129
+ super().__init__(source_position=source_position)
130
+ self.conn_type = conn_type
131
+
132
+ @staticmethod
133
+ @check_parameters
134
+ def create_input(*, source_position: SourcePosition):
135
+ return BlockConnectorTypeSpecifier(BlockConnectorType.BlockInput, source_position=source_position)
136
+
137
+ @staticmethod
138
+ @check_parameters
139
+ def create_output(*, source_position: SourcePosition):
140
+ return BlockConnectorTypeSpecifier(BlockConnectorType.BlockOutput, source_position=source_position)
141
+
142
+
143
+ class ListSpecifier(SourceElement):
144
+ @check_parameters
145
+ def __init__(self, is_list: bool, *, source_position: SourcePosition):
146
+ super().__init__(source_position=source_position)
147
+ self.is_list = is_list
148
+
149
+ @staticmethod
150
+ @check_parameters
151
+ def create_list(*, source_position: SourcePosition):
152
+ return ListSpecifier(True, source_position=source_position)
153
+
154
+ @staticmethod
155
+ @check_parameters
156
+ def create_nolist(*, source_position: SourcePosition):
157
+ return ListSpecifier(False, source_position=source_position)
158
+
159
+
160
+ # TODO: Rename Value to Level or State or keep Value?
161
+ # TODO: Rename Signal to Impulse or Trigger or Event?
162
+ class ConnectionKind(enum.Enum):
163
+ Value = enum.auto()
164
+ Event = enum.auto()
165
+
166
+
167
+ class ConnectionKindSpecifier(SourceElement):
168
+ @check_parameters
169
+ def __init__(self, kind: ConnectionKind, *, source_position: SourcePosition):
170
+ super().__init__(source_position=source_position)
171
+ self.kind = kind
172
+
173
+ @staticmethod
174
+ @check_parameters
175
+ def create_value(*, source_position: SourcePosition):
176
+ return ConnectionKindSpecifier(ConnectionKind.Value, source_position=source_position)
177
+
178
+ @staticmethod
179
+ @check_parameters
180
+ def create_event(*, source_position: SourcePosition):
181
+ return ConnectionKindSpecifier(ConnectionKind.Event, source_position=source_position)
182
+
183
+
184
+ class Value(SourceElement):
185
+ @check_parameters
186
+ def __init__(self, *, source_position: SourcePosition):
187
+ super().__init__(source_position=source_position)
188
+
189
+
190
+ class ConnectorSpecification(SourceElement):
191
+ @check_parameters
192
+ def __init__(self, list_spec: ListSpecifier, *, source_position: SourcePosition):
193
+ super().__init__(source_position=source_position)
194
+ self.list_spec = list_spec
195
+
196
+
197
+ class ConnectorSpecificationData(ConnectorSpecification):
198
+ @check_parameters
199
+ def __init__(self, list_spec: ListSpecifier, kind_spec: ConnectionKindSpecifier, ty: Value, *, source_position: SourcePosition):
200
+ super().__init__(list_spec=list_spec, source_position=source_position)
201
+ self.kind_spec = kind_spec
202
+ self.ty = ty
203
+
204
+
205
+ class ConnectorSpecificationKnxBus(ConnectorSpecification):
206
+ @check_parameters
207
+ def __init__(self, list_spec: ListSpecifier, *, source_position: SourcePosition):
208
+ super().__init__(list_spec=list_spec, source_position=source_position)
209
+
210
+
211
+ class ConnectorSpecificationMqttServer(ConnectorSpecification):
212
+ @check_parameters
213
+ def __init__(self, list_spec: ListSpecifier, *, source_position: SourcePosition):
214
+ super().__init__(list_spec=list_spec, source_position=source_position)
215
+
216
+
217
+ class ConnectorSpecificationTwoNDevice(ConnectorSpecification):
218
+ @check_parameters
219
+ def __init__(self, list_spec: ListSpecifier, *, source_position: SourcePosition):
220
+ super().__init__(list_spec=list_spec, source_position=source_position)
221
+
222
+
223
+ class BlockTypeSpecifier(SourceElement):
224
+ @check_parameters
225
+ def __init__(self, specifier_str, *, source_position: SourcePosition):
226
+ super().__init__(source_position=source_position)
227
+
228
+ self.specifier_str = specifier_str
229
+
230
+ @staticmethod
231
+ def create_singleton(*, source_position: SourcePosition):
232
+ return BlockTypeSpecifier(specifier_str='singleton', source_position=source_position)
233
+
234
+
235
+ class NativeSpecifier(BlockTypeSpecifier):
236
+ @check_parameters
237
+ def __init__(self, *, source_position: SourcePosition):
238
+ super().__init__(specifier_str='native', source_position=source_position)
239
+
240
+
241
+ class OptionalSpecifier(BlockTypeSpecifier):
242
+ @check_parameters
243
+ def __init__(self, *, source_position: SourcePosition):
244
+ super().__init__(specifier_str='optional', source_position=source_position)
245
+
246
+
247
+ class IntNumber(Value):
248
+ @check_parameters
249
+ def __init__(self, token: lark.Token, *, source_position: SourcePosition):
250
+ super().__init__(source_position=source_position)
251
+ self.token = token
252
+ self.value = int(token)
253
+
254
+
255
+ class FloatNumber(Value):
256
+ @check_parameters
257
+ def __init__(self, token: lark.Token, *, source_position: SourcePosition):
258
+ super().__init__(source_position=source_position)
259
+ self.token = token
260
+ # TODO: Does this parse the value correctly?
261
+ self.value = float(token)
262
+
263
+
264
+ class StringConstant(Value):
265
+ @check_parameters
266
+ def __init__(self, token: lark.Token, *, source_position: SourcePosition):
267
+ super().__init__(source_position=source_position)
268
+ self.token = token
269
+
270
+ val = token.value
271
+ # https://doc.rust-lang.org/reference/tokens.html#r-lex.token.literal.str
272
+ # https://doc.rust-lang.org/reference/tokens.html#r-lex.token.literal.str-raw
273
+ if val.startswith('r"'):
274
+ if len(val) < 3 or val[-1] != '"':
275
+ raise Exception(f'Error while getting string constant {val!r}')
276
+ value = val[2:-1]
277
+ if '"' in value:
278
+ # Should never happen, the lexer should not return such strings
279
+ raise Exception('Error while parsing string constant: " in value')
280
+ self.value = value
281
+ # TODO: Also allow different # levels (support also needed in lexer)
282
+ elif val.startswith('r#"'):
283
+ if len(val) < 5 or val[-2:] != '"#':
284
+ raise Exception(f'Error while getting string constant {val!r}')
285
+ value = val[3:-2]
286
+ if '"#' in value:
287
+ # Should never happen, the lexer should not return such strings
288
+ raise Exception('Error while parsing string constant: "# in value')
289
+ self.value = value
290
+ else:
291
+ if len(val) < 2 or val[0] != '"' or val[-1] != '"':
292
+ raise Exception(f'Error while getting string constant {val!r}')
293
+ # TODO: Do this manually and use rust rules instead of python
294
+ self.value = ast.literal_eval(val)
295
+
296
+
297
+ class Reference(Value):
298
+ @check_parameters
299
+ def __init__(self, identifiers: typing.List[Identifier], *, source_position: SourcePosition):
300
+ super().__init__(source_position=source_position)
301
+ self.identifiers = identifiers
302
+
303
+ @staticmethod
304
+ @check_parameters
305
+ def from_list(*identifiers, source_position: SourcePosition):
306
+ return Reference(list(identifiers), source_position=source_position)
307
+
308
+ @staticmethod
309
+ @check_parameters
310
+ def create_implicit(conn_type_spec: BlockConnectorTypeSpecifier, *, source_position: SourcePosition):
311
+ conn_type = conn_type_spec.conn_type
312
+ # TODO: Clean up
313
+ if conn_type == BlockConnectorType.BlockInput:
314
+ name = 'implicit input'
315
+ elif conn_type == BlockConnectorType.BlockOutput:
316
+ name = 'implicit output'
317
+ else:
318
+ raise Exception(f'Unknown value for conn_type: {conn_type}')
319
+ identifier = Identifier(name, source_position=source_position)
320
+ return Reference([identifier], source_position=source_position)
321
+
322
+ def __repr__(self):
323
+ # TODO: Somehow show which enclosing block a parameter belongs to?
324
+ # return '.'.join([i.id_str for i in self.identifiers]) + f' [{self.source_position.start_line}]'
325
+ return '.'.join([i.id_str for i in self.identifiers])
326
+
327
+ @property
328
+ def length(self):
329
+ return len(self.identifiers)
330
+
331
+ @check_parameters
332
+ def resolve(self, graph, ref_type: RefType, purpose: str):
333
+ if ref_type in [RefType.BlockInput, RefType.BlockOutput]:
334
+ if self.length == 1:
335
+ block_name_id = self.identifiers[0]
336
+ member_name_id = None
337
+ elif self.length == 2:
338
+ block_name_id = self.identifiers[0]
339
+ member_name_id = self.identifiers[1]
340
+ else:
341
+ raise ProgramError(self, f'Got unexpected number of identifiers in {purpose}: {self}')
342
+ return ResolvedReferenceBlockMember(graph, ref_type, block_name_id, member_name_id, purpose)
343
+ else:
344
+ raise Exception(f'Unknown RefType: {ref_type!r}')
345
+
346
+
347
+ class ListLiteral(Value):
348
+ @check_parameters
349
+ def __init__(self, values: list[Value], *, source_position: SourcePosition):
350
+ super().__init__(source_position=source_position)
351
+ self.values = values
352
+
353
+
354
+ class FunctionCall(Value):
355
+ @check_parameters
356
+ def __init__(self, fun: Value, parameters: list[Value], *, source_position: SourcePosition):
357
+ super().__init__(source_position=source_position)
358
+ self.fun = fun
359
+ self.parameters = parameters
360
+
361
+
362
+ class BracketOperator(Value):
363
+ @check_parameters
364
+ def __init__(self, base: Value, parameter: Value, *, source_position: SourcePosition):
365
+ super().__init__(source_position=source_position)
366
+ self.base = base
367
+ self.parameter = parameter
368
+
369
+
370
+ # TODO: Remove and merge into Reference?
371
+ class ResolvedReferenceBlockMember(SourceElement):
372
+ @check_parameters
373
+ # TODO: member_name should not be typing.Optional
374
+ def __init__(self, graph, ref_type: RefType, block_name: Identifier, member_name: typing.Optional[Identifier], purpose: str):
375
+ if ref_type == RefType.BlockInput:
376
+ member_type = 'input'
377
+ member_dict_lambda = lambda block: block.block_inputs_byname
378
+ elif ref_type == RefType.BlockOutput:
379
+ member_type = 'output'
380
+ member_dict_lambda = lambda block: block.block_outputs_byname
381
+ else:
382
+ raise Exception(f'Unknown RefType: {ref_type!r}')
383
+
384
+ self.block_name = block_name
385
+ self.member_name = member_name
386
+
387
+ self.block_name_str = self.block_name.id_str
388
+ if self.member_name is None:
389
+ # TODO: Clean up
390
+ if ref_type == RefType.BlockInput:
391
+ # self.member_name_str = 'implicit input'
392
+ self.member_name_str = ''
393
+ elif ref_type == RefType.BlockOutput:
394
+ # self.member_name_str = 'implicit output'
395
+ self.member_name_str = ''
396
+ else:
397
+ raise Exception(f'Unknown value for ref_type: {ref_type}')
398
+ # TODO: Remove member_name_elem (Hack)
399
+ self.member_name_elem = block_name
400
+ else:
401
+ self.member_name_str = self.member_name.id_str
402
+ self.member_name_elem = self.member_name
403
+
404
+ # print(ref_type, repr(self.block_name_str), repr(self.member_name_str))
405
+ # TODO: Should this be here?
406
+ if ref_type == RefType.BlockInput:
407
+ if self.block_name_str in graph.block_outputs and self.member_name_str == '':
408
+ self.member = graph.block_outputs[self.block_name_str]
409
+ # TODO: This is a hack
410
+ return
411
+ if ref_type == RefType.BlockOutput:
412
+ if self.block_name_str in graph.block_inputs and self.member_name_str == '':
413
+ self.member = graph.block_inputs[self.block_name_str]
414
+ # TODO: This is a hack
415
+ return
416
+ if self.block_name_str in graph.block_state_variables and self.member_name_str == '':
417
+ self.member = graph.block_state_variables[self.block_name_str]
418
+ # TODO: This is a hack
419
+ return
420
+ if self.block_name_str in graph.block_temporary_variables and self.member_name_str == '':
421
+ self.member = graph.block_state_variables[self.block_name_str]
422
+ # TODO: This is a hack
423
+ return
424
+
425
+ if self.block_name_str not in graph.blocks:
426
+ raise ProgramError(self.block_name, f'Unable to find {purpose} block {self.block_name_str!r}')
427
+ self.block = graph.blocks[self.block_name_str]
428
+ member_dict = member_dict_lambda(self.block)
429
+ if self.member_name_str in member_dict:
430
+ self.member = member_dict[self.member_name_str]
431
+ # TODO: Clean this up. This falls back to looking for BlockInput / BlockOutput nodes because knx_bus connectors don't create Block.Input and Block.Output objects
432
+ else:
433
+ if self.member_name_str == '':
434
+ raise ProgramError(self.block_name, f'{purpose} block {self.block_name_str} does not have an implicit {member_type}')
435
+ else:
436
+ raise ProgramError(self.member_name_elem, f'Unable to find {purpose} block {member_type} {self.block_name_str}.{self.member_name_str}')
437
+
438
+
439
+ class Declaration(SourceElement, abc.ABC):
440
+ @check_parameters
441
+ def __init__(self, source_position: SourcePosition):
442
+ super().__init__(source_position=source_position)
443
+
444
+ # TODO: Move implementation here?
445
+ @abc.abstractmethod
446
+ def compile_pass1_btd(self, interface):
447
+ ...
448
+
449
+ @abc.abstractmethod
450
+ def create_io(self, new_graph):
451
+ ...
452
+
453
+ @abc.abstractmethod
454
+ def compile_pass1(self, graph):
455
+ ...
456
+
457
+ @abc.abstractmethod
458
+ def compile_pass1_5(self, graph):
459
+ ...
460
+
461
+ @abc.abstractmethod
462
+ def compile_pass2(self, graph):
463
+ ...
464
+
465
+
466
+ # This class contains methods for resolving references in a scope
467
+ class Scope(Declaration):
468
+ @check_parameters
469
+ def __init__(self, source_position: SourcePosition):
470
+ super().__init__(source_position=source_position)
471
+
472
+ # TODO: Remove one of block_type_interfaces / block_type_implementations
473
+ self.block_type_interfaces = {}
474
+ self.block_type_implementations = {}
475
+
476
+ # TODO: Move to Declaration?
477
+ @property
478
+ @abc.abstractmethod
479
+ def fullname(self):
480
+ ...
481
+
482
+ # TODO: Handle parameters for parent types properly? Does this even make sense?
483
+ def resolve_type(self, ty, par_values={}):
484
+ with ty.context:
485
+ # TODO: Clean up
486
+ if isinstance(ty, BracketOperator):
487
+ if not isinstance(ty.base, Reference):
488
+ raise blocks.ProgramError(ty, f'Expected Reference as generic type base, got {ty}')
489
+ if ty.base.length == 1 and ty.base.identifiers[0].id_str == 'Option':
490
+ gen_type = blocks.types.TypeOption
491
+ elif ty.base.length == 1 and ty.base.identifiers[0].id_str == 'List':
492
+ gen_type = blocks.types.TypeList
493
+ else:
494
+ raise blocks.ProgramError(ty, f'Cannot find generic type {ty.base}')
495
+ # if len(ty.parameters) != 1:
496
+ # raise blocks.ProgramError(ty, f'Expected 1 type parameter, got {len(ty.parameters)}: {ty}')
497
+ par_type = self.resolve_type(ty.parameter, par_values=par_values)
498
+ return gen_type(par_type)
499
+ elif not isinstance(ty, Reference):
500
+ raise blocks.ProgramError(ty, f'Expected Reference or BracketOperator as type name, got {ty}')
501
+
502
+ # TODO: Move somewhere else, clean up
503
+ if ty.length == 1:
504
+ name = ty.identifiers[0].id_str
505
+ # TODO: Avoid hasattr
506
+ if hasattr(self, 'parsed_parameters') and name in self.parsed_parameters:
507
+ par = self.parsed_parameters[name]
508
+ # TODO
509
+ # if par.ty == blocks.types.ValueType:
510
+ if par.ty.length == 1 and par.ty.identifiers[0].id_str == 'ValueType':
511
+ # TODO: Handle par_values=None?
512
+ # TODO: More checks?
513
+ if par not in par_values:
514
+ # TODO: Should TypeParameter creation happen here?
515
+ return blocks.TypeParameter(par)
516
+ return par_values[par]
517
+ if name in blocks.types.type_by_name:
518
+ return blocks.types.type_by_name[name]
519
+ raise blocks.ProgramError(ty, f'Cannot find type {ty}')
520
+
521
+ # TODO: Should this be named get_const_value()? (Also returns e.g. ValueFunctionCall)
522
+ def get_const_value(self, value, value_type, par_values={}):
523
+ with value.context:
524
+ if isinstance(value, blocks.FunctionCall):
525
+ if not isinstance(value.fun, blocks.Reference) or len(value.fun.identifiers) != 1:
526
+ raise blocks.ProgramError(value.fun, f'Function is not a single identifier')
527
+ function_name = value.fun.identifiers[0].id_str
528
+ if function_name not in blocks.value.functions:
529
+ raise blocks.ProgramError(value.fun, f'Cannot find function {function_name!r}')
530
+ function = blocks.value.functions[function_name]
531
+ if len(value.parameters) != len(function.parameter_types):
532
+ raise blocks.ProgramError(value, f'Function {function_name!r} expected {len(function.parameter_types)} parameters, but got {len(value.parameters)}')
533
+ parameters = [self.get_const_value(parameter, function.parameter_types[i], par_values=par_values) for i, parameter in enumerate(value.parameters)]
534
+ return blocks.value.ValueFunctionCall(function=function, parameters=parameters, ast_element=value)
535
+
536
+ # TODO: Clean up
537
+ found = True
538
+ while found:
539
+ found = False
540
+ if isinstance(value, blocks.Reference) and value.length == 1:
541
+ name = value.identifiers[0].id_str
542
+ if name in self.parsed_parameters:
543
+ par = self.parsed_parameters[name]
544
+ if not(par.ty.length == 1 and par.ty.identifiers[0].id_str == 'ValueType'):
545
+ # value = par_values[name]
546
+ # found = True
547
+ # print(f'{par.name} in {par_values}')
548
+ # TODO: Clean up / Check whether this really is a parameter
549
+ if par not in par_values:
550
+ return blocks.ValueParameter(value_type, par, ast_element=value)
551
+ return blocks.ConstantValue(value_type, value_type.verify_value(par_values[par].value), ast_element=value)
552
+ return blocks.ConstantValue(value_type, value_type.parse_value(value), ast_element=value)
553
+ # raise blocks.ProgramError(value, f'Cannot turn {value!r} into constant value of type {value_type}')
554
+
555
+ def resolve_block_type(self, name, par_values={}):
556
+ # print(self, 'resolve_block_type', name)
557
+
558
+ if name.length >= 2:
559
+ library = self.libraries.get(name.identifiers[0].id_str)
560
+ for identifier in name.identifiers[1:-1]:
561
+ if library is None:
562
+ break
563
+ library = library.libraries.get(identifier.id_str)
564
+ if library is not None:
565
+ block_name = name.identifiers[-1].id_str
566
+ if block_name in library.block_type_implementations:
567
+ return library.block_type_implementations[block_name]
568
+
569
+ if name.length == 1:
570
+ block_type_name_id = name.identifiers[0]
571
+
572
+ if block_type_name_id.id_str in self.block_type_implementations:
573
+ return self.block_type_implementations[block_type_name_id.id_str]
574
+
575
+ if self.parent_graph is None:
576
+ raise blocks.ProgramError(name, f'Cannot find block type {name}')
577
+ return self.parent_graph.resolve_block_type(name, par_values=par_values)
578
+
579
+
580
+ class DeclarationConnector(Declaration):
581
+ @check_parameters
582
+ def __init__(self, name: typing.Optional[Identifier], conn_type_spec: BlockConnectorTypeSpecifier, conn_spec: ConnectorSpecification, *, source_position: SourcePosition):
583
+ super().__init__(source_position=source_position)
584
+ self.name = name
585
+ self.conn_type_spec = conn_type_spec
586
+ self.conn_spec = conn_spec
587
+
588
+ # TODO: Remove?
589
+ self.list_spec = self.conn_spec.list_spec
590
+ if isinstance(self.conn_spec, ConnectorSpecificationData):
591
+ self.ty = self.conn_spec.ty
592
+ self.kind_spec = self.conn_spec.kind_spec
593
+
594
+ self.kind = self.kind_spec.kind
595
+
596
+ self.conn_type = self.conn_type_spec.conn_type
597
+
598
+ if self.list_spec.is_list and self.conn_type != BlockConnectorType.BlockInput:
599
+ raise ProgramError(self, f'Got list specifier for non-input')
600
+
601
+ if self.name is None:
602
+ if self.conn_type == BlockConnectorType.BlockInput:
603
+ self.name_str = 'implicit input'
604
+ elif self.conn_type == BlockConnectorType.BlockOutput:
605
+ self.name_str = 'implicit output'
606
+ else:
607
+ raise Exception(f'Unknown value for conn_type: {self.conn_type}')
608
+ else:
609
+ self.name_str = self.name.id_str
610
+
611
+ @staticmethod
612
+ @check_parameters
613
+ def create(conn_type_spec: BlockConnectorTypeSpecifier, name: Identifier, conn_spec : ConnectorSpecification, *, source_position: SourcePosition):
614
+ return DeclarationConnector(name, conn_spec=conn_spec, conn_type_spec=conn_type_spec, source_position=source_position)
615
+
616
+ @staticmethod
617
+ @check_parameters
618
+ def create_implicit(conn_type_spec: BlockConnectorTypeSpecifier, conn_spec : ConnectorSpecification, *, source_position: SourcePosition):
619
+ return DeclarationConnector(None, conn_spec=conn_spec, conn_type_spec=conn_type_spec, source_position=source_position)
620
+
621
+ @check_parameters
622
+ def compile_pass1_btd(self, interface):
623
+ self.interface = interface
624
+
625
+ if self.interface.is_singleton and self.conn_type_spec.conn_type == BlockConnectorType.BlockInput:
626
+ raise blocks.ProgramError(self, f'Singleton block cannot have inputs')
627
+
628
+ def create_io(self, new_graph):
629
+ # TODO: Use self.kind ConnectionKind
630
+ # TODO: Pass par_values to resolve_type?
631
+ # TODO: Should the entires conn_spec instance be passed?
632
+ if isinstance(self.conn_spec, ConnectorSpecificationData):
633
+ ty = self.interface.resolve_type(self.ty)
634
+ else:
635
+ ty = None
636
+ if self.conn_type == BlockConnectorType.BlockInput:
637
+ name = self.name_str
638
+ if name == '':
639
+ name = 'implicit input'
640
+ blocks.BlockInput(new_graph, ty, name=name, input_name=self.name_str, is_list=self.list_spec.is_list, conn_spec=self.conn_spec)
641
+ elif self.conn_type == BlockConnectorType.BlockOutput:
642
+ name = self.name_str
643
+ if name == '':
644
+ name = 'implicit output'
645
+ blocks.BlockOutput(new_graph, ty, name=name, output_name=self.name_str, conn_spec=self.conn_spec)
646
+ else:
647
+ raise Exception(f'Unknown conn_type: {self.conn_type!r}')
648
+
649
+ # TODO: Clean up compilation passes
650
+
651
+ def compile_pass1(self, graph):
652
+ pass
653
+
654
+ def compile_pass1_5(self, graph):
655
+ pass
656
+
657
+ def compile_pass2(self, graph):
658
+ pass
659
+
660
+
661
+ class DeclarationParameter(Declaration):
662
+ @check_parameters
663
+ def __init__(self, optional_specifier: typing.Optional[OptionalSpecifier], name: Identifier, ty, *, default_value=None, source_position: SourcePosition):
664
+ super().__init__(source_position=source_position)
665
+ self.name = name
666
+ self.ty = ty
667
+ self.optional_specifier = optional_specifier
668
+ self.default_value = default_value
669
+
670
+ if self.optional_specifier is not None and self.default_value is not None:
671
+ raise Exception('self.optional_specifier is not None and self.default_value is not None')
672
+
673
+ self.is_optional = self.optional_specifier is not None or self.default_value is not None
674
+
675
+ @staticmethod
676
+ def with_default_value(name: Identifier, ty, default_value, *, source_position: SourcePosition):
677
+ return DeclarationParameter(optional_specifier=None, name=name, ty=ty, default_value=default_value, source_position=source_position)
678
+
679
+ @check_parameters
680
+ def compile_pass1_btd(self, interface):
681
+ self.interface = interface
682
+
683
+ def create_io(self, new_graph):
684
+ pass
685
+
686
+ def resolve_type(self):
687
+ if hasattr(self, 'type_resolved'):
688
+ raise Exception("hasattr(self, 'type_resolved')")
689
+
690
+ # TODO: Is this correct?
691
+ self.type_resolved = self.interface.resolve_type(self.ty)
692
+
693
+ def __repr__(self):
694
+ # return f'Parameter <{self.name}> [{self.name.source_position.start_line}] : <{self.ty}>'
695
+ if not hasattr(self, 'interface'):
696
+ # return f'Parameter <(unknown block).{self.name}> : <{self.ty}>'
697
+ return f'(unknown block).{self.name}'
698
+ else:
699
+ return f'{self.interface.name}.{self.name}'
700
+
701
+ # TODO: Clean up compilation passes
702
+
703
+ def compile_pass1(self, graph):
704
+ pass
705
+
706
+ def compile_pass1_5(self, graph):
707
+ pass
708
+
709
+ def compile_pass2(self, graph):
710
+ pass
711
+
712
+
713
+ class BlockParameter(SourceElement):
714
+ @check_parameters
715
+ def __init__(self, name: Identifier, value: Value, *, source_position: SourcePosition):
716
+ super().__init__(source_position=source_position)
717
+ self.name = name
718
+ self.value = value
719
+
720
+ def __repr__(self):
721
+ return f'Parameter <{self.name}> = <{self.value}>'
722
+ # return f'Parameter <{self.name}> [{self.name.source_position.start_line}] = <{self.value}>'
723
+
724
+
725
+ class BlockInstantiation(SourceElement):
726
+ @check_parameters
727
+ def __init__(self, block_class_name: Reference, parameters: typing.List[BlockParameter]=[], *, source_position: SourcePosition):
728
+ super().__init__(source_position=source_position)
729
+ self.block_class_name = block_class_name
730
+ self.parameters = parameters
731
+
732
+
733
+ class DeclarationBlockInstantiation(Declaration):
734
+ @check_parameters
735
+ def __init__(self, name: Identifier, instantiation: BlockInstantiation, *, source_position: SourcePosition):
736
+ super().__init__(source_position=source_position)
737
+ self.name = name
738
+ self.instantiation = instantiation
739
+
740
+ def compile_pass1(self, graph):
741
+ pass
742
+
743
+ def compile_pass1_5(self, graph):
744
+ name = self.name
745
+ inst = self.instantiation
746
+
747
+ self.block_name = name.id_str
748
+ block_arg = inst.parameters
749
+
750
+ self.block_type = graph.resolve_block_type(inst.block_class_name)
751
+ # print(self.block_name, self.block_type, block_arg)
752
+
753
+ # TODO: Is this needed?
754
+ # self.block_type.compile_pass1_5_if_needed()
755
+
756
+ self.par_values = {}
757
+ for parameter in block_arg:
758
+ key = parameter.name
759
+ value = parameter.value
760
+
761
+ if key.id_str not in self.block_type.parameters_by_name:
762
+ raise ProgramError(key, f'Unknown parameter for {self.block_type.name.id_str}: {key}')
763
+ par = self.block_type.parameters_by_name[key.id_str]
764
+ par_type = par.type_resolved.resolve_self(self.par_values)
765
+ # TODO: Is passing par_values=self.par_values here the correct thing to do?
766
+ if par_type == blocks.types.ValueType:
767
+ ty = graph.resolve_type(parameter.value, par_values=self.par_values)
768
+ self.par_values[par] = ty
769
+ else:
770
+ val = graph.get_const_value(value, par_type, par_values=self.par_values)
771
+ self.par_values[par] = val
772
+
773
+ def compile_pass2(self, graph):
774
+ block = self.block_type.create(graph=graph, par_values=self.par_values, name=self.block_name, instantiation=self)
775
+
776
+ # TODO: Clean up compilation passes
777
+
778
+ def compile_pass1_btd(self, interface):
779
+ pass
780
+
781
+ def create_io(self, new_graph):
782
+ pass
783
+
784
+
785
+ class DeclarationConnection(Declaration):
786
+ @check_parameters
787
+ def __init__(self, sources: typing.List[Reference], target: Reference, *, source_position: SourcePosition):
788
+ super().__init__(source_position=source_position)
789
+ self.sources = sources
790
+ self.target = target
791
+
792
+ def compile_pass1(self, graph):
793
+ pass
794
+
795
+ def compile_pass1_5(self, graph):
796
+ pass
797
+
798
+ def compile_pass2(self, graph):
799
+ sources = self.sources
800
+ target = self.target
801
+
802
+ # print(source_block_name, source_output_name, target_block_name, target_input_name)
803
+ res_sources = [source.resolve(graph, RefType.BlockOutput, 'source') for source in sources]
804
+
805
+ res_target = target.resolve(graph, RefType.BlockInput, 'target')
806
+
807
+ # TODO: Disallow multiple connect statements to a list input?
808
+ with self.context:
809
+ for source in res_sources:
810
+ blocks.connect(source.member, res_target.member, self)
811
+
812
+ # TODO: Clean up compilation passes
813
+
814
+ def compile_pass1_btd(self, interface):
815
+ pass
816
+
817
+ def create_io(self, new_graph):
818
+ pass
819
+
820
+
821
+ class DeclarationStateVariable(Declaration):
822
+ @check_parameters
823
+ def __init__(self, name: Identifier, var_type: Value, initial_value: Value, *, source_position: SourcePosition):
824
+ super().__init__(source_position=source_position)
825
+ self.name = name
826
+ self.var_type = var_type
827
+ self.initial_value = initial_value
828
+
829
+ self.name_str = self.name.id_str
830
+
831
+ def compile_pass1(self, graph):
832
+ name = self.name.id_str
833
+ if name in graph.variables:
834
+ hints = [
835
+ CompilerHint(graph.variables[name], 'Previous variable was here'),
836
+ ]
837
+ raise ProgramError(self, f'Duplicate variable name {name} in {graph}', hints=hints)
838
+ graph.variables[name] = self
839
+
840
+ def compile_pass1_5(self, graph):
841
+ pass
842
+
843
+ def compile_pass2(self, graph):
844
+ pass
845
+
846
+ # TODO: Clean up compilation passes
847
+
848
+ def compile_pass1_btd(self, interface):
849
+ self.interface = interface
850
+
851
+ def create_io(self, new_graph):
852
+ ty = self.interface.resolve_type(self.var_type)
853
+ # TODO: Resolve initial_value
854
+ blocks.BlockStateVariable(new_graph, ty, name=self.name_str, initial_value=self.initial_value)
855
+
856
+
857
+ class DeclarationTemporaryVariable(Declaration):
858
+ @check_parameters
859
+ def __init__(self, name: Identifier, var_type: Reference, *, source_position: SourcePosition):
860
+ super().__init__(source_position=source_position)
861
+ self.name = name
862
+ self.var_type = var_type
863
+
864
+ self.name_str = self.name.id_str
865
+
866
+ def compile_pass1(self, graph):
867
+ name = self.name.id_str
868
+ if name in graph.variables:
869
+ hints = [
870
+ CompilerHint(graph.variables[name], 'Previous variable was here'),
871
+ ]
872
+ raise ProgramError(self, f'Duplicate variable name {name} in {graph}', hints=hints)
873
+ graph.variables[name] = self
874
+
875
+ def compile_pass1_5(self, graph):
876
+ pass
877
+
878
+ def compile_pass2(self, graph):
879
+ pass
880
+
881
+ # TODO: Clean up compilation passes
882
+
883
+ def compile_pass1_btd(self, interface):
884
+ self.interface = interface
885
+
886
+ def create_io(self, new_graph):
887
+ ty = self.interface.resolve_type(self.var_type)
888
+ blocks.BlockTemporaryVariable(new_graph, ty, name=self.name_str)
889
+
890
+
891
+ class StageDeclaration(SourceElement):
892
+ @check_parameters
893
+ def __init__(self, stage: RunStage, source_position: SourcePosition):
894
+ super().__init__(source_position=source_position)
895
+ self.stage = stage
896
+
897
+ @staticmethod
898
+ def create_input(*, source_position: SourcePosition):
899
+ return StageDeclaration(stage=RunStage.Input, source_position=source_position)
900
+
901
+ @staticmethod
902
+ def create_default(*, source_position: SourcePosition):
903
+ return StageDeclaration(stage=RunStage.Default, source_position=source_position)
904
+
905
+ @staticmethod
906
+ def create_output(*, source_position: SourcePosition):
907
+ return StageDeclaration(stage=RunStage.Output, source_position=source_position)
908
+
909
+
910
+ class DependsDeclaration(SourceElement):
911
+ # TODO: Allow dependencies on the old and/or new values of state variables
912
+ # Should dependencies on old state variables be gathered automatically?
913
+ # (Similar to a dependency on the current clock value, i.e. that the dependency is recorded while running, because it cannot affect execution order.)
914
+ # Then dependencies on the new values of state variables would not need a '.new', only when using the new/old values of state variables this would have to be distinguished somehow.
915
+ # Or: In "depends on" and "produces" .new is implicit, for "connect" and using in procedures ".new" and ".old" is needed.
916
+
917
+ @check_parameters
918
+ def __init__(self, *dependencies: typing.List[Reference], source_position: SourcePosition):
919
+ super().__init__(source_position=source_position)
920
+ self.dependencies = dependencies
921
+
922
+ self.resolved_dependencies_value = None
923
+ self.resolved_dependencies_value_in_process = False
924
+
925
+ def compile_pass1(self, graph, proc):
926
+ self.graph = graph
927
+ self.proc = proc
928
+
929
+ # Trigger resolution of dependencies and return them
930
+ def get_resolved_dependencies(self):
931
+ if self.resolved_dependencies_value is not None:
932
+ return self.resolved_dependencies_value
933
+ if self.resolved_dependencies_value_in_process:
934
+ raise Exception('self.resolved_dependencies_value_in_process')
935
+ self.resolved_dependencies_value_in_process = True
936
+
937
+ resolved = []
938
+ for dep in self.dependencies:
939
+ if dep.length != 1:
940
+ raise ProgramError(dep, f'Dependency does not have exactly one component')
941
+ identifier = dep.identifiers[0]
942
+ name = identifier.id_str
943
+ # TODO: Should not be done using blocks
944
+ if name in self.graph.blocks:
945
+ res = self.graph.blocks[name]
946
+ elif name in self.graph.variables:
947
+ # TODO: Allow state variables here?
948
+ res = self.graph.variables[name]
949
+ elif name in self.graph.block_inputs:
950
+ res = self.graph.block_inputs[name]
951
+ else:
952
+ raise ProgramError(dep, f'Could not find dependency')
953
+ resolved.append(res)
954
+
955
+ self.resolved_dependencies_value = resolved
956
+ return self.resolved_dependencies_value
957
+
958
+ def compile_pass2(self, graph, proc):
959
+ # Make sure get_resolved_dependencies() is executed at some point
960
+ self.get_resolved_dependencies()
961
+
962
+
963
+ class ProducesDeclaration(SourceElement):
964
+ @check_parameters
965
+ def __init__(self, *outputs: typing.List[Reference], source_position: SourcePosition):
966
+ super().__init__(source_position=source_position)
967
+ self.outputs = outputs
968
+
969
+ self.resolved_outputs_value = None
970
+ self.resolved_outputs_value_in_process = False
971
+
972
+ def compile_pass1(self, graph, proc):
973
+ self.graph = graph
974
+ self.proc = proc
975
+
976
+ def get_resolved_outputs(self):
977
+ if self.resolved_outputs_value is not None:
978
+ return self.resolved_outputs_value
979
+ if self.resolved_outputs_value_in_process:
980
+ raise Exception('self.resolved_outputs_value_in_process')
981
+ self.resolved_outputs_value_in_process = True
982
+
983
+ resolved = []
984
+ for out in self.outputs:
985
+ if out.length != 1:
986
+ raise ProgramError(out, f'Output does not have exactly one component')
987
+ identifier = out.identifiers[0]
988
+ name = identifier.id_str
989
+ # TODO: Should not be done using blocks
990
+ if name in self.graph.blocks:
991
+ res = self.graph.blocks[name]
992
+ elif name in self.graph.variables:
993
+ res = self.graph.variables[name]
994
+ elif name in self.graph.block_outputs:
995
+ res = self.graph.block_outputs[name]
996
+ else:
997
+ raise ProgramError(out, f'Could not find output')
998
+ resolved.append(res)
999
+
1000
+ self.resolved_outputs_value = resolved
1001
+ return self.resolved_outputs_value
1002
+
1003
+ def compile_pass2(self, graph, proc):
1004
+ # Make sure get_resolved_outputs() is executed at some point
1005
+ self.get_resolved_outputs()
1006
+
1007
+
1008
+ class DeclarationProcedure(Declaration):
1009
+ @check_parameters
1010
+ def __init__(self, native_spec: typing.Optional[NativeSpecifier], name: Identifier, stage: StageDeclaration, depends: DependsDeclaration, produces: ProducesDeclaration, *, source_position: SourcePosition):
1011
+ super().__init__(source_position=source_position)
1012
+ self.is_native = native_spec is not None
1013
+ self.name = name
1014
+ self.stage = stage
1015
+ self.depends = depends
1016
+ self.produces = produces
1017
+
1018
+ if self.stage.stage == RunStage.Input and len(self.depends.dependencies) > 0:
1019
+ # TODO: Allow this?
1020
+ hints = [
1021
+ CompilerHint(self.depends, 'Dependencies declared here'),
1022
+ ]
1023
+ raise ProgramError(self.stage, f'Procedure name {self.name} is declared as input stage but depends on something else', hints=hints)
1024
+
1025
+ # TODO: Allow this? In which cases? Does this need checks that the outputs are not used in earlier stages?
1026
+ # Currently this is used to update a state variable after writing to a CSV file. The new value for the state variable might depend on the result (success or not) of writing to the file. Probably at least updates to state variables should be allowed in the output stage.
1027
+ # Disallowing this would make it possible to save the state variables before the output stage (and therefore before any results of the current step become externally visible). Or state variables could be saved twice, once before and once after the output stage.
1028
+ # if self.stage.stage == RunStage.Output and len(self.produces.outputs) > 0:
1029
+ # hints = [
1030
+ # CompilerHint(self.depends, 'Output declared here'),
1031
+ # ]
1032
+ # raise ProgramError(self.stage, f'Procedure name {self.name} is declared as output stage but produces an output', hints=hints)
1033
+
1034
+ def compile_pass1(self, graph):
1035
+ name = self.name.id_str
1036
+ if name in graph.procedures:
1037
+ hints = [
1038
+ CompilerHint(graph.procedures[name], 'Previous procedure was here'),
1039
+ ]
1040
+ raise ProgramError(self, f'Duplicate procedure name {name} in {graph}', hints=hints)
1041
+ graph.procedures[name] = self
1042
+
1043
+ self.depends.compile_pass1(graph, self)
1044
+ self.produces.compile_pass1(graph, self)
1045
+
1046
+ def compile_pass1_5(self, graph):
1047
+ pass
1048
+
1049
+ def compile_pass2(self, graph):
1050
+ self.depends.compile_pass2(graph, self)
1051
+ self.produces.compile_pass2(graph, self)
1052
+
1053
+ # TODO: Clean up compilation passes
1054
+
1055
+ def compile_pass1_btd(self, interface):
1056
+ pass
1057
+
1058
+ def create_io(self, new_graph):
1059
+ pass
1060
+
1061
+
1062
+ # TODO: Should this to some extend be merged with BlockType? Should there be a common base class?
1063
+ class DeclarationLibrary(Scope):
1064
+ @check_parameters
1065
+ def __init__(self, name: Identifier, declarations: typing.List[Declaration], *, source_position: SourcePosition):
1066
+ super().__init__(source_position=source_position)
1067
+ self.name = name
1068
+ self.declarations = declarations
1069
+
1070
+ # TODO?
1071
+ self.libraries = {}
1072
+
1073
+ @staticmethod
1074
+ def make(*args, source_position: SourcePosition):
1075
+ names = args[:-1]
1076
+ declarations = args[-1]
1077
+ for name in reversed(names):
1078
+ declarations = [DeclarationLibrary(
1079
+ name, declarations, source_position=source_position)]
1080
+ return declarations[0]
1081
+
1082
+ @property
1083
+ def fullname(self):
1084
+ if isinstance(self.parent_graph, DeclarationLibrary):
1085
+ return self.parent_graph.fullname + '.' + self.name.id_str
1086
+ return self.name.id_str
1087
+
1088
+ @property
1089
+ def fullname_tuple(self):
1090
+ if isinstance(self.parent_graph, DeclarationLibrary):
1091
+ return self.parent_graph.fullname_tuple + (self.name.id_str,)
1092
+ return (self.name.id_str,)
1093
+
1094
+ def compile_pass1(self, graph):
1095
+ if hasattr(self, 'parent_graph'):
1096
+ raise Exception("hasattr(self, 'parent_graph')")
1097
+ self.parent_graph = graph
1098
+ # TODO: What should be allowed here?
1099
+ if not isinstance(graph, DeclarationLibrary) and graph.name is not None:
1100
+ raise ProgramError(self, 'Attempt to put namespace into invalid context')
1101
+
1102
+ self.compiled_scope = graph.libraries.setdefault(self.name.id_str, self)
1103
+
1104
+ for decl in self.declarations:
1105
+ with decl.context:
1106
+ # TODO: What is allowed here?
1107
+ if not isinstance(decl, (blocks.BlockType, DeclarationLibrary)):
1108
+ hints = [
1109
+ CompilerHint(self, 'In this namespace'),
1110
+ ]
1111
+ raise ProgramError(decl, 'Declaration not allowed in namespace', hints=hints)
1112
+
1113
+ decl.compile_pass1(self.compiled_scope)
1114
+
1115
+
1116
+ def compile_pass1_5(self, graph):
1117
+ for decl in self.declarations:
1118
+ with decl.context:
1119
+ decl.compile_pass1_5(self.compiled_scope)
1120
+
1121
+ def compile_pass2(self, graph):
1122
+ for decl in self.declarations:
1123
+ with decl.context:
1124
+ decl.compile_pass2(self.compiled_scope)
1125
+
1126
+ # TODO: Clean up compilation passes
1127
+
1128
+ def compile_pass1_btd(self, interface):
1129
+ pass
1130
+
1131
+ def create_io(self, new_graph):
1132
+ pass
1133
+
1134
+ def __repr__(self):
1135
+ return self.fullname