bloqade-circuit 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.

Potentially problematic release.


This version of bloqade-circuit might be problematic. Click here for more details.

Files changed (153) hide show
  1. bloqade/analysis/__init__.py +0 -0
  2. bloqade/analysis/address/__init__.py +11 -0
  3. bloqade/analysis/address/analysis.py +60 -0
  4. bloqade/analysis/address/impls.py +228 -0
  5. bloqade/analysis/address/lattice.py +85 -0
  6. bloqade/noise/__init__.py +1 -0
  7. bloqade/noise/native/__init__.py +20 -0
  8. bloqade/noise/native/_dialect.py +3 -0
  9. bloqade/noise/native/_wrappers.py +34 -0
  10. bloqade/noise/native/model.py +347 -0
  11. bloqade/noise/native/rewrite.py +35 -0
  12. bloqade/noise/native/stmts.py +46 -0
  13. bloqade/pyqrack/__init__.py +18 -0
  14. bloqade/pyqrack/base.py +131 -0
  15. bloqade/pyqrack/noise/__init__.py +0 -0
  16. bloqade/pyqrack/noise/native.py +100 -0
  17. bloqade/pyqrack/qasm2/__init__.py +0 -0
  18. bloqade/pyqrack/qasm2/core.py +79 -0
  19. bloqade/pyqrack/qasm2/parallel.py +46 -0
  20. bloqade/pyqrack/qasm2/uop.py +247 -0
  21. bloqade/pyqrack/reg.py +109 -0
  22. bloqade/pyqrack/target.py +112 -0
  23. bloqade/qasm2/__init__.py +19 -0
  24. bloqade/qasm2/_wrappers.py +674 -0
  25. bloqade/qasm2/dialects/__init__.py +10 -0
  26. bloqade/qasm2/dialects/core/__init__.py +3 -0
  27. bloqade/qasm2/dialects/core/_dialect.py +3 -0
  28. bloqade/qasm2/dialects/core/_emit.py +68 -0
  29. bloqade/qasm2/dialects/core/_typeinfer.py +23 -0
  30. bloqade/qasm2/dialects/core/address.py +38 -0
  31. bloqade/qasm2/dialects/core/stmts.py +94 -0
  32. bloqade/qasm2/dialects/expr/__init__.py +3 -0
  33. bloqade/qasm2/dialects/expr/_dialect.py +3 -0
  34. bloqade/qasm2/dialects/expr/_emit.py +103 -0
  35. bloqade/qasm2/dialects/expr/_from_python.py +86 -0
  36. bloqade/qasm2/dialects/expr/_interp.py +75 -0
  37. bloqade/qasm2/dialects/expr/stmts.py +262 -0
  38. bloqade/qasm2/dialects/glob.py +45 -0
  39. bloqade/qasm2/dialects/indexing.py +64 -0
  40. bloqade/qasm2/dialects/inline.py +76 -0
  41. bloqade/qasm2/dialects/noise.py +16 -0
  42. bloqade/qasm2/dialects/parallel.py +110 -0
  43. bloqade/qasm2/dialects/uop/__init__.py +4 -0
  44. bloqade/qasm2/dialects/uop/_dialect.py +3 -0
  45. bloqade/qasm2/dialects/uop/_emit.py +211 -0
  46. bloqade/qasm2/dialects/uop/schedule.py +89 -0
  47. bloqade/qasm2/dialects/uop/stmts.py +325 -0
  48. bloqade/qasm2/emit/__init__.py +1 -0
  49. bloqade/qasm2/emit/base.py +72 -0
  50. bloqade/qasm2/emit/gate.py +102 -0
  51. bloqade/qasm2/emit/main.py +106 -0
  52. bloqade/qasm2/emit/target.py +165 -0
  53. bloqade/qasm2/glob.py +24 -0
  54. bloqade/qasm2/groups.py +120 -0
  55. bloqade/qasm2/parallel.py +48 -0
  56. bloqade/qasm2/parse/__init__.py +37 -0
  57. bloqade/qasm2/parse/ast.py +235 -0
  58. bloqade/qasm2/parse/build.py +289 -0
  59. bloqade/qasm2/parse/lowering.py +553 -0
  60. bloqade/qasm2/parse/parser.py +5 -0
  61. bloqade/qasm2/parse/print.py +293 -0
  62. bloqade/qasm2/parse/qasm2.lark +75 -0
  63. bloqade/qasm2/parse/visitor.py +16 -0
  64. bloqade/qasm2/parse/visitor.pyi +39 -0
  65. bloqade/qasm2/passes/__init__.py +5 -0
  66. bloqade/qasm2/passes/fold.py +94 -0
  67. bloqade/qasm2/passes/glob.py +119 -0
  68. bloqade/qasm2/passes/noise.py +61 -0
  69. bloqade/qasm2/passes/parallel.py +176 -0
  70. bloqade/qasm2/passes/py2qasm.py +63 -0
  71. bloqade/qasm2/passes/qasm2py.py +61 -0
  72. bloqade/qasm2/rewrite/__init__.py +12 -0
  73. bloqade/qasm2/rewrite/desugar.py +28 -0
  74. bloqade/qasm2/rewrite/glob.py +103 -0
  75. bloqade/qasm2/rewrite/heuristic_noise.py +247 -0
  76. bloqade/qasm2/rewrite/native_gates.py +447 -0
  77. bloqade/qasm2/rewrite/parallel_to_uop.py +83 -0
  78. bloqade/qasm2/rewrite/register.py +45 -0
  79. bloqade/qasm2/rewrite/uop_to_parallel.py +395 -0
  80. bloqade/qasm2/types.py +39 -0
  81. bloqade/qbraid/__init__.py +2 -0
  82. bloqade/qbraid/lowering.py +324 -0
  83. bloqade/qbraid/schema.py +252 -0
  84. bloqade/qbraid/simulation_result.py +99 -0
  85. bloqade/qbraid/target.py +86 -0
  86. bloqade/squin/__init__.py +2 -0
  87. bloqade/squin/analysis/__init__.py +0 -0
  88. bloqade/squin/analysis/nsites/__init__.py +8 -0
  89. bloqade/squin/analysis/nsites/analysis.py +52 -0
  90. bloqade/squin/analysis/nsites/impls.py +69 -0
  91. bloqade/squin/analysis/nsites/lattice.py +49 -0
  92. bloqade/squin/analysis/schedule.py +244 -0
  93. bloqade/squin/groups.py +38 -0
  94. bloqade/squin/op/__init__.py +132 -0
  95. bloqade/squin/op/_dialect.py +3 -0
  96. bloqade/squin/op/complex.py +6 -0
  97. bloqade/squin/op/stmts.py +220 -0
  98. bloqade/squin/op/traits.py +43 -0
  99. bloqade/squin/op/types.py +10 -0
  100. bloqade/squin/qubit.py +118 -0
  101. bloqade/squin/wire.py +103 -0
  102. bloqade/stim/__init__.py +6 -0
  103. bloqade/stim/_wrappers.py +186 -0
  104. bloqade/stim/dialects/__init__.py +5 -0
  105. bloqade/stim/dialects/aux/__init__.py +11 -0
  106. bloqade/stim/dialects/aux/_dialect.py +3 -0
  107. bloqade/stim/dialects/aux/emit.py +102 -0
  108. bloqade/stim/dialects/aux/interp.py +39 -0
  109. bloqade/stim/dialects/aux/lowering.py +40 -0
  110. bloqade/stim/dialects/aux/stmts/__init__.py +14 -0
  111. bloqade/stim/dialects/aux/stmts/annotate.py +47 -0
  112. bloqade/stim/dialects/aux/stmts/const.py +95 -0
  113. bloqade/stim/dialects/aux/types.py +19 -0
  114. bloqade/stim/dialects/collapse/__init__.py +3 -0
  115. bloqade/stim/dialects/collapse/_dialect.py +3 -0
  116. bloqade/stim/dialects/collapse/emit.py +68 -0
  117. bloqade/stim/dialects/collapse/stmts/__init__.py +3 -0
  118. bloqade/stim/dialects/collapse/stmts/measure.py +45 -0
  119. bloqade/stim/dialects/collapse/stmts/pp_measure.py +14 -0
  120. bloqade/stim/dialects/collapse/stmts/reset.py +26 -0
  121. bloqade/stim/dialects/gate/__init__.py +3 -0
  122. bloqade/stim/dialects/gate/_dialect.py +3 -0
  123. bloqade/stim/dialects/gate/emit.py +87 -0
  124. bloqade/stim/dialects/gate/stmts/__init__.py +14 -0
  125. bloqade/stim/dialects/gate/stmts/base.py +31 -0
  126. bloqade/stim/dialects/gate/stmts/clifford_1q.py +53 -0
  127. bloqade/stim/dialects/gate/stmts/clifford_2q.py +11 -0
  128. bloqade/stim/dialects/gate/stmts/control_2q.py +21 -0
  129. bloqade/stim/dialects/gate/stmts/pp.py +15 -0
  130. bloqade/stim/dialects/noise/__init__.py +3 -0
  131. bloqade/stim/dialects/noise/_dialect.py +3 -0
  132. bloqade/stim/dialects/noise/emit.py +66 -0
  133. bloqade/stim/dialects/noise/stmts.py +77 -0
  134. bloqade/stim/emit/__init__.py +1 -0
  135. bloqade/stim/emit/stim.py +54 -0
  136. bloqade/stim/groups.py +26 -0
  137. bloqade/test_utils.py +35 -0
  138. bloqade/types.py +24 -0
  139. bloqade/visual/__init__.py +1 -0
  140. bloqade/visual/animation/__init__.py +0 -0
  141. bloqade/visual/animation/animate.py +267 -0
  142. bloqade/visual/animation/base.py +346 -0
  143. bloqade/visual/animation/gate_event.py +24 -0
  144. bloqade/visual/animation/runtime/__init__.py +0 -0
  145. bloqade/visual/animation/runtime/aod.py +36 -0
  146. bloqade/visual/animation/runtime/atoms.py +55 -0
  147. bloqade/visual/animation/runtime/ppoly.py +50 -0
  148. bloqade/visual/animation/runtime/qpustate.py +119 -0
  149. bloqade/visual/animation/runtime/utils.py +43 -0
  150. bloqade_circuit-0.1.0.dist-info/METADATA +70 -0
  151. bloqade_circuit-0.1.0.dist-info/RECORD +153 -0
  152. bloqade_circuit-0.1.0.dist-info/WHEEL +4 -0
  153. bloqade_circuit-0.1.0.dist-info/licenses/LICENSE +234 -0
