qubasic 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- qbasic.py +113 -0
- qbasic_core/__init__.py +80 -0
- qbasic_core/__main__.py +6 -0
- qbasic_core/analysis.py +179 -0
- qbasic_core/backend.py +76 -0
- qbasic_core/classic.py +419 -0
- qbasic_core/control_flow.py +576 -0
- qbasic_core/debug.py +327 -0
- qbasic_core/demos.py +356 -0
- qbasic_core/display.py +274 -0
- qbasic_core/engine.py +126 -0
- qbasic_core/engine_state.py +109 -0
- qbasic_core/errors.py +37 -0
- qbasic_core/exec_context.py +24 -0
- qbasic_core/executor.py +861 -0
- qbasic_core/expression.py +228 -0
- qbasic_core/file_io.py +457 -0
- qbasic_core/gates.py +284 -0
- qbasic_core/help_text.py +167 -0
- qbasic_core/io_protocol.py +33 -0
- qbasic_core/locc.py +10 -0
- qbasic_core/locc_commands.py +221 -0
- qbasic_core/locc_display.py +61 -0
- qbasic_core/locc_engine.py +195 -0
- qbasic_core/locc_execution.py +389 -0
- qbasic_core/memory.py +369 -0
- qbasic_core/mock_backend.py +66 -0
- qbasic_core/noise_mixin.py +96 -0
- qbasic_core/parser.py +564 -0
- qbasic_core/patterns.py +186 -0
- qbasic_core/profiler.py +156 -0
- qbasic_core/program_mgmt.py +369 -0
- qbasic_core/protocol.py +77 -0
- qbasic_core/py.typed +0 -0
- qbasic_core/scope.py +74 -0
- qbasic_core/screen.py +115 -0
- qbasic_core/state_display.py +60 -0
- qbasic_core/statements.py +387 -0
- qbasic_core/strings.py +107 -0
- qbasic_core/subs.py +261 -0
- qbasic_core/sweep.py +82 -0
- qbasic_core/terminal.py +1697 -0
- qubasic-0.1.0.dist-info/METADATA +736 -0
- qubasic-0.1.0.dist-info/RECORD +48 -0
- qubasic-0.1.0.dist-info/WHEEL +5 -0
- qubasic-0.1.0.dist-info/entry_points.txt +2 -0
- qubasic-0.1.0.dist-info/licenses/LICENSE +21 -0
- qubasic-0.1.0.dist-info/top_level.txt +2 -0
qbasic_core/parser.py
ADDED
|
@@ -0,0 +1,564 @@
|
|
|
1
|
+
"""QBASIC parser — converts raw statement strings into typed Stmt objects."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import re
|
|
6
|
+
|
|
7
|
+
from qbasic_core.engine import (
|
|
8
|
+
GATE_TABLE, GATE_ALIASES,
|
|
9
|
+
RE_LET_ARRAY, RE_LET_VAR, RE_PRINT,
|
|
10
|
+
RE_GOTO, RE_GOSUB, RE_FOR, RE_NEXT, RE_WHILE, RE_IF_THEN,
|
|
11
|
+
RE_MEAS, RE_RESET, RE_SEND, RE_SHARE, RE_AT_REG_LINE, RE_AT_REG,
|
|
12
|
+
RE_DATA, RE_READ, RE_ON_GOTO, RE_ON_GOSUB,
|
|
13
|
+
RE_SELECT_CASE, RE_CASE, RE_DO, RE_LOOP_STMT, RE_EXIT,
|
|
14
|
+
RE_SUB, RE_END_SUB, RE_FUNCTION, RE_END_FUNCTION, RE_CALL,
|
|
15
|
+
RE_LOCAL, RE_STATIC_DECL, RE_SHARED,
|
|
16
|
+
RE_ON_ERROR, RE_RESUME, RE_ERROR_STMT, RE_ASSERT,
|
|
17
|
+
RE_SWAP, RE_DEF_FN, RE_OPTION_BASE,
|
|
18
|
+
RE_POKE, RE_SYS, RE_UNITARY, RE_DIM, RE_REDIM, RE_ERASE,
|
|
19
|
+
RE_GET, RE_INPUT, RE_LINE_INPUT,
|
|
20
|
+
RE_PRINT_USING, RE_OPEN, RE_CLOSE, RE_PRINT_FILE, RE_INPUT_FILE,
|
|
21
|
+
RE_LPRINT, RE_SCREEN, RE_COLOR, RE_LOCATE,
|
|
22
|
+
RE_ON_MEASURE, RE_ON_TIMER, RE_IMPORT, RE_CHAIN, RE_MERGE,
|
|
23
|
+
RE_LET_STR, RE_DIM_MULTI, RE_MEASURE_BASIS, RE_SYNDROME,
|
|
24
|
+
)
|
|
25
|
+
from qbasic_core.statements import (
|
|
26
|
+
Stmt, RawStmt, GateStmt, RemStmt, MeasureStmt, EndStmt, ReturnStmt,
|
|
27
|
+
BarrierStmt, WendStmt, RestoreStmt, EndSelectStmt, EndSubStmt,
|
|
28
|
+
EndFunctionStmt, StopStmt,
|
|
29
|
+
GotoStmt, GosubStmt, ForStmt, NextStmt, WhileStmt, IfThenStmt,
|
|
30
|
+
DoStmt, LoopStmt, ExitStmt,
|
|
31
|
+
OnGotoStmt, OnGosubStmt, SelectCaseStmt, CaseStmt,
|
|
32
|
+
CallStmt, SubStmt, FunctionStmt,
|
|
33
|
+
OnErrorStmt, ResumeStmt, ErrorStmt, AssertStmt,
|
|
34
|
+
SwapStmt, DefFnStmt, OptionBaseStmt,
|
|
35
|
+
OnMeasureStmt, OnTimerStmt, DataStmt, ReadStmt,
|
|
36
|
+
LocalStmt, StaticStmt, SharedStmt,
|
|
37
|
+
LetStmt, LetArrayStmt, LetStrStmt, PrintStmt, PrintUsingStmt,
|
|
38
|
+
InputStmt, LineInputStmt, GetStmt,
|
|
39
|
+
DimStmt, RedimStmt, EraseStmt,
|
|
40
|
+
PokeStmt, SysStmt, UnitaryStmt,
|
|
41
|
+
OpenStmt, CloseStmt, PrintFileStmt, InputFileStmt, LprintStmt,
|
|
42
|
+
ScreenStmt, ColorStmt, LocateStmt, ImportStmt, ChainStmt, MergeStmt,
|
|
43
|
+
MeasStmt, ResetStmt, MeasureBasisStmt, SyndromeStmt,
|
|
44
|
+
SendStmt, ShareStmt, AtRegStmt,
|
|
45
|
+
CompoundStmt,
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
# ═══════════════════════════════════════════════════════════════════════
|
|
50
|
+
# Keyword dispatch handlers
|
|
51
|
+
# ═══════════════════════════════════════════════════════════════════════
|
|
52
|
+
|
|
53
|
+
def _handle_goto(text, raw):
|
|
54
|
+
m = RE_GOTO.match(text)
|
|
55
|
+
if m:
|
|
56
|
+
return GotoStmt(raw=raw, target=int(m.group(1)))
|
|
57
|
+
return None
|
|
58
|
+
|
|
59
|
+
def _handle_gosub(text, raw):
|
|
60
|
+
m = RE_GOSUB.match(text)
|
|
61
|
+
if m:
|
|
62
|
+
return GosubStmt(raw=raw, target=int(m.group(1)))
|
|
63
|
+
return None
|
|
64
|
+
|
|
65
|
+
def _handle_for(text, raw):
|
|
66
|
+
m = RE_FOR.match(text)
|
|
67
|
+
if m:
|
|
68
|
+
return ForStmt(raw=raw, var=m.group(1),
|
|
69
|
+
start_expr=m.group(2), end_expr=m.group(3),
|
|
70
|
+
step_expr=m.group(4))
|
|
71
|
+
return None
|
|
72
|
+
|
|
73
|
+
def _handle_next(text, raw):
|
|
74
|
+
m = RE_NEXT.match(text)
|
|
75
|
+
if m:
|
|
76
|
+
return NextStmt(raw=raw, var=m.group(1))
|
|
77
|
+
return None
|
|
78
|
+
|
|
79
|
+
def _handle_while(text, raw):
|
|
80
|
+
m = RE_WHILE.match(text)
|
|
81
|
+
if m:
|
|
82
|
+
return WhileStmt(raw=raw, condition=m.group(1).strip())
|
|
83
|
+
return None
|
|
84
|
+
|
|
85
|
+
def _handle_if(text, raw):
|
|
86
|
+
m = RE_IF_THEN.match(text)
|
|
87
|
+
if m:
|
|
88
|
+
return IfThenStmt(raw=raw, condition=m.group(1).strip(),
|
|
89
|
+
then_clause=m.group(2).strip(),
|
|
90
|
+
else_clause=m.group(3).strip() if m.group(3) else None)
|
|
91
|
+
return None
|
|
92
|
+
|
|
93
|
+
def _handle_do(text, raw):
|
|
94
|
+
m = RE_DO.match(text)
|
|
95
|
+
if m:
|
|
96
|
+
return DoStmt(raw=raw, kind=m.group(1), condition=m.group(2))
|
|
97
|
+
return None
|
|
98
|
+
|
|
99
|
+
def _handle_loop(text, raw):
|
|
100
|
+
m = RE_LOOP_STMT.match(text)
|
|
101
|
+
if m:
|
|
102
|
+
return LoopStmt(raw=raw, kind=m.group(1), condition=m.group(2))
|
|
103
|
+
return None
|
|
104
|
+
|
|
105
|
+
def _handle_exit(text, raw):
|
|
106
|
+
m = RE_EXIT.match(text)
|
|
107
|
+
if m:
|
|
108
|
+
return ExitStmt(raw=raw, target=m.group(1).upper())
|
|
109
|
+
return None
|
|
110
|
+
|
|
111
|
+
def _handle_on(text, raw):
|
|
112
|
+
m = RE_ON_MEASURE.match(text)
|
|
113
|
+
if m:
|
|
114
|
+
return OnMeasureStmt(raw=raw, target=int(m.group(1)))
|
|
115
|
+
m = RE_ON_TIMER.match(text)
|
|
116
|
+
if m:
|
|
117
|
+
return OnTimerStmt(raw=raw, interval=m.group(1), target=int(m.group(2)))
|
|
118
|
+
m = RE_ON_ERROR.match(text)
|
|
119
|
+
if m:
|
|
120
|
+
return OnErrorStmt(raw=raw, target=int(m.group(1)))
|
|
121
|
+
m = RE_ON_GOTO.match(text)
|
|
122
|
+
if m:
|
|
123
|
+
targets = tuple(int(t.strip()) for t in m.group(2).split(',') if t.strip())
|
|
124
|
+
return OnGotoStmt(raw=raw, expr=m.group(1).strip(), targets=targets)
|
|
125
|
+
m = RE_ON_GOSUB.match(text)
|
|
126
|
+
if m:
|
|
127
|
+
targets = tuple(int(t.strip()) for t in m.group(2).split(',') if t.strip())
|
|
128
|
+
return OnGosubStmt(raw=raw, expr=m.group(1).strip(), targets=targets)
|
|
129
|
+
return None
|
|
130
|
+
|
|
131
|
+
def _handle_select(text, raw):
|
|
132
|
+
m = RE_SELECT_CASE.match(text)
|
|
133
|
+
if m:
|
|
134
|
+
return SelectCaseStmt(raw=raw, expr=m.group(1).strip())
|
|
135
|
+
return None
|
|
136
|
+
|
|
137
|
+
def _handle_case(text, raw):
|
|
138
|
+
m = RE_CASE.match(text)
|
|
139
|
+
if m:
|
|
140
|
+
return CaseStmt(raw=raw, value=m.group(1).strip())
|
|
141
|
+
return None
|
|
142
|
+
|
|
143
|
+
def _handle_sub(text, raw):
|
|
144
|
+
m = RE_SUB.match(text)
|
|
145
|
+
if m:
|
|
146
|
+
return SubStmt(raw=raw, name=m.group(1).upper(),
|
|
147
|
+
params=m.group(2) or '')
|
|
148
|
+
return None
|
|
149
|
+
|
|
150
|
+
def _handle_function(text, raw):
|
|
151
|
+
m = RE_FUNCTION.match(text)
|
|
152
|
+
if m:
|
|
153
|
+
return FunctionStmt(raw=raw, name=m.group(1).upper(),
|
|
154
|
+
params=m.group(2) or '')
|
|
155
|
+
return None
|
|
156
|
+
|
|
157
|
+
def _handle_call(text, raw):
|
|
158
|
+
m = RE_CALL.match(text)
|
|
159
|
+
if m:
|
|
160
|
+
return CallStmt(raw=raw, name=m.group(1).upper(),
|
|
161
|
+
args=m.group(2) or '')
|
|
162
|
+
return None
|
|
163
|
+
|
|
164
|
+
def _handle_local(text, raw):
|
|
165
|
+
m = RE_LOCAL.match(text)
|
|
166
|
+
if m:
|
|
167
|
+
return LocalStmt(raw=raw, var_list=m.group(1))
|
|
168
|
+
return None
|
|
169
|
+
|
|
170
|
+
def _handle_static(text, raw):
|
|
171
|
+
m = RE_STATIC_DECL.match(text)
|
|
172
|
+
if m:
|
|
173
|
+
return StaticStmt(raw=raw, var_list=m.group(1))
|
|
174
|
+
return None
|
|
175
|
+
|
|
176
|
+
def _handle_shared(text, raw):
|
|
177
|
+
m = RE_SHARED.match(text)
|
|
178
|
+
if m:
|
|
179
|
+
return SharedStmt(raw=raw, var_list=m.group(1))
|
|
180
|
+
return None
|
|
181
|
+
|
|
182
|
+
def _handle_resume(text, raw):
|
|
183
|
+
m = RE_RESUME.match(text)
|
|
184
|
+
if m:
|
|
185
|
+
return ResumeStmt(raw=raw, arg=m.group(1))
|
|
186
|
+
return None
|
|
187
|
+
|
|
188
|
+
def _handle_error(text, raw):
|
|
189
|
+
m = RE_ERROR_STMT.match(text)
|
|
190
|
+
if m:
|
|
191
|
+
return ErrorStmt(raw=raw, code=int(m.group(1)))
|
|
192
|
+
return None
|
|
193
|
+
|
|
194
|
+
def _handle_assert(text, raw):
|
|
195
|
+
m = RE_ASSERT.match(text)
|
|
196
|
+
if m:
|
|
197
|
+
return AssertStmt(raw=raw, condition=m.group(1).strip())
|
|
198
|
+
return None
|
|
199
|
+
|
|
200
|
+
def _handle_swap(text, raw):
|
|
201
|
+
m = RE_SWAP.match(text)
|
|
202
|
+
if m:
|
|
203
|
+
return SwapStmt(raw=raw, a=m.group(1), b=m.group(2))
|
|
204
|
+
return None
|
|
205
|
+
|
|
206
|
+
def _handle_def(text, raw):
|
|
207
|
+
m = RE_DEF_FN.match(text)
|
|
208
|
+
if m:
|
|
209
|
+
return DefFnStmt(raw=raw, name=m.group(1),
|
|
210
|
+
params=m.group(2), body=m.group(3))
|
|
211
|
+
return None
|
|
212
|
+
|
|
213
|
+
def _handle_option(text, raw):
|
|
214
|
+
m = RE_OPTION_BASE.match(text)
|
|
215
|
+
if m:
|
|
216
|
+
return OptionBaseStmt(raw=raw, base=int(m.group(1)))
|
|
217
|
+
return None
|
|
218
|
+
|
|
219
|
+
def _handle_data(text, raw):
|
|
220
|
+
m = RE_DATA.match(text)
|
|
221
|
+
if m:
|
|
222
|
+
return DataStmt(raw=raw, values=m.group(1))
|
|
223
|
+
return None
|
|
224
|
+
|
|
225
|
+
def _handle_read(text, raw):
|
|
226
|
+
m = RE_READ.match(text)
|
|
227
|
+
if m:
|
|
228
|
+
return ReadStmt(raw=raw, var_list=m.group(1))
|
|
229
|
+
return None
|
|
230
|
+
|
|
231
|
+
def _handle_let(text, raw):
|
|
232
|
+
m = RE_LET_ARRAY.match(text)
|
|
233
|
+
if m:
|
|
234
|
+
return LetArrayStmt(raw=raw, name=m.group(1),
|
|
235
|
+
index_expr=m.group(2), value_expr=m.group(3))
|
|
236
|
+
m = RE_LET_STR.match(text)
|
|
237
|
+
if m:
|
|
238
|
+
return LetStrStmt(raw=raw, name=m.group(1), expr=m.group(2))
|
|
239
|
+
m = RE_LET_VAR.match(text)
|
|
240
|
+
if m:
|
|
241
|
+
return LetStmt(raw=raw, name=m.group(1), expr=m.group(2))
|
|
242
|
+
return None
|
|
243
|
+
|
|
244
|
+
def _handle_print(text, raw):
|
|
245
|
+
m = RE_PRINT_USING.match(text)
|
|
246
|
+
if m:
|
|
247
|
+
return PrintUsingStmt(raw=raw, fmt=m.group(1), values=m.group(2))
|
|
248
|
+
m = RE_PRINT_FILE.match(text)
|
|
249
|
+
if m:
|
|
250
|
+
return PrintFileStmt(raw=raw, handle=int(m.group(1)), data=m.group(2))
|
|
251
|
+
m = RE_PRINT.match(text)
|
|
252
|
+
if m:
|
|
253
|
+
return PrintStmt(raw=raw, expr=m.group(1).strip())
|
|
254
|
+
return None
|
|
255
|
+
|
|
256
|
+
def _handle_line(text, raw):
|
|
257
|
+
m = RE_LINE_INPUT.match(text)
|
|
258
|
+
if m:
|
|
259
|
+
return LineInputStmt(raw=raw, prompt=m.group(1), var=m.group(2))
|
|
260
|
+
return None
|
|
261
|
+
|
|
262
|
+
def _handle_get(text, raw):
|
|
263
|
+
m = RE_GET.match(text)
|
|
264
|
+
if m:
|
|
265
|
+
return GetStmt(raw=raw, var=m.group(1))
|
|
266
|
+
return None
|
|
267
|
+
|
|
268
|
+
def _handle_input(text, raw):
|
|
269
|
+
m = RE_INPUT_FILE.match(text)
|
|
270
|
+
if m:
|
|
271
|
+
return InputFileStmt(raw=raw, handle=int(m.group(1)), var=m.group(2))
|
|
272
|
+
m = RE_INPUT.match(text)
|
|
273
|
+
if m:
|
|
274
|
+
return InputStmt(raw=raw, prompt=m.group(1), var=m.group(2))
|
|
275
|
+
return None
|
|
276
|
+
|
|
277
|
+
def _handle_lprint(text, raw):
|
|
278
|
+
m = RE_LPRINT.match(text)
|
|
279
|
+
if m:
|
|
280
|
+
return LprintStmt(raw=raw, expr=m.group(1))
|
|
281
|
+
return None
|
|
282
|
+
|
|
283
|
+
def _handle_poke(text, raw):
|
|
284
|
+
m = RE_POKE.match(text)
|
|
285
|
+
if m:
|
|
286
|
+
return PokeStmt(raw=raw, addr_expr=m.group(1), value_expr=m.group(2))
|
|
287
|
+
return None
|
|
288
|
+
|
|
289
|
+
def _handle_sys(text, raw):
|
|
290
|
+
m = RE_SYS.match(text)
|
|
291
|
+
if m:
|
|
292
|
+
return SysStmt(raw=raw, arg=m.group(1))
|
|
293
|
+
return None
|
|
294
|
+
|
|
295
|
+
def _handle_unitary(text, raw):
|
|
296
|
+
m = RE_UNITARY.match(text)
|
|
297
|
+
if m:
|
|
298
|
+
return UnitaryStmt(raw=raw, name=m.group(1), matrix=m.group(2))
|
|
299
|
+
return None
|
|
300
|
+
|
|
301
|
+
def _handle_redim(text, raw):
|
|
302
|
+
m = RE_REDIM.match(text)
|
|
303
|
+
if m:
|
|
304
|
+
return RedimStmt(raw=raw, name=m.group(1), size=m.group(2))
|
|
305
|
+
return None
|
|
306
|
+
|
|
307
|
+
def _handle_erase(text, raw):
|
|
308
|
+
m = RE_ERASE.match(text)
|
|
309
|
+
if m:
|
|
310
|
+
return EraseStmt(raw=raw, name=m.group(1))
|
|
311
|
+
return None
|
|
312
|
+
|
|
313
|
+
def _handle_dim(text, raw):
|
|
314
|
+
m = RE_DIM_MULTI.match(text)
|
|
315
|
+
if m:
|
|
316
|
+
return DimStmt(raw=raw, name=m.group(1), size=m.group(2))
|
|
317
|
+
m = RE_DIM.match(text)
|
|
318
|
+
if m:
|
|
319
|
+
return DimStmt(raw=raw, name=m.group(1), size=m.group(2))
|
|
320
|
+
return None
|
|
321
|
+
|
|
322
|
+
def _handle_open(text, raw):
|
|
323
|
+
m = RE_OPEN.match(text)
|
|
324
|
+
if m:
|
|
325
|
+
return OpenStmt(raw=raw, path=m.group(1).strip(),
|
|
326
|
+
mode=m.group(2).upper(), handle=int(m.group(3)),
|
|
327
|
+
encoding=m.group(4).strip() if m.group(4) else None)
|
|
328
|
+
return None
|
|
329
|
+
|
|
330
|
+
def _handle_close(text, raw):
|
|
331
|
+
m = RE_CLOSE.match(text)
|
|
332
|
+
if m:
|
|
333
|
+
return CloseStmt(raw=raw, handle=int(m.group(1)))
|
|
334
|
+
return None
|
|
335
|
+
|
|
336
|
+
def _handle_import(text, raw):
|
|
337
|
+
m = RE_IMPORT.match(text)
|
|
338
|
+
if m:
|
|
339
|
+
return ImportStmt(raw=raw, path=m.group(1).strip())
|
|
340
|
+
return None
|
|
341
|
+
|
|
342
|
+
def _handle_chain(text, raw):
|
|
343
|
+
m = RE_CHAIN.match(text)
|
|
344
|
+
if m:
|
|
345
|
+
return ChainStmt(raw=raw, path=m.group(1).strip())
|
|
346
|
+
return None
|
|
347
|
+
|
|
348
|
+
def _handle_merge(text, raw):
|
|
349
|
+
m = RE_MERGE.match(text)
|
|
350
|
+
if m:
|
|
351
|
+
return MergeStmt(raw=raw, path=m.group(1).strip())
|
|
352
|
+
return None
|
|
353
|
+
|
|
354
|
+
def _handle_screen(text, raw):
|
|
355
|
+
m = RE_SCREEN.match(text)
|
|
356
|
+
if m:
|
|
357
|
+
return ScreenStmt(raw=raw, mode=m.group(1))
|
|
358
|
+
return None
|
|
359
|
+
|
|
360
|
+
def _handle_color(text, raw):
|
|
361
|
+
m = RE_COLOR.match(text)
|
|
362
|
+
if m:
|
|
363
|
+
return ColorStmt(raw=raw, fg=m.group(1), bg=m.group(2))
|
|
364
|
+
return None
|
|
365
|
+
|
|
366
|
+
def _handle_locate(text, raw):
|
|
367
|
+
m = RE_LOCATE.match(text)
|
|
368
|
+
if m:
|
|
369
|
+
return LocateStmt(raw=raw, row=m.group(1), col=m.group(2))
|
|
370
|
+
return None
|
|
371
|
+
|
|
372
|
+
def _handle_measure_basis(text, raw):
|
|
373
|
+
m = RE_MEASURE_BASIS.match(text)
|
|
374
|
+
if m:
|
|
375
|
+
return MeasureBasisStmt(raw=raw, basis=m.group(1).upper(),
|
|
376
|
+
qubit_expr=m.group(2))
|
|
377
|
+
return None
|
|
378
|
+
|
|
379
|
+
def _handle_syndrome(text, raw):
|
|
380
|
+
m = RE_SYNDROME.match(text)
|
|
381
|
+
if m:
|
|
382
|
+
return SyndromeStmt(raw=raw, rest=m.group(1).strip())
|
|
383
|
+
return None
|
|
384
|
+
|
|
385
|
+
def _handle_meas(text, raw):
|
|
386
|
+
m = RE_MEAS.match(text)
|
|
387
|
+
if m:
|
|
388
|
+
return MeasStmt(raw=raw, qubit_expr=m.group(1), var=m.group(2))
|
|
389
|
+
return None
|
|
390
|
+
|
|
391
|
+
def _handle_reset(text, raw):
|
|
392
|
+
m = RE_RESET.match(text)
|
|
393
|
+
if m:
|
|
394
|
+
return ResetStmt(raw=raw, qubit_expr=m.group(1))
|
|
395
|
+
return None
|
|
396
|
+
|
|
397
|
+
def _handle_send(text, raw):
|
|
398
|
+
m = RE_SEND.match(text)
|
|
399
|
+
if m:
|
|
400
|
+
return SendStmt(raw=raw, reg=m.group(1).upper(),
|
|
401
|
+
qubit_expr=m.group(2), var=m.group(3))
|
|
402
|
+
return None
|
|
403
|
+
|
|
404
|
+
def _handle_share(text, raw):
|
|
405
|
+
m = RE_SHARE.match(text)
|
|
406
|
+
if m:
|
|
407
|
+
return ShareStmt(raw=raw, reg1=m.group(1).upper(), q1=int(m.group(2)),
|
|
408
|
+
reg2=m.group(3).upper(), q2=int(m.group(4)))
|
|
409
|
+
return None
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
# ═══════════════════════════════════════════════════════════════════════
|
|
413
|
+
# First-word dispatch table
|
|
414
|
+
# ═══════════════════════════════════════════════════════════════════════
|
|
415
|
+
|
|
416
|
+
_KEYWORD_PARSERS = {
|
|
417
|
+
'GOTO': _handle_goto,
|
|
418
|
+
'GOSUB': _handle_gosub,
|
|
419
|
+
'FOR': _handle_for,
|
|
420
|
+
'NEXT': _handle_next,
|
|
421
|
+
'WHILE': _handle_while,
|
|
422
|
+
'IF': _handle_if,
|
|
423
|
+
'DO': _handle_do,
|
|
424
|
+
'LOOP': _handle_loop,
|
|
425
|
+
'EXIT': _handle_exit,
|
|
426
|
+
'ON': _handle_on,
|
|
427
|
+
'SELECT': _handle_select,
|
|
428
|
+
'CASE': _handle_case,
|
|
429
|
+
'SUB': _handle_sub,
|
|
430
|
+
'FUNCTION': _handle_function,
|
|
431
|
+
'CALL': _handle_call,
|
|
432
|
+
'LOCAL': _handle_local,
|
|
433
|
+
'STATIC': _handle_static,
|
|
434
|
+
'SHARED': _handle_shared,
|
|
435
|
+
'RESUME': _handle_resume,
|
|
436
|
+
'ERROR': _handle_error,
|
|
437
|
+
'ASSERT': _handle_assert,
|
|
438
|
+
'SWAP': _handle_swap,
|
|
439
|
+
'DEF': _handle_def,
|
|
440
|
+
'OPTION': _handle_option,
|
|
441
|
+
'DATA': _handle_data,
|
|
442
|
+
'READ': _handle_read,
|
|
443
|
+
'LET': _handle_let,
|
|
444
|
+
'PRINT': _handle_print,
|
|
445
|
+
'LINE': _handle_line,
|
|
446
|
+
'GET': _handle_get,
|
|
447
|
+
'INPUT': _handle_input,
|
|
448
|
+
'LPRINT': _handle_lprint,
|
|
449
|
+
'POKE': _handle_poke,
|
|
450
|
+
'SYS': _handle_sys,
|
|
451
|
+
'UNITARY': _handle_unitary,
|
|
452
|
+
'REDIM': _handle_redim,
|
|
453
|
+
'ERASE': _handle_erase,
|
|
454
|
+
'DIM': _handle_dim,
|
|
455
|
+
'OPEN': _handle_open,
|
|
456
|
+
'CLOSE': _handle_close,
|
|
457
|
+
'IMPORT': _handle_import,
|
|
458
|
+
'CHAIN': _handle_chain,
|
|
459
|
+
'MERGE': _handle_merge,
|
|
460
|
+
'SCREEN': _handle_screen,
|
|
461
|
+
'COLOR': _handle_color,
|
|
462
|
+
'LOCATE': _handle_locate,
|
|
463
|
+
'MEASURE_X': _handle_measure_basis,
|
|
464
|
+
'MEASURE_Y': _handle_measure_basis,
|
|
465
|
+
'MEASURE_Z': _handle_measure_basis,
|
|
466
|
+
'SYNDROME': _handle_syndrome,
|
|
467
|
+
'MEAS': _handle_meas,
|
|
468
|
+
'RESET': _handle_reset,
|
|
469
|
+
'SEND': _handle_send,
|
|
470
|
+
'SHARE': _handle_share,
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
|
|
474
|
+
def _split_colon_stmts(stmt: str) -> list[str]:
|
|
475
|
+
"""Split colon-separated statements, inheriting @register prefixes."""
|
|
476
|
+
parts = []
|
|
477
|
+
last_reg = None
|
|
478
|
+
for sub in stmt.split(':'):
|
|
479
|
+
sub = sub.strip()
|
|
480
|
+
if not sub:
|
|
481
|
+
continue
|
|
482
|
+
m_reg = RE_AT_REG.match(sub)
|
|
483
|
+
if m_reg:
|
|
484
|
+
last_reg = m_reg.group(1).upper()
|
|
485
|
+
elif last_reg and not sub.upper().startswith((
|
|
486
|
+
'SEND', 'IF ', 'REM', 'FOR', 'NEXT',
|
|
487
|
+
'SHARE', 'MEASURE', '@')):
|
|
488
|
+
sub = f"@{last_reg} {sub}"
|
|
489
|
+
parts.append(sub)
|
|
490
|
+
return parts
|
|
491
|
+
|
|
492
|
+
|
|
493
|
+
def parse_stmt(raw: str) -> Stmt:
|
|
494
|
+
"""Parse a raw statement string into a typed Stmt.
|
|
495
|
+
|
|
496
|
+
Returns RawStmt only for gate applications and truly unrecognized input.
|
|
497
|
+
"""
|
|
498
|
+
text = raw.strip()
|
|
499
|
+
if not text:
|
|
500
|
+
return RawStmt(raw=raw)
|
|
501
|
+
|
|
502
|
+
upper = text.upper()
|
|
503
|
+
|
|
504
|
+
# ── Terminals ──────────────────────────────────────────────────
|
|
505
|
+
if upper.startswith('REM') or upper.startswith("'"):
|
|
506
|
+
return RemStmt(raw=raw)
|
|
507
|
+
if upper == 'MEASURE':
|
|
508
|
+
return MeasureStmt(raw=raw)
|
|
509
|
+
if upper == 'END':
|
|
510
|
+
return EndStmt(raw=raw)
|
|
511
|
+
if upper == 'RETURN':
|
|
512
|
+
return ReturnStmt(raw=raw)
|
|
513
|
+
if upper == 'BARRIER':
|
|
514
|
+
return BarrierStmt(raw=raw)
|
|
515
|
+
if upper == 'WEND':
|
|
516
|
+
return WendStmt(raw=raw)
|
|
517
|
+
if upper == 'RESTORE':
|
|
518
|
+
return RestoreStmt(raw=raw)
|
|
519
|
+
if upper == 'END SELECT':
|
|
520
|
+
return EndSelectStmt(raw=raw)
|
|
521
|
+
if upper == 'STOP':
|
|
522
|
+
return StopStmt(raw=raw)
|
|
523
|
+
|
|
524
|
+
# ── END SUB / END FUNCTION ────────────────────────────────────
|
|
525
|
+
m = RE_END_SUB.match(text)
|
|
526
|
+
if m:
|
|
527
|
+
return EndSubStmt(raw=raw)
|
|
528
|
+
m = RE_END_FUNCTION.match(text)
|
|
529
|
+
if m:
|
|
530
|
+
return EndFunctionStmt(raw=raw)
|
|
531
|
+
|
|
532
|
+
# ── First-word dispatch ───────────────────────────────────────
|
|
533
|
+
first_word = text.split(None, 1)[0].upper()
|
|
534
|
+
handler = _KEYWORD_PARSERS.get(first_word)
|
|
535
|
+
if handler is not None:
|
|
536
|
+
result = handler(text, raw)
|
|
537
|
+
if result is not None:
|
|
538
|
+
return result
|
|
539
|
+
|
|
540
|
+
# ── @REG lines ────────────────────────────────────────────────
|
|
541
|
+
if text.startswith('@'):
|
|
542
|
+
m = RE_AT_REG_LINE.match(text)
|
|
543
|
+
if m:
|
|
544
|
+
return AtRegStmt(raw=raw, reg=m.group(1).upper(),
|
|
545
|
+
inner=m.group(2).strip())
|
|
546
|
+
|
|
547
|
+
# ── Compound (colon-separated) ────────────────────────────────
|
|
548
|
+
if ':' in text:
|
|
549
|
+
parts = _split_colon_stmts(text)
|
|
550
|
+
if len(parts) > 1:
|
|
551
|
+
return CompoundStmt(raw=raw, parts=tuple(parts))
|
|
552
|
+
|
|
553
|
+
# ── Gate application ────────────────────────────────────────────
|
|
554
|
+
canonical = GATE_ALIASES.get(first_word, first_word)
|
|
555
|
+
if canonical in GATE_TABLE:
|
|
556
|
+
rest_args = text.split(None, 1)[1].strip() if ' ' in text.strip() else ''
|
|
557
|
+
if ',' in rest_args:
|
|
558
|
+
args = tuple(a.strip() for a in rest_args.split(',') if a.strip())
|
|
559
|
+
else:
|
|
560
|
+
args = tuple(a.strip() for a in rest_args.split() if a.strip())
|
|
561
|
+
return GateStmt(raw=raw, name=canonical, args=args)
|
|
562
|
+
|
|
563
|
+
# ── Fallback (subroutine calls, truly unrecognized) ─────────────
|
|
564
|
+
return RawStmt(raw=raw)
|