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/block_type.py ADDED
@@ -0,0 +1,342 @@
1
+ import typing
2
+
3
+ import wirely as blocks
4
+ from wirely.util import check_parameters
5
+
6
+
7
+ class BlockType(blocks.Scope):
8
+ @check_parameters
9
+ def __init__(self, specifiers: typing.List[blocks.BlockTypeSpecifier], name: typing.Optional[blocks.Identifier], declarations: typing.List[blocks.Declaration], *, source_position: typing.Optional[blocks.SourcePosition]):
10
+ super().__init__(source_position=source_position)
11
+ self.name = name
12
+ self.declarations = declarations
13
+
14
+ all_specifiers = ['native', 'singleton']
15
+ specifiers_status = dict({key: False for key in all_specifiers})
16
+ for specifier in specifiers:
17
+ if specifier.specifier_str not in all_specifiers:
18
+ raise Exception(f'Unknown specifier: {specifier.specifier_str!r}')
19
+ if specifiers_status[specifier.specifier_str]:
20
+ raise blocks.ProgramError(specifier, f'Got duplicate {specifier.specifier_str} specifier')
21
+ specifiers_status[specifier.specifier_str] = True
22
+ self.is_native = specifiers_status['native']
23
+ self.is_singleton = specifiers_status['singleton']
24
+
25
+ self.blocks = {}
26
+ # TODO: Remove one of block_type_interfaces / block_type_implementations
27
+ self.block_type_interfaces = {}
28
+ self.block_type_implementations = {}
29
+ self.procedures = {}
30
+ self.variables = {}
31
+ # TODO: Clarify what is the key here (name or input_name)
32
+ self.block_inputs = {}
33
+ self.block_outputs = {}
34
+ self.block_state_variables = {}
35
+ self.block_temporary_variables = {}
36
+ self.libraries = {}
37
+
38
+ @staticmethod
39
+ def make(*args, source_position: typing.Optional[blocks.SourcePosition]):
40
+ specifiers = args[:-2]
41
+ name = args[-2]
42
+ declarations = args[-1]
43
+ return BlockType(list(specifiers), name, declarations, source_position=source_position)
44
+
45
+ def compile_pass1(self, graph):
46
+ if hasattr(self, 'parent_graph'):
47
+ raise Exception("hasattr(self, 'parent_graph')")
48
+ self.parent_graph = graph
49
+
50
+ if self.parent_graph is not None:
51
+ if self.name.id_str in graph.block_type_interfaces:
52
+ hints = [
53
+ blocks.CompilerHint(graph.block_type_interfaces[self.name.id_str], 'Previous block type was here'),
54
+ ]
55
+ raise blocks.ProgramError(self, f'Duplicate block type {self.name!r}', hints=hints)
56
+ graph.block_type_interfaces[self.name.id_str] = self
57
+
58
+ # TODO: Merge parsed_parameters and parameters?
59
+ parsed_parameters = {}
60
+ for decl in self.declarations:
61
+ decl.compile_pass1_btd(self)
62
+
63
+ # TODO: Clean up isinstance?
64
+ if not isinstance(decl, blocks.DeclarationParameter):
65
+ continue
66
+ with decl.context:
67
+ if self.is_singleton:
68
+ # TODO: Allow parameters for singleton blocks and create one instance for each parameter combination?
69
+ raise blocks.ProgramError(decl, f'Singleton block cannot have parameters')
70
+
71
+ name = decl.name.id_str
72
+ if name in parsed_parameters:
73
+ hints = [
74
+ blocks.CompilerHint(parsed_parameters[name], 'Previous parameter was here'),
75
+ ]
76
+ raise blocks.ProgramError(decl, f'Duplicate parameter name {name!r}', hints=hints)
77
+ parsed_parameters[name] = decl
78
+ if hasattr(self, 'parsed_parameters'):
79
+ raise Exception("hasattr(self, 'parsed_parameters')")
80
+ self.parsed_parameters = parsed_parameters
81
+
82
+ # Former "implementation" part
83
+
84
+ for decl in self.declarations:
85
+ if isinstance(decl, blocks.Declaration):
86
+ with decl.context:
87
+ decl.compile_pass1(self)
88
+
89
+ if self.parent_graph is not None:
90
+ if self.name.id_str in self.parent_graph.block_type_implementations:
91
+ hints = [
92
+ blocks.CompilerHint(graph.block_type_implementations[self.name.id_str], 'Previous implementation was here'),
93
+ ]
94
+ raise blocks.ProgramError(self, f'Duplicate block type implementation for {self.name!r}', hints=hints)
95
+ self.parent_graph.block_type_implementations[self.name.id_str] = self
96
+
97
+ def compile_pass1_5(self, graph):
98
+ self.compile_pass1_5_if_needed()
99
+
100
+ def compile_pass1_5_if_needed(self):
101
+ # TODO: Make this more generic, provide proper error messages on cycles
102
+ if hasattr(self, 'pass1_5_done'):
103
+ return
104
+ self.pass1_5_done = True
105
+
106
+ # Former "implementation" part
107
+
108
+ for decl in self.declarations:
109
+ with decl.context:
110
+ decl.create_io(self)
111
+
112
+ for decl in self.declarations:
113
+ if isinstance(decl, blocks.Declaration):
114
+ with decl.context:
115
+ decl.compile_pass1_5(self)
116
+
117
+ # Resolve Parameters
118
+ for par_name in self.parsed_parameters:
119
+ par = self.parsed_parameters[par_name]
120
+
121
+ par.resolve_type()
122
+
123
+ # TODO: Remove
124
+ self.parameters_by_name = self.parsed_parameters
125
+
126
+ def compile_pass2(self, graph):
127
+ for decl in self.declarations:
128
+ if isinstance(decl, blocks.Declaration):
129
+ with decl.context:
130
+ decl.compile_pass2(self)
131
+
132
+ # TODO: Allow interfaces without implementations?
133
+ # if self.name.id_str not in graph.block_type_implementations:
134
+ # raise blocks.ProgramError(self, f'Block type interface for {self.name!r} without implementation')
135
+
136
+ # TODO: Validate parameters
137
+
138
+ def create(self, *, name, graph, par_values, instantiation):
139
+ # Do this here to avoid problems with circular imports
140
+ import wirely.native
141
+
142
+ # print('create', name, graph, par_values)
143
+ block = blocks.Block(name=name, graph=graph, block_type=self, par_values=par_values, instantiation=instantiation)
144
+ # TODO: Remove
145
+ block.parameter_values = dict(par_values)
146
+
147
+ for par in self.parsed_parameters.values():
148
+ # print(par, par.is_optional)
149
+ if not par.is_optional and par not in block.parameter_values:
150
+ # TODO: Raise ProgramError and pass source location
151
+ raise Exception(f'Missing value for parameter {par.name!r}')
152
+
153
+ # TODO: Pass list of connections to constructor instead of getting them from self.graph
154
+ block.custom_inputs = {}
155
+ block.custom_outputs = {}
156
+ for input in self.block_inputs.values():
157
+ # TODO
158
+ # if not isinstance(input.conn_spec, blocks.ConnectorSpecificationData):
159
+ # continue
160
+ # TODO: Is the name of the block and the name of the input always the same?
161
+ new_input = blocks.Block.Input(block, input.input_name, input.is_list, input.var_type, block_input=input)
162
+ block.custom_inputs[input] = new_input
163
+ for output in self.block_outputs.values():
164
+ # TODO
165
+ # if not isinstance(output.conn_spec, blocks.ConnectorSpecificationData):
166
+ # continue
167
+ # TODO: Is the name of the block and the name of the output always the same?
168
+ new_output = blocks.Block.Output(block, output.output_name, output.var_type, block_output=output)
169
+ block.custom_outputs[output] = new_output
170
+
171
+ # TODO: Probably more stuff should be done at an earlier stage
172
+ state_variables = {}
173
+ for variable in self.variables.values():
174
+ if isinstance(variable, blocks.ast.DeclarationStateVariable):
175
+ var_type = self.resolve_type(variable.var_type, par_values=block.parameter_values)
176
+ initial_value = self.get_const_value(variable.initial_value, value_type=var_type, par_values=block.parameter_values)
177
+ # TODO: Can there be type parameters here?
178
+ initial_value = initial_value.value
179
+ block_var = blocks.Block.StateVariable(block, variable.name.id_str, var_type, initial_value)
180
+ if hasattr(var_type, 'block_var'):
181
+ raise Exception("hasattr(var_type, 'block_var')")
182
+ variable.block_var = block_var
183
+ state_variables[variable.name.id_str] = block_var
184
+ elif isinstance(variable, blocks.ast.DeclarationTemporaryVariable):
185
+ var_type = self.resolve_type(variable.var_type, par_values=block.parameter_values)
186
+ impl_var_type = var_type # TODO: ?
187
+ block_var = blocks.Block.Temporary(block, variable.name.id_str, var_type, impl_var_type=impl_var_type)
188
+ if hasattr(var_type, 'block_var'):
189
+ raise Exception("hasattr(var_type, 'block_var')")
190
+ variable.block_var = block_var
191
+ else:
192
+ raise Exception(f'Got unexpected variable: {variable}')
193
+
194
+ have_native = self.is_native
195
+ for procedure in self.procedures.values():
196
+ have_native = have_native or procedure.is_native
197
+ native_class = None
198
+ if have_native:
199
+ # TODO: Should this be done here? This will be called if 'self' is used in another block, even if that block is not instantiated. Probably looking up the native block type should be delayed until then to avoid loading the native module needed for that. Alternatively, the native class should be looked up even if 'self' is not used at all to do proper error checking.
200
+ native_class = wirely.native.get_native_block_type(self)
201
+ if self.is_native and not native_class.native_block_has_state:
202
+ # TODO: Allow this?
203
+ raise Exception(f'Block type {self.fullname!r} requires native state but native class is stateless')
204
+ if not self.is_native and native_class.native_block_has_state:
205
+ raise Exception(f'Block type {self.fullname!r} does not requires native state but native class is stateful')
206
+ block.native_class = native_class
207
+
208
+ # TODO: This is an ugly hack, native class resolution should be done per-type, not per-instance
209
+ if not hasattr(self, 'native_class'):
210
+ self.native_class = native_class
211
+
212
+ # TODO: Probably more stuff should be done at an earlier stage
213
+ for procedure in self.procedures.values():
214
+ # print(procedure, procedure.depends.get_resolved_dependencies(), procedure.produces.get_resolved_outputs())
215
+ sources = {}
216
+ targets = {}
217
+ inputs = []
218
+ procedures = {}
219
+ for dep in procedure.depends.get_resolved_dependencies():
220
+ # print(type(dep))
221
+ # TODO: Clean up
222
+ if isinstance(dep, blocks.DeclarationTemporaryVariable):
223
+ input = dep.block_var
224
+ inputs.append(input)
225
+ sources[dep.name.id_str] = input
226
+ elif isinstance(dep, blocks.BlockInput):
227
+ input = block.custom_inputs[dep]
228
+ inputs.append(input)
229
+ sources[dep.name] = input
230
+ elif isinstance(dep, blocks.Block):
231
+ # TODO: Clean up handling of implicit output of block, allow explicit output
232
+ input = dep.block_outputs_byname[''].temporary
233
+ inputs.append(input)
234
+ # TODO: What should be the name of this in sources?
235
+ sources[dep.name] = input
236
+ else:
237
+ raise Exception(f'Got unknown dependency: {dep}')
238
+ outputs = []
239
+ for out in procedure.produces.get_resolved_outputs():
240
+ # print(type(out))
241
+ if isinstance(out, blocks.ast.DeclarationStateVariable):
242
+ output = out.block_var
243
+ outputs.append(output)
244
+ targets[output.name] = output
245
+ elif isinstance(out, blocks.ast.DeclarationTemporaryVariable):
246
+ output = out.block_var
247
+ outputs.append(output)
248
+ targets[output.name] = output
249
+ elif isinstance(out, blocks.BlockOutput):
250
+ # TODO: Clean up
251
+ output = block.custom_outputs[out]
252
+ outputs.append(output)
253
+ targets[output.name] = output
254
+ # print(type(out.inp))
255
+ # print(out.inp)
256
+ # # outputs.append(out.inp)
257
+ # # targets[out.name] = out.inp
258
+ # temp = blocks.Block.Temporary(block, procedure.name.id_str + '__' + out.name, out.inp.var_type, impl_var_type=...)
259
+ # outputs.append(temp)
260
+ # targets[out.name] = temp
261
+ # # TODO: Remove this, connecting the different graphs should be done at instantiation time, not here
262
+ # # out.inp.connect(temp)
263
+ # # if out.inp.connected_source is not None:
264
+ # # raise Exception('out.inp.connected_source is not None')
265
+ # # out.inp.connected_source = temp
266
+ # # TODO: This is a hack to avoid a "Input graph1.Delay.implicit output. is not connected" exception, this should not be needed because graph1.Delay (without instantiation) should not be used for anything
267
+ # out.inp.connected_source = temp
268
+ else:
269
+ raise Exception(f'Unknown output: {out}')
270
+
271
+ static_context = blocks.StaticProcedureContext(sources=sources, targets=targets, state=state_variables, name=block.name, block_parameter_values={key.name.id_str: value for key, value in block.par_values.items()})
272
+
273
+ if not procedure.is_native:
274
+ raise Exception('Got non-native procedure')
275
+ else:
276
+ # TODO: Think about e.g. static stateful procedures
277
+ func = getattr(native_class, procedure.name.id_str, None)
278
+ if func is None:
279
+ raise Exception(f'Cannot find native procedure {procedure.name.id_str!r} in native block type {self.name!r}')
280
+ if self.is_native:
281
+ def run(*, context, native_state, func=func):
282
+ func(native_state, context=context)
283
+ else:
284
+ def run(*, context, native_state, func=func):
285
+ func(context=context)
286
+ # print('QQ', [type(x) for x in inputs], [type(x) for x in outputs])
287
+ block_procedure = blocks.Block.Procedure(block, inputs, outputs, run, name=procedure.name.id_str, static_context=static_context, declaration_procedure=procedure)
288
+ if procedure in procedures:
289
+ raise Exception('procedure in procedures')
290
+ procedures[procedure] = block_procedure
291
+
292
+ # print(block)
293
+ # print(block.block_inputs)
294
+ # print(block.block_outputs)
295
+ # print(block.all_block_inputs)
296
+
297
+ return block
298
+
299
+ def __repr__(self):
300
+ return self.fullname
301
+
302
+ @property
303
+ def fullname(self):
304
+ if self.name is None and self.parent_graph is None:
305
+ return '<global>'
306
+ elif self.parent_graph.name is None:
307
+ return self.name.id_str
308
+ else:
309
+ return self.parent_graph.fullname + '.' + self.name.id_str
310
+
311
+ @property
312
+ def fullname_tuple(self):
313
+ if self.name is None and self.parent_graph is None:
314
+ return ()
315
+ elif self.parent_graph.name is None:
316
+ return (self.name.id_str),
317
+ else:
318
+ return self.parent_graph.fullname_tuple + (self.name.id_str,)
319
+
320
+ # TODO: Clean up compilation passes
321
+
322
+ def compile_pass1_btd(self, interface):
323
+ pass
324
+
325
+ def create_io(self, new_graph):
326
+ pass
327
+
328
+ # TODO: Currently only used for singletons, clean this up, merge with block.create_native_state
329
+ def create_native_state(self, path, engine, static_block_context):
330
+ if len(path) != 1:
331
+ raise Exception('len(path) != 1')
332
+ if path[-1] != self:
333
+ raise Exception('path[-1] != self')
334
+
335
+ if self.is_native:
336
+ # print('QQQ', self.name)
337
+ return self.native_class(path, engine, static_block_context=static_block_context)
338
+ else:
339
+ # print('WWW', self.name)
340
+ pass
341
+
342
+ return None
wirely/blocks.lark ADDED
@@ -0,0 +1,126 @@
1
+ file_input: declaration*
2
+
3
+ declaration: "node" identifier ":" block_instantiation ";" -> declaration_block_instantiation
4
+ | "connect" reference_list "->" reference ";" -> declaration_connection
5
+ // TODO: Can this be done in a nicer way?
6
+ | block_type_specifier* "node" "type" identifier "{" declaration_list "}" -> block_type__make
7
+ | "namespace" identifier ("." identifier)* "{" declaration_list "}" -> declaration_library__make
8
+ | "state" identifier ":" value "=" value ";" -> declaration_state_variable
9
+ | "temporary" identifier ":" value ";" -> declaration_temporary_variable
10
+ | native_specifier "procedure" identifier stage_declaration depends_declaration produces_declaration ";" -> declaration_procedure
11
+ | block_connector_type identifier ":" connector_specification ";" -> declaration_connector__create
12
+ | "implicit" block_connector_type ":" connector_specification ";" -> declaration_connector__create_implicit
13
+ | [optional_specifier] "parameter" identifier ":" value ";" -> declaration_parameter
14
+ | "parameter" identifier ":" value "=" value ";" -> declaration_parameter__with_default_value
15
+ declaration_list: declaration* -> copy_list
16
+
17
+ block_instantiation: reference -> block_instantiation
18
+ | reference "(" block_parameter_list ")" -> block_instantiation
19
+
20
+ stage_declaration: -> stage_declaration__create_default
21
+ | "stage" "input" -> stage_declaration__create_input
22
+ | "stage" "output" -> stage_declaration__create_output
23
+
24
+ depends_declaration: -> depends_declaration
25
+ | "depends" "on" _separated_empty{reference, ","} -> depends_declaration
26
+
27
+ produces_declaration: -> produces_declaration
28
+ | "produces" _separated_empty{reference, ","} -> produces_declaration
29
+
30
+ block_parameter_list: _separated_empty{block_parameter, ","} -> copy_list
31
+
32
+ block_parameter: identifier "=" value
33
+
34
+ native_specifier: "native" -> native_specifier
35
+
36
+ optional_specifier: "optional" -> optional_specifier
37
+
38
+ block_type_specifier: native_specifier -> copy
39
+ | "singleton" -> block_type_specifier__create_singleton
40
+ block_type_specifier_list: block_type_specifier* -> copy_list
41
+
42
+ connector_specification: list_specifier connection_kind value -> connector_specification_data
43
+ // TODO: Make this more generic
44
+ | list_specifier "knx_bus" -> connector_specification_knx_bus
45
+ | list_specifier "mqtt_server" -> connector_specification_mqtt_server
46
+ | list_specifier "two_n_device" -> connector_specification_two_n_device
47
+
48
+ connection_kind: "value" -> connection_kind_specifier__create_value
49
+ | "event" -> connection_kind_specifier__create_event
50
+
51
+ block_connector_type: "input" -> block_connector_type_specifier__create_input
52
+ | "output" -> block_connector_type_specifier__create_output
53
+
54
+ list_specifier: "list" -> list_specifier__create_list
55
+ | () -> list_specifier__create_nolist
56
+
57
+ value: reference -> copy
58
+ | int_number -> copy
59
+ | float_number -> copy
60
+ | string_constant -> copy
61
+ | value "[" value "]" -> bracket_operator
62
+ | value "(" value_list ")" -> function_call
63
+ | "[" value_list "]" -> list_literal
64
+
65
+ value_list: _separated_empty{value, ","} -> copy_list
66
+
67
+ reference: identifier ("." identifier)* -> reference__from_list
68
+ | "implicit" block_connector_type -> reference__create_implicit
69
+ reference_list: _separated_empty{reference, ","} -> copy_list
70
+
71
+ int_number: DEC_NUMBER | HEX_NUMBER | OCT_NUMBER
72
+ float_number: FLOAT
73
+ string_constant: STRING | RAW_STRING
74
+ // Tokens
75
+
76
+ // COMMENT: /\/\/[^\n]*/
77
+ // | /\/\*([^\*]|\*[^\/])*\*\//
78
+
79
+ COMMENT: /\/\/[^\n]*/
80
+ %ignore COMMENT
81
+
82
+ // COMMENT_START, COMMENT_NEST, COMMENT_CONTENT and COMMENT_END are handled specially by the lexer
83
+ COMMENT_START: "/*"
84
+ // Similar to COMMENT_START, but allows mulitple slashes at the beginning
85
+ COMMENT_NEST: /\/+\*/
86
+ // A comment content consists of one or more of:
87
+ // - something which is neither * nor /
88
+ // - one or more * followed by something which is neither * nor /
89
+ // - one or more / followed by something which is neither * nor /
90
+ COMMENT_CONTENT: /([^\*\/]|\*+[^\*\/]|\/+[^\*\/])+/
91
+ // Note: Multiple stars are considered part of the comment end
92
+ COMMENT_END: /\*+\//
93
+
94
+ // %ignore is needed to prevent these terminals from being dropped as unused
95
+ %ignore COMMENT_START
96
+ %ignore COMMENT_NEST
97
+ %ignore COMMENT_CONTENT
98
+ %ignore COMMENT_END
99
+
100
+ // String: A quote, followed an arbitrary number of normal character (preceeded by an arbitrary number of backslashes) or quotes (preceeded by an odd number of backslashes), followed by a quote (preceeded by an even number of backslashes)
101
+ // https://doc.rust-lang.org/reference/tokens.html#r-lex.token.literal.str
102
+ STRING: /\"(\\*[^\"\\]|(\\\\)*\\\")*(\\\\)*\"/s
103
+ // https://doc.rust-lang.org/reference/tokens.html#r-lex.token.literal.str-raw
104
+ // TODO: Support raw strings with more than one #?
105
+ RAW_STRING.2: /r\"[^\"]*\"|r#\"(\"*[^\"#]|#)*\"*\"#/s
106
+
107
+ DEC_NUMBER: /[1-9]\d*l?/i
108
+ HEX_NUMBER: /0x[\da-f]*l?/i
109
+ OCT_NUMBER: /0o?[0-7]*l?/i
110
+ %import common.FLOAT -> FLOAT
111
+
112
+ %import common.CNAME
113
+ // https://doc.rust-lang.org/reference/identifiers.html
114
+ // https://doc.rust-lang.org/rust-by-example/compatibility/raw_identifiers.html
115
+ // https://lark-parser.readthedocs.io/en/latest/tree_construction.html#pinning-rule-terminals-with
116
+ !identifier: CNAME
117
+ | RAW_IDENTIFIER
118
+ // TODO: Which keywords should be "weak" keywords?
119
+ | "type"
120
+ | "value"
121
+ RAW_IDENTIFIER: "r#" CNAME
122
+
123
+ %ignore /[\t \f\r\n]+/
124
+
125
+ _separated_nonempty{x, sep}: x (sep x)*
126
+ _separated_empty{x, sep}: (x (sep x)*)?
wirely/compiler.py ADDED
@@ -0,0 +1,125 @@
1
+ import os
2
+ import sys
3
+ import traceback
4
+
5
+ import lark
6
+
7
+ import wirely as blocks
8
+ from wirely.util import check_parameters
9
+
10
+
11
+ def compile_strs(filename_inpudatas, *, verbose_passes=None, always_print_stacktrace=None):
12
+ # TODO: Where should this import be done?
13
+ import wirely.parser
14
+
15
+ if verbose_passes is None:
16
+ verbose_passes = blocks.util.getEnvironmentBool('WIRELY_COMPILER_VERBOSE_PASSES', False)
17
+ if always_print_stacktrace is None:
18
+ always_print_stacktrace = blocks.util.getEnvironmentBool('WIRELY_COMPILER_ALWAYS_PRINT_STACKTRACE', False)
19
+
20
+ exc = None
21
+ try:
22
+ decls = []
23
+ for filename, input_data in filename_inpudatas:
24
+ decls += blocks.parser.parse_str(input_data, filename=filename)
25
+ filename = None
26
+ global_source_position = blocks.SourcePosition(filename='<global>', meta=None, input_data='')
27
+ global_block_type = wirely.block_type.BlockType([], None, decls, source_position=global_source_position)
28
+ if verbose_passes:
29
+ print('Starting pass 1', flush=True)
30
+ global_block_type.compile_pass1(None)
31
+ if verbose_passes:
32
+ print('Starting pass 1.5', flush=True)
33
+ global_block_type.compile_pass1_5(None)
34
+ if verbose_passes:
35
+ print('Starting pass 2', flush=True)
36
+ global_block_type.compile_pass2(None)
37
+ if verbose_passes:
38
+ print('Finished all passes', flush=True)
39
+ # TODO: Clean up
40
+ # for decl in decls:
41
+ # with decl.context:
42
+ # decl.compile_pass1(graph)
43
+ # for decl in decls:
44
+ # with decl.context:
45
+ # decl.compile_pass1_5(graph)
46
+ # for decl in decls:
47
+ # with decl.context:
48
+ # decl.compile_pass2(graph)
49
+ except lark.UnexpectedInput as e:
50
+ print(f'Error in {filename}:{e.line}')
51
+ print(e)
52
+ # TODO: Using input_data here is kind of a hack
53
+ print(e.get_context(input_data))
54
+ return None
55
+ except lark.exceptions.VisitError as e:
56
+ exc = e.orig_exc
57
+ except blocks.ProgramError as e:
58
+ exc = e
59
+ except Exception as e:
60
+ exc = e
61
+
62
+ if exc is None:
63
+ return global_block_type
64
+
65
+ if not isinstance(exc, blocks.ProgramError):
66
+ exc = blocks.InternalCompilerError(None, exc)
67
+
68
+ if isinstance(exc, blocks.InternalCompilerError):
69
+ if exc.source_element is not None:
70
+ print(f'Internal compiler error while compiling line {exc.source_element.source_position.start_line}: {exc.inner_exception}')
71
+ else:
72
+ print(f'Internal compiler error: {exc.inner_exception}')
73
+ else:
74
+ print(f'Error in {exc.source_element.source_position.filename}:{exc.source_element.source_position.start_line}: {exc}')
75
+ if exc.source_element is not None:
76
+ print(exc.get_context())
77
+ if isinstance(exc, blocks.InternalCompilerError):
78
+ traceback.print_exception(exc.inner_exception)
79
+ elif always_print_stacktrace:
80
+ traceback.print_exception(exc)
81
+ for hint in exc.hints:
82
+ print(f'Hint: {hint.source_element.source_position.filename}:{hint.source_element.source_position.start_line}: {hint}')
83
+ print(hint.get_context())
84
+ return None
85
+
86
+
87
+ def compile_files(input_filenames, *, add_std=True):
88
+ if add_std:
89
+ std_lib = [
90
+ 'std',
91
+ 'time',
92
+ 'knx',
93
+ 'abb_jra',
94
+ 'mqtt',
95
+ 'csv',
96
+ 'two_n',
97
+ 'gpio',
98
+ 'memory',
99
+ 'ext_prog',
100
+ ]
101
+ input_filenames = [os.path.dirname(__file__) + '/lib/' + name + '.wrl' for name in std_lib] + input_filenames
102
+
103
+ filename_inpudatas = []
104
+ for input_filename in input_filenames:
105
+ with open(input_filename, 'r') as file:
106
+ filename_inpudatas.append((input_filename, file.read()))
107
+ return compile_strs(filename_inpudatas)
108
+
109
+
110
+ def run(args):
111
+ if args.create_engine and not args.create_program:
112
+ print('Error: --create-engine cannot be used with --no-create-program',
113
+ file=sys.stderr)
114
+ return 2
115
+
116
+ global_block_type = compile_files(args.input_filename)
117
+ if global_block_type is None:
118
+ return 1
119
+
120
+ if args.create_program:
121
+ prog = blocks.Program.compile(global_block_type)
122
+ if prog is None:
123
+ return 1
124
+ if args.create_engine:
125
+ blocks.Engine(prog)