@@ -0,0 +1,235 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Union, Literal
4
+ from dataclasses import dataclass
5
+
6
+
7
+ @dataclass
8
+ class Node:
9
+ pass
10
+
11
+
12
+ @dataclass
13
+ class Header(Node):
14
+ pass
15
+
16
+
17
+ @dataclass
18
+ class Kirin(Header):
19
+ dialects: list[str]
20
+
21
+
22
+ @dataclass
23
+ class OPENQASM(Header):
24
+ version: Version
25
+
26
+
27
+ @dataclass
28
+ class MainProgram(Node):
29
+ header: Kirin | OPENQASM
30
+ statements: list[Statement]
31
+
32
+
33
+ @dataclass
34
+ class Version(Node):
35
+ major: int
36
+ minor: int
37
+
38
+
39
+ @dataclass
40
+ class Statement(Node):
41
+ pass
42
+
43
+
44
+ @dataclass
45
+ class Comment(Statement):
46
+ text: str
47
+
48
+
49
+ @dataclass
50
+ class QReg(Statement):
51
+ name: str
52
+ size: int
53
+
54
+
55
+ @dataclass
56
+ class CReg(Statement):
57
+ name: str
58
+ size: int
59
+
60
+
61
+ @dataclass
62
+ class Gate(Statement):
63
+ name: str
64
+ cparams: list[str]
65
+ qparams: list[str]
66
+ body: list[UOp | Barrier]
67
+
68
+
69
+ @dataclass
70
+ class Opaque(Statement):
71
+ name: str
72
+ cparams: list[Expr]
73
+ qparams: list[Bit | Name]
74
+
75
+
76
+ @dataclass
77
+ class QOp(Statement):
78
+ pass
79
+
80
+
81
+ @dataclass
82
+ class IfStmt(Statement):
83
+ cond: Cmp
84
+ body: list[QOp]
85
+
86
+
87
+ @dataclass
88
+ class Barrier(Statement):
89
+ qargs: list[Bit | Name]
90
+
91
+
92
+ @dataclass
93
+ class Include(Statement):
94
+ filename: str
95
+
96
+
97
+ @dataclass
98
+ class Measure(QOp):
99
+ qarg: Bit | Name
100
+ carg: Bit | Name
101
+
102
+
103
+ @dataclass
104
+ class Reset(QOp):
105
+ qarg: Bit | Name
106
+
107
+
108
+ @dataclass
109
+ class UOp(QOp):
110
+ pass
111
+
112
+
113
+ @dataclass
114
+ class Instruction(UOp):
115
+ name: Name
116
+ params: list[Expr]
117
+ qargs: list[Bit | Name]
118
+
119
+
120
+ @dataclass
121
+ class UGate(UOp):
122
+ theta: Expr
123
+ phi: Expr
124
+ lam: Expr
125
+ qarg: Bit | Name
126
+
127
+
128
+ @dataclass
129
+ class CXGate(UOp):
130
+ ctrl: Bit | Name
131
+ qarg: Bit | Name
132
+
133
+
134
+ @dataclass
135
+ class Extension(UOp):
136
+ pass
137
+
138
+
139
+ @dataclass
140
+ class ParallelQArgs(Extension):
141
+ qargs: list[tuple[Bit | Name, ...]]
142
+
143
+ def __iter__(self):
144
+ return iter(self.qargs)
145
+
146
+
147
+ @dataclass
148
+ class GlobUGate(Extension):
149
+ registers: list[Name]
150
+ theta: Expr
151
+ phi: Expr
152
+ lam: Expr
153
+
154
+
155
+ @dataclass
156
+ class NoisePAULI1(Extension):
157
+ px: Expr
158
+ py: Expr
159
+ pz: Expr
160
+ qarg: Bit | Name
161
+
162
+
163
+ @dataclass
164
+ class ParaU3Gate(Extension):
165
+ theta: Expr
166
+ phi: Expr
167
+ lam: Expr
168
+ qargs: ParallelQArgs
169
+
170
+
171
+ @dataclass
172
+ class ParaRZGate(Extension):
173
+ theta: Expr
174
+ qargs: ParallelQArgs
175
+
176
+
177
+ @dataclass
178
+ class ParaCZGate(Extension):
179
+ qargs: ParallelQArgs
180
+
181
+
182
+ @dataclass
183
+ class Expr(Node):
184
+ pass
185
+
186
+
187
+ @dataclass
188
+ class BinOp(Expr):
189
+ op: Literal["+", "-", "*", "/", "^"]
190
+ lhs: Expr
191
+ rhs: Expr
192
+
193
+
194
+ @dataclass
195
+ class Cmp(Statement):
196
+ lhs: Expr
197
+ rhs: Expr
198
+
199
+
200
+ @dataclass
201
+ class UnaryOp(Expr):
202
+ op: Literal["-", "+"]
203
+ operand: Expr
204
+
205
+
206
+ @dataclass
207
+ class Call(Expr):
208
+ name: Literal["sin", "cos", "tan", "exp", "ln", "sqrt"]
209
+ args: list[Expr]
210
+
211
+
212
+ @dataclass
213
+ class Constant(Expr):
214
+ pass
215
+
216
+
217
+ @dataclass
218
+ class Number(Constant):
219
+ value: Union[int, float]
220
+
221
+
222
+ @dataclass
223
+ class Pi(Constant):
224
+ pass
225
+
226
+
227
+ @dataclass
228
+ class Name(Expr):
229
+ id: str
230
+
231
+
232
+ @dataclass
233
+ class Bit(Expr):
234
+ name: Name
235
+ addr: int
@@ -0,0 +1,289 @@
1
+ # type: ignore
2
+ from lark import Token
3
+ from lark.tree import ParseTree
4
+
5
+ from . import ast
6
+
7
+
8
+ class BuildError(Exception):
9
+ pass
10
+
11
+
12
+ class Build:
13
+
14
+ def build(self, tree: ParseTree) -> ast.Node:
15
+ return getattr(self, f"build_{tree.data}", self.build_generic)(tree)
16
+
17
+ def build_generic(self, tree: ParseTree) -> ast.Node:
18
+ raise NotImplementedError(f"No build_{tree.data} method")
19
+
20
+ def build_mainprogram(self, tree: ParseTree) -> ast.MainProgram:
21
+ header = self.build(tree.children[0])
22
+ return ast.MainProgram(
23
+ header=header,
24
+ statements=[self.build(stmt) for stmt in tree.children[1:]],
25
+ )
26
+
27
+ def build_openqasm(self, tree: ParseTree) -> ast.OPENQASM:
28
+ return ast.OPENQASM(version=self.build(tree.children[0]))
29
+
30
+ def build_kirin(self, tree: ParseTree) -> ast.Kirin:
31
+ return ast.Kirin(dialects=[self.build_dialect(each) for each in tree.children])
32
+
33
+ def build_dialect(self, tree: ParseTree) -> str:
34
+ return ".".join(each.value for each in tree.children)
35
+
36
+ def build_version(self, tree: ParseTree) -> ast.Version:
37
+ return ast.Version(
38
+ int(tree.children[0].value),
39
+ int(tree.children[1].value),
40
+ )
41
+
42
+ def build_include(self, tree: ParseTree) -> ast.Include:
43
+ return ast.Include(filename=tree.children[0].value[1:-1])
44
+
45
+ def build_qreg(self, tree: ParseTree) -> ast.QReg:
46
+ return ast.QReg(name=tree.children[0].value, size=int(tree.children[1].value))
47
+
48
+ def build_creg(self, tree: ParseTree) -> ast.CReg:
49
+ return ast.CReg(name=tree.children[0].value, size=int(tree.children[1].value))
50
+
51
+ def build_cx_gate(self, tree: ParseTree) -> ast.Gate:
52
+ return ast.CXGate(
53
+ self.build_bit(tree.children[0]), self.build_bit(tree.children[1])
54
+ )
55
+
56
+ def build_ugate(self, tree: ParseTree) -> ast.Gate:
57
+ return ast.UGate(
58
+ theta=self.build_expr(tree.children[0]),
59
+ phi=self.build_expr(tree.children[1]),
60
+ lam=self.build_expr(tree.children[2]),
61
+ qarg=self.build_bit(tree.children[3]),
62
+ )
63
+
64
+ def build_barrier(self, tree: ParseTree) -> ast.Barrier:
65
+ return ast.Barrier(qargs=self.build(tree.children[0]))
66
+
67
+ def build_inst(self, tree: ParseTree) -> ast.Instruction:
68
+ if tree.children[1] is None:
69
+ params = []
70
+ else:
71
+ params = self.build(tree.children[1])
72
+ return ast.Instruction(
73
+ name=ast.Name(tree.children[0].value),
74
+ params=params,
75
+ qargs=self.build(tree.children[2]),
76
+ )
77
+
78
+ def build_opaque(self, tree: ParseTree) -> ast.Opaque:
79
+ return ast.Opaque(
80
+ name=tree.children[0].value,
81
+ cparams=self.build(tree.children[1]) if tree.children[1] else [],
82
+ qparams=self.build(tree.children[2]) if tree.children[2] else [],
83
+ )
84
+
85
+ def build_gate(self, tree: ParseTree) -> ast.Gate:
86
+ cparams = tree.children[1]
87
+ qparams = tree.children[2]
88
+ return ast.Gate(
89
+ name=tree.children[0].value,
90
+ cparams=self.build(cparams) if cparams else [],
91
+ qparams=self.build(qparams) if qparams else [],
92
+ body=[self.build(stmt) for stmt in tree.children[3:]],
93
+ )
94
+
95
+ def build_noise_pauli1(self, tree: ParseTree) -> ast.NoisePAULI1:
96
+ return ast.NoisePAULI1(
97
+ px=self.build_expr(tree.children[0]),
98
+ py=self.build_expr(tree.children[1]),
99
+ pz=self.build_expr(tree.children[2]),
100
+ qarg=self.build_bit(tree.children[3]),
101
+ )
102
+
103
+ def build_glob_u_gate(self, tree: ParseTree) -> ast.GlobUGate:
104
+ return ast.GlobUGate(
105
+ [self.build_bit(each) for each in tree.children[3].children],
106
+ self.build_expr(tree.children[0]),
107
+ self.build_expr(tree.children[1]),
108
+ self.build_expr(tree.children[2]),
109
+ )
110
+
111
+ def build_para_u_gate(self, tree: ParseTree) -> ast.ParaU3Gate:
112
+ return ast.ParaU3Gate(
113
+ self.build_expr(tree.children[0]),
114
+ self.build_expr(tree.children[1]),
115
+ self.build_expr(tree.children[2]),
116
+ self.build_parallel_body(tree.children[3]),
117
+ )
118
+
119
+ def build_para_cz_gate(self, tree: ParseTree) -> ast.ParaCZGate:
120
+ return ast.ParaCZGate(
121
+ self.build_parallel_body(tree.children[0]),
122
+ )
123
+
124
+ def build_para_rz_gate(self, tree: ParseTree) -> ast.ParaRZGate:
125
+ return ast.ParaRZGate(
126
+ self.build_expr(tree.children[0]),
127
+ self.build_parallel_body(tree.children[1]),
128
+ )
129
+
130
+ def build_parallel_body(self, tree: ParseTree) -> ast.ParallelQArgs:
131
+ return ast.ParallelQArgs([self.build_task_args(each) for each in tree.children])
132
+
133
+ def build_task_args(self, tree: ParseTree) -> tuple[ast.Bit | ast.Name, ...]:
134
+ return tuple(self.build(each) for each in tree.children)
135
+
136
+ def build_measure(self, tree: ParseTree) -> ast.Measure:
137
+ return ast.Measure(
138
+ qarg=self.build_bit(tree.children[0]),
139
+ carg=self.build_bit(tree.children[1]),
140
+ )
141
+
142
+ def build_reset(self, tree: ParseTree) -> ast.Reset:
143
+ return ast.Reset(qarg=self.build_bit(tree.children[0]))
144
+
145
+ def build_ifstmt(self, tree: ParseTree) -> ast.IfStmt:
146
+ return ast.IfStmt(
147
+ cond=ast.Cmp(
148
+ lhs=self.build_expr(tree.children[0]),
149
+ rhs=self.build_expr(tree.children[1]),
150
+ ),
151
+ body=self.build(tree.children[2]),
152
+ )
153
+
154
+ def build_ifbody(self, tree: ParseTree) -> list[ast.Node]:
155
+ return [self.build(stmt) for stmt in tree.children]
156
+
157
+ def build_qparams(self, tree: ParseTree) -> list[ast.Expr]:
158
+ return [param.value for param in tree.children]
159
+
160
+ def build_cparams(self, tree: ParseTree) -> list[ast.Expr]:
161
+ return [param.value for param in tree.children]
162
+
163
+ def build_qubits(self, tree: ParseTree) -> list[ast.Bit]:
164
+ return [self.build_bit(qubit) for qubit in tree.children]
165
+
166
+ def build_bit(self, tree: ParseTree) -> ast.Name | ast.Bit:
167
+ if isinstance(tree, Token) and tree.type == "IDENTIFIER":
168
+ return ast.Name(tree.value)
169
+ else:
170
+ return ast.Bit(
171
+ name=ast.Name(tree.children[0].value), addr=int(tree.children[1].value)
172
+ )
173
+
174
+ def build_params(self, tree: ParseTree) -> list[ast.Expr]:
175
+ return [self.build_expr(param) for param in tree.children]
176
+
177
+ def build_call(self, tree: ParseTree) -> ast.Call:
178
+ return ast.Call(
179
+ name=tree.children[0].value,
180
+ args=self.build(tree.children[1]),
181
+ )
182
+
183
+ def build_arglist(self, tree: ParseTree) -> list[ast.Expr]:
184
+ return [self.build_expr(arg) for arg in tree.children]
185
+
186
+ def build_expr(self, tree: ParseTree) -> ast.Expr:
187
+ if isinstance(tree, Token):
188
+ return self._build_token_expr(tree)
189
+ elif tree.data != "expr":
190
+ return self.build(tree)
191
+
192
+ if tree.children[1].type == "PLUS":
193
+ lhs = ast.BinOp(
194
+ op="+",
195
+ lhs=self.build_term(tree.children[0]),
196
+ rhs=self.build_term(tree.children[2]),
197
+ )
198
+ elif tree.children[1].type == "MINUS":
199
+ lhs = ast.BinOp(
200
+ op="-",
201
+ lhs=self.build_term(tree.children[0]),
202
+ rhs=self.build_term(tree.children[2]),
203
+ )
204
+ else:
205
+ raise BuildError(f"Unsupported operator {tree.children[1].type}")
206
+
207
+ for i in range(3, len(tree.children), 2):
208
+ if tree.children[i].type == "PLUS":
209
+ lhs = ast.BinOp(
210
+ op="+",
211
+ lhs=lhs,
212
+ rhs=self.build_term(tree.children[i + 1]),
213
+ )
214
+ elif tree.children[i].type == "MINUS":
215
+ lhs = ast.BinOp(
216
+ op="-",
217
+ lhs=lhs,
218
+ rhs=self.build_term(tree.children[i + 1]),
219
+ )
220
+ else:
221
+ raise BuildError(f"Unsupported operator {tree.children[i].type}")
222
+
223
+ return lhs
224
+
225
+ def build_term(self, tree: ParseTree) -> ast.Expr:
226
+ if isinstance(tree, Token):
227
+ return self._build_token_expr(tree)
228
+ elif tree.data != "term":
229
+ return self.build(tree)
230
+
231
+ if tree.children[1].type == "TIMES":
232
+ lhs = ast.BinOp(
233
+ op="*",
234
+ lhs=self.build_factor(tree.children[0]),
235
+ rhs=self.build_factor(tree.children[2]),
236
+ )
237
+ elif tree.children[1].type == "DIVIDE":
238
+ lhs = ast.BinOp(
239
+ op="/",
240
+ lhs=self.build_factor(tree.children[0]),
241
+ rhs=self.build_factor(tree.children[2]),
242
+ )
243
+ else:
244
+ raise BuildError(f"Unsupported operator {tree.children[1].type}")
245
+
246
+ for i in range(3, len(tree.children), 2):
247
+ if tree.children[i].type == "TIMES":
248
+ lhs = ast.BinOp(
249
+ op="*",
250
+ lhs=lhs,
251
+ rhs=self.build_factor(tree.children[i + 1]),
252
+ )
253
+ elif tree.children[i].type == "DIVIDE":
254
+ lhs = ast.BinOp(
255
+ op="/",
256
+ lhs=lhs,
257
+ rhs=self.build_factor(tree.children[i + 1]),
258
+ )
259
+ else:
260
+ raise BuildError(f"Unsupported operator {tree.children[i].type}")
261
+
262
+ return lhs
263
+
264
+ def build_factor(self, tree: ParseTree) -> ast.Expr:
265
+ if isinstance(tree, Token):
266
+ return self._build_token_expr(tree)
267
+ elif tree.data != "factor":
268
+ return self.build(tree)
269
+
270
+ if tree.children[0].type == "PLUS":
271
+ return ast.UnaryOp(op="+", operand=self.build_expr(tree.children[1]))
272
+ elif tree.children[0].type == "MINUS":
273
+ return ast.UnaryOp(op="-", operand=self.build_expr(tree.children[1]))
274
+ else:
275
+ raise BuildError(f"Unsupported operator {tree.children[0].type}")
276
+
277
+ def _build_token_expr(self, token: Token) -> ast.Node:
278
+ if token.type == "NUMBER":
279
+ return ast.Number(float(token.value))
280
+ elif token.type == "IDENTIFIER":
281
+ return ast.Name(token.value)
282
+ elif token.type == "PI":
283
+ return ast.Pi()
284
+ elif token.type == "INT":
285
+ return ast.Number(int(token.value))
286
+ elif token.type == "FLOAT":
287
+ return ast.Number(float(token.value))
288
+ else:
289
+ raise BuildError(f"Unsupported token type {token.type}")