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.
Files changed (87) hide show
  1. Barmaja_PyArduino/BARMAJA_PyArduino.ico +0 -0
  2. Barmaja_PyArduino/__init__.py +528 -0
  3. Barmaja_PyArduino/__init__.pyi +331 -0
  4. Barmaja_PyArduino/__main__.py +2 -0
  5. Barmaja_PyArduino/adapters/__init__.py +35 -0
  6. Barmaja_PyArduino/api_registry.py +459 -0
  7. Barmaja_PyArduino/backend/__init__.py +3 -0
  8. Barmaja_PyArduino/backend/uno_cpp.py +1680 -0
  9. Barmaja_PyArduino/board.py +80 -0
  10. Barmaja_PyArduino/cli.py +223 -0
  11. Barmaja_PyArduino/compiler.py +1721 -0
  12. Barmaja_PyArduino/console.py +217 -0
  13. Barmaja_PyArduino/device_packs.py +367 -0
  14. Barmaja_PyArduino/device_packs_builtin/Button/arduino/Barmaja_Button/library.properties +9 -0
  15. Barmaja_PyArduino/device_packs_builtin/Button/arduino/Barmaja_Button/src/Barmaja_Button.h +18 -0
  16. Barmaja_PyArduino/device_packs_builtin/Button/example.py +10 -0
  17. Barmaja_PyArduino/device_packs_builtin/Button/pack.json +40 -0
  18. Barmaja_PyArduino/device_packs_builtin/ClockDS3231/arduino/Barmaja_ClockDS3231/library.properties +9 -0
  19. Barmaja_PyArduino/device_packs_builtin/ClockDS3231/arduino/Barmaja_ClockDS3231/src/Barmaja_ClockDS3231.h +20 -0
  20. Barmaja_PyArduino/device_packs_builtin/ClockDS3231/example.py +8 -0
  21. Barmaja_PyArduino/device_packs_builtin/ClockDS3231/pack.json +136 -0
  22. Barmaja_PyArduino/device_packs_builtin/DistanceVL53L0X/arduino/Barmaja_DistanceVL53L0X/library.properties +9 -0
  23. Barmaja_PyArduino/device_packs_builtin/DistanceVL53L0X/arduino/Barmaja_DistanceVL53L0X/src/Barmaja_DistanceVL53L0X.h +12 -0
  24. Barmaja_PyArduino/device_packs_builtin/DistanceVL53L0X/example.py +8 -0
  25. Barmaja_PyArduino/device_packs_builtin/DistanceVL53L0X/pack.json +48 -0
  26. Barmaja_PyArduino/device_packs_builtin/LCDParallel/arduino/Barmaja_LCDParallel/library.properties +9 -0
  27. Barmaja_PyArduino/device_packs_builtin/LCDParallel/arduino/Barmaja_LCDParallel/src/Barmaja_LCDParallel.h +17 -0
  28. Barmaja_PyArduino/device_packs_builtin/LCDParallel/example.py +10 -0
  29. Barmaja_PyArduino/device_packs_builtin/LCDParallel/pack.json +137 -0
  30. Barmaja_PyArduino/device_packs_builtin/LoadCellHX711/arduino/Barmaja_LoadCellHX711/library.properties +9 -0
  31. Barmaja_PyArduino/device_packs_builtin/LoadCellHX711/arduino/Barmaja_LoadCellHX711/src/Barmaja_LoadCellHX711.h +16 -0
  32. Barmaja_PyArduino/device_packs_builtin/LoadCellHX711/example.py +11 -0
  33. Barmaja_PyArduino/device_packs_builtin/LoadCellHX711/pack.json +74 -0
  34. Barmaja_PyArduino/device_packs_builtin/NeoPixelStrip/arduino/Barmaja_NeoPixelStrip/library.properties +9 -0
  35. Barmaja_PyArduino/device_packs_builtin/NeoPixelStrip/arduino/Barmaja_NeoPixelStrip/src/Barmaja_NeoPixelStrip.h +15 -0
  36. Barmaja_PyArduino/device_packs_builtin/NeoPixelStrip/example.py +13 -0
  37. Barmaja_PyArduino/device_packs_builtin/NeoPixelStrip/pack.json +114 -0
  38. Barmaja_PyArduino/device_packs_builtin/OLEDText/arduino/Barmaja_OLEDText/library.properties +9 -0
  39. Barmaja_PyArduino/device_packs_builtin/OLEDText/arduino/Barmaja_OLEDText/src/Barmaja_OLEDText.h +21 -0
  40. Barmaja_PyArduino/device_packs_builtin/OLEDText/example.py +9 -0
  41. Barmaja_PyArduino/device_packs_builtin/OLEDText/pack.json +103 -0
  42. Barmaja_PyArduino/device_packs_builtin/PressureBMP280/arduino/Barmaja_PressureBMP280/library.properties +9 -0
  43. Barmaja_PyArduino/device_packs_builtin/PressureBMP280/arduino/Barmaja_PressureBMP280/src/Barmaja_PressureBMP280.h +16 -0
  44. Barmaja_PyArduino/device_packs_builtin/PressureBMP280/example.py +8 -0
  45. Barmaja_PyArduino/device_packs_builtin/PressureBMP280/pack.json +92 -0
  46. Barmaja_PyArduino/device_packs_builtin/RotaryEncoder/arduino/Barmaja_RotaryEncoder/library.properties +9 -0
  47. Barmaja_PyArduino/device_packs_builtin/RotaryEncoder/arduino/Barmaja_RotaryEncoder/src/Barmaja_RotaryEncoder.h +18 -0
  48. Barmaja_PyArduino/device_packs_builtin/RotaryEncoder/example.py +10 -0
  49. Barmaja_PyArduino/device_packs_builtin/RotaryEncoder/pack.json +44 -0
  50. Barmaja_PyArduino/device_packs_builtin/ServoDriverPCA9685/arduino/Barmaja_ServoDriverPCA9685/library.properties +9 -0
  51. Barmaja_PyArduino/device_packs_builtin/ServoDriverPCA9685/arduino/Barmaja_ServoDriverPCA9685/src/Barmaja_ServoDriverPCA9685.h +15 -0
  52. Barmaja_PyArduino/device_packs_builtin/ServoDriverPCA9685/example.py +10 -0
  53. Barmaja_PyArduino/device_packs_builtin/ServoDriverPCA9685/pack.json +117 -0
  54. Barmaja_PyArduino/device_packs_builtin/ShiftRegister595/arduino/Barmaja_ShiftRegister595/library.properties +9 -0
  55. Barmaja_PyArduino/device_packs_builtin/ShiftRegister595/arduino/Barmaja_ShiftRegister595/src/Barmaja_ShiftRegister595.h +14 -0
  56. Barmaja_PyArduino/device_packs_builtin/ShiftRegister595/example.py +9 -0
  57. Barmaja_PyArduino/device_packs_builtin/ShiftRegister595/pack.json +83 -0
  58. Barmaja_PyArduino/device_packs_builtin/TemperatureDS18B20/arduino/Barmaja_TemperatureDS18B20/library.properties +9 -0
  59. Barmaja_PyArduino/device_packs_builtin/TemperatureDS18B20/arduino/Barmaja_TemperatureDS18B20/src/Barmaja_TemperatureDS18B20.h +16 -0
  60. Barmaja_PyArduino/device_packs_builtin/TemperatureDS18B20/example.py +8 -0
  61. Barmaja_PyArduino/device_packs_builtin/TemperatureDS18B20/pack.json +63 -0
  62. Barmaja_PyArduino/doctor.py +406 -0
  63. Barmaja_PyArduino/environment.py +595 -0
  64. Barmaja_PyArduino/errors.py +166 -0
  65. Barmaja_PyArduino/flow.py +754 -0
  66. Barmaja_PyArduino/frontend.py +611 -0
  67. Barmaja_PyArduino/installer.py +151 -0
  68. Barmaja_PyArduino/ir.py +660 -0
  69. Barmaja_PyArduino/library_adder.py +440 -0
  70. Barmaja_PyArduino/live.py +859 -0
  71. Barmaja_PyArduino/lowering.py +596 -0
  72. Barmaja_PyArduino/numeric.py +748 -0
  73. Barmaja_PyArduino/pack_builder.py +544 -0
  74. Barmaja_PyArduino/py.typed +0 -0
  75. Barmaja_PyArduino/serial_monitor.py +172 -0
  76. Barmaja_PyArduino/source.py +184 -0
  77. Barmaja_PyArduino/stubgen.py +148 -0
  78. Barmaja_PyArduino/symbols.py +646 -0
  79. Barmaja_PyArduino/target.py +1631 -0
  80. Barmaja_PyArduino/toolchain.py +506 -0
  81. Barmaja_PyArduino/typesys.py +917 -0
  82. Barmaja_PyArduino/workflow.py +213 -0
  83. barmaja_pyarduino-2.0.0.dist-info/METADATA +39 -0
  84. barmaja_pyarduino-2.0.0.dist-info/RECORD +87 -0
  85. barmaja_pyarduino-2.0.0.dist-info/WHEEL +5 -0
  86. barmaja_pyarduino-2.0.0.dist-info/licenses/LICENSE.txt +7 -0
  87. barmaja_pyarduino-2.0.0.dist-info/top_level.txt +1 -0
@@ -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))