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
|
@@ -0,0 +1,596 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import ast
|
|
4
|
+
|
|
5
|
+
from .errors import InternalCompilerError
|
|
6
|
+
from .frontend import is_noop_stmt
|
|
7
|
+
from .flow import StorageClass
|
|
8
|
+
from .ir import (
|
|
9
|
+
IRAugStore,
|
|
10
|
+
IRBinary,
|
|
11
|
+
IRBinaryOp,
|
|
12
|
+
IRBool,
|
|
13
|
+
IRBoolOp,
|
|
14
|
+
IRBreak,
|
|
15
|
+
IRCall,
|
|
16
|
+
IRCallKind,
|
|
17
|
+
IRCompareChain,
|
|
18
|
+
IRCompareOp,
|
|
19
|
+
IRConditional,
|
|
20
|
+
IRConstant,
|
|
21
|
+
IRContinue,
|
|
22
|
+
IREffect,
|
|
23
|
+
IRExpr,
|
|
24
|
+
IRExprStmt,
|
|
25
|
+
IRFormatString,
|
|
26
|
+
IRFormattedValue,
|
|
27
|
+
IRFunction,
|
|
28
|
+
IRFunctionRef,
|
|
29
|
+
IRIf,
|
|
30
|
+
IRIndex,
|
|
31
|
+
IRIndexStore,
|
|
32
|
+
IRListLiteral,
|
|
33
|
+
IRFixedListLoop,
|
|
34
|
+
IRLoad,
|
|
35
|
+
IRLoopControlTarget,
|
|
36
|
+
IRModule,
|
|
37
|
+
IRObjectDecl,
|
|
38
|
+
IRPrint,
|
|
39
|
+
IRRangeLoop,
|
|
40
|
+
IRReturn,
|
|
41
|
+
IRStorageDecl,
|
|
42
|
+
IRStore,
|
|
43
|
+
IRSymbolRef,
|
|
44
|
+
IRTempId,
|
|
45
|
+
IRTempRef,
|
|
46
|
+
IRUnary,
|
|
47
|
+
IRUnaryOp,
|
|
48
|
+
IRWhile,
|
|
49
|
+
validate_ir,
|
|
50
|
+
)
|
|
51
|
+
from .numeric import NumericAnalysis
|
|
52
|
+
from .source import SourceSpan
|
|
53
|
+
from .symbols import Symbol, SymbolKind
|
|
54
|
+
from .typesys import BOOL, INT, STRING, BarmajaType, TypedModule, TypeKind
|
|
55
|
+
from .api_registry import HARDWARE_CLASSES
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
_BINARY_OPS = {
|
|
59
|
+
ast.Add: IRBinaryOp.ADD,
|
|
60
|
+
ast.Sub: IRBinaryOp.SUBTRACT,
|
|
61
|
+
ast.Mult: IRBinaryOp.MULTIPLY,
|
|
62
|
+
ast.Div: IRBinaryOp.TRUE_DIVIDE,
|
|
63
|
+
ast.Mod: IRBinaryOp.MODULO,
|
|
64
|
+
}
|
|
65
|
+
_AUG_OPS = {
|
|
66
|
+
ast.Add: IRBinaryOp.ADD,
|
|
67
|
+
ast.Sub: IRBinaryOp.SUBTRACT,
|
|
68
|
+
ast.Mult: IRBinaryOp.MULTIPLY,
|
|
69
|
+
ast.Div: IRBinaryOp.TRUE_DIVIDE,
|
|
70
|
+
ast.Mod: IRBinaryOp.MODULO,
|
|
71
|
+
}
|
|
72
|
+
_COMPARE_OPS = {
|
|
73
|
+
ast.Eq: IRCompareOp.EQ,
|
|
74
|
+
ast.NotEq: IRCompareOp.NE,
|
|
75
|
+
ast.Lt: IRCompareOp.LT,
|
|
76
|
+
ast.LtE: IRCompareOp.LE,
|
|
77
|
+
ast.Gt: IRCompareOp.GT,
|
|
78
|
+
ast.GtE: IRCompareOp.GE,
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class IRLowerer:
|
|
83
|
+
"""Lower the fully resolved/typed frontend into target-independent IR.
|
|
84
|
+
|
|
85
|
+
This phase may inspect Python AST only while lowering. The returned IR owns
|
|
86
|
+
no AST objects and carries stable SymbolIds, types, spans, and conservative
|
|
87
|
+
effects. C++ generation remains on the legacy AST path until Step 15.
|
|
88
|
+
"""
|
|
89
|
+
|
|
90
|
+
def __init__(self, typed: TypedModule, numeric: NumericAnalysis, source_name: str) -> None:
|
|
91
|
+
self.typed = typed
|
|
92
|
+
self.numeric = numeric
|
|
93
|
+
self.resolved = typed.resolved
|
|
94
|
+
self.flow = typed.flow
|
|
95
|
+
self.frontend = self.resolved.frontend
|
|
96
|
+
self.source_name = source_name
|
|
97
|
+
self._temp_counter = 0
|
|
98
|
+
self._temps: list[IRTempRef] = []
|
|
99
|
+
|
|
100
|
+
def lower(self) -> IRModule:
|
|
101
|
+
storages = self._lower_storage_declarations()
|
|
102
|
+
objects: list[IRObjectDecl] = []
|
|
103
|
+
setup: list = []
|
|
104
|
+
functions: list[IRFunction] = []
|
|
105
|
+
|
|
106
|
+
main_loop_node = self.frontend.main_loop
|
|
107
|
+
for stmt in self.frontend.tree.body:
|
|
108
|
+
if isinstance(stmt, ast.ImportFrom):
|
|
109
|
+
continue
|
|
110
|
+
if isinstance(stmt, ast.FunctionDef):
|
|
111
|
+
functions.append(self._lower_function(stmt))
|
|
112
|
+
continue
|
|
113
|
+
if stmt is main_loop_node:
|
|
114
|
+
continue
|
|
115
|
+
if is_noop_stmt(stmt, docstring_allowed=True):
|
|
116
|
+
continue
|
|
117
|
+
if self._is_hardware_object_assignment(stmt):
|
|
118
|
+
objects.append(self._lower_object_decl(stmt))
|
|
119
|
+
continue
|
|
120
|
+
setup.extend(self._lower_stmt(stmt, in_main=False, nested_loop_depth=0))
|
|
121
|
+
|
|
122
|
+
main_loop = tuple(
|
|
123
|
+
item
|
|
124
|
+
for stmt in main_loop_node.body
|
|
125
|
+
for item in self._lower_stmt(stmt, in_main=True, nested_loop_depth=0)
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
module = IRModule(
|
|
129
|
+
source_name=self.source_name,
|
|
130
|
+
storages=tuple(storages),
|
|
131
|
+
objects=tuple(objects),
|
|
132
|
+
setup=tuple(setup),
|
|
133
|
+
main_loop=main_loop,
|
|
134
|
+
functions=tuple(functions),
|
|
135
|
+
generated_temporaries=tuple(self._temps),
|
|
136
|
+
)
|
|
137
|
+
validate_ir(module)
|
|
138
|
+
return module
|
|
139
|
+
|
|
140
|
+
# ---------- declarations ----------
|
|
141
|
+
def _lower_storage_declarations(self) -> list[IRStorageDecl]:
|
|
142
|
+
result: list[IRStorageDecl] = []
|
|
143
|
+
symbols = sorted(
|
|
144
|
+
(
|
|
145
|
+
s for s in self.resolved.module_scope.symbols.values()
|
|
146
|
+
if s.kind == SymbolKind.MODULE
|
|
147
|
+
),
|
|
148
|
+
key=lambda s: s.symbol_id.value,
|
|
149
|
+
)
|
|
150
|
+
for symbol in symbols:
|
|
151
|
+
typ = self.typed.symbol_types.get(symbol.symbol_id)
|
|
152
|
+
if typ is None or typ.kind == TypeKind.UNKNOWN:
|
|
153
|
+
raise InternalCompilerError(f"No resolved type is available for module symbol '{symbol.name}'.")
|
|
154
|
+
facts = self.flow.symbol_facts.get(symbol.symbol_id)
|
|
155
|
+
if facts is None:
|
|
156
|
+
raise InternalCompilerError(f"No flow/storage facts are available for module symbol '{symbol.name}'.")
|
|
157
|
+
result.append(
|
|
158
|
+
IRStorageDecl(
|
|
159
|
+
symbol=self._symbol_ref(symbol),
|
|
160
|
+
storage=facts.storage,
|
|
161
|
+
initializer=None,
|
|
162
|
+
span=self._declaration_span(symbol),
|
|
163
|
+
)
|
|
164
|
+
)
|
|
165
|
+
return result
|
|
166
|
+
|
|
167
|
+
def _declaration_span(self, symbol: Symbol) -> SourceSpan | None:
|
|
168
|
+
if symbol.declaration_line is None:
|
|
169
|
+
return None
|
|
170
|
+
return SourceSpan(symbol.declaration_line, 0, symbol.declaration_line, 0)
|
|
171
|
+
|
|
172
|
+
def _lower_function(self, node: ast.FunctionDef) -> IRFunction:
|
|
173
|
+
signature = self.typed.signature_for(node.name)
|
|
174
|
+
function_symbol = self.resolved.symbol_for_function(node)
|
|
175
|
+
params = tuple(self._symbol_ref(s) for s in signature.parameters)
|
|
176
|
+
body = tuple(
|
|
177
|
+
item
|
|
178
|
+
for stmt in node.body
|
|
179
|
+
for item in self._lower_stmt(stmt, in_main=False, nested_loop_depth=0)
|
|
180
|
+
)
|
|
181
|
+
return IRFunction(
|
|
182
|
+
symbol=IRFunctionRef(function_symbol.symbol_id, function_symbol.name, function_symbol.cpp_name),
|
|
183
|
+
parameters=params,
|
|
184
|
+
return_type=signature.return_type,
|
|
185
|
+
body=body,
|
|
186
|
+
span=self._span(node),
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
@staticmethod
|
|
190
|
+
def _is_hardware_object_assignment(node: ast.AST) -> bool:
|
|
191
|
+
return (
|
|
192
|
+
isinstance(node, ast.Assign)
|
|
193
|
+
and len(node.targets) == 1
|
|
194
|
+
and isinstance(node.targets[0], ast.Name)
|
|
195
|
+
and isinstance(node.value, ast.Call)
|
|
196
|
+
and isinstance(node.value.func, ast.Name)
|
|
197
|
+
and node.value.func.id in HARDWARE_CLASSES
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
def _lower_object_decl(self, node: ast.Assign) -> IRObjectDecl:
|
|
201
|
+
target = node.targets[0]
|
|
202
|
+
assert isinstance(target, ast.Name)
|
|
203
|
+
call = node.value
|
|
204
|
+
assert isinstance(call, ast.Call) and isinstance(call.func, ast.Name)
|
|
205
|
+
symbol = self.resolved.symbol_for_name(target)
|
|
206
|
+
return IRObjectDecl(
|
|
207
|
+
symbol=self._symbol_ref(symbol),
|
|
208
|
+
class_name=call.func.id,
|
|
209
|
+
args=tuple(self._expr(a) for a in call.args),
|
|
210
|
+
span=self._span(node),
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
# ---------- statements ----------
|
|
214
|
+
def _lower_stmt(self, node: ast.stmt, *, in_main: bool, nested_loop_depth: int) -> tuple:
|
|
215
|
+
if is_noop_stmt(node, docstring_allowed=True) or isinstance(node, ast.Global):
|
|
216
|
+
return ()
|
|
217
|
+
|
|
218
|
+
if isinstance(node, ast.Assign):
|
|
219
|
+
if len(node.targets) == 1 and isinstance(node.targets[0], ast.Subscript):
|
|
220
|
+
target = node.targets[0]
|
|
221
|
+
if not isinstance(target.value, ast.Name):
|
|
222
|
+
raise InternalCompilerError("Validated fixed-list store has no list-name target.")
|
|
223
|
+
return (
|
|
224
|
+
IRIndexStore(
|
|
225
|
+
span=self._span(node),
|
|
226
|
+
collection=self._symbol_ref(self.resolved.symbol_for_name(target.value)),
|
|
227
|
+
index=self._expr(target.slice),
|
|
228
|
+
value=self._expr(node.value),
|
|
229
|
+
),
|
|
230
|
+
)
|
|
231
|
+
if (
|
|
232
|
+
len(node.targets) == 1
|
|
233
|
+
and isinstance(node.targets[0], (ast.Tuple, ast.List))
|
|
234
|
+
and isinstance(node.value, (ast.Tuple, ast.List))
|
|
235
|
+
):
|
|
236
|
+
return tuple(
|
|
237
|
+
IRStore(self._span(node), self._symbol_ref(self.resolved.symbol_for_name(t)), self._expr(v))
|
|
238
|
+
for t, v in zip(node.targets[0].elts, node.value.elts)
|
|
239
|
+
if isinstance(t, ast.Name)
|
|
240
|
+
)
|
|
241
|
+
target = node.targets[0]
|
|
242
|
+
if not isinstance(target, ast.Name):
|
|
243
|
+
raise InternalCompilerError("Lowering received an unsupported assignment target.")
|
|
244
|
+
return (
|
|
245
|
+
IRStore(
|
|
246
|
+
span=self._span(node),
|
|
247
|
+
target=self._symbol_ref(self.resolved.symbol_for_name(target)),
|
|
248
|
+
value=self._expr(node.value),
|
|
249
|
+
),
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
if isinstance(node, ast.AugAssign):
|
|
253
|
+
if not isinstance(node.target, ast.Name):
|
|
254
|
+
raise InternalCompilerError("Lowering received an unsupported augmented-assignment target.")
|
|
255
|
+
op = _AUG_OPS.get(type(node.op))
|
|
256
|
+
if op is None:
|
|
257
|
+
raise InternalCompilerError("Lowering received an unsupported augmented-assignment operator.")
|
|
258
|
+
checked = self.numeric.int_expr_needs_runtime_check(node)
|
|
259
|
+
return (
|
|
260
|
+
IRAugStore(
|
|
261
|
+
span=self._span(node),
|
|
262
|
+
target=self._symbol_ref(self.resolved.symbol_for_name(node.target)),
|
|
263
|
+
op=op,
|
|
264
|
+
value=self._expr(node.value),
|
|
265
|
+
checked=checked,
|
|
266
|
+
),
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
if isinstance(node, ast.Expr):
|
|
270
|
+
if isinstance(node.value, ast.Call) and isinstance(node.value.func, ast.Name) and node.value.func.id == "print":
|
|
271
|
+
return (
|
|
272
|
+
IRPrint(
|
|
273
|
+
span=self._span(node),
|
|
274
|
+
values=tuple(self._expr(arg) for arg in node.value.args),
|
|
275
|
+
newline=True,
|
|
276
|
+
),
|
|
277
|
+
)
|
|
278
|
+
return (IRExprStmt(self._span(node), self._expr(node.value)),)
|
|
279
|
+
|
|
280
|
+
if isinstance(node, ast.If):
|
|
281
|
+
return (
|
|
282
|
+
IRIf(
|
|
283
|
+
span=self._span(node),
|
|
284
|
+
condition=self._expr(node.test),
|
|
285
|
+
body=tuple(
|
|
286
|
+
item for stmt in node.body
|
|
287
|
+
for item in self._lower_stmt(stmt, in_main=in_main, nested_loop_depth=nested_loop_depth)
|
|
288
|
+
),
|
|
289
|
+
otherwise=tuple(
|
|
290
|
+
item for stmt in node.orelse
|
|
291
|
+
for item in self._lower_stmt(stmt, in_main=in_main, nested_loop_depth=nested_loop_depth)
|
|
292
|
+
),
|
|
293
|
+
),
|
|
294
|
+
)
|
|
295
|
+
|
|
296
|
+
if isinstance(node, ast.While):
|
|
297
|
+
return (
|
|
298
|
+
IRWhile(
|
|
299
|
+
span=self._span(node),
|
|
300
|
+
condition=self._expr(node.test),
|
|
301
|
+
body=tuple(
|
|
302
|
+
item for stmt in node.body
|
|
303
|
+
for item in self._lower_stmt(stmt, in_main=in_main, nested_loop_depth=nested_loop_depth + 1)
|
|
304
|
+
),
|
|
305
|
+
),
|
|
306
|
+
)
|
|
307
|
+
|
|
308
|
+
if isinstance(node, ast.For):
|
|
309
|
+
if isinstance(node.iter, ast.Call) and isinstance(node.iter.func, ast.Name) and node.iter.func.id == "range":
|
|
310
|
+
if not isinstance(node.target, ast.Name):
|
|
311
|
+
raise InternalCompilerError("Validated range loop has no simple target.")
|
|
312
|
+
args = list(node.iter.args)
|
|
313
|
+
if len(args) == 1:
|
|
314
|
+
start_expr = self._generated_int_constant(0, node.iter)
|
|
315
|
+
stop_expr = self._expr(args[0])
|
|
316
|
+
step_expr = self._generated_int_constant(1, node.iter)
|
|
317
|
+
elif len(args) == 2:
|
|
318
|
+
start_expr, stop_expr = self._expr(args[0]), self._expr(args[1])
|
|
319
|
+
step_expr = self._generated_int_constant(1, node.iter)
|
|
320
|
+
elif len(args) == 3:
|
|
321
|
+
start_expr, stop_expr, step_expr = (self._expr(a) for a in args)
|
|
322
|
+
else:
|
|
323
|
+
raise InternalCompilerError("Validated range() reached lowering with invalid arity.")
|
|
324
|
+
return (
|
|
325
|
+
IRRangeLoop(
|
|
326
|
+
span=self._span(node),
|
|
327
|
+
target=self._symbol_ref(self.resolved.symbol_for_name(node.target)),
|
|
328
|
+
iterator_temp=self._new_temp(INT, "range_iterator", node),
|
|
329
|
+
start_temp=self._new_temp(INT, "range_start_once", node.iter),
|
|
330
|
+
stop_temp=self._new_temp(INT, "range_stop_once", node.iter),
|
|
331
|
+
step_temp=self._new_temp(INT, "range_step_once", node.iter),
|
|
332
|
+
start=start_expr,
|
|
333
|
+
stop=stop_expr,
|
|
334
|
+
step=step_expr,
|
|
335
|
+
body=tuple(
|
|
336
|
+
item for stmt in node.body
|
|
337
|
+
for item in self._lower_stmt(stmt, in_main=in_main, nested_loop_depth=nested_loop_depth + 1)
|
|
338
|
+
),
|
|
339
|
+
),
|
|
340
|
+
)
|
|
341
|
+
|
|
342
|
+
enumerate_mode = isinstance(node.iter, ast.Call) and isinstance(node.iter.func, ast.Name) and node.iter.func.id == "enumerate"
|
|
343
|
+
collection_node = node.iter.args[0] if enumerate_mode else node.iter
|
|
344
|
+
if not isinstance(collection_node, ast.Name):
|
|
345
|
+
raise InternalCompilerError("Validated fixed-list loop has no list-name iterator.")
|
|
346
|
+
collection_ref = self._symbol_ref(self.resolved.symbol_for_name(collection_node))
|
|
347
|
+
if enumerate_mode:
|
|
348
|
+
if not isinstance(node.target, (ast.Tuple, ast.List)) or len(node.target.elts) != 2:
|
|
349
|
+
raise InternalCompilerError("Validated enumerate target changed before lowering.")
|
|
350
|
+
index_node, value_node = node.target.elts
|
|
351
|
+
if not isinstance(index_node, ast.Name) or not isinstance(value_node, ast.Name):
|
|
352
|
+
raise InternalCompilerError("Validated enumerate target is not two simple names.")
|
|
353
|
+
index_ref = self._symbol_ref(self.resolved.symbol_for_name(index_node))
|
|
354
|
+
value_ref = self._symbol_ref(self.resolved.symbol_for_name(value_node))
|
|
355
|
+
else:
|
|
356
|
+
if not isinstance(node.target, ast.Name):
|
|
357
|
+
raise InternalCompilerError("Validated fixed-list loop target is not a simple name.")
|
|
358
|
+
index_ref = None
|
|
359
|
+
value_ref = self._symbol_ref(self.resolved.symbol_for_name(node.target))
|
|
360
|
+
index_start = None
|
|
361
|
+
index_start_temp = None
|
|
362
|
+
if enumerate_mode and len(node.iter.args) == 2:
|
|
363
|
+
index_start = self._expr(node.iter.args[1])
|
|
364
|
+
index_start_temp = self._new_temp(INT, "enumerate_start_once", node.iter.args[1])
|
|
365
|
+
return (
|
|
366
|
+
IRFixedListLoop(
|
|
367
|
+
span=self._span(node),
|
|
368
|
+
collection=collection_ref,
|
|
369
|
+
value_target=value_ref,
|
|
370
|
+
index_target=index_ref,
|
|
371
|
+
iterator_temp=self._new_temp(INT, "fixed_list_iterator", node),
|
|
372
|
+
index_start=index_start,
|
|
373
|
+
index_start_temp=index_start_temp,
|
|
374
|
+
body=tuple(
|
|
375
|
+
item for stmt in node.body
|
|
376
|
+
for item in self._lower_stmt(stmt, in_main=in_main, nested_loop_depth=nested_loop_depth + 1)
|
|
377
|
+
),
|
|
378
|
+
),
|
|
379
|
+
)
|
|
380
|
+
|
|
381
|
+
if isinstance(node, ast.Break):
|
|
382
|
+
target = IRLoopControlTarget.NESTED_LOOP
|
|
383
|
+
if in_main and nested_loop_depth == 0:
|
|
384
|
+
target = IRLoopControlTarget.MAIN_LOOP
|
|
385
|
+
return (IRBreak(self._span(node), target),)
|
|
386
|
+
|
|
387
|
+
if isinstance(node, ast.Continue):
|
|
388
|
+
target = IRLoopControlTarget.MAIN_LOOP if in_main and nested_loop_depth == 0 else IRLoopControlTarget.NESTED_LOOP
|
|
389
|
+
return (IRContinue(self._span(node), target),)
|
|
390
|
+
|
|
391
|
+
if isinstance(node, ast.Return):
|
|
392
|
+
return (IRReturn(self._span(node), None if node.value is None else self._expr(node.value)),)
|
|
393
|
+
|
|
394
|
+
raise InternalCompilerError(f"Validated statement {type(node).__name__} has no typed IR lowering path.")
|
|
395
|
+
|
|
396
|
+
# ---------- expressions ----------
|
|
397
|
+
def _expr(self, node: ast.AST) -> IRExpr:
|
|
398
|
+
typ = self.typed.type_for_expr(node)
|
|
399
|
+
span = self._span(node)
|
|
400
|
+
|
|
401
|
+
if isinstance(node, ast.Constant):
|
|
402
|
+
if not isinstance(node.value, (bool, int, float, str)):
|
|
403
|
+
raise InternalCompilerError("Validated constant has no typed IR representation.")
|
|
404
|
+
return IRConstant(typ, span, frozenset(), node.value)
|
|
405
|
+
|
|
406
|
+
if isinstance(node, ast.Name):
|
|
407
|
+
return IRLoad(typ, span, frozenset(), self._symbol_ref(self.resolved.symbol_for_name(node)))
|
|
408
|
+
|
|
409
|
+
if isinstance(node, ast.List):
|
|
410
|
+
elements = tuple(self._expr(item) for item in node.elts)
|
|
411
|
+
return IRListLiteral(typ, span, frozenset(self._union_effects(*elements)), elements)
|
|
412
|
+
|
|
413
|
+
if isinstance(node, ast.Subscript):
|
|
414
|
+
collection = self._expr(node.value)
|
|
415
|
+
index = self._expr(node.slice)
|
|
416
|
+
effects = self._union_effects(collection, index)
|
|
417
|
+
if not (isinstance(node.slice, ast.Constant) and isinstance(node.slice.value, int)):
|
|
418
|
+
effects.add(IREffect.MAY_FAIL)
|
|
419
|
+
return IRIndex(typ, span, frozenset(effects), collection, index)
|
|
420
|
+
|
|
421
|
+
if isinstance(node, ast.UnaryOp):
|
|
422
|
+
operand = self._expr(node.operand)
|
|
423
|
+
if isinstance(node.op, ast.USub):
|
|
424
|
+
op = IRUnaryOp.NEGATE
|
|
425
|
+
elif isinstance(node.op, ast.Not):
|
|
426
|
+
op = IRUnaryOp.NOT
|
|
427
|
+
else:
|
|
428
|
+
raise InternalCompilerError("Validated unary operator has no typed IR representation.")
|
|
429
|
+
effects = self._union_effects(operand)
|
|
430
|
+
if self.numeric.int_expr_needs_runtime_check(node):
|
|
431
|
+
effects = effects | {IREffect.MAY_FAIL}
|
|
432
|
+
return IRUnary(typ, span, frozenset(effects), op, operand)
|
|
433
|
+
|
|
434
|
+
if isinstance(node, ast.BinOp):
|
|
435
|
+
left, right = self._expr(node.left), self._expr(node.right)
|
|
436
|
+
op = _BINARY_OPS.get(type(node.op))
|
|
437
|
+
if op is None:
|
|
438
|
+
raise InternalCompilerError("Validated binary operator has no typed IR representation.")
|
|
439
|
+
checked = self.numeric.int_expr_needs_runtime_check(node)
|
|
440
|
+
effects = self._union_effects(left, right)
|
|
441
|
+
if checked or isinstance(node.op, (ast.Div, ast.Mod)):
|
|
442
|
+
effects.add(IREffect.MAY_FAIL)
|
|
443
|
+
return IRBinary(typ, span, frozenset(effects), op, left, right, checked)
|
|
444
|
+
|
|
445
|
+
if isinstance(node, ast.Compare):
|
|
446
|
+
operands = (self._expr(node.left),) + tuple(self._expr(c) for c in node.comparators)
|
|
447
|
+
operators = tuple(_COMPARE_OPS[type(op)] for op in node.ops)
|
|
448
|
+
effects = self._union_effects(*operands)
|
|
449
|
+
if any(self.numeric.time_threshold_needs_runtime_check(c) for c in node.comparators):
|
|
450
|
+
effects.add(IREffect.MAY_FAIL)
|
|
451
|
+
temps = tuple(self._new_temp(expr.type, "compare_operand_once", source) for expr, source in zip(operands, [node.left, *node.comparators]))
|
|
452
|
+
return IRCompareChain(typ, span, frozenset(effects), operands, operators, temps)
|
|
453
|
+
|
|
454
|
+
if isinstance(node, ast.BoolOp):
|
|
455
|
+
values = tuple(self._expr(v) for v in node.values)
|
|
456
|
+
op = IRBoolOp.AND if isinstance(node.op, ast.And) else IRBoolOp.OR
|
|
457
|
+
return IRBool(typ, span, frozenset(self._union_effects(*values)), op, values)
|
|
458
|
+
|
|
459
|
+
if isinstance(node, ast.IfExp):
|
|
460
|
+
cond, yes, no = self._expr(node.test), self._expr(node.body), self._expr(node.orelse)
|
|
461
|
+
return IRConditional(typ, span, frozenset(self._union_effects(cond, yes, no)), cond, yes, no)
|
|
462
|
+
|
|
463
|
+
if isinstance(node, ast.JoinedStr):
|
|
464
|
+
parts: list[str | IRFormattedValue] = []
|
|
465
|
+
child_exprs: list[IRExpr] = []
|
|
466
|
+
for part in node.values:
|
|
467
|
+
if isinstance(part, ast.Constant) and isinstance(part.value, str):
|
|
468
|
+
parts.append(part.value)
|
|
469
|
+
continue
|
|
470
|
+
if not isinstance(part, ast.FormattedValue):
|
|
471
|
+
raise InternalCompilerError("Validated f-string part has no typed IR representation.")
|
|
472
|
+
value = self._expr(part.value)
|
|
473
|
+
child_exprs.append(value)
|
|
474
|
+
parts.append(IRFormattedValue(value, self._fstring_precision(part), self._span(part)))
|
|
475
|
+
return IRFormatString(STRING, span, frozenset(self._union_effects(*child_exprs)), tuple(parts))
|
|
476
|
+
|
|
477
|
+
if isinstance(node, ast.Call):
|
|
478
|
+
return self._call(node, typ)
|
|
479
|
+
|
|
480
|
+
raise InternalCompilerError(f"Validated expression {type(node).__name__} has no typed IR lowering path.")
|
|
481
|
+
|
|
482
|
+
def _call(self, node: ast.Call, typ: BarmajaType) -> IRCall:
|
|
483
|
+
args = tuple(self._expr(a) for a in node.args)
|
|
484
|
+
effects = self._union_effects(*args)
|
|
485
|
+
span = self._span(node)
|
|
486
|
+
|
|
487
|
+
if isinstance(node.func, ast.Name):
|
|
488
|
+
name = node.func.id
|
|
489
|
+
symbol = self.resolved.symbol_for_name(node.func)
|
|
490
|
+
if symbol.kind == SymbolKind.FUNCTION:
|
|
491
|
+
effects.add(IREffect.USER_CALL)
|
|
492
|
+
function_ref = IRFunctionRef(symbol.symbol_id, symbol.name, symbol.cpp_name)
|
|
493
|
+
return IRCall(
|
|
494
|
+
typ, span, frozenset(effects), IRCallKind.USER_FUNCTION, name, args,
|
|
495
|
+
function_symbol=function_ref,
|
|
496
|
+
)
|
|
497
|
+
if symbol.kind == SymbolKind.BUILTIN:
|
|
498
|
+
# Checked int conversion and later semantic helpers may fail.
|
|
499
|
+
if name == "int" or self.numeric.int_expr_needs_runtime_check(node):
|
|
500
|
+
effects.add(IREffect.MAY_FAIL)
|
|
501
|
+
return IRCall(typ, span, frozenset(effects), IRCallKind.BUILTIN, name, args)
|
|
502
|
+
if symbol.kind == SymbolKind.API:
|
|
503
|
+
effects.add(IREffect.HARDWARE_IO)
|
|
504
|
+
if name == "print":
|
|
505
|
+
effects.add(IREffect.OUTPUT)
|
|
506
|
+
if name == "analogWrite" and len(node.args) > 1 and self.numeric.pwm_arg_needs_runtime_check(node.args[1]):
|
|
507
|
+
effects.add(IREffect.MAY_FAIL)
|
|
508
|
+
return IRCall(typ, span, frozenset(effects), IRCallKind.API_FUNCTION, name, args)
|
|
509
|
+
raise InternalCompilerError(f"Resolved direct call '{name}' has no IR call category.")
|
|
510
|
+
|
|
511
|
+
if isinstance(node.func, ast.Attribute) and isinstance(node.func.value, ast.Name):
|
|
512
|
+
owner_node = node.func.value
|
|
513
|
+
owner_name = owner_node.id
|
|
514
|
+
owner_symbol = self.resolved.symbol_for_name(owner_node)
|
|
515
|
+
owner_ref = None
|
|
516
|
+
kind = IRCallKind.SINGLETON_METHOD
|
|
517
|
+
if owner_symbol.kind == SymbolKind.HARDWARE:
|
|
518
|
+
owner_ref = self._symbol_ref(owner_symbol)
|
|
519
|
+
kind = IRCallKind.OBJECT_METHOD
|
|
520
|
+
effects.add(IREffect.HARDWARE_IO)
|
|
521
|
+
if (owner_name == "Serial" and node.func.attr in {"print", "println"}) or node.func.attr == "print":
|
|
522
|
+
effects.add(IREffect.OUTPUT)
|
|
523
|
+
return IRCall(
|
|
524
|
+
typ, span, frozenset(effects), kind,
|
|
525
|
+
f"{owner_name}.{node.func.attr}", args,
|
|
526
|
+
owner_symbol=owner_ref,
|
|
527
|
+
owner_name=owner_name,
|
|
528
|
+
method_name=node.func.attr,
|
|
529
|
+
)
|
|
530
|
+
|
|
531
|
+
raise InternalCompilerError("Validated call has no typed IR lowering path.")
|
|
532
|
+
|
|
533
|
+
# ---------- helpers ----------
|
|
534
|
+
def _symbol_ref(self, symbol: Symbol) -> IRSymbolRef:
|
|
535
|
+
typ = self.typed.symbol_types.get(symbol.symbol_id)
|
|
536
|
+
if typ is None:
|
|
537
|
+
# Constants/API singleton spellings are represented as API value
|
|
538
|
+
# loads when used as values. Their language value type is Int.
|
|
539
|
+
if symbol.kind == SymbolKind.API and symbol.name in {
|
|
540
|
+
"HIGH", "LOW", "INPUT", "OUTPUT", "INPUT_PULLUP",
|
|
541
|
+
"A0", "A1", "A2", "A3", "A4", "A5",
|
|
542
|
+
"DHT11", "DHT22", "CM", "INC",
|
|
543
|
+
}:
|
|
544
|
+
typ = INT
|
|
545
|
+
else:
|
|
546
|
+
raise InternalCompilerError(f"No resolved type is available for symbol '{symbol.name}'.")
|
|
547
|
+
if typ.kind == TypeKind.UNKNOWN:
|
|
548
|
+
raise InternalCompilerError(f"Symbol '{symbol.name}' reached IR lowering with Unknown type.")
|
|
549
|
+
return IRSymbolRef(symbol.symbol_id, symbol.name, symbol.cpp_name, symbol.kind, typ)
|
|
550
|
+
|
|
551
|
+
def _new_temp(self, typ: BarmajaType, purpose: str, owner: ast.AST) -> IRTempRef:
|
|
552
|
+
self._temp_counter += 1
|
|
553
|
+
temp = IRTempRef(IRTempId(self._temp_counter), f"_bj_t{self._temp_counter:04d}", typ, purpose, self._span(owner))
|
|
554
|
+
self._temps.append(temp)
|
|
555
|
+
return temp
|
|
556
|
+
|
|
557
|
+
def _generated_int_constant(self, value: int, owner: ast.AST) -> IRConstant:
|
|
558
|
+
return IRConstant(INT, self._span(owner), frozenset(), value)
|
|
559
|
+
|
|
560
|
+
@staticmethod
|
|
561
|
+
def _union_effects(*expressions: IRExpr) -> set[IREffect]:
|
|
562
|
+
result: set[IREffect] = set()
|
|
563
|
+
for expr in expressions:
|
|
564
|
+
result.update(expr.effects)
|
|
565
|
+
return result
|
|
566
|
+
|
|
567
|
+
@staticmethod
|
|
568
|
+
def _span(node: ast.AST) -> SourceSpan | None:
|
|
569
|
+
line = getattr(node, "lineno", None)
|
|
570
|
+
column = getattr(node, "col_offset", None)
|
|
571
|
+
if line is None or column is None:
|
|
572
|
+
return None
|
|
573
|
+
return SourceSpan(
|
|
574
|
+
line,
|
|
575
|
+
column,
|
|
576
|
+
getattr(node, "end_lineno", None) or line,
|
|
577
|
+
getattr(node, "end_col_offset", None) if getattr(node, "end_col_offset", None) is not None else column,
|
|
578
|
+
)
|
|
579
|
+
|
|
580
|
+
@staticmethod
|
|
581
|
+
def _fstring_precision(node: ast.FormattedValue) -> int | None:
|
|
582
|
+
if node.format_spec is None:
|
|
583
|
+
return None
|
|
584
|
+
if not isinstance(node.format_spec, ast.JoinedStr):
|
|
585
|
+
raise InternalCompilerError("Validated f-string format has no typed IR representation.")
|
|
586
|
+
text = "".join(
|
|
587
|
+
part.value for part in node.format_spec.values
|
|
588
|
+
if isinstance(part, ast.Constant) and isinstance(part.value, str)
|
|
589
|
+
)
|
|
590
|
+
if len(text) >= 3 and text.startswith(".") and text.endswith("f") and text[1:-1].isdigit():
|
|
591
|
+
return int(text[1:-1])
|
|
592
|
+
raise InternalCompilerError("Validated f-string precision has no typed IR representation.")
|
|
593
|
+
|
|
594
|
+
|
|
595
|
+
def lower_to_ir(typed: TypedModule, numeric: NumericAnalysis, source_name: str = "<student.py>") -> IRModule:
|
|
596
|
+
return IRLowerer(typed, numeric, source_name).lower()
|