Barmaja-PyArduino 2.0.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.
- Barmaja_PyArduino/BARMAJA_PyArduino.ico +0 -0
- Barmaja_PyArduino/__init__.py +528 -0
- Barmaja_PyArduino/__init__.pyi +331 -0
- Barmaja_PyArduino/__main__.py +2 -0
- Barmaja_PyArduino/adapters/__init__.py +35 -0
- Barmaja_PyArduino/api_registry.py +459 -0
- Barmaja_PyArduino/backend/__init__.py +3 -0
- Barmaja_PyArduino/backend/uno_cpp.py +1680 -0
- Barmaja_PyArduino/board.py +80 -0
- Barmaja_PyArduino/cli.py +223 -0
- Barmaja_PyArduino/compiler.py +1721 -0
- Barmaja_PyArduino/console.py +217 -0
- Barmaja_PyArduino/device_packs.py +367 -0
- Barmaja_PyArduino/device_packs_builtin/Button/arduino/Barmaja_Button/library.properties +9 -0
- Barmaja_PyArduino/device_packs_builtin/Button/arduino/Barmaja_Button/src/Barmaja_Button.h +18 -0
- Barmaja_PyArduino/device_packs_builtin/Button/example.py +10 -0
- Barmaja_PyArduino/device_packs_builtin/Button/pack.json +40 -0
- Barmaja_PyArduino/device_packs_builtin/ClockDS3231/arduino/Barmaja_ClockDS3231/library.properties +9 -0
- Barmaja_PyArduino/device_packs_builtin/ClockDS3231/arduino/Barmaja_ClockDS3231/src/Barmaja_ClockDS3231.h +20 -0
- Barmaja_PyArduino/device_packs_builtin/ClockDS3231/example.py +8 -0
- Barmaja_PyArduino/device_packs_builtin/ClockDS3231/pack.json +136 -0
- Barmaja_PyArduino/device_packs_builtin/DistanceVL53L0X/arduino/Barmaja_DistanceVL53L0X/library.properties +9 -0
- Barmaja_PyArduino/device_packs_builtin/DistanceVL53L0X/arduino/Barmaja_DistanceVL53L0X/src/Barmaja_DistanceVL53L0X.h +12 -0
- Barmaja_PyArduino/device_packs_builtin/DistanceVL53L0X/example.py +8 -0
- Barmaja_PyArduino/device_packs_builtin/DistanceVL53L0X/pack.json +48 -0
- Barmaja_PyArduino/device_packs_builtin/LCDParallel/arduino/Barmaja_LCDParallel/library.properties +9 -0
- Barmaja_PyArduino/device_packs_builtin/LCDParallel/arduino/Barmaja_LCDParallel/src/Barmaja_LCDParallel.h +17 -0
- Barmaja_PyArduino/device_packs_builtin/LCDParallel/example.py +10 -0
- Barmaja_PyArduino/device_packs_builtin/LCDParallel/pack.json +137 -0
- Barmaja_PyArduino/device_packs_builtin/LoadCellHX711/arduino/Barmaja_LoadCellHX711/library.properties +9 -0
- Barmaja_PyArduino/device_packs_builtin/LoadCellHX711/arduino/Barmaja_LoadCellHX711/src/Barmaja_LoadCellHX711.h +16 -0
- Barmaja_PyArduino/device_packs_builtin/LoadCellHX711/example.py +11 -0
- Barmaja_PyArduino/device_packs_builtin/LoadCellHX711/pack.json +74 -0
- Barmaja_PyArduino/device_packs_builtin/NeoPixelStrip/arduino/Barmaja_NeoPixelStrip/library.properties +9 -0
- Barmaja_PyArduino/device_packs_builtin/NeoPixelStrip/arduino/Barmaja_NeoPixelStrip/src/Barmaja_NeoPixelStrip.h +15 -0
- Barmaja_PyArduino/device_packs_builtin/NeoPixelStrip/example.py +13 -0
- Barmaja_PyArduino/device_packs_builtin/NeoPixelStrip/pack.json +114 -0
- Barmaja_PyArduino/device_packs_builtin/OLEDText/arduino/Barmaja_OLEDText/library.properties +9 -0
- Barmaja_PyArduino/device_packs_builtin/OLEDText/arduino/Barmaja_OLEDText/src/Barmaja_OLEDText.h +21 -0
- Barmaja_PyArduino/device_packs_builtin/OLEDText/example.py +9 -0
- Barmaja_PyArduino/device_packs_builtin/OLEDText/pack.json +103 -0
- Barmaja_PyArduino/device_packs_builtin/PressureBMP280/arduino/Barmaja_PressureBMP280/library.properties +9 -0
- Barmaja_PyArduino/device_packs_builtin/PressureBMP280/arduino/Barmaja_PressureBMP280/src/Barmaja_PressureBMP280.h +16 -0
- Barmaja_PyArduino/device_packs_builtin/PressureBMP280/example.py +8 -0
- Barmaja_PyArduino/device_packs_builtin/PressureBMP280/pack.json +92 -0
- Barmaja_PyArduino/device_packs_builtin/RotaryEncoder/arduino/Barmaja_RotaryEncoder/library.properties +9 -0
- Barmaja_PyArduino/device_packs_builtin/RotaryEncoder/arduino/Barmaja_RotaryEncoder/src/Barmaja_RotaryEncoder.h +18 -0
- Barmaja_PyArduino/device_packs_builtin/RotaryEncoder/example.py +10 -0
- Barmaja_PyArduino/device_packs_builtin/RotaryEncoder/pack.json +44 -0
- Barmaja_PyArduino/device_packs_builtin/ServoDriverPCA9685/arduino/Barmaja_ServoDriverPCA9685/library.properties +9 -0
- Barmaja_PyArduino/device_packs_builtin/ServoDriverPCA9685/arduino/Barmaja_ServoDriverPCA9685/src/Barmaja_ServoDriverPCA9685.h +15 -0
- Barmaja_PyArduino/device_packs_builtin/ServoDriverPCA9685/example.py +10 -0
- Barmaja_PyArduino/device_packs_builtin/ServoDriverPCA9685/pack.json +117 -0
- Barmaja_PyArduino/device_packs_builtin/ShiftRegister595/arduino/Barmaja_ShiftRegister595/library.properties +9 -0
- Barmaja_PyArduino/device_packs_builtin/ShiftRegister595/arduino/Barmaja_ShiftRegister595/src/Barmaja_ShiftRegister595.h +14 -0
- Barmaja_PyArduino/device_packs_builtin/ShiftRegister595/example.py +9 -0
- Barmaja_PyArduino/device_packs_builtin/ShiftRegister595/pack.json +83 -0
- Barmaja_PyArduino/device_packs_builtin/TemperatureDS18B20/arduino/Barmaja_TemperatureDS18B20/library.properties +9 -0
- Barmaja_PyArduino/device_packs_builtin/TemperatureDS18B20/arduino/Barmaja_TemperatureDS18B20/src/Barmaja_TemperatureDS18B20.h +16 -0
- Barmaja_PyArduino/device_packs_builtin/TemperatureDS18B20/example.py +8 -0
- Barmaja_PyArduino/device_packs_builtin/TemperatureDS18B20/pack.json +63 -0
- Barmaja_PyArduino/doctor.py +406 -0
- Barmaja_PyArduino/environment.py +595 -0
- Barmaja_PyArduino/errors.py +166 -0
- Barmaja_PyArduino/flow.py +754 -0
- Barmaja_PyArduino/frontend.py +611 -0
- Barmaja_PyArduino/installer.py +151 -0
- Barmaja_PyArduino/ir.py +660 -0
- Barmaja_PyArduino/library_adder.py +440 -0
- Barmaja_PyArduino/live.py +859 -0
- Barmaja_PyArduino/lowering.py +596 -0
- Barmaja_PyArduino/numeric.py +748 -0
- Barmaja_PyArduino/pack_builder.py +544 -0
- Barmaja_PyArduino/py.typed +0 -0
- Barmaja_PyArduino/serial_monitor.py +172 -0
- Barmaja_PyArduino/source.py +184 -0
- Barmaja_PyArduino/stubgen.py +148 -0
- Barmaja_PyArduino/symbols.py +646 -0
- Barmaja_PyArduino/target.py +1631 -0
- Barmaja_PyArduino/toolchain.py +506 -0
- Barmaja_PyArduino/typesys.py +917 -0
- Barmaja_PyArduino/workflow.py +213 -0
- barmaja_pyarduino-2.0.0.dist-info/METADATA +39 -0
- barmaja_pyarduino-2.0.0.dist-info/RECORD +87 -0
- barmaja_pyarduino-2.0.0.dist-info/WHEEL +5 -0
- barmaja_pyarduino-2.0.0.dist-info/licenses/LICENSE.txt +7 -0
- barmaja_pyarduino-2.0.0.dist-info/top_level.txt +1 -0
Barmaja_PyArduino/ir.py
ADDED
|
@@ -0,0 +1,660 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import ast
|
|
4
|
+
from dataclasses import dataclass, fields, is_dataclass
|
|
5
|
+
from enum import Enum
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from .errors import InternalCompilerError
|
|
9
|
+
from .flow import StorageClass
|
|
10
|
+
from .source import SourceSpan
|
|
11
|
+
from .symbols import SymbolId, SymbolKind
|
|
12
|
+
from .typesys import BarmajaType, TypeKind
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class IREffect(Enum):
|
|
16
|
+
"""Target-independent observable expression effects.
|
|
17
|
+
|
|
18
|
+
Effects are conservative. They exist so later target/backend phases never
|
|
19
|
+
need to rediscover whether evaluating an expression can touch hardware,
|
|
20
|
+
emit output, invoke user code, or fail a Barmaja runtime safety check.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
HARDWARE_IO = "hardware_io"
|
|
24
|
+
OUTPUT = "output"
|
|
25
|
+
USER_CALL = "user_call"
|
|
26
|
+
MAY_FAIL = "may_fail"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class IRUnaryOp(Enum):
|
|
30
|
+
NEGATE = "negate"
|
|
31
|
+
NOT = "not"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class IRBinaryOp(Enum):
|
|
35
|
+
ADD = "add"
|
|
36
|
+
SUBTRACT = "subtract"
|
|
37
|
+
MULTIPLY = "multiply"
|
|
38
|
+
TRUE_DIVIDE = "true_divide"
|
|
39
|
+
MODULO = "modulo"
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class IRCompareOp(Enum):
|
|
43
|
+
EQ = "eq"
|
|
44
|
+
NE = "ne"
|
|
45
|
+
LT = "lt"
|
|
46
|
+
LE = "le"
|
|
47
|
+
GT = "gt"
|
|
48
|
+
GE = "ge"
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class IRBoolOp(Enum):
|
|
52
|
+
AND = "and"
|
|
53
|
+
OR = "or"
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class IRCallKind(Enum):
|
|
57
|
+
USER_FUNCTION = "user_function"
|
|
58
|
+
API_FUNCTION = "api_function"
|
|
59
|
+
BUILTIN = "builtin"
|
|
60
|
+
OBJECT_METHOD = "object_method"
|
|
61
|
+
SINGLETON_METHOD = "singleton_method"
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class IRLoopControlTarget(Enum):
|
|
65
|
+
NESTED_LOOP = "nested_loop"
|
|
66
|
+
MAIN_LOOP = "main_loop"
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@dataclass(frozen=True)
|
|
70
|
+
class IRSymbolRef:
|
|
71
|
+
symbol_id: SymbolId
|
|
72
|
+
source_name: str
|
|
73
|
+
backend_name: str
|
|
74
|
+
kind: SymbolKind
|
|
75
|
+
type: BarmajaType
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
@dataclass(frozen=True, order=True)
|
|
79
|
+
class IRTempId:
|
|
80
|
+
value: int
|
|
81
|
+
|
|
82
|
+
def __str__(self) -> str:
|
|
83
|
+
return f"T{self.value:04d}"
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@dataclass(frozen=True)
|
|
87
|
+
class IRFunctionRef:
|
|
88
|
+
symbol_id: SymbolId
|
|
89
|
+
source_name: str
|
|
90
|
+
backend_name: str
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
@dataclass(frozen=True)
|
|
94
|
+
class IRTempRef:
|
|
95
|
+
temp_id: IRTempId
|
|
96
|
+
backend_name: str
|
|
97
|
+
type: BarmajaType
|
|
98
|
+
purpose: str
|
|
99
|
+
span: SourceSpan | None
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
@dataclass(frozen=True)
|
|
103
|
+
class IRExpr:
|
|
104
|
+
type: BarmajaType
|
|
105
|
+
span: SourceSpan | None
|
|
106
|
+
effects: frozenset[IREffect]
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
@dataclass(frozen=True)
|
|
110
|
+
class IRConstant(IRExpr):
|
|
111
|
+
value: bool | int | float | str
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
@dataclass(frozen=True)
|
|
115
|
+
class IRLoad(IRExpr):
|
|
116
|
+
symbol: IRSymbolRef
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
@dataclass(frozen=True)
|
|
120
|
+
class IRListLiteral(IRExpr):
|
|
121
|
+
elements: tuple[IRExpr, ...]
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
@dataclass(frozen=True)
|
|
125
|
+
class IRIndex(IRExpr):
|
|
126
|
+
collection: IRExpr
|
|
127
|
+
index: IRExpr
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
@dataclass(frozen=True)
|
|
131
|
+
class IRUnary(IRExpr):
|
|
132
|
+
op: IRUnaryOp
|
|
133
|
+
operand: IRExpr
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
@dataclass(frozen=True)
|
|
137
|
+
class IRBinary(IRExpr):
|
|
138
|
+
op: IRBinaryOp
|
|
139
|
+
left: IRExpr
|
|
140
|
+
right: IRExpr
|
|
141
|
+
checked: bool = False
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
@dataclass(frozen=True)
|
|
145
|
+
class IRCompareChain(IRExpr):
|
|
146
|
+
"""Normalized Python comparison chain.
|
|
147
|
+
|
|
148
|
+
``operand_temps`` are evaluated sequentially. Later operands are only
|
|
149
|
+
evaluated after the preceding comparison succeeds, preserving Python
|
|
150
|
+
short-circuit behavior while guaranteeing each reached operand is evaluated
|
|
151
|
+
exactly once.
|
|
152
|
+
"""
|
|
153
|
+
|
|
154
|
+
operands: tuple[IRExpr, ...]
|
|
155
|
+
operators: tuple[IRCompareOp, ...]
|
|
156
|
+
operand_temps: tuple[IRTempRef, ...]
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
@dataclass(frozen=True)
|
|
160
|
+
class IRBool(IRExpr):
|
|
161
|
+
op: IRBoolOp
|
|
162
|
+
values: tuple[IRExpr, ...]
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
@dataclass(frozen=True)
|
|
166
|
+
class IRConditional(IRExpr):
|
|
167
|
+
condition: IRExpr
|
|
168
|
+
when_true: IRExpr
|
|
169
|
+
when_false: IRExpr
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
@dataclass(frozen=True)
|
|
173
|
+
class IRCall(IRExpr):
|
|
174
|
+
kind: IRCallKind
|
|
175
|
+
name: str
|
|
176
|
+
args: tuple[IRExpr, ...]
|
|
177
|
+
function_symbol: IRFunctionRef | None = None
|
|
178
|
+
owner_symbol: IRSymbolRef | None = None
|
|
179
|
+
owner_name: str | None = None
|
|
180
|
+
method_name: str | None = None
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
@dataclass(frozen=True)
|
|
184
|
+
class IRFormattedValue:
|
|
185
|
+
value: IRExpr
|
|
186
|
+
precision: int | None
|
|
187
|
+
span: SourceSpan | None
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
@dataclass(frozen=True)
|
|
191
|
+
class IRFormatString(IRExpr):
|
|
192
|
+
parts: tuple[str | IRFormattedValue, ...]
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
@dataclass(frozen=True)
|
|
196
|
+
class IRStmt:
|
|
197
|
+
span: SourceSpan | None
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
@dataclass(frozen=True)
|
|
201
|
+
class IRStore(IRStmt):
|
|
202
|
+
target: IRSymbolRef
|
|
203
|
+
value: IRExpr
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
@dataclass(frozen=True)
|
|
207
|
+
class IRIndexStore(IRStmt):
|
|
208
|
+
collection: IRSymbolRef
|
|
209
|
+
index: IRExpr
|
|
210
|
+
value: IRExpr
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
@dataclass(frozen=True)
|
|
214
|
+
class IRAugStore(IRStmt):
|
|
215
|
+
target: IRSymbolRef
|
|
216
|
+
op: IRBinaryOp
|
|
217
|
+
value: IRExpr
|
|
218
|
+
checked: bool = False
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
@dataclass(frozen=True)
|
|
222
|
+
class IRExprStmt(IRStmt):
|
|
223
|
+
expression: IRExpr
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
@dataclass(frozen=True)
|
|
227
|
+
class IRPrint(IRStmt):
|
|
228
|
+
values: tuple[IRExpr, ...]
|
|
229
|
+
newline: bool = True
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
@dataclass(frozen=True)
|
|
233
|
+
class IRIf(IRStmt):
|
|
234
|
+
condition: IRExpr
|
|
235
|
+
body: tuple[IRStmt, ...]
|
|
236
|
+
otherwise: tuple[IRStmt, ...]
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
@dataclass(frozen=True)
|
|
240
|
+
class IRWhile(IRStmt):
|
|
241
|
+
condition: IRExpr
|
|
242
|
+
body: tuple[IRStmt, ...]
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
@dataclass(frozen=True)
|
|
246
|
+
class IRRangeLoop(IRStmt):
|
|
247
|
+
target: IRSymbolRef
|
|
248
|
+
iterator_temp: IRTempRef
|
|
249
|
+
start_temp: IRTempRef
|
|
250
|
+
stop_temp: IRTempRef
|
|
251
|
+
step_temp: IRTempRef
|
|
252
|
+
start: IRExpr
|
|
253
|
+
stop: IRExpr
|
|
254
|
+
step: IRExpr
|
|
255
|
+
body: tuple[IRStmt, ...]
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
@dataclass(frozen=True)
|
|
259
|
+
class IRFixedListLoop(IRStmt):
|
|
260
|
+
collection: IRSymbolRef
|
|
261
|
+
value_target: IRSymbolRef
|
|
262
|
+
index_target: IRSymbolRef | None
|
|
263
|
+
iterator_temp: IRTempRef
|
|
264
|
+
index_start: IRExpr | None
|
|
265
|
+
index_start_temp: IRTempRef | None
|
|
266
|
+
body: tuple[IRStmt, ...]
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
@dataclass(frozen=True)
|
|
270
|
+
class IRBreak(IRStmt):
|
|
271
|
+
target: IRLoopControlTarget
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
@dataclass(frozen=True)
|
|
275
|
+
class IRContinue(IRStmt):
|
|
276
|
+
target: IRLoopControlTarget
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
@dataclass(frozen=True)
|
|
280
|
+
class IRReturn(IRStmt):
|
|
281
|
+
value: IRExpr | None
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
@dataclass(frozen=True)
|
|
285
|
+
class IRObjectDecl:
|
|
286
|
+
symbol: IRSymbolRef
|
|
287
|
+
class_name: str
|
|
288
|
+
args: tuple[IRExpr, ...]
|
|
289
|
+
span: SourceSpan | None
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
@dataclass(frozen=True)
|
|
293
|
+
class IRStorageDecl:
|
|
294
|
+
symbol: IRSymbolRef
|
|
295
|
+
storage: StorageClass
|
|
296
|
+
initializer: IRExpr | None
|
|
297
|
+
span: SourceSpan | None
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
@dataclass(frozen=True)
|
|
301
|
+
class IRFunction:
|
|
302
|
+
symbol: IRFunctionRef
|
|
303
|
+
parameters: tuple[IRSymbolRef, ...]
|
|
304
|
+
return_type: BarmajaType
|
|
305
|
+
body: tuple[IRStmt, ...]
|
|
306
|
+
span: SourceSpan | None
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
@dataclass(frozen=True)
|
|
310
|
+
class IRModule:
|
|
311
|
+
source_name: str
|
|
312
|
+
storages: tuple[IRStorageDecl, ...]
|
|
313
|
+
objects: tuple[IRObjectDecl, ...]
|
|
314
|
+
setup: tuple[IRStmt, ...]
|
|
315
|
+
main_loop: tuple[IRStmt, ...]
|
|
316
|
+
functions: tuple[IRFunction, ...]
|
|
317
|
+
generated_temporaries: tuple[IRTempRef, ...]
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
IR_EXPRESSION_TYPES = (
|
|
321
|
+
IRConstant, IRLoad, IRListLiteral, IRIndex, IRUnary, IRBinary, IRCompareChain, IRBool,
|
|
322
|
+
IRConditional, IRCall, IRFormatString,
|
|
323
|
+
)
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
def _walk_values(value: Any):
|
|
327
|
+
yield value
|
|
328
|
+
if is_dataclass(value):
|
|
329
|
+
for f in fields(value):
|
|
330
|
+
yield from _walk_values(getattr(value, f.name))
|
|
331
|
+
elif isinstance(value, (tuple, list, frozenset)):
|
|
332
|
+
for item in value:
|
|
333
|
+
yield from _walk_values(item)
|
|
334
|
+
elif isinstance(value, dict):
|
|
335
|
+
for key, item in value.items():
|
|
336
|
+
yield from _walk_values(key)
|
|
337
|
+
yield from _walk_values(item)
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
def validate_ir(module: IRModule) -> None:
|
|
341
|
+
"""Assert the typed-IR boundary invariants.
|
|
342
|
+
|
|
343
|
+
Malformed IR is always a compiler defect, never a student diagnostic. The
|
|
344
|
+
validator therefore converts structural corruption into a stable
|
|
345
|
+
``InternalCompilerError`` before target/backend code can dereference bad
|
|
346
|
+
fields or silently emit incorrect C++.
|
|
347
|
+
"""
|
|
348
|
+
|
|
349
|
+
def fail(message: str) -> None:
|
|
350
|
+
raise InternalCompilerError(message)
|
|
351
|
+
|
|
352
|
+
def require_tuple(value: Any, cls: type, label: str) -> None:
|
|
353
|
+
if not isinstance(value, tuple) or not all(isinstance(item, cls) for item in value):
|
|
354
|
+
fail(f"Typed IR field '{label}' must be a tuple of {cls.__name__} values.")
|
|
355
|
+
|
|
356
|
+
def require_span(value: Any, label: str) -> None:
|
|
357
|
+
if not isinstance(value, SourceSpan):
|
|
358
|
+
fail(f"Typed IR field '{label}' is missing a valid source span.")
|
|
359
|
+
|
|
360
|
+
def validate_type(value: Any, label: str) -> None:
|
|
361
|
+
if not isinstance(value, BarmajaType) or not isinstance(value.kind, TypeKind):
|
|
362
|
+
fail(f"Typed IR field '{label}' does not contain a valid Barmaja type.")
|
|
363
|
+
if value.kind == TypeKind.UNKNOWN:
|
|
364
|
+
fail("Typed IR contains an unresolved Unknown type.")
|
|
365
|
+
if value.kind == TypeKind.HARDWARE:
|
|
366
|
+
if not isinstance(value.hardware_class, str) or not value.hardware_class:
|
|
367
|
+
fail(f"Typed IR field '{label}' contains an incomplete Hardware type.")
|
|
368
|
+
if value.kind == TypeKind.FIXED_LIST:
|
|
369
|
+
if not isinstance(value.element_type, BarmajaType) or not isinstance(value.length, int) or value.length <= 0:
|
|
370
|
+
fail(f"Typed IR field '{label}' contains an incomplete FixedList type.")
|
|
371
|
+
validate_type(value.element_type, f"{label}.element_type")
|
|
372
|
+
|
|
373
|
+
def validate_symbol_ref(value: Any, label: str) -> None:
|
|
374
|
+
if not isinstance(value, IRSymbolRef):
|
|
375
|
+
fail(f"Typed IR field '{label}' must contain a symbol reference.")
|
|
376
|
+
if not isinstance(value.symbol_id, SymbolId):
|
|
377
|
+
fail(f"Typed IR symbol in '{label}' has an invalid symbol ID.")
|
|
378
|
+
if not isinstance(value.source_name, str) or not value.source_name:
|
|
379
|
+
fail(f"Typed IR symbol in '{label}' has an invalid source name.")
|
|
380
|
+
if not isinstance(value.backend_name, str) or not value.backend_name:
|
|
381
|
+
fail(f"Typed IR symbol '{value.source_name}' has an invalid backend name.")
|
|
382
|
+
if not isinstance(value.kind, SymbolKind):
|
|
383
|
+
fail(f"Typed IR symbol '{value.source_name}' has an invalid symbol kind.")
|
|
384
|
+
validate_type(value.type, f"{label}.type")
|
|
385
|
+
|
|
386
|
+
def validate_function_ref(value: Any, label: str) -> None:
|
|
387
|
+
if not isinstance(value, IRFunctionRef):
|
|
388
|
+
fail(f"Typed IR field '{label}' must contain a function reference.")
|
|
389
|
+
if not isinstance(value.symbol_id, SymbolId):
|
|
390
|
+
fail(f"Typed IR function in '{label}' has an invalid symbol ID.")
|
|
391
|
+
if not isinstance(value.source_name, str) or not value.source_name:
|
|
392
|
+
fail(f"Typed IR function in '{label}' has an invalid source name.")
|
|
393
|
+
if not isinstance(value.backend_name, str) or not value.backend_name:
|
|
394
|
+
fail(f"Typed IR function '{value.source_name}' has an invalid backend name.")
|
|
395
|
+
|
|
396
|
+
def validate_temp_ref(value: Any, label: str) -> None:
|
|
397
|
+
if not isinstance(value, IRTempRef):
|
|
398
|
+
fail(f"Typed IR field '{label}' must contain a temporary reference.")
|
|
399
|
+
if not isinstance(value.temp_id, IRTempId) or not isinstance(value.temp_id.value, int):
|
|
400
|
+
fail(f"Typed IR temporary in '{label}' has an invalid temporary ID.")
|
|
401
|
+
if not isinstance(value.backend_name, str) or not value.backend_name:
|
|
402
|
+
fail(f"Typed IR temporary in '{label}' has an invalid backend name.")
|
|
403
|
+
validate_type(value.type, f"{label}.type")
|
|
404
|
+
if not isinstance(value.purpose, str) or not value.purpose:
|
|
405
|
+
fail(f"Typed IR temporary in '{label}' has an invalid purpose.")
|
|
406
|
+
require_span(value.span, f"{label}.span")
|
|
407
|
+
|
|
408
|
+
def validate_expr(value: Any, label: str = "expression") -> None:
|
|
409
|
+
if not isinstance(value, IRExpr):
|
|
410
|
+
fail(f"Typed IR field '{label}' must contain an IR expression.")
|
|
411
|
+
validate_type(value.type, f"{label}.type")
|
|
412
|
+
require_span(value.span, f"{label}.span")
|
|
413
|
+
if not isinstance(value.effects, frozenset) or not all(isinstance(effect, IREffect) for effect in value.effects):
|
|
414
|
+
fail(f"Typed IR field '{label}.effects' must be a frozenset of IREffect values.")
|
|
415
|
+
|
|
416
|
+
if isinstance(value, IRConstant):
|
|
417
|
+
if not isinstance(value.value, (bool, int, float, str)):
|
|
418
|
+
fail("Typed IR constant contains an unsupported value type.")
|
|
419
|
+
elif isinstance(value, IRLoad):
|
|
420
|
+
validate_symbol_ref(value.symbol, f"{label}.symbol")
|
|
421
|
+
elif isinstance(value, IRListLiteral):
|
|
422
|
+
require_tuple(value.elements, IRExpr, f"{label}.elements")
|
|
423
|
+
for i, item in enumerate(value.elements):
|
|
424
|
+
validate_expr(item, f"{label}.elements[{i}]")
|
|
425
|
+
elif isinstance(value, IRIndex):
|
|
426
|
+
validate_expr(value.collection, f"{label}.collection")
|
|
427
|
+
validate_expr(value.index, f"{label}.index")
|
|
428
|
+
elif isinstance(value, IRUnary):
|
|
429
|
+
if not isinstance(value.op, IRUnaryOp):
|
|
430
|
+
fail("Typed IR unary expression has an invalid operator.")
|
|
431
|
+
validate_expr(value.operand, f"{label}.operand")
|
|
432
|
+
elif isinstance(value, IRBinary):
|
|
433
|
+
if not isinstance(value.op, IRBinaryOp):
|
|
434
|
+
fail("Typed IR binary expression has an invalid operator.")
|
|
435
|
+
if not isinstance(value.checked, bool):
|
|
436
|
+
fail("Typed IR binary expression has an invalid checked flag.")
|
|
437
|
+
validate_expr(value.left, f"{label}.left")
|
|
438
|
+
validate_expr(value.right, f"{label}.right")
|
|
439
|
+
elif isinstance(value, IRCompareChain):
|
|
440
|
+
require_tuple(value.operands, IRExpr, f"{label}.operands")
|
|
441
|
+
require_tuple(value.operators, IRCompareOp, f"{label}.operators")
|
|
442
|
+
require_tuple(value.operand_temps, IRTempRef, f"{label}.operand_temps")
|
|
443
|
+
if len(value.operands) < 2 or len(value.operators) != len(value.operands) - 1:
|
|
444
|
+
fail("Typed IR comparison chain has inconsistent operand/operator arity.")
|
|
445
|
+
if len(value.operand_temps) != len(value.operands):
|
|
446
|
+
fail("Typed IR comparison chain has inconsistent temporary arity.")
|
|
447
|
+
for i, item in enumerate(value.operands):
|
|
448
|
+
validate_expr(item, f"{label}.operands[{i}]")
|
|
449
|
+
for i, temp in enumerate(value.operand_temps):
|
|
450
|
+
validate_temp_ref(temp, f"{label}.operand_temps[{i}]")
|
|
451
|
+
elif isinstance(value, IRBool):
|
|
452
|
+
if not isinstance(value.op, IRBoolOp):
|
|
453
|
+
fail("Typed IR boolean expression has an invalid operator.")
|
|
454
|
+
require_tuple(value.values, IRExpr, f"{label}.values")
|
|
455
|
+
if len(value.values) < 2:
|
|
456
|
+
fail("Typed IR boolean expression must contain at least two values.")
|
|
457
|
+
for i, item in enumerate(value.values):
|
|
458
|
+
validate_expr(item, f"{label}.values[{i}]")
|
|
459
|
+
elif isinstance(value, IRConditional):
|
|
460
|
+
validate_expr(value.condition, f"{label}.condition")
|
|
461
|
+
validate_expr(value.when_true, f"{label}.when_true")
|
|
462
|
+
validate_expr(value.when_false, f"{label}.when_false")
|
|
463
|
+
elif isinstance(value, IRCall):
|
|
464
|
+
if not isinstance(value.kind, IRCallKind):
|
|
465
|
+
fail("Typed IR call has an invalid call kind.")
|
|
466
|
+
if not isinstance(value.name, str) or not value.name:
|
|
467
|
+
fail("Typed IR call has an invalid name.")
|
|
468
|
+
require_tuple(value.args, IRExpr, f"{label}.args")
|
|
469
|
+
for i, arg in enumerate(value.args):
|
|
470
|
+
validate_expr(arg, f"{label}.args[{i}]")
|
|
471
|
+
if value.function_symbol is not None:
|
|
472
|
+
validate_function_ref(value.function_symbol, f"{label}.function_symbol")
|
|
473
|
+
if value.owner_symbol is not None:
|
|
474
|
+
validate_symbol_ref(value.owner_symbol, f"{label}.owner_symbol")
|
|
475
|
+
if value.owner_name is not None and not isinstance(value.owner_name, str):
|
|
476
|
+
fail("Typed IR call owner_name must be text when present.")
|
|
477
|
+
if value.method_name is not None and not isinstance(value.method_name, str):
|
|
478
|
+
fail("Typed IR call method_name must be text when present.")
|
|
479
|
+
elif isinstance(value, IRFormatString):
|
|
480
|
+
if not isinstance(value.parts, tuple) or not all(isinstance(part, (str, IRFormattedValue)) for part in value.parts):
|
|
481
|
+
fail("Typed IR format string contains an invalid part.")
|
|
482
|
+
for i, part in enumerate(value.parts):
|
|
483
|
+
if isinstance(part, IRFormattedValue):
|
|
484
|
+
validate_expr(part.value, f"{label}.parts[{i}].value")
|
|
485
|
+
if part.precision is not None and not isinstance(part.precision, int):
|
|
486
|
+
fail("Typed IR formatted value precision must be an integer or None.")
|
|
487
|
+
require_span(part.span, f"{label}.parts[{i}].span")
|
|
488
|
+
else:
|
|
489
|
+
fail(f"Unknown typed IR expression node {type(value).__name__}.")
|
|
490
|
+
|
|
491
|
+
def validate_stmt(value: Any, label: str = "statement") -> None:
|
|
492
|
+
if not isinstance(value, IRStmt):
|
|
493
|
+
fail(f"Typed IR field '{label}' must contain an IR statement.")
|
|
494
|
+
require_span(value.span, f"{label}.span")
|
|
495
|
+
|
|
496
|
+
if isinstance(value, IRStore):
|
|
497
|
+
validate_symbol_ref(value.target, f"{label}.target")
|
|
498
|
+
validate_expr(value.value, f"{label}.value")
|
|
499
|
+
elif isinstance(value, IRIndexStore):
|
|
500
|
+
validate_symbol_ref(value.collection, f"{label}.collection")
|
|
501
|
+
validate_expr(value.index, f"{label}.index")
|
|
502
|
+
validate_expr(value.value, f"{label}.value")
|
|
503
|
+
elif isinstance(value, IRAugStore):
|
|
504
|
+
validate_symbol_ref(value.target, f"{label}.target")
|
|
505
|
+
if not isinstance(value.op, IRBinaryOp) or not isinstance(value.checked, bool):
|
|
506
|
+
fail("Typed IR augmented store has an invalid operator/checked flag.")
|
|
507
|
+
validate_expr(value.value, f"{label}.value")
|
|
508
|
+
elif isinstance(value, IRExprStmt):
|
|
509
|
+
validate_expr(value.expression, f"{label}.expression")
|
|
510
|
+
elif isinstance(value, IRPrint):
|
|
511
|
+
require_tuple(value.values, IRExpr, f"{label}.values")
|
|
512
|
+
if not isinstance(value.newline, bool):
|
|
513
|
+
fail("Typed IR print newline flag must be boolean.")
|
|
514
|
+
for i, expr in enumerate(value.values):
|
|
515
|
+
validate_expr(expr, f"{label}.values[{i}]")
|
|
516
|
+
elif isinstance(value, IRIf):
|
|
517
|
+
validate_expr(value.condition, f"{label}.condition")
|
|
518
|
+
require_tuple(value.body, IRStmt, f"{label}.body")
|
|
519
|
+
require_tuple(value.otherwise, IRStmt, f"{label}.otherwise")
|
|
520
|
+
for i, stmt in enumerate(value.body):
|
|
521
|
+
validate_stmt(stmt, f"{label}.body[{i}]")
|
|
522
|
+
for i, stmt in enumerate(value.otherwise):
|
|
523
|
+
validate_stmt(stmt, f"{label}.otherwise[{i}]")
|
|
524
|
+
elif isinstance(value, IRWhile):
|
|
525
|
+
validate_expr(value.condition, f"{label}.condition")
|
|
526
|
+
require_tuple(value.body, IRStmt, f"{label}.body")
|
|
527
|
+
for i, stmt in enumerate(value.body):
|
|
528
|
+
validate_stmt(stmt, f"{label}.body[{i}]")
|
|
529
|
+
elif isinstance(value, IRRangeLoop):
|
|
530
|
+
validate_symbol_ref(value.target, f"{label}.target")
|
|
531
|
+
for name in ("iterator_temp", "start_temp", "stop_temp", "step_temp"):
|
|
532
|
+
validate_temp_ref(getattr(value, name), f"{label}.{name}")
|
|
533
|
+
validate_expr(value.start, f"{label}.start")
|
|
534
|
+
validate_expr(value.stop, f"{label}.stop")
|
|
535
|
+
validate_expr(value.step, f"{label}.step")
|
|
536
|
+
require_tuple(value.body, IRStmt, f"{label}.body")
|
|
537
|
+
for i, stmt in enumerate(value.body):
|
|
538
|
+
validate_stmt(stmt, f"{label}.body[{i}]")
|
|
539
|
+
elif isinstance(value, IRFixedListLoop):
|
|
540
|
+
validate_symbol_ref(value.collection, f"{label}.collection")
|
|
541
|
+
validate_symbol_ref(value.value_target, f"{label}.value_target")
|
|
542
|
+
if value.index_target is not None:
|
|
543
|
+
validate_symbol_ref(value.index_target, f"{label}.index_target")
|
|
544
|
+
validate_temp_ref(value.iterator_temp, f"{label}.iterator_temp")
|
|
545
|
+
if (value.index_start is None) != (value.index_start_temp is None):
|
|
546
|
+
fail("Typed IR fixed-list loop has inconsistent index-start fields.")
|
|
547
|
+
if value.index_start is not None:
|
|
548
|
+
validate_expr(value.index_start, f"{label}.index_start")
|
|
549
|
+
validate_temp_ref(value.index_start_temp, f"{label}.index_start_temp")
|
|
550
|
+
require_tuple(value.body, IRStmt, f"{label}.body")
|
|
551
|
+
for i, stmt in enumerate(value.body):
|
|
552
|
+
validate_stmt(stmt, f"{label}.body[{i}]")
|
|
553
|
+
elif isinstance(value, (IRBreak, IRContinue)):
|
|
554
|
+
if not isinstance(value.target, IRLoopControlTarget):
|
|
555
|
+
fail("Typed IR loop-control statement has an invalid target.")
|
|
556
|
+
elif isinstance(value, IRReturn):
|
|
557
|
+
if value.value is not None:
|
|
558
|
+
validate_expr(value.value, f"{label}.value")
|
|
559
|
+
else:
|
|
560
|
+
fail(f"Unknown typed IR statement node {type(value).__name__}.")
|
|
561
|
+
|
|
562
|
+
if not isinstance(module, IRModule):
|
|
563
|
+
fail("Typed IR validation received a non-IR module.")
|
|
564
|
+
if not isinstance(module.source_name, str) or not module.source_name:
|
|
565
|
+
fail("Typed IR module source_name must be non-empty text.")
|
|
566
|
+
require_tuple(module.storages, IRStorageDecl, "module.storages")
|
|
567
|
+
require_tuple(module.objects, IRObjectDecl, "module.objects")
|
|
568
|
+
require_tuple(module.setup, IRStmt, "module.setup")
|
|
569
|
+
require_tuple(module.main_loop, IRStmt, "module.main_loop")
|
|
570
|
+
require_tuple(module.functions, IRFunction, "module.functions")
|
|
571
|
+
require_tuple(module.generated_temporaries, IRTempRef, "module.generated_temporaries")
|
|
572
|
+
|
|
573
|
+
for i, decl in enumerate(module.storages):
|
|
574
|
+
validate_symbol_ref(decl.symbol, f"module.storages[{i}].symbol")
|
|
575
|
+
if not isinstance(decl.storage, StorageClass):
|
|
576
|
+
fail("Typed IR storage declaration has an invalid storage class.")
|
|
577
|
+
if decl.initializer is not None:
|
|
578
|
+
validate_expr(decl.initializer, f"module.storages[{i}].initializer")
|
|
579
|
+
require_span(decl.span, f"module.storages[{i}].span")
|
|
580
|
+
|
|
581
|
+
for i, obj in enumerate(module.objects):
|
|
582
|
+
validate_symbol_ref(obj.symbol, f"module.objects[{i}].symbol")
|
|
583
|
+
if not isinstance(obj.class_name, str) or not obj.class_name:
|
|
584
|
+
fail("Typed IR object declaration has an invalid class name.")
|
|
585
|
+
require_tuple(obj.args, IRExpr, f"module.objects[{i}].args")
|
|
586
|
+
for j, arg in enumerate(obj.args):
|
|
587
|
+
validate_expr(arg, f"module.objects[{i}].args[{j}]")
|
|
588
|
+
require_span(obj.span, f"module.objects[{i}].span")
|
|
589
|
+
|
|
590
|
+
for i, fn in enumerate(module.functions):
|
|
591
|
+
validate_function_ref(fn.symbol, f"module.functions[{i}].symbol")
|
|
592
|
+
require_tuple(fn.parameters, IRSymbolRef, f"module.functions[{i}].parameters")
|
|
593
|
+
for j, param in enumerate(fn.parameters):
|
|
594
|
+
validate_symbol_ref(param, f"module.functions[{i}].parameters[{j}]")
|
|
595
|
+
validate_type(fn.return_type, f"module.functions[{i}].return_type")
|
|
596
|
+
require_tuple(fn.body, IRStmt, f"module.functions[{i}].body")
|
|
597
|
+
for j, stmt in enumerate(fn.body):
|
|
598
|
+
validate_stmt(stmt, f"module.functions[{i}].body[{j}]")
|
|
599
|
+
require_span(fn.span, f"module.functions[{i}].span")
|
|
600
|
+
|
|
601
|
+
for i, stmt in enumerate(module.setup):
|
|
602
|
+
validate_stmt(stmt, f"module.setup[{i}]")
|
|
603
|
+
for i, stmt in enumerate(module.main_loop):
|
|
604
|
+
validate_stmt(stmt, f"module.main_loop[{i}]")
|
|
605
|
+
for i, temp in enumerate(module.generated_temporaries):
|
|
606
|
+
validate_temp_ref(temp, f"module.generated_temporaries[{i}]")
|
|
607
|
+
|
|
608
|
+
# Raw AST is forbidden anywhere, including inside values not otherwise
|
|
609
|
+
# reachable through the typed-node checks above.
|
|
610
|
+
if any(isinstance(item, ast.AST) for item in _walk_values(module)):
|
|
611
|
+
fail("Typed IR illegally contains a raw Python AST node.")
|
|
612
|
+
|
|
613
|
+
declared_symbols = {
|
|
614
|
+
decl.symbol.symbol_id for decl in module.storages
|
|
615
|
+
} | {
|
|
616
|
+
obj.symbol.symbol_id for obj in module.objects
|
|
617
|
+
} | {
|
|
618
|
+
fn.symbol.symbol_id for fn in module.functions
|
|
619
|
+
}
|
|
620
|
+
for fn in module.functions:
|
|
621
|
+
declared_symbols.update(p.symbol_id for p in fn.parameters)
|
|
622
|
+
|
|
623
|
+
# Function-local symbols are intentionally not all represented as module
|
|
624
|
+
# storage declarations. Collect every store/loop target as a declaration point.
|
|
625
|
+
for item in _walk_values(module):
|
|
626
|
+
if isinstance(item, (IRStore, IRAugStore, IRRangeLoop)):
|
|
627
|
+
declared_symbols.add(item.target.symbol_id)
|
|
628
|
+
if isinstance(item, IRIndexStore):
|
|
629
|
+
declared_symbols.add(item.collection.symbol_id)
|
|
630
|
+
if isinstance(item, IRFixedListLoop):
|
|
631
|
+
declared_symbols.add(item.value_target.symbol_id)
|
|
632
|
+
if item.index_target is not None:
|
|
633
|
+
declared_symbols.add(item.index_target.symbol_id)
|
|
634
|
+
|
|
635
|
+
temp_ids = {temp.temp_id for temp in module.generated_temporaries}
|
|
636
|
+
if len(temp_ids) != len(module.generated_temporaries):
|
|
637
|
+
fail("Typed IR contains duplicate generated temporary IDs.")
|
|
638
|
+
temp_names = {temp.backend_name for temp in module.generated_temporaries}
|
|
639
|
+
if len(temp_names) != len(module.generated_temporaries):
|
|
640
|
+
fail("Typed IR contains duplicate generated temporary backend names.")
|
|
641
|
+
|
|
642
|
+
# Every repeated symbol ID must describe the same symbol. Conflicting
|
|
643
|
+
# metadata could otherwise silently generate C++ using the wrong storage.
|
|
644
|
+
symbol_refs: dict[SymbolId, IRSymbolRef] = {}
|
|
645
|
+
for item in _walk_values(module):
|
|
646
|
+
if isinstance(item, IRSymbolRef):
|
|
647
|
+
previous = symbol_refs.get(item.symbol_id)
|
|
648
|
+
if previous is not None and previous != item:
|
|
649
|
+
fail(f"Typed IR contains conflicting references for symbol {item.symbol_id}.")
|
|
650
|
+
symbol_refs[item.symbol_id] = item
|
|
651
|
+
if item.kind not in {SymbolKind.API, SymbolKind.BUILTIN} and item.symbol_id not in declared_symbols:
|
|
652
|
+
fail(f"Typed IR references undeclared symbol '{item.source_name}' ({item.symbol_id}).")
|
|
653
|
+
elif isinstance(item, IRFunctionRef) and item.symbol_id not in declared_symbols:
|
|
654
|
+
fail(f"Typed IR references undeclared function '{item.source_name}' ({item.symbol_id}).")
|
|
655
|
+
elif isinstance(item, IRTempRef) and item.temp_id not in temp_ids:
|
|
656
|
+
fail(f"Typed IR references undeclared temporary {item.temp_id}.")
|
|
657
|
+
|
|
658
|
+
|
|
659
|
+
def ir_contains_raw_ast(module: IRModule) -> bool:
|
|
660
|
+
return any(isinstance(item, ast.AST) for item in _walk_values(module))
|