qasm-ts 2.1.3 → 2.1.4
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.
- package/dist/errors.d.ts +88 -0
- package/dist/errors.js +162 -0
- package/dist/lexer.d.ts +78 -0
- package/dist/lexer.js +116 -0
- package/dist/main.d.ts +87 -0
- package/dist/main.js +122 -0
- package/dist/parser.d.ts +75 -0
- package/dist/parser.js +121 -0
- package/dist/qasm2/ast.d.ts +159 -0
- package/dist/qasm2/ast.js +186 -0
- package/dist/qasm2/lexer.d.ts +135 -0
- package/dist/qasm2/lexer.js +483 -0
- package/dist/qasm2/parser.d.ts +187 -0
- package/dist/qasm2/parser.js +655 -0
- package/dist/qasm2/token.d.ts +89 -0
- package/dist/qasm2/token.js +155 -0
- package/dist/qasm3/ast.d.ts +652 -0
- package/dist/qasm3/ast.js +746 -0
- package/dist/qasm3/lexer.d.ts +156 -0
- package/dist/qasm3/lexer.js +667 -0
- package/dist/qasm3/parser.d.ts +407 -0
- package/dist/qasm3/parser.js +2079 -0
- package/dist/qasm3/token.d.ts +197 -0
- package/dist/qasm3/token.js +420 -0
- package/dist/version.d.ts +41 -0
- package/dist/version.js +53 -0
- package/package.json +1 -1
|
@@ -0,0 +1,2079 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
/**
|
|
3
|
+
* OpenQASM 3.0 Parser Implementation
|
|
4
|
+
*
|
|
5
|
+
* This module implements a recursive descent parser for OpenQASM 3.0 that transforms
|
|
6
|
+
* a stream of tokens into an Abstract Syntax Tree (AST). The parser handles the full
|
|
7
|
+
* OpenQASM 3.0 language specification including classical programming constructs,
|
|
8
|
+
* control flow, quantum operations, and advanced features.
|
|
9
|
+
*
|
|
10
|
+
* Key parsing capabilities:
|
|
11
|
+
* - **Classical types**: int, uint, float, bool, bit, complex, angle
|
|
12
|
+
* - **Control flow**: if/else, for/while loops, switch/case statements
|
|
13
|
+
* - **Functions**: def, return, extern declarations, subroutine calls
|
|
14
|
+
* - **Quantum operations**: gate definitions/calls, measurements, barriers, delays
|
|
15
|
+
* - **Advanced features**: arrays, timing constructs, calibration grammar
|
|
16
|
+
* - **Expressions**: Arithmetic, logical, function calls with proper precedence
|
|
17
|
+
*
|
|
18
|
+
* The parser maintains context about:
|
|
19
|
+
* - Defined gates (built-in, standard library, custom)
|
|
20
|
+
* - Declared subroutines and external functions
|
|
21
|
+
* - Array declarations and aliases
|
|
22
|
+
* - Type information for validation
|
|
23
|
+
*
|
|
24
|
+
* @module
|
|
25
|
+
*
|
|
26
|
+
* @example Basic parsing workflow
|
|
27
|
+
* ```typescript
|
|
28
|
+
* const tokens = lexer.lex();
|
|
29
|
+
* const parser = new Parser(tokens);
|
|
30
|
+
* const ast = parser.parse();
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
import { Token } from "./token";
|
|
34
|
+
import { OpenQASMVersion } from "../version";
|
|
35
|
+
import { BadQregError, BadMeasurementError, BadGateError, BadParameterError, UnsupportedOpenQASMVersionError, BadStringLiteralError, BadClassicalTypeError, BadExpressionError, MissingSemicolonError, MissingBraceError, BadLoopError, BadQuantumInstructionError, BadSubroutineError, } from "../errors";
|
|
36
|
+
import { Parameters, ArrayReferenceModifier, ArrayReference, CalibrationGrammarDeclaration, Include, Version, FloatType, ComplexType, BoolType, IntType, UIntType, BitType, AngleType, DurationType, Range, Identifier, SubscriptedIdentifier, Pi, Euler, Tau, MathFunctionTypes, MathFunction, TrigFunctionTypes, TrigFunction, IntegerLiteral, NumericLiteral, FloatLiteral, BooleanLiteral, BitstringLiteral, DurationUnit, DurationLiteral, DurationOf, Unary, Binary, Cast, IndexSet, ArrayDeclaration, ArrayInitializer, ArrayAccess, QuantumMeasurement, QuantumMeasurementAssignment, ClassicalDeclaration, AssignmentStatement, QuantumDeclaration, AliasStatement, QuantumGateModifierName, QuantumGateModifier, QuantumGateCall, QuantumBarrier, QuantumReset, QuantumDelay, ReturnStatement, ProgramBlock, QuantumGateDefinition, BoxDefinition, ExternSignature, SubroutineDefinition, SubroutineCall, BranchingStatement, ForLoopStatement, WhileLoopStatement, BreakStatement, ContinueStatement, IOModifier, IODeclaration, SwitchStatement, CaseStatement, DefaultStatement, ArithmeticOp, Arithmetic, ImaginaryLiteral, HardwareQubit, StretchType, SizeOf, } from "./ast";
|
|
37
|
+
/**
|
|
38
|
+
* Handles throwing parser errors with basic stack trace.
|
|
39
|
+
* @param error - The error to throw.
|
|
40
|
+
* @param token - The source token for the error.
|
|
41
|
+
* @param index - The token index.
|
|
42
|
+
* @param message - Optional additional error context.
|
|
43
|
+
*/
|
|
44
|
+
function throwParserError(error, token, index, message) {
|
|
45
|
+
const errorMessage = message
|
|
46
|
+
? `index: ${index} at token [${token}], ${message}`
|
|
47
|
+
: `index: ${index} at token [${token}]`;
|
|
48
|
+
throw new error(errorMessage);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* OpenQASM 3.0 Recursive Descent Parser
|
|
52
|
+
*
|
|
53
|
+
* Implements a comprehensive parser for the OpenQASM 3.0 language specification.
|
|
54
|
+
* The parser uses recursive descent parsing with appropriate error recovery and
|
|
55
|
+
* maintains symbol tables for gates, subroutines, and variables.
|
|
56
|
+
*
|
|
57
|
+
* Parser state includes:
|
|
58
|
+
* - Token stream and current position
|
|
59
|
+
* - Symbol tables for gates, subroutines, arrays, aliases
|
|
60
|
+
* - Machine-specific defaults (float width, int size)
|
|
61
|
+
*
|
|
62
|
+
* @example Creating and using the parser
|
|
63
|
+
* ```typescript
|
|
64
|
+
* const parser = new Parser(tokens);
|
|
65
|
+
*
|
|
66
|
+
* // Parse the entire program
|
|
67
|
+
* const ast = parser.parse();
|
|
68
|
+
*
|
|
69
|
+
* // AST contains array of top-level statements and declarations
|
|
70
|
+
* console.log(ast); // [VersionNode, IncludeNode, DeclarationNode, ...]
|
|
71
|
+
* ```
|
|
72
|
+
*/
|
|
73
|
+
class Parser {
|
|
74
|
+
/**
|
|
75
|
+
* Creates a parser.
|
|
76
|
+
* @param tokens - Tokens to parse.
|
|
77
|
+
* @param defaultFloatWidth - Optional default float width override.
|
|
78
|
+
* @param machineIntSize - Optional default int size override.
|
|
79
|
+
*/
|
|
80
|
+
constructor(tokens, defaultFloatWidth, machineIntSize) {
|
|
81
|
+
this.tokens = tokens;
|
|
82
|
+
this.gates = new Set(["U", "gphase"]);
|
|
83
|
+
this.standardGates = new Set();
|
|
84
|
+
this.customGates = new Set();
|
|
85
|
+
this.subroutines = new Set();
|
|
86
|
+
this.customArrays = new Set();
|
|
87
|
+
this.aliases = new Map();
|
|
88
|
+
this.index = 0;
|
|
89
|
+
this.machineFloatWidth = defaultFloatWidth ? defaultFloatWidth : 64;
|
|
90
|
+
this.machineIntSize = machineIntSize ? machineIntSize : 32;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Loads the standard library gates.
|
|
94
|
+
*/
|
|
95
|
+
stdGates() {
|
|
96
|
+
const gates = [
|
|
97
|
+
"p",
|
|
98
|
+
"x",
|
|
99
|
+
"y",
|
|
100
|
+
"z",
|
|
101
|
+
"h",
|
|
102
|
+
"s",
|
|
103
|
+
"sdg",
|
|
104
|
+
"t",
|
|
105
|
+
"tdg",
|
|
106
|
+
"sx",
|
|
107
|
+
"rx",
|
|
108
|
+
"ry",
|
|
109
|
+
"rz",
|
|
110
|
+
"cx",
|
|
111
|
+
"cy",
|
|
112
|
+
"cz",
|
|
113
|
+
"cp",
|
|
114
|
+
"crx",
|
|
115
|
+
"cry",
|
|
116
|
+
"crz",
|
|
117
|
+
"ch",
|
|
118
|
+
"swap",
|
|
119
|
+
"ccx",
|
|
120
|
+
"cswap",
|
|
121
|
+
"cu",
|
|
122
|
+
// OpenQASM 2 backwards compatibility gates
|
|
123
|
+
"CX",
|
|
124
|
+
"phase",
|
|
125
|
+
"cphase",
|
|
126
|
+
"id",
|
|
127
|
+
"u1",
|
|
128
|
+
"u2",
|
|
129
|
+
"u3",
|
|
130
|
+
];
|
|
131
|
+
gates.forEach(this.standardGates.add, this.standardGates);
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Parses the token stream and generates an abstract syntax tree.
|
|
135
|
+
* @return The abstract syntax tree.
|
|
136
|
+
*/
|
|
137
|
+
parse() {
|
|
138
|
+
let ast = [];
|
|
139
|
+
while (this.index < this.tokens.length - 1) {
|
|
140
|
+
const [nodes, consumed] = this.parseNode(this.tokens.slice(this.index));
|
|
141
|
+
ast = ast.concat(nodes ? nodes : []);
|
|
142
|
+
this.index += consumed;
|
|
143
|
+
}
|
|
144
|
+
return ast;
|
|
145
|
+
// let i = 0;
|
|
146
|
+
// while (i < this.tokens.length - 1) {
|
|
147
|
+
// this.index = i;
|
|
148
|
+
// const nodes = this.parseNode(this.tokens.slice(i))[0];
|
|
149
|
+
// ast = ast.concat(nodes ? nodes : []);
|
|
150
|
+
// while (!this.matchNext(this.tokens.slice(i), [Token.Semicolon])) {
|
|
151
|
+
// if (this.matchNext(this.tokens.slice(i), [Token.LCParen])) {
|
|
152
|
+
// while (!this.matchNext(this.tokens.slice(i), [Token.RCParen])) {
|
|
153
|
+
// i++;
|
|
154
|
+
// }
|
|
155
|
+
// break;
|
|
156
|
+
// } else if (this.matchNext(this.tokens.slice(i), [Token.RCParen])) {
|
|
157
|
+
// break;
|
|
158
|
+
// }
|
|
159
|
+
// i++;
|
|
160
|
+
// }
|
|
161
|
+
// i++;
|
|
162
|
+
// }
|
|
163
|
+
// return ast;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Parses a single statement or declaration by delegating the parsing of the next set of tokens to the appropriate method.
|
|
167
|
+
* @param tokens - Remaining tokens to parse.
|
|
168
|
+
* @param allowVariables - Whether encountered identifiers should be consider
|
|
169
|
+
variable initializations or references.
|
|
170
|
+
* @return A set of AST nodes and the number of consumed tokens.
|
|
171
|
+
*/
|
|
172
|
+
parseNode(tokens, allowVariables = true) {
|
|
173
|
+
const token = tokens[0];
|
|
174
|
+
switch (token[0]) {
|
|
175
|
+
case Token.DefcalGrammar: {
|
|
176
|
+
const [defcalGrammarNode, consumed] = this.defcalGrammarDeclaration(tokens);
|
|
177
|
+
return [[defcalGrammarNode], consumed];
|
|
178
|
+
}
|
|
179
|
+
case Token.Include: {
|
|
180
|
+
const [includeNode, consumed] = this.include(tokens);
|
|
181
|
+
if (includeNode.filename === '"stdgates.inc"') {
|
|
182
|
+
this.stdGates();
|
|
183
|
+
}
|
|
184
|
+
return [[includeNode], consumed];
|
|
185
|
+
}
|
|
186
|
+
case Token.OpenQASM: {
|
|
187
|
+
const [qasmNode, consumed] = this.versionHeader(tokens);
|
|
188
|
+
return [[qasmNode], consumed];
|
|
189
|
+
}
|
|
190
|
+
case Token.Const: {
|
|
191
|
+
const [constNode, consumed] = this.classicalDeclaration(tokens.slice(1), true);
|
|
192
|
+
return [[constNode], consumed + 1];
|
|
193
|
+
}
|
|
194
|
+
case Token.Input:
|
|
195
|
+
case Token.Output: {
|
|
196
|
+
const [ioType, consumed] = this.ioType(tokens);
|
|
197
|
+
return [[ioType], consumed];
|
|
198
|
+
}
|
|
199
|
+
case Token.Float:
|
|
200
|
+
case Token.Int:
|
|
201
|
+
case Token.UInt:
|
|
202
|
+
case Token.Bool:
|
|
203
|
+
case Token.Angle:
|
|
204
|
+
case Token.Stretch:
|
|
205
|
+
case Token.Complex:
|
|
206
|
+
case Token.Duration: {
|
|
207
|
+
const [classicalNode, consumed] = this.classicalDeclaration(tokens, false);
|
|
208
|
+
return [[classicalNode], consumed];
|
|
209
|
+
}
|
|
210
|
+
case Token.Qubit:
|
|
211
|
+
case Token.QReg: {
|
|
212
|
+
const [qregNode, qregConsumed] = this.quantumDeclaration(tokens);
|
|
213
|
+
return [[qregNode], qregConsumed];
|
|
214
|
+
}
|
|
215
|
+
case Token.Break:
|
|
216
|
+
return [[new BreakStatement()], 1];
|
|
217
|
+
case Token.Reset: {
|
|
218
|
+
const [resetNode, resetConsumed] = this.resetStatement(tokens);
|
|
219
|
+
return [[resetNode], resetConsumed];
|
|
220
|
+
}
|
|
221
|
+
case Token.Continue:
|
|
222
|
+
return [[new ContinueStatement()], 1];
|
|
223
|
+
case Token.Let: {
|
|
224
|
+
const [letNode, letConsumed] = this.aliasStatement(tokens);
|
|
225
|
+
return [[letNode], letConsumed];
|
|
226
|
+
}
|
|
227
|
+
case Token.Bit:
|
|
228
|
+
if (this.isMeasurement(tokens.slice(1))) {
|
|
229
|
+
const [measureNode, measureConsumed] = this.measureStatement(tokens);
|
|
230
|
+
return [[measureNode], measureConsumed];
|
|
231
|
+
}
|
|
232
|
+
else {
|
|
233
|
+
const [classicalNode, consumed] = this.classicalDeclaration(tokens, false);
|
|
234
|
+
return [[classicalNode], consumed];
|
|
235
|
+
}
|
|
236
|
+
case Token.CReg: {
|
|
237
|
+
const [classicalNode, consumed] = this.classicalDeclaration(tokens, false);
|
|
238
|
+
return [[classicalNode], consumed];
|
|
239
|
+
}
|
|
240
|
+
case Token.Measure: {
|
|
241
|
+
const [measureNode, measureConsumed] = this.measureStatement(tokens);
|
|
242
|
+
return [[measureNode], measureConsumed];
|
|
243
|
+
}
|
|
244
|
+
case Token.Gate: {
|
|
245
|
+
const [gateNode, gateConsumed] = this.quantumGateDefinition(tokens);
|
|
246
|
+
this.customGates.add(gateNode.name.name);
|
|
247
|
+
return [[gateNode], gateConsumed];
|
|
248
|
+
}
|
|
249
|
+
case Token.Return: {
|
|
250
|
+
const [returnNode, returnConsumed] = this.returnStatement(tokens);
|
|
251
|
+
return [[returnNode], returnConsumed];
|
|
252
|
+
}
|
|
253
|
+
case Token.Def: {
|
|
254
|
+
const [subroutineNode, subroutineConsumed] = this.subroutineDefinition(tokens);
|
|
255
|
+
this.subroutines.add(subroutineNode.name.name);
|
|
256
|
+
return [[subroutineNode], subroutineConsumed];
|
|
257
|
+
}
|
|
258
|
+
case Token.Extern: {
|
|
259
|
+
const [externNode, consumed] = this.externSignature(tokens);
|
|
260
|
+
this.subroutines.add(externNode.name.name);
|
|
261
|
+
return [[externNode], consumed];
|
|
262
|
+
}
|
|
263
|
+
case Token.Ctrl:
|
|
264
|
+
case Token.NegCtrl:
|
|
265
|
+
case Token.Inv:
|
|
266
|
+
case Token.PowM: {
|
|
267
|
+
const [gateCallNode, gateCallConsumed] = this.quantumGateCall(tokens);
|
|
268
|
+
return [[gateCallNode], gateCallConsumed];
|
|
269
|
+
}
|
|
270
|
+
case Token.Ceiling:
|
|
271
|
+
case Token.Exp:
|
|
272
|
+
case Token.Floor:
|
|
273
|
+
case Token.Log:
|
|
274
|
+
case Token.Mod:
|
|
275
|
+
case Token.Popcount:
|
|
276
|
+
case Token.Pow:
|
|
277
|
+
case Token.Sqrt:
|
|
278
|
+
case Token.Rotr:
|
|
279
|
+
case Token.Rotl: {
|
|
280
|
+
const [expr, exprConsumed] = this.binaryExpression(tokens.slice(1));
|
|
281
|
+
let consumed = exprConsumed + 1;
|
|
282
|
+
const [math, mathConsumed] = this.createMathOrTrigFunction(token[0], expr);
|
|
283
|
+
consumed += mathConsumed;
|
|
284
|
+
return [[math], consumed];
|
|
285
|
+
}
|
|
286
|
+
case Token.Opaque: {
|
|
287
|
+
let consumed = 1;
|
|
288
|
+
while (consumed < this.tokens.length &&
|
|
289
|
+
!this.matchNext(tokens.slice(consumed), [Token.Semicolon])) {
|
|
290
|
+
consumed++;
|
|
291
|
+
}
|
|
292
|
+
return [[], consumed];
|
|
293
|
+
}
|
|
294
|
+
case Token.DurationOf: {
|
|
295
|
+
const [durationOfNOde, consumed] = this.durationOf(tokens);
|
|
296
|
+
return [[durationOfNOde], consumed];
|
|
297
|
+
}
|
|
298
|
+
case Token.SizeOf: {
|
|
299
|
+
const [sizeOfNode, consumed] = this.sizeOf(tokens);
|
|
300
|
+
return [[sizeOfNode], consumed];
|
|
301
|
+
}
|
|
302
|
+
case Token.Delay: {
|
|
303
|
+
const [delayNode, delayConsumed] = this.delay(tokens);
|
|
304
|
+
return [[delayNode], delayConsumed];
|
|
305
|
+
}
|
|
306
|
+
case Token.If: {
|
|
307
|
+
const [ifNode, ifConsumed] = this.ifStatement(tokens);
|
|
308
|
+
return [[ifNode], ifConsumed];
|
|
309
|
+
}
|
|
310
|
+
case Token.For: {
|
|
311
|
+
const [forNode, forConsumed] = this.forLoopStatement(tokens);
|
|
312
|
+
return [[forNode], forConsumed];
|
|
313
|
+
}
|
|
314
|
+
case Token.While: {
|
|
315
|
+
const [whileNode, whileConsumed] = this.whileLoopStatement(tokens);
|
|
316
|
+
return [[whileNode], whileConsumed];
|
|
317
|
+
}
|
|
318
|
+
case Token.Switch: {
|
|
319
|
+
const [switchNode, switchConsumed] = this.switchStatement(tokens);
|
|
320
|
+
return [[switchNode], switchConsumed];
|
|
321
|
+
}
|
|
322
|
+
case Token.Array: {
|
|
323
|
+
const [arrayNode, arrayConsumed] = this.arrayDeclaration(tokens);
|
|
324
|
+
this.customArrays.add(arrayNode.identifier.name);
|
|
325
|
+
return [[arrayNode], arrayConsumed];
|
|
326
|
+
}
|
|
327
|
+
case Token.Box: {
|
|
328
|
+
const [boxNode, boxConsumed] = this.box(tokens);
|
|
329
|
+
return [[boxNode], boxConsumed];
|
|
330
|
+
}
|
|
331
|
+
case Token.Barrier: {
|
|
332
|
+
const [barrierNode, barrierConsumed] = this.barrier(tokens);
|
|
333
|
+
return [[barrierNode], barrierConsumed];
|
|
334
|
+
}
|
|
335
|
+
case Token.Id:
|
|
336
|
+
if (this.isQuantumGateCall(tokens)) {
|
|
337
|
+
const [gateCallNode, gateCallConsumed] = this.quantumGateCall(tokens);
|
|
338
|
+
return [[gateCallNode], gateCallConsumed];
|
|
339
|
+
}
|
|
340
|
+
else if (this.isSubroutineCall(tokens)) {
|
|
341
|
+
const [subroutineCallNode, subroutineCallConsumed] = this.subroutineCall(tokens);
|
|
342
|
+
return [[subroutineCallNode], subroutineCallConsumed];
|
|
343
|
+
}
|
|
344
|
+
else if (this.isMeasurement(tokens)) {
|
|
345
|
+
const [measureNode, measureConsumed] = this.measureStatement(tokens);
|
|
346
|
+
return [[measureNode], measureConsumed];
|
|
347
|
+
}
|
|
348
|
+
else if (this.matchNext(tokens.slice(1), [Token.EqualsAssmt]) ||
|
|
349
|
+
this.matchNext(tokens.slice(1), [Token.CompoundArithmeticOp]) ||
|
|
350
|
+
this.matchNext(tokens.slice(1), [Token.CompoundBinaryOp]) ||
|
|
351
|
+
this.isAssignment(tokens)) {
|
|
352
|
+
const [assignmentNode, consumed] = this.assignment(tokens);
|
|
353
|
+
return [[assignmentNode], consumed];
|
|
354
|
+
}
|
|
355
|
+
else if (allowVariables) {
|
|
356
|
+
const [expr, consumed] = this.binaryExpression(tokens);
|
|
357
|
+
if (this.matchNext(tokens.slice(consumed), [Token.Semicolon])) {
|
|
358
|
+
return [[expr], consumed + 1];
|
|
359
|
+
}
|
|
360
|
+
else {
|
|
361
|
+
throwParserError(MissingSemicolonError, tokens[consumed], this.index + consumed, "expected semicolon");
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
break;
|
|
365
|
+
default:
|
|
366
|
+
return [[], 1];
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* Checks if the next tokens match those expected.
|
|
371
|
+
* @param tokens - Remaining tokens to parse.
|
|
372
|
+
* @param expectedTokens - Expected tokens.
|
|
373
|
+
* @return Whether these is a match.
|
|
374
|
+
*/
|
|
375
|
+
matchNext(tokens, expectedTokens) {
|
|
376
|
+
let matches = true;
|
|
377
|
+
let i = 0;
|
|
378
|
+
if (tokens.length == 0) {
|
|
379
|
+
return false;
|
|
380
|
+
}
|
|
381
|
+
while (i < expectedTokens.length) {
|
|
382
|
+
if (tokens[i][0] != expectedTokens[i]) {
|
|
383
|
+
matches = false;
|
|
384
|
+
break;
|
|
385
|
+
}
|
|
386
|
+
i++;
|
|
387
|
+
}
|
|
388
|
+
return matches;
|
|
389
|
+
}
|
|
390
|
+
/**
|
|
391
|
+
* Parses a `defcalgrammar` declaration.
|
|
392
|
+
*
|
|
393
|
+
* calibrationGrammarStatement:
|
|
394
|
+
* DEFCALGRAMMAR StringLiteral SEMICOLON
|
|
395
|
+
*
|
|
396
|
+
* @param tokens - Remaining tokens to parse.
|
|
397
|
+
* @return The parsed CalibrationGrammarDeclaration AstNode node and the number of consumed tokens.
|
|
398
|
+
*/
|
|
399
|
+
defcalGrammarDeclaration(tokens) {
|
|
400
|
+
const consumed = 1;
|
|
401
|
+
if (this.matchNext(tokens.slice(consumed), [Token.String, Token.Semicolon])) {
|
|
402
|
+
return [
|
|
403
|
+
new CalibrationGrammarDeclaration(tokens[0][1].toString()),
|
|
404
|
+
consumed + 2,
|
|
405
|
+
];
|
|
406
|
+
}
|
|
407
|
+
throwParserError(BadStringLiteralError, tokens[consumed], this.index + consumed, "expected string literal following `defcalgrammar` keyword");
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* Parses a classical type declaration.
|
|
411
|
+
* @param tokens - Remaining tokens to parse.
|
|
412
|
+
* @param isConst - Whether the declaration is for a constant, defaults to False.
|
|
413
|
+
* @return The parsed ClassicalDeclaration AstNode and the number of consumed tokens.
|
|
414
|
+
*/
|
|
415
|
+
classicalDeclaration(tokens, isConst) {
|
|
416
|
+
const isConstParam = isConst ? isConst : false;
|
|
417
|
+
let id = null;
|
|
418
|
+
let initialValue;
|
|
419
|
+
let consumed = 0;
|
|
420
|
+
const [classicalType, classicalTypeConsumed] = this.parseClassicalType(tokens);
|
|
421
|
+
consumed += classicalTypeConsumed;
|
|
422
|
+
if (!this.matchNext(tokens.slice(consumed), [Token.RParen]) &&
|
|
423
|
+
!this.matchNext(tokens.slice(consumed), [Token.Comma])) {
|
|
424
|
+
const [identifier, idConsumed] = this.unaryExpression(tokens.slice(consumed));
|
|
425
|
+
id = identifier;
|
|
426
|
+
consumed += idConsumed;
|
|
427
|
+
if (this.matchNext(tokens.slice(consumed), [Token.EqualsAssmt])) {
|
|
428
|
+
consumed++;
|
|
429
|
+
const [value, valueConsumed] = this.binaryExpression(tokens.slice(consumed));
|
|
430
|
+
initialValue = value;
|
|
431
|
+
consumed += valueConsumed;
|
|
432
|
+
}
|
|
433
|
+
if (this.matchNext(tokens.slice(consumed), [Token.Semicolon])) {
|
|
434
|
+
consumed++;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
return [
|
|
438
|
+
new ClassicalDeclaration(classicalType, id, initialValue, isConstParam),
|
|
439
|
+
consumed,
|
|
440
|
+
];
|
|
441
|
+
}
|
|
442
|
+
/**
|
|
443
|
+
* Parses an IO declaration statement.
|
|
444
|
+
* @param tokens - Remaining tokens to parse.
|
|
445
|
+
* @return A tuple containing the parsed IODeclaration and the number of tokens consumed.
|
|
446
|
+
*/
|
|
447
|
+
ioType(tokens) {
|
|
448
|
+
let consumed = 0;
|
|
449
|
+
let modifier;
|
|
450
|
+
switch (tokens[consumed][0]) {
|
|
451
|
+
case Token.Input:
|
|
452
|
+
modifier = IOModifier.INPUT;
|
|
453
|
+
break;
|
|
454
|
+
case Token.Output:
|
|
455
|
+
modifier = IOModifier.OUTPUT;
|
|
456
|
+
break;
|
|
457
|
+
default:
|
|
458
|
+
throwParserError(BadExpressionError, tokens[consumed], this.index + consumed, "unsupported IO modifier");
|
|
459
|
+
}
|
|
460
|
+
consumed++;
|
|
461
|
+
const [classicalDeclaration, typeConsumed] = this.classicalDeclaration(tokens.slice(consumed));
|
|
462
|
+
consumed += typeConsumed;
|
|
463
|
+
return [new IODeclaration(modifier, classicalDeclaration), consumed];
|
|
464
|
+
}
|
|
465
|
+
/**
|
|
466
|
+
* Parses a unary expression.
|
|
467
|
+
* @param tokens - Remaining tokens to parse.
|
|
468
|
+
* @return A tuple containing the parsed Expression and the number of tokens consumed.
|
|
469
|
+
*/
|
|
470
|
+
unaryExpression(tokens) {
|
|
471
|
+
const token = tokens[0];
|
|
472
|
+
let consumed = 1;
|
|
473
|
+
switch (token[0]) {
|
|
474
|
+
case Token.NNInteger: {
|
|
475
|
+
if (this.isImaginary(tokens[consumed])) {
|
|
476
|
+
return [
|
|
477
|
+
new ImaginaryLiteral(`${token[1].toString()}im`),
|
|
478
|
+
consumed + 1,
|
|
479
|
+
];
|
|
480
|
+
}
|
|
481
|
+
else if (this.isDuration(tokens.slice(consumed))) {
|
|
482
|
+
return [
|
|
483
|
+
new DurationLiteral(Number(token[1]), tokens[consumed][1].toString()),
|
|
484
|
+
consumed + 1,
|
|
485
|
+
];
|
|
486
|
+
}
|
|
487
|
+
return [new IntegerLiteral(Number(token[1])), consumed];
|
|
488
|
+
}
|
|
489
|
+
case Token.Integer: {
|
|
490
|
+
if (this.isImaginary(tokens[consumed])) {
|
|
491
|
+
return [
|
|
492
|
+
new ImaginaryLiteral(`${token[1].toString()}im`),
|
|
493
|
+
consumed + 1,
|
|
494
|
+
];
|
|
495
|
+
}
|
|
496
|
+
return [new IntegerLiteral(token[1].toString()), consumed];
|
|
497
|
+
}
|
|
498
|
+
case Token.BinaryLiteral:
|
|
499
|
+
case Token.OctalLiteral:
|
|
500
|
+
case Token.HexLiteral:
|
|
501
|
+
case Token.ScientificNotation:
|
|
502
|
+
return [new NumericLiteral(token[1].toString()), consumed];
|
|
503
|
+
case Token.Real:
|
|
504
|
+
if (this.isImaginary(tokens[consumed])) {
|
|
505
|
+
return [
|
|
506
|
+
new ImaginaryLiteral(`${token[1].toString()}im`),
|
|
507
|
+
consumed + 1,
|
|
508
|
+
];
|
|
509
|
+
}
|
|
510
|
+
return [new FloatLiteral(Number(token[1])), consumed];
|
|
511
|
+
case Token.BoolLiteral:
|
|
512
|
+
return [new BooleanLiteral(token[1] === "true"), consumed];
|
|
513
|
+
case Token.Arccos:
|
|
514
|
+
case Token.Arcsin:
|
|
515
|
+
case Token.Arctan:
|
|
516
|
+
case Token.Cos:
|
|
517
|
+
case Token.Sin:
|
|
518
|
+
case Token.Tan:
|
|
519
|
+
case Token.Ceiling:
|
|
520
|
+
case Token.Exp:
|
|
521
|
+
case Token.Floor:
|
|
522
|
+
case Token.Log:
|
|
523
|
+
case Token.Mod:
|
|
524
|
+
case Token.Popcount:
|
|
525
|
+
case Token.Pow:
|
|
526
|
+
case Token.Sqrt:
|
|
527
|
+
case Token.Rotr:
|
|
528
|
+
case Token.Rotl: {
|
|
529
|
+
const [funcExpr, funcConsumed] = this.unaryExpression(tokens.slice(1));
|
|
530
|
+
consumed += funcConsumed;
|
|
531
|
+
return [
|
|
532
|
+
this.createMathOrTrigFunction(tokens[0][0], funcExpr),
|
|
533
|
+
consumed,
|
|
534
|
+
];
|
|
535
|
+
}
|
|
536
|
+
case Token.Id:
|
|
537
|
+
// Handle array identifiers, identifiers, and subscripted identifiers
|
|
538
|
+
if (this.matchNext(tokens.slice(1), [Token.LSParen])) {
|
|
539
|
+
if (this.isArray(tokens)) {
|
|
540
|
+
const [arrayAccess, arrayAccessConsumed] = this.parseArrayAccess(tokens);
|
|
541
|
+
return [arrayAccess, arrayAccessConsumed];
|
|
542
|
+
}
|
|
543
|
+
else {
|
|
544
|
+
const identifier = new Identifier(token[1].toString());
|
|
545
|
+
const [subscript, subscriptConsumed] = this.parseSubscript(tokens.slice(1));
|
|
546
|
+
consumed += subscriptConsumed;
|
|
547
|
+
return [
|
|
548
|
+
new SubscriptedIdentifier(identifier.name, subscript),
|
|
549
|
+
consumed,
|
|
550
|
+
];
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
return [new Identifier(token[1].toString()), consumed];
|
|
554
|
+
case Token.Pi:
|
|
555
|
+
return [new Pi(), consumed];
|
|
556
|
+
case Token.Euler:
|
|
557
|
+
return [new Euler(), consumed];
|
|
558
|
+
case Token.Tau:
|
|
559
|
+
return [new Tau(), consumed];
|
|
560
|
+
case Token.String:
|
|
561
|
+
return [new BitstringLiteral(token[1].toString()), consumed];
|
|
562
|
+
case Token.DurationOf:
|
|
563
|
+
return this.durationOf(tokens);
|
|
564
|
+
case Token.SizeOf:
|
|
565
|
+
return this.sizeOf(tokens);
|
|
566
|
+
case Token.UnaryOp: {
|
|
567
|
+
const [expr, exprConsumed] = this.unaryExpression(tokens.slice(1));
|
|
568
|
+
return [new Unary(token[1], expr), consumed + exprConsumed];
|
|
569
|
+
}
|
|
570
|
+
case Token.LParen: {
|
|
571
|
+
let i = 1;
|
|
572
|
+
let parenCount = 1;
|
|
573
|
+
while (parenCount > 0 && i < tokens.length) {
|
|
574
|
+
if (tokens[i][0] === Token.LParen) {
|
|
575
|
+
parenCount++;
|
|
576
|
+
}
|
|
577
|
+
else if (tokens[i][0] === Token.RParen) {
|
|
578
|
+
parenCount--;
|
|
579
|
+
}
|
|
580
|
+
else if (parenCount === 1 && tokens[i][0] === Token.Comma) {
|
|
581
|
+
return this.parseParameters(tokens);
|
|
582
|
+
}
|
|
583
|
+
i++;
|
|
584
|
+
}
|
|
585
|
+
const [expr, exprConsumed] = this.binaryExpression(tokens.slice(1));
|
|
586
|
+
consumed += exprConsumed;
|
|
587
|
+
if (!this.matchNext(tokens.slice(consumed), [Token.RParen])) {
|
|
588
|
+
throwParserError(MissingBraceError, tokens[consumed], this.index + consumed, "expected closing parenthesis");
|
|
589
|
+
}
|
|
590
|
+
consumed++;
|
|
591
|
+
return [expr, consumed];
|
|
592
|
+
}
|
|
593
|
+
default:
|
|
594
|
+
if (isTypeToken(token[0])) {
|
|
595
|
+
return this.parseTypeCast(tokens);
|
|
596
|
+
}
|
|
597
|
+
throwParserError(BadExpressionError, token, this.index, "invalid expression");
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
/**
|
|
601
|
+
* Parses a binary expression.
|
|
602
|
+
* @param tokens - Remaining tokens to parse.
|
|
603
|
+
* @return A tuple containing the parsed Expression and the number of tokens consumed.
|
|
604
|
+
*/
|
|
605
|
+
binaryExpression(tokens) {
|
|
606
|
+
const unaryExpr = this.unaryExpression(tokens);
|
|
607
|
+
let leftExpr = unaryExpr[0];
|
|
608
|
+
const leftConsumed = unaryExpr[1];
|
|
609
|
+
let consumed = leftConsumed;
|
|
610
|
+
while (consumed < tokens.length) {
|
|
611
|
+
const token = tokens[consumed];
|
|
612
|
+
if (token[0] === Token.BinaryOp ||
|
|
613
|
+
token[0] === Token.ArithmeticOp ||
|
|
614
|
+
(token[0] === Token.UnaryOp && token[1] === "-")) {
|
|
615
|
+
consumed++;
|
|
616
|
+
const [rightExpr, rightConsumed] = this.unaryExpression(tokens.slice(consumed));
|
|
617
|
+
if (token[0] === Token.BinaryOp) {
|
|
618
|
+
leftExpr = new Binary(token[1], leftExpr, rightExpr);
|
|
619
|
+
}
|
|
620
|
+
else if (token[0] === Token.UnaryOp && token[1] === "-") {
|
|
621
|
+
leftExpr = new Arithmetic(ArithmeticOp.MINUS, leftExpr, rightExpr);
|
|
622
|
+
}
|
|
623
|
+
else {
|
|
624
|
+
leftExpr = new Arithmetic(token[1], leftExpr, rightExpr);
|
|
625
|
+
}
|
|
626
|
+
consumed += rightConsumed;
|
|
627
|
+
}
|
|
628
|
+
else {
|
|
629
|
+
break;
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
return [leftExpr, consumed];
|
|
633
|
+
}
|
|
634
|
+
/**
|
|
635
|
+
* Parses an assignment statement.
|
|
636
|
+
* @param tokens - Remaining tokens to parse.
|
|
637
|
+
* @return The parsed AssignmentStatement AstNode and the number of consumed tokens.
|
|
638
|
+
*/
|
|
639
|
+
assignment(tokens) {
|
|
640
|
+
let consumed = 0;
|
|
641
|
+
const [lhs, lhsConsumed] = this.unaryExpression(tokens);
|
|
642
|
+
consumed += lhsConsumed;
|
|
643
|
+
// Handle compound assignments
|
|
644
|
+
const operatorToken = tokens[consumed][0];
|
|
645
|
+
if (operatorToken === Token.CompoundArithmeticOp ||
|
|
646
|
+
operatorToken === Token.CompoundBinaryOp) {
|
|
647
|
+
consumed++;
|
|
648
|
+
const operator = tokens[lhsConsumed][1].toString();
|
|
649
|
+
const [rhs, rhsConsumed] = this.binaryExpression(tokens.slice(consumed));
|
|
650
|
+
consumed += rhsConsumed;
|
|
651
|
+
if (!this.matchNext(tokens.slice(consumed), [Token.Semicolon])) {
|
|
652
|
+
throwParserError(MissingSemicolonError, tokens[consumed], this.index + consumed, "expected semicolon");
|
|
653
|
+
}
|
|
654
|
+
consumed++;
|
|
655
|
+
let op;
|
|
656
|
+
let expression;
|
|
657
|
+
switch (operatorToken) {
|
|
658
|
+
case Token.CompoundArithmeticOp:
|
|
659
|
+
op = operator.slice(0, -1);
|
|
660
|
+
expression = new Arithmetic(op, lhs, rhs);
|
|
661
|
+
break;
|
|
662
|
+
case Token.CompoundBinaryOp:
|
|
663
|
+
op = operator.slice(0, -1);
|
|
664
|
+
expression = new Binary(op, lhs, rhs);
|
|
665
|
+
break;
|
|
666
|
+
default:
|
|
667
|
+
throwParserError(BadExpressionError, tokens[consumed], this.index + consumed, "invalid compound operator");
|
|
668
|
+
}
|
|
669
|
+
return [
|
|
670
|
+
new AssignmentStatement(lhs, expression),
|
|
671
|
+
consumed,
|
|
672
|
+
];
|
|
673
|
+
}
|
|
674
|
+
if (this.matchNext(tokens.slice(consumed), [Token.EqualsAssmt])) {
|
|
675
|
+
consumed++;
|
|
676
|
+
}
|
|
677
|
+
else {
|
|
678
|
+
throwParserError(BadExpressionError, tokens[consumed], this.index + consumed, "expected `=` in assignment statement");
|
|
679
|
+
}
|
|
680
|
+
let rhs;
|
|
681
|
+
if (this.isSubroutineCall(tokens.slice(consumed))) {
|
|
682
|
+
const [subroutineCall, subroutineCallConsumed] = this.subroutineCall(tokens.slice(consumed));
|
|
683
|
+
rhs = subroutineCall;
|
|
684
|
+
consumed += subroutineCallConsumed;
|
|
685
|
+
}
|
|
686
|
+
else {
|
|
687
|
+
const [rhsExpression, rhsConsumed] = this.binaryExpression(tokens.slice(consumed));
|
|
688
|
+
rhs = rhsExpression;
|
|
689
|
+
consumed += rhsConsumed;
|
|
690
|
+
}
|
|
691
|
+
// if (!this.matchNext(tokens.slice(consumed), [Token.Semicolon])) {
|
|
692
|
+
// throwParserError(
|
|
693
|
+
// MissingSemicolonError,
|
|
694
|
+
// tokens[consumed],
|
|
695
|
+
// this.index + consumed,
|
|
696
|
+
// "expected semicolon",
|
|
697
|
+
// );
|
|
698
|
+
// }
|
|
699
|
+
// consumed++;
|
|
700
|
+
return [
|
|
701
|
+
new AssignmentStatement(lhs, rhs),
|
|
702
|
+
consumed,
|
|
703
|
+
];
|
|
704
|
+
}
|
|
705
|
+
/**
|
|
706
|
+
* Parses a quantum declaration.
|
|
707
|
+
* @param tokens - Tokens to parse.
|
|
708
|
+
* @return A QuantumDeclaration node and the number of tokens consumed.
|
|
709
|
+
*/
|
|
710
|
+
quantumDeclaration(tokens) {
|
|
711
|
+
let consumed = 1;
|
|
712
|
+
const isQubit = tokens[0][0] === Token.Qubit;
|
|
713
|
+
let size = null;
|
|
714
|
+
// Qubit
|
|
715
|
+
if (isQubit) {
|
|
716
|
+
if (this.matchNext(tokens.slice(consumed), [Token.LSParen])) {
|
|
717
|
+
consumed++;
|
|
718
|
+
const [sizeExpr, sizeConsumed] = this.binaryExpression(tokens.slice(consumed));
|
|
719
|
+
consumed += sizeConsumed;
|
|
720
|
+
size = sizeExpr;
|
|
721
|
+
if (!this.matchNext(tokens.slice(consumed), [Token.RSParen])) {
|
|
722
|
+
throwParserError(MissingBraceError, tokens[consumed], this.index + consumed, "expected closing bracket ] for qubit declaration size");
|
|
723
|
+
}
|
|
724
|
+
consumed++;
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
if (!this.matchNext(tokens.slice(consumed), [Token.Id])) {
|
|
728
|
+
throwParserError(BadQregError, tokens[consumed], this.index + consumed, "expected identifier for quantum declaration");
|
|
729
|
+
}
|
|
730
|
+
const [identifier, idConsumed] = this.unaryExpression(tokens.slice(consumed, consumed + 1));
|
|
731
|
+
consumed += idConsumed;
|
|
732
|
+
// Qreg
|
|
733
|
+
if (!isQubit && this.matchNext(tokens.slice(consumed), [Token.LSParen])) {
|
|
734
|
+
consumed++;
|
|
735
|
+
const [sizeExpr, sizeConsumed] = this.binaryExpression(tokens.slice(consumed));
|
|
736
|
+
size = sizeExpr;
|
|
737
|
+
consumed += sizeConsumed;
|
|
738
|
+
if (!this.matchNext(tokens.slice(consumed), [Token.RSParen])) {
|
|
739
|
+
throwParserError(MissingBraceError, tokens[consumed], this.index + consumed, "expected closing bracket ] for quantum register size");
|
|
740
|
+
}
|
|
741
|
+
consumed++;
|
|
742
|
+
}
|
|
743
|
+
if (!(this.matchNext(tokens.slice(consumed), [Token.Semicolon]) ||
|
|
744
|
+
this.matchNext(tokens.slice(consumed), [Token.Comma]) ||
|
|
745
|
+
this.matchNext(tokens.slice(consumed), [Token.RParen]))) {
|
|
746
|
+
throwParserError(BadQregError, tokens[consumed], this.index + consumed, "expected semicolon, comma, or closing parenthesis after quantum register declaration");
|
|
747
|
+
}
|
|
748
|
+
// Only consume semicolon, if paren or comma that token will be handled in calling function
|
|
749
|
+
if (this.matchNext(tokens.slice(consumed), [Token.Semicolon])) {
|
|
750
|
+
consumed++;
|
|
751
|
+
}
|
|
752
|
+
return [new QuantumDeclaration(identifier, size), consumed];
|
|
753
|
+
}
|
|
754
|
+
/**
|
|
755
|
+
* Parses a measure statement.
|
|
756
|
+
* @param tokens - Remaining tokens to parse.
|
|
757
|
+
* @return A tuple containing the QuantumMeasurementAssignment or QuantumMeasurement and the number of tokens consumed.
|
|
758
|
+
*/
|
|
759
|
+
measureStatement(tokens) {
|
|
760
|
+
let consumed = 0;
|
|
761
|
+
// Legacy syntax: measure qubit|qubit[] -> bit|bit[];
|
|
762
|
+
if (this.matchNext(tokens.slice(consumed), [Token.Measure])) {
|
|
763
|
+
consumed++;
|
|
764
|
+
const qubitIdentifiers = [];
|
|
765
|
+
while (!(this.matchNext(tokens.slice(consumed), [Token.Arrow]) ||
|
|
766
|
+
this.matchNext(tokens.slice(consumed), [Token.Semicolon]))) {
|
|
767
|
+
const [identifier, idConsumed] = this.unaryExpression(tokens.slice(consumed));
|
|
768
|
+
qubitIdentifiers.push(identifier);
|
|
769
|
+
consumed += idConsumed;
|
|
770
|
+
if (this.matchNext(tokens.slice(consumed), [Token.Comma])) {
|
|
771
|
+
consumed++;
|
|
772
|
+
}
|
|
773
|
+
else if (!(this.matchNext(tokens.slice(consumed), [Token.Arrow]) ||
|
|
774
|
+
this.matchNext(tokens.slice(consumed), [Token.Semicolon]))) {
|
|
775
|
+
throwParserError(BadMeasurementError, tokens[consumed], this.index + consumed, "expected comma or arrow in measurement statement");
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
const measurement = new QuantumMeasurement(qubitIdentifiers);
|
|
779
|
+
// If there's an arrow, build a QuantumMeasurementAssignment
|
|
780
|
+
if (this.matchNext(tokens.slice(consumed), [Token.Arrow])) {
|
|
781
|
+
consumed++;
|
|
782
|
+
const [identifier, idConsumed] = this.unaryExpression(tokens.slice(consumed));
|
|
783
|
+
const classicalBit = identifier;
|
|
784
|
+
consumed += idConsumed;
|
|
785
|
+
if (!this.matchNext(tokens.slice(consumed), [Token.Semicolon])) {
|
|
786
|
+
throwParserError(MissingSemicolonError, tokens[consumed], this.index + consumed);
|
|
787
|
+
}
|
|
788
|
+
consumed++;
|
|
789
|
+
return [
|
|
790
|
+
new QuantumMeasurementAssignment(classicalBit, measurement),
|
|
791
|
+
consumed,
|
|
792
|
+
];
|
|
793
|
+
}
|
|
794
|
+
else {
|
|
795
|
+
if (!this.matchNext(tokens.slice(consumed), [Token.Semicolon])) {
|
|
796
|
+
throwParserError(MissingSemicolonError, tokens[consumed], this.index + consumed);
|
|
797
|
+
}
|
|
798
|
+
consumed++;
|
|
799
|
+
return [measurement, consumed];
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
// New syntax: bit|bit[] = measure qubit|qreg;
|
|
803
|
+
else {
|
|
804
|
+
const [leftIdentifier, leftIdConsumed] = this.unaryExpression(tokens.slice(consumed));
|
|
805
|
+
const classicalBit = leftIdentifier;
|
|
806
|
+
consumed += leftIdConsumed;
|
|
807
|
+
if (!this.matchNext(tokens.slice(consumed), [Token.EqualsAssmt])) {
|
|
808
|
+
throwParserError(BadMeasurementError, tokens[consumed], this.index + consumed, "expected `=` in measurement assignment");
|
|
809
|
+
}
|
|
810
|
+
consumed++;
|
|
811
|
+
if (!this.matchNext(tokens.slice(consumed), [Token.Measure])) {
|
|
812
|
+
throwParserError(BadMeasurementError, tokens[consumed], this.index + consumed, "expected `measure` keyword in measurement assignment");
|
|
813
|
+
}
|
|
814
|
+
consumed++;
|
|
815
|
+
const [rightIdentifier, rightIdConsumed] = this.unaryExpression(tokens.slice(consumed));
|
|
816
|
+
const qubitIdentifier = rightIdentifier;
|
|
817
|
+
consumed += rightIdConsumed;
|
|
818
|
+
if (!this.matchNext(tokens.slice(consumed), [Token.Semicolon])) {
|
|
819
|
+
throwParserError(MissingSemicolonError, tokens[consumed], this.index + consumed);
|
|
820
|
+
}
|
|
821
|
+
consumed++;
|
|
822
|
+
const measurement = new QuantumMeasurement([
|
|
823
|
+
qubitIdentifier,
|
|
824
|
+
]);
|
|
825
|
+
return [
|
|
826
|
+
new QuantumMeasurementAssignment(classicalBit, measurement),
|
|
827
|
+
consumed,
|
|
828
|
+
];
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
/**
|
|
832
|
+
* Parses a subroutine return statement.
|
|
833
|
+
* @param tokens - Remaining tokens to parse.
|
|
834
|
+
* @return A tuple containing the SubroutineDefinition and the number of tokens consumed.
|
|
835
|
+
*/
|
|
836
|
+
returnStatement(tokens) {
|
|
837
|
+
let consumed = 1;
|
|
838
|
+
let expression = null;
|
|
839
|
+
if (!this.matchNext(tokens.slice(consumed), [Token.Semicolon])) {
|
|
840
|
+
const [expr, exprConsumed] = this.parseNode(tokens.slice(consumed));
|
|
841
|
+
expression = expr;
|
|
842
|
+
consumed += exprConsumed;
|
|
843
|
+
}
|
|
844
|
+
return [new ReturnStatement(expression), consumed];
|
|
845
|
+
}
|
|
846
|
+
/**
|
|
847
|
+
* Parses an extern signature.
|
|
848
|
+
* @param tokens - Remaining tokens to parse.
|
|
849
|
+
* @return A tuple containing the ExternSignature and the number of tokens consumed.
|
|
850
|
+
*/
|
|
851
|
+
externSignature(tokens) {
|
|
852
|
+
let consumed = 1;
|
|
853
|
+
if (!this.matchNext(tokens.slice(consumed), [Token.Id])) {
|
|
854
|
+
throwParserError(BadSubroutineError, tokens[consumed], this.index + consumed, "expected identifier following extern keyword");
|
|
855
|
+
}
|
|
856
|
+
const [identifier, identifierConsumed] = this.unaryExpression(tokens.slice(consumed));
|
|
857
|
+
consumed += identifierConsumed;
|
|
858
|
+
let externParams = null;
|
|
859
|
+
if (this.matchNext(tokens.slice(consumed), [Token.LParen])) {
|
|
860
|
+
const [params, paramsConsumed] = this.parseParameters(tokens.slice(consumed));
|
|
861
|
+
externParams = params;
|
|
862
|
+
consumed += paramsConsumed;
|
|
863
|
+
}
|
|
864
|
+
let returnType = null;
|
|
865
|
+
if (this.matchNext(tokens.slice(consumed), [Token.Arrow])) {
|
|
866
|
+
consumed++;
|
|
867
|
+
const [type, typeConsumed] = this.parseClassicalType(tokens.slice(consumed));
|
|
868
|
+
returnType = type;
|
|
869
|
+
consumed += typeConsumed;
|
|
870
|
+
}
|
|
871
|
+
if (!this.matchNext(tokens.slice(consumed), [Token.Semicolon])) {
|
|
872
|
+
throwParserError(MissingSemicolonError, tokens[consumed], this.index + consumed, "expected semicolon");
|
|
873
|
+
}
|
|
874
|
+
consumed++;
|
|
875
|
+
return [
|
|
876
|
+
new ExternSignature(identifier, externParams, returnType),
|
|
877
|
+
consumed,
|
|
878
|
+
];
|
|
879
|
+
}
|
|
880
|
+
/**
|
|
881
|
+
* Parses a subroutine definition.
|
|
882
|
+
* @param tokens - Remaining tokens to parse.
|
|
883
|
+
* @return A tuple containing the SubroutineDefinition and the number of tokens consumed.
|
|
884
|
+
*/
|
|
885
|
+
subroutineDefinition(tokens) {
|
|
886
|
+
let consumed = 1;
|
|
887
|
+
if (!this.matchNext(tokens.slice(consumed), [Token.Id])) {
|
|
888
|
+
throwParserError(BadSubroutineError, tokens[consumed], this.index + consumed, "expected subroutine name");
|
|
889
|
+
}
|
|
890
|
+
const [name, nameConsumed] = this.unaryExpression(tokens.slice(consumed));
|
|
891
|
+
consumed += nameConsumed;
|
|
892
|
+
if (!this.matchNext(tokens.slice(consumed), [Token.LParen])) {
|
|
893
|
+
throwParserError(MissingBraceError, tokens[consumed], this.index + consumed, "expected opening parenthesis for subroutine definition");
|
|
894
|
+
}
|
|
895
|
+
const [params, paramsConsumed] = this.parseParameters(tokens.slice(consumed));
|
|
896
|
+
consumed += paramsConsumed;
|
|
897
|
+
let returnType = null;
|
|
898
|
+
if (this.matchNext(tokens.slice(consumed), [Token.Arrow])) {
|
|
899
|
+
consumed++;
|
|
900
|
+
const [type, typeConsumed] = this.parseClassicalType(tokens.slice(consumed));
|
|
901
|
+
returnType = type;
|
|
902
|
+
consumed += typeConsumed;
|
|
903
|
+
}
|
|
904
|
+
if (!this.matchNext(tokens.slice(consumed), [Token.LCParen])) {
|
|
905
|
+
throwParserError(MissingBraceError, tokens[consumed], this.index + consumed, "expected opening brace for subroutine body");
|
|
906
|
+
}
|
|
907
|
+
const [body, bodyConsumed] = this.programBlock(tokens.slice(consumed));
|
|
908
|
+
consumed += bodyConsumed;
|
|
909
|
+
return [
|
|
910
|
+
new SubroutineDefinition(name, body, params, returnType),
|
|
911
|
+
consumed,
|
|
912
|
+
];
|
|
913
|
+
}
|
|
914
|
+
/**
|
|
915
|
+
* Parses a subroutine call.
|
|
916
|
+
* @param tokens - Remaining tokens to parse.
|
|
917
|
+
* @return A tuple containing the SubroutineCall and the number of tokens consumed.
|
|
918
|
+
*/
|
|
919
|
+
subroutineCall(tokens) {
|
|
920
|
+
let consumed = 0;
|
|
921
|
+
if (!this.matchNext(tokens.slice(consumed), [Token.Id])) {
|
|
922
|
+
throwParserError(BadSubroutineError, tokens[consumed], this.index + consumed, "expected subroutine name");
|
|
923
|
+
}
|
|
924
|
+
const [subroutineName, subroutineNameConsumed] = this.unaryExpression(tokens.slice(consumed));
|
|
925
|
+
consumed += subroutineNameConsumed;
|
|
926
|
+
let callParams = null;
|
|
927
|
+
if (this.matchNext(tokens.slice(consumed), [Token.LParen])) {
|
|
928
|
+
const [params, paramsConsumed] = this.parseParameters(tokens.slice(consumed));
|
|
929
|
+
callParams = params;
|
|
930
|
+
consumed += paramsConsumed;
|
|
931
|
+
}
|
|
932
|
+
// if (!this.matchNext(tokens.slice(consumed), [Token.Semicolon])) {
|
|
933
|
+
// throwParserError(
|
|
934
|
+
// MissingSemicolonError,
|
|
935
|
+
// tokens[consumed],
|
|
936
|
+
// this.index + consumed,
|
|
937
|
+
// "expected semicolon after subroutine call",
|
|
938
|
+
// );
|
|
939
|
+
// }
|
|
940
|
+
if (this.matchNext(tokens.slice(consumed), [Token.Semicolon])) {
|
|
941
|
+
consumed++;
|
|
942
|
+
}
|
|
943
|
+
return [
|
|
944
|
+
new SubroutineCall(subroutineName, callParams),
|
|
945
|
+
consumed,
|
|
946
|
+
];
|
|
947
|
+
}
|
|
948
|
+
/**
|
|
949
|
+
* Parses a quantum gate definition.
|
|
950
|
+
* @param tokens - Remaining tokens to parse.
|
|
951
|
+
* @return A tuple containing the QuantumGateDefinition and the number of tokens consumed.
|
|
952
|
+
*/
|
|
953
|
+
quantumGateDefinition(tokens) {
|
|
954
|
+
let consumed = 1;
|
|
955
|
+
if (!this.matchNext(tokens.slice(consumed), [Token.Id])) {
|
|
956
|
+
throwParserError(BadGateError, tokens[consumed], this.index + consumed, "expected gate name");
|
|
957
|
+
}
|
|
958
|
+
const [name, nameConsumed] = this.unaryExpression(tokens.slice(consumed));
|
|
959
|
+
consumed += nameConsumed;
|
|
960
|
+
// Parse optional parameters
|
|
961
|
+
const [params, paramsConsumed] = this.parseParameters(tokens.slice(consumed));
|
|
962
|
+
consumed += paramsConsumed;
|
|
963
|
+
// Parse qubits
|
|
964
|
+
const qubits = [];
|
|
965
|
+
while (!this.matchNext(tokens.slice(consumed), [Token.LCParen])) {
|
|
966
|
+
if (!this.matchNext(tokens.slice(consumed), [Token.Id])) {
|
|
967
|
+
throwParserError(BadGateError, tokens[consumed], this.index + consumed, "expected qubit argument");
|
|
968
|
+
}
|
|
969
|
+
const [qubit, qubitConsumed] = this.unaryExpression(tokens.slice(consumed));
|
|
970
|
+
qubits.push(qubit);
|
|
971
|
+
consumed += qubitConsumed;
|
|
972
|
+
if (this.matchNext(tokens.slice(consumed), [Token.Comma])) {
|
|
973
|
+
consumed++;
|
|
974
|
+
}
|
|
975
|
+
else if (!this.matchNext(tokens.slice(consumed), [Token.LCParen])) {
|
|
976
|
+
throwParserError(BadGateError, tokens[consumed], this.index + consumed, "expected comma or opening brace for gate body");
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
const [body, bodyConsumed] = this.programBlock(tokens.slice(consumed));
|
|
980
|
+
consumed += bodyConsumed;
|
|
981
|
+
return [
|
|
982
|
+
new QuantumGateDefinition(name, params, qubits, body),
|
|
983
|
+
consumed,
|
|
984
|
+
];
|
|
985
|
+
}
|
|
986
|
+
/**
|
|
987
|
+
* Parses a quantum gate call.
|
|
988
|
+
* @param tokens - Remaining tokens to parse.
|
|
989
|
+
* @return A tuple containing the QuantumGateCall and the number of tokens consumed.
|
|
990
|
+
*/
|
|
991
|
+
quantumGateCall(tokens) {
|
|
992
|
+
let consumed = 0;
|
|
993
|
+
const modifiers = [];
|
|
994
|
+
// Parse modifier(s)
|
|
995
|
+
while (this.matchNext(tokens.slice(consumed), [Token.Ctrl]) ||
|
|
996
|
+
this.matchNext(tokens.slice(consumed), [Token.NegCtrl]) ||
|
|
997
|
+
this.matchNext(tokens.slice(consumed), [Token.Inv]) ||
|
|
998
|
+
this.matchNext(tokens.slice(consumed), [Token.PowM])) {
|
|
999
|
+
const [modifier, modifierConsumed] = this.parseQuantumGateModifier(tokens.slice(consumed));
|
|
1000
|
+
modifiers.push(modifier);
|
|
1001
|
+
consumed += modifierConsumed;
|
|
1002
|
+
}
|
|
1003
|
+
// Parse gate name
|
|
1004
|
+
if (!this.matchNext(tokens.slice(consumed), [Token.Id])) {
|
|
1005
|
+
throwParserError(BadGateError, tokens[consumed], this.index + consumed, "expected gate name");
|
|
1006
|
+
}
|
|
1007
|
+
const [gateName, gateNameConsumed] = this.unaryExpression(tokens.slice(consumed));
|
|
1008
|
+
consumed += gateNameConsumed;
|
|
1009
|
+
let callParams = null;
|
|
1010
|
+
// Parse optional parameters
|
|
1011
|
+
if (this.matchNext(tokens.slice(consumed), [Token.LParen])) {
|
|
1012
|
+
const [params, paramsConsumed] = this.parseParameters(tokens.slice(consumed));
|
|
1013
|
+
callParams = params;
|
|
1014
|
+
consumed += paramsConsumed;
|
|
1015
|
+
}
|
|
1016
|
+
// Parse qubit arguments
|
|
1017
|
+
const [qubits, qubitsConsumed] = this.parseQubitList(tokens.slice(consumed));
|
|
1018
|
+
consumed += qubitsConsumed;
|
|
1019
|
+
if (!this.matchNext(tokens.slice(consumed), [Token.Semicolon])) {
|
|
1020
|
+
throwParserError(MissingSemicolonError, tokens[consumed], this.index + consumed, "expected semicolon");
|
|
1021
|
+
}
|
|
1022
|
+
consumed++;
|
|
1023
|
+
return [
|
|
1024
|
+
new QuantumGateCall(gateName, qubits, callParams, modifiers),
|
|
1025
|
+
consumed,
|
|
1026
|
+
];
|
|
1027
|
+
}
|
|
1028
|
+
/**
|
|
1029
|
+
* Parses a list of qubits.
|
|
1030
|
+
* @param tokens - Remaining tokens to parse.
|
|
1031
|
+
* @return A tuple containing the list of qubits and the number of tokens consumed.
|
|
1032
|
+
*/
|
|
1033
|
+
parseQubitList(tokens) {
|
|
1034
|
+
let consumed = 0;
|
|
1035
|
+
const qubits = [];
|
|
1036
|
+
while (!this.matchNext(tokens.slice(consumed), [Token.Semicolon])) {
|
|
1037
|
+
if (this.matchNext(tokens.slice(consumed), [Token.Dollar, Token.NNInteger])) {
|
|
1038
|
+
const [hardwareQubit, hardwareQubitConsumed] = this.parseHardwareQubit(tokens.slice(consumed));
|
|
1039
|
+
qubits.push(hardwareQubit);
|
|
1040
|
+
consumed += hardwareQubitConsumed;
|
|
1041
|
+
if (this.matchNext(tokens.slice(consumed), [Token.Comma])) {
|
|
1042
|
+
consumed++;
|
|
1043
|
+
}
|
|
1044
|
+
continue;
|
|
1045
|
+
}
|
|
1046
|
+
else if (!this.matchNext(tokens.slice(consumed), [Token.Id])) {
|
|
1047
|
+
throwParserError(BadExpressionError, tokens[consumed], this.index + consumed, "expected qubit argument");
|
|
1048
|
+
}
|
|
1049
|
+
const [qubit, qubitConsumed] = this.unaryExpression(tokens.slice(consumed));
|
|
1050
|
+
qubits.push(qubit);
|
|
1051
|
+
consumed += qubitConsumed;
|
|
1052
|
+
if (this.matchNext(tokens.slice(consumed), [Token.Comma])) {
|
|
1053
|
+
consumed++;
|
|
1054
|
+
}
|
|
1055
|
+
else if (!this.matchNext(tokens.slice(consumed), [Token.Semicolon])) {
|
|
1056
|
+
break;
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
return [qubits, consumed];
|
|
1060
|
+
}
|
|
1061
|
+
/**
|
|
1062
|
+
* Parses a hardware qubit.
|
|
1063
|
+
* @param tokens - Remaining tokens to parse.
|
|
1064
|
+
* @return A tuple containing the HardwareQubit and the number of tokens consumed.
|
|
1065
|
+
*/
|
|
1066
|
+
parseHardwareQubit(tokens) {
|
|
1067
|
+
let consumed = 0;
|
|
1068
|
+
if (!this.matchNext(tokens.slice(consumed), [Token.Dollar])) {
|
|
1069
|
+
throwParserError(BadGateError, tokens[consumed], this.index + consumed, "expected dollar sign for hardware qubit");
|
|
1070
|
+
}
|
|
1071
|
+
consumed++;
|
|
1072
|
+
if (!this.matchNext(tokens.slice(consumed), [Token.NNInteger])) {
|
|
1073
|
+
throwParserError(BadGateError, tokens[consumed], this.index + consumed, "expected non-negative integer for hardware qubit");
|
|
1074
|
+
}
|
|
1075
|
+
return [new HardwareQubit(Number(tokens[consumed][1])), consumed + 1];
|
|
1076
|
+
}
|
|
1077
|
+
/**
|
|
1078
|
+
* Parses a gate modifier.
|
|
1079
|
+
* @param tokens - Remaining tokens to parse.
|
|
1080
|
+
* @return A tuple containing the gate modifier and the number of tokens consumed.
|
|
1081
|
+
*/
|
|
1082
|
+
parseQuantumGateModifier(tokens) {
|
|
1083
|
+
let consumed = 1;
|
|
1084
|
+
const modifierToken = tokens[0][0];
|
|
1085
|
+
let modifier;
|
|
1086
|
+
let argument = null;
|
|
1087
|
+
switch (modifierToken) {
|
|
1088
|
+
case Token.Ctrl:
|
|
1089
|
+
modifier = QuantumGateModifierName.CTRL;
|
|
1090
|
+
break;
|
|
1091
|
+
case Token.NegCtrl:
|
|
1092
|
+
modifier = QuantumGateModifierName.NRGCTRL;
|
|
1093
|
+
break;
|
|
1094
|
+
case Token.Inv:
|
|
1095
|
+
modifier = QuantumGateModifierName.INV;
|
|
1096
|
+
break;
|
|
1097
|
+
case Token.PowM:
|
|
1098
|
+
modifier = QuantumGateModifierName.POW;
|
|
1099
|
+
break;
|
|
1100
|
+
default:
|
|
1101
|
+
throwParserError(BadGateError, tokens[0], this.index, "invalid gate modifier");
|
|
1102
|
+
}
|
|
1103
|
+
if (this.matchNext(tokens.slice(consumed), [Token.LParen])) {
|
|
1104
|
+
consumed++;
|
|
1105
|
+
const [arg, argConsumed] = this.binaryExpression(tokens.slice(consumed));
|
|
1106
|
+
argument = arg;
|
|
1107
|
+
consumed += argConsumed;
|
|
1108
|
+
if (!this.matchNext(tokens.slice(consumed), [Token.RParen])) {
|
|
1109
|
+
throwParserError(BadGateError, tokens[consumed], this.index + consumed, "expected closing parenthesis after gate modifier argument");
|
|
1110
|
+
}
|
|
1111
|
+
consumed++;
|
|
1112
|
+
}
|
|
1113
|
+
if (!this.matchNext(tokens.slice(consumed), [Token.At])) {
|
|
1114
|
+
throwParserError(BadGateError, tokens[consumed], this.index + consumed, "expected `@` symbol after gate modifier");
|
|
1115
|
+
}
|
|
1116
|
+
consumed++;
|
|
1117
|
+
return [new QuantumGateModifier(modifier, argument), consumed];
|
|
1118
|
+
}
|
|
1119
|
+
/**
|
|
1120
|
+
* Parses a set of parameters.
|
|
1121
|
+
* @param tokens - Remaining tokens to parse.
|
|
1122
|
+
* @return A tuple containing the Parameters and the number of tokens consumed.
|
|
1123
|
+
*/
|
|
1124
|
+
parseParameters(tokens) {
|
|
1125
|
+
let consumed = 0;
|
|
1126
|
+
const args = [];
|
|
1127
|
+
if (this.matchNext(tokens, [Token.LParen])) {
|
|
1128
|
+
consumed++;
|
|
1129
|
+
while (!this.matchNext(tokens.slice(consumed), [Token.RParen])) {
|
|
1130
|
+
if (this.matchNext(tokens.slice(consumed), [Token.Qubit])) {
|
|
1131
|
+
const [qubitParam, qubitConsumed] = this.parseNode(tokens.slice(consumed));
|
|
1132
|
+
args.push(qubitParam);
|
|
1133
|
+
consumed += qubitConsumed;
|
|
1134
|
+
}
|
|
1135
|
+
else if (this.matchNext(tokens.slice(consumed), [Token.Bit]) ||
|
|
1136
|
+
this.matchNext(tokens.slice(consumed), [Token.CReg])) {
|
|
1137
|
+
const [bitParam, bitConsumed] = this.parseNode(tokens.slice(consumed));
|
|
1138
|
+
args.push(bitParam);
|
|
1139
|
+
consumed += bitConsumed;
|
|
1140
|
+
}
|
|
1141
|
+
else if (isTypeToken(tokens[consumed][0])) {
|
|
1142
|
+
const [param, paramConsumed] = this.classicalDeclaration(tokens.slice(consumed));
|
|
1143
|
+
args.push(param);
|
|
1144
|
+
consumed += paramConsumed;
|
|
1145
|
+
}
|
|
1146
|
+
else if (this.matchNext(tokens.slice(consumed), [Token.ReadOnly]) ||
|
|
1147
|
+
this.matchNext(tokens.slice(consumed), [Token.Mutable])) {
|
|
1148
|
+
const [arrayRef, arrayRefConsumed] = this.parseArrayReference(tokens.slice(consumed));
|
|
1149
|
+
args.push(arrayRef);
|
|
1150
|
+
consumed += arrayRefConsumed;
|
|
1151
|
+
}
|
|
1152
|
+
else {
|
|
1153
|
+
const [param, paramConsumed] = this.binaryExpression(tokens.slice(consumed));
|
|
1154
|
+
args.push(param);
|
|
1155
|
+
consumed += paramConsumed;
|
|
1156
|
+
}
|
|
1157
|
+
if (this.matchNext(tokens.slice(consumed), [Token.Comma])) {
|
|
1158
|
+
consumed++;
|
|
1159
|
+
}
|
|
1160
|
+
else if (!this.matchNext(tokens.slice(consumed), [Token.RParen])) {
|
|
1161
|
+
throwParserError(BadParameterError, tokens[consumed], this.index + consumed, "expected comma or closing parenthesis in parameter list");
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
consumed++;
|
|
1165
|
+
}
|
|
1166
|
+
return [new Parameters(args), consumed];
|
|
1167
|
+
}
|
|
1168
|
+
/**
|
|
1169
|
+
* Parses an array reference.
|
|
1170
|
+
* @param tokens - Remaining tokens to parse.
|
|
1171
|
+
* @return A tuple containing the ArrayReference and the number of tokens consumed.
|
|
1172
|
+
*/
|
|
1173
|
+
parseArrayReference(tokens) {
|
|
1174
|
+
let consumed = 0;
|
|
1175
|
+
let modifier;
|
|
1176
|
+
switch (tokens[consumed][0]) {
|
|
1177
|
+
case Token.Mutable:
|
|
1178
|
+
modifier = ArrayReferenceModifier.MUTABLE;
|
|
1179
|
+
break;
|
|
1180
|
+
case Token.ReadOnly:
|
|
1181
|
+
modifier = ArrayReferenceModifier.READONLY;
|
|
1182
|
+
break;
|
|
1183
|
+
default:
|
|
1184
|
+
throwParserError(BadExpressionError, tokens[consumed], this.index + consumed, "unsupported array reference modifier");
|
|
1185
|
+
}
|
|
1186
|
+
consumed++;
|
|
1187
|
+
const [arrayNode, arrayConsumed] = this.arrayDeclaration(tokens.slice(consumed));
|
|
1188
|
+
consumed += arrayConsumed;
|
|
1189
|
+
return [new ArrayReference(arrayNode, modifier), consumed];
|
|
1190
|
+
}
|
|
1191
|
+
/**
|
|
1192
|
+
* Parses an alias statement.
|
|
1193
|
+
* @param tokens - Remaining tokens to parse.
|
|
1194
|
+
* @return A tuple containing the AliasStatement and the number of tokens consumed.
|
|
1195
|
+
*/
|
|
1196
|
+
aliasStatement(tokens) {
|
|
1197
|
+
let consumed = 1;
|
|
1198
|
+
if (!this.matchNext(tokens.slice(consumed), [Token.Id])) {
|
|
1199
|
+
throwParserError(BadExpressionError, tokens[consumed], this.index + consumed, "expected identifier for alias statement");
|
|
1200
|
+
}
|
|
1201
|
+
const [identifier, identifierConsumed] = this.unaryExpression(tokens.slice(consumed));
|
|
1202
|
+
consumed += identifierConsumed;
|
|
1203
|
+
if (!this.matchNext(tokens.slice(consumed), [Token.EqualsAssmt])) {
|
|
1204
|
+
throwParserError(BadExpressionError, tokens[consumed], this.index + consumed, "expected `=` symbol following identifier for alias statement");
|
|
1205
|
+
}
|
|
1206
|
+
consumed++;
|
|
1207
|
+
const [aliasExpression, aliasExpressionConsumed] = this.binaryExpression(tokens.slice(consumed));
|
|
1208
|
+
consumed += aliasExpressionConsumed;
|
|
1209
|
+
if (!this.matchNext(tokens.slice(consumed), [Token.Semicolon])) {
|
|
1210
|
+
throwParserError(MissingSemicolonError, tokens[consumed], this.index + consumed, "expected semicolon");
|
|
1211
|
+
}
|
|
1212
|
+
consumed++;
|
|
1213
|
+
return [
|
|
1214
|
+
new AliasStatement(identifier, aliasExpression),
|
|
1215
|
+
consumed,
|
|
1216
|
+
];
|
|
1217
|
+
}
|
|
1218
|
+
/**
|
|
1219
|
+
* Parses a quantum reset.
|
|
1220
|
+
* @param tokens - Remaining tokens to parse.
|
|
1221
|
+
* @return A tuple containing the QuantumReset and the number of tokens consumed.
|
|
1222
|
+
*/
|
|
1223
|
+
resetStatement(tokens) {
|
|
1224
|
+
let consumed = 1;
|
|
1225
|
+
if (!this.matchNext(tokens.slice(consumed), [Token.Id])) {
|
|
1226
|
+
throwParserError(BadQuantumInstructionError, tokens[consumed], this.index + consumed, "expected identifier after reset keyword");
|
|
1227
|
+
}
|
|
1228
|
+
const [idNode, idConsumed] = this.unaryExpression(tokens.slice(consumed));
|
|
1229
|
+
consumed += idConsumed;
|
|
1230
|
+
let id = null;
|
|
1231
|
+
if (idNode instanceof SubscriptedIdentifier) {
|
|
1232
|
+
id = idNode;
|
|
1233
|
+
}
|
|
1234
|
+
else {
|
|
1235
|
+
id = idNode;
|
|
1236
|
+
}
|
|
1237
|
+
consumed++;
|
|
1238
|
+
return [new QuantumReset(id), consumed];
|
|
1239
|
+
}
|
|
1240
|
+
/**
|
|
1241
|
+
* Parses a subscript expression as a Range.
|
|
1242
|
+
* @param tokens - Remaining tokens to parse.
|
|
1243
|
+
* @return A tuple containing the parsed Expression or Range and the number of tokens consumed.
|
|
1244
|
+
*/
|
|
1245
|
+
parseSubscript(tokens) {
|
|
1246
|
+
let consumed = 1;
|
|
1247
|
+
let start = null;
|
|
1248
|
+
let step = new IntegerLiteral(1);
|
|
1249
|
+
let stop = null;
|
|
1250
|
+
if (this.matchNext(tokens.slice(consumed), [Token.LCParen])) {
|
|
1251
|
+
const [indexSet, indexSetConsumed] = this.parseIndexSet(tokens.slice(consumed));
|
|
1252
|
+
consumed += indexSetConsumed;
|
|
1253
|
+
if (!this.matchNext(tokens.slice(consumed), [Token.RSParen])) {
|
|
1254
|
+
throwParserError(MissingBraceError, tokens[consumed], this.index + consumed, "expected closing curly bracket } for index set");
|
|
1255
|
+
}
|
|
1256
|
+
consumed++;
|
|
1257
|
+
return [indexSet, consumed];
|
|
1258
|
+
}
|
|
1259
|
+
if (!this.matchNext(tokens.slice(consumed), [Token.Colon])) {
|
|
1260
|
+
// Parse start
|
|
1261
|
+
const [startExpr, startConsumed] = this.binaryExpression(tokens.slice(consumed));
|
|
1262
|
+
start = startExpr;
|
|
1263
|
+
consumed += startConsumed;
|
|
1264
|
+
}
|
|
1265
|
+
if (this.matchNext(tokens.slice(consumed), [Token.RSParen])) {
|
|
1266
|
+
return [start, consumed + 1];
|
|
1267
|
+
}
|
|
1268
|
+
// Check for colon after start
|
|
1269
|
+
if (!this.matchNext(tokens.slice(consumed), [Token.Colon])) {
|
|
1270
|
+
throwParserError(BadExpressionError, tokens[consumed], this.index + consumed, "expected colon in range expression");
|
|
1271
|
+
}
|
|
1272
|
+
consumed++;
|
|
1273
|
+
if (!this.matchNext(tokens.slice(consumed), [Token.RSParen])) {
|
|
1274
|
+
const [expr, exprConsumed] = this.binaryExpression(tokens.slice(consumed));
|
|
1275
|
+
consumed += exprConsumed;
|
|
1276
|
+
// Check if its a step or stop
|
|
1277
|
+
if (this.matchNext(tokens.slice(consumed), [Token.Colon])) {
|
|
1278
|
+
step = expr;
|
|
1279
|
+
consumed++;
|
|
1280
|
+
// Parse stop
|
|
1281
|
+
if (this.matchNext(tokens.slice(consumed), [Token.RSParen])) {
|
|
1282
|
+
throwParserError(BadExpressionError, tokens[consumed], this.index + consumed, "expected stop value in range expression");
|
|
1283
|
+
}
|
|
1284
|
+
const [stopExpr, stopConsumed] = this.binaryExpression(tokens.slice(consumed));
|
|
1285
|
+
stop = stopExpr;
|
|
1286
|
+
consumed += stopConsumed;
|
|
1287
|
+
}
|
|
1288
|
+
else {
|
|
1289
|
+
stop = expr;
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
// Check for closing square bracket
|
|
1293
|
+
if (!this.matchNext(tokens.slice(consumed), [Token.RSParen])) {
|
|
1294
|
+
throwParserError(MissingBraceError, tokens[consumed], this.index + consumed, "expected closing bracket ] for range expression");
|
|
1295
|
+
}
|
|
1296
|
+
consumed++;
|
|
1297
|
+
// Validate that both start and stop are provided
|
|
1298
|
+
if (start === null || stop === null) {
|
|
1299
|
+
throwParserError(BadExpressionError, tokens[0], this.index, "both start and stop must be provided in range expression");
|
|
1300
|
+
}
|
|
1301
|
+
return [new Range(start, stop, step), consumed];
|
|
1302
|
+
}
|
|
1303
|
+
/**
|
|
1304
|
+
* Parses a type cast expression.
|
|
1305
|
+
* @param tokens - Remaining tokens to parse.
|
|
1306
|
+
* @return A tuple containing the parsed Cast Expression and the number of tokens consumed.
|
|
1307
|
+
*/
|
|
1308
|
+
parseTypeCast(tokens) {
|
|
1309
|
+
let consumed = 1;
|
|
1310
|
+
let widthExpr = null;
|
|
1311
|
+
if (this.matchNext(tokens.slice(consumed), [Token.LSParen])) {
|
|
1312
|
+
consumed++;
|
|
1313
|
+
const [width, widthConsumed] = this.binaryExpression(tokens.slice(consumed));
|
|
1314
|
+
consumed += widthConsumed;
|
|
1315
|
+
widthExpr = width;
|
|
1316
|
+
if (!this.matchNext(tokens.slice(consumed), [Token.RSParen])) {
|
|
1317
|
+
throwParserError(MissingBraceError, tokens[consumed], this.index + consumed, "missing closing bracket ] for type designator");
|
|
1318
|
+
}
|
|
1319
|
+
consumed++;
|
|
1320
|
+
}
|
|
1321
|
+
if (!this.matchNext(tokens.slice(consumed), [Token.LParen])) {
|
|
1322
|
+
throwParserError(MissingBraceError, tokens[0], this.index, "expected opening parenthesis ( for type cast");
|
|
1323
|
+
}
|
|
1324
|
+
consumed++;
|
|
1325
|
+
const [castExpr, castConsumed] = this.binaryExpression(tokens.slice(consumed));
|
|
1326
|
+
consumed += castConsumed;
|
|
1327
|
+
if (!this.matchNext(tokens.slice(consumed), [Token.RParen])) {
|
|
1328
|
+
throwParserError(MissingBraceError, tokens[0], this.index, "expected closing parenthesis ) for type cast");
|
|
1329
|
+
}
|
|
1330
|
+
consumed++;
|
|
1331
|
+
const castType = this.createClassicalType(tokens[0][0], widthExpr);
|
|
1332
|
+
return [new Cast(castType, castExpr), consumed];
|
|
1333
|
+
}
|
|
1334
|
+
/**
|
|
1335
|
+
* Parses a branching condition (if) statement.
|
|
1336
|
+
* @param tokens - Tokens to parse.
|
|
1337
|
+
* @return A BranchingStatement node representing the if statement and the number of tokens consumed.
|
|
1338
|
+
*/
|
|
1339
|
+
ifStatement(tokens) {
|
|
1340
|
+
let consumed = 1;
|
|
1341
|
+
if (!this.matchNext(tokens.slice(consumed), [Token.LParen])) {
|
|
1342
|
+
throwParserError(MissingBraceError, tokens[consumed], this.index + consumed, "expected opening parenthesis after if");
|
|
1343
|
+
}
|
|
1344
|
+
consumed++;
|
|
1345
|
+
const [condition, conditionConsumed] = this.binaryExpression(tokens.slice(consumed));
|
|
1346
|
+
consumed += conditionConsumed;
|
|
1347
|
+
if (!this.matchNext(tokens.slice(consumed), [Token.RParen])) {
|
|
1348
|
+
throwParserError(MissingBraceError, tokens[consumed], this.index + consumed, "expected closing parenthesis after if statement condition");
|
|
1349
|
+
}
|
|
1350
|
+
consumed++;
|
|
1351
|
+
const [trueBody, trueBodyConsumed] = this.programBlock(tokens.slice(consumed));
|
|
1352
|
+
consumed += trueBodyConsumed;
|
|
1353
|
+
let falseBody = null;
|
|
1354
|
+
if (this.matchNext(tokens.slice(consumed), [Token.Else])) {
|
|
1355
|
+
consumed++;
|
|
1356
|
+
const [elseBody, elseBodyConsumed] = this.programBlock(tokens.slice(consumed));
|
|
1357
|
+
falseBody = elseBody;
|
|
1358
|
+
consumed += elseBodyConsumed;
|
|
1359
|
+
}
|
|
1360
|
+
return [new BranchingStatement(condition, trueBody, falseBody), consumed];
|
|
1361
|
+
}
|
|
1362
|
+
/**
|
|
1363
|
+
* Parses a for loop.
|
|
1364
|
+
* @param tokens - Tokens to parse.
|
|
1365
|
+
* @return A ForLoopStatement node and the number of tokens consumed.
|
|
1366
|
+
*/
|
|
1367
|
+
forLoopStatement(tokens) {
|
|
1368
|
+
let consumed = 1;
|
|
1369
|
+
// Parse the type of the loop variable
|
|
1370
|
+
const [loopVarType, loopVarTypeConsumed] = this.parseClassicalType(tokens.slice(consumed));
|
|
1371
|
+
consumed += loopVarTypeConsumed;
|
|
1372
|
+
// Expect an identifier (loop variable)
|
|
1373
|
+
if (!this.matchNext(tokens.slice(consumed), [Token.Id])) {
|
|
1374
|
+
throwParserError(BadLoopError, tokens[consumed], this.index + consumed, "expected identifier for loop variable");
|
|
1375
|
+
}
|
|
1376
|
+
const [loopVar, loopVarConsumed] = this.unaryExpression(tokens.slice(consumed));
|
|
1377
|
+
if (!(loopVar instanceof Identifier)) {
|
|
1378
|
+
throwParserError(BadLoopError, tokens[consumed], this.index + consumed, "expected identifier for loop variable");
|
|
1379
|
+
}
|
|
1380
|
+
consumed += loopVarConsumed;
|
|
1381
|
+
// Expect `in` keyword
|
|
1382
|
+
if (!this.matchNext(tokens.slice(consumed), [Token.In])) {
|
|
1383
|
+
throwParserError(BadLoopError, tokens[consumed], this.index + consumed, "expected `in` keyword in for loop");
|
|
1384
|
+
}
|
|
1385
|
+
consumed++;
|
|
1386
|
+
const [indexSet, indexSetConsumed] = this.parseSetDeclaration(tokens.slice(consumed));
|
|
1387
|
+
consumed += indexSetConsumed;
|
|
1388
|
+
const [body, bodyConsumed] = this.programBlock(tokens.slice(consumed));
|
|
1389
|
+
consumed += bodyConsumed;
|
|
1390
|
+
return [
|
|
1391
|
+
new ForLoopStatement(indexSet, loopVarType, loopVar, body),
|
|
1392
|
+
consumed,
|
|
1393
|
+
];
|
|
1394
|
+
}
|
|
1395
|
+
/**
|
|
1396
|
+
* Parses a while loop.
|
|
1397
|
+
* @param tokens - Tokens to parse.
|
|
1398
|
+
* @return A ForLoopStatement node and the number of tokens consumed.
|
|
1399
|
+
*/
|
|
1400
|
+
whileLoopStatement(tokens) {
|
|
1401
|
+
let consumed = 1;
|
|
1402
|
+
if (!this.matchNext(tokens.slice(consumed), [Token.LParen])) {
|
|
1403
|
+
throwParserError(MissingBraceError, tokens[consumed], this.index + consumed, "expected opening parenthesis after while keyword");
|
|
1404
|
+
}
|
|
1405
|
+
consumed++;
|
|
1406
|
+
const [condition, conditionConsumed] = this.binaryExpression(tokens.slice(consumed));
|
|
1407
|
+
consumed += conditionConsumed;
|
|
1408
|
+
if (!this.matchNext(tokens.slice(consumed), [Token.RParen])) {
|
|
1409
|
+
throwParserError(MissingBraceError, tokens[consumed], this.index + consumed, "expected closing parenthesis after while loop condition");
|
|
1410
|
+
}
|
|
1411
|
+
consumed++;
|
|
1412
|
+
const [body, bodyConsumed] = this.programBlock(tokens.slice(consumed));
|
|
1413
|
+
consumed += bodyConsumed;
|
|
1414
|
+
return [new WhileLoopStatement(condition, body), consumed];
|
|
1415
|
+
}
|
|
1416
|
+
/**
|
|
1417
|
+
* Parses a barrier statement.
|
|
1418
|
+
* @param tokens - Tokens to parse.
|
|
1419
|
+
* @return An QuantumBarrier node and the number of tokens consumed.
|
|
1420
|
+
*/
|
|
1421
|
+
barrier(tokens) {
|
|
1422
|
+
let consumed = 1;
|
|
1423
|
+
const [qubits, qubitsConsumed] = this.parseQubitList(tokens.slice(consumed));
|
|
1424
|
+
consumed += qubitsConsumed;
|
|
1425
|
+
return [new QuantumBarrier(qubits), consumed];
|
|
1426
|
+
}
|
|
1427
|
+
/**
|
|
1428
|
+
* Parses a box statement.
|
|
1429
|
+
* @param tokens - Tokens to parse.
|
|
1430
|
+
* @return An BoxDefinition node and the number of tokens consumed.
|
|
1431
|
+
*/
|
|
1432
|
+
box(tokens) {
|
|
1433
|
+
let consumed = 1;
|
|
1434
|
+
let designator = null;
|
|
1435
|
+
if (this.matchNext(tokens.slice(consumed), [Token.LSParen])) {
|
|
1436
|
+
consumed++;
|
|
1437
|
+
const [desginatorNode, designatorConsumed] = this.binaryExpression(tokens.slice(consumed));
|
|
1438
|
+
consumed += designatorConsumed;
|
|
1439
|
+
designator = desginatorNode;
|
|
1440
|
+
if (!this.matchNext(tokens.slice(consumed), [Token.RSParen])) {
|
|
1441
|
+
throwParserError(MissingBraceError, tokens[consumed], this.index + consumed, "expected closing ] bracket following box designator");
|
|
1442
|
+
}
|
|
1443
|
+
consumed++;
|
|
1444
|
+
}
|
|
1445
|
+
const [scope, scopeConsumed] = this.programBlock(tokens.slice(consumed));
|
|
1446
|
+
consumed += scopeConsumed;
|
|
1447
|
+
return [new BoxDefinition(scope, designator), consumed];
|
|
1448
|
+
}
|
|
1449
|
+
/**
|
|
1450
|
+
* Parses an array statement.
|
|
1451
|
+
* @param tokens - Tokens to parse.
|
|
1452
|
+
* @return An ArrayDeclaration node and the number of tokens consumed.
|
|
1453
|
+
*/
|
|
1454
|
+
arrayDeclaration(tokens) {
|
|
1455
|
+
let consumed = 1;
|
|
1456
|
+
if (!this.matchNext(tokens.slice(consumed), [Token.LSParen])) {
|
|
1457
|
+
throwParserError(MissingBraceError, tokens[consumed], this.index + consumed, "expected [ after array keyword");
|
|
1458
|
+
}
|
|
1459
|
+
consumed++;
|
|
1460
|
+
const [baseType, baseTypeConsumed] = this.parseClassicalType(tokens.slice(consumed));
|
|
1461
|
+
consumed += baseTypeConsumed;
|
|
1462
|
+
let dimensions = [];
|
|
1463
|
+
while (this.matchNext(tokens.slice(consumed), [Token.Comma])) {
|
|
1464
|
+
consumed++;
|
|
1465
|
+
if (this.matchNext(tokens.slice(consumed), [
|
|
1466
|
+
Token.Dim,
|
|
1467
|
+
Token.EqualsAssmt,
|
|
1468
|
+
Token.NNInteger,
|
|
1469
|
+
])) {
|
|
1470
|
+
consumed += 2;
|
|
1471
|
+
dimensions = Number(tokens[consumed][1]);
|
|
1472
|
+
consumed++;
|
|
1473
|
+
break;
|
|
1474
|
+
}
|
|
1475
|
+
const [dimension, dimensionConsumed] = this.binaryExpression(tokens.slice(consumed));
|
|
1476
|
+
dimensions.push(dimension);
|
|
1477
|
+
consumed += dimensionConsumed;
|
|
1478
|
+
}
|
|
1479
|
+
if (!this.matchNext(tokens.slice(consumed), [Token.RSParen])) {
|
|
1480
|
+
throwParserError(MissingBraceError, tokens[consumed], this.index + consumed, "expected ] after array type declaration");
|
|
1481
|
+
}
|
|
1482
|
+
consumed++;
|
|
1483
|
+
if (!this.matchNext(tokens.slice(consumed), [Token.Id])) {
|
|
1484
|
+
throwParserError(BadExpressionError, tokens[consumed], this.index + consumed, "expected identifier for array declaration");
|
|
1485
|
+
}
|
|
1486
|
+
const [identifier, identifierConsumed] = this.unaryExpression(tokens.slice(consumed));
|
|
1487
|
+
consumed += identifierConsumed;
|
|
1488
|
+
let initializer = null;
|
|
1489
|
+
if (this.matchNext(tokens.slice(consumed), [Token.EqualsAssmt])) {
|
|
1490
|
+
consumed++;
|
|
1491
|
+
const [parsedInitializer, initializerConsumed] = this.parseArrayInitializer(tokens.slice(consumed));
|
|
1492
|
+
initializer = parsedInitializer;
|
|
1493
|
+
consumed += initializerConsumed;
|
|
1494
|
+
}
|
|
1495
|
+
if (this.matchNext(tokens.slice(consumed), [Token.Semicolon])) {
|
|
1496
|
+
consumed++;
|
|
1497
|
+
}
|
|
1498
|
+
return [
|
|
1499
|
+
new ArrayDeclaration(baseType, dimensions, identifier, initializer),
|
|
1500
|
+
consumed,
|
|
1501
|
+
];
|
|
1502
|
+
}
|
|
1503
|
+
/**
|
|
1504
|
+
* Parses an array initializer.
|
|
1505
|
+
* @param tokens - Tokens to parse.
|
|
1506
|
+
* @return An ArrayInitializer node and the number of tokens consumed.
|
|
1507
|
+
*/
|
|
1508
|
+
parseArrayInitializer(tokens) {
|
|
1509
|
+
let consumed = 0;
|
|
1510
|
+
if (!this.matchNext(tokens, [Token.LCParen])) {
|
|
1511
|
+
const [expr, exprConsumed] = this.binaryExpression(tokens.slice(consumed));
|
|
1512
|
+
consumed += exprConsumed;
|
|
1513
|
+
return [expr, consumed];
|
|
1514
|
+
}
|
|
1515
|
+
consumed++;
|
|
1516
|
+
const values = [];
|
|
1517
|
+
while (!this.matchNext(tokens.slice(consumed), [Token.RCParen])) {
|
|
1518
|
+
if (this.matchNext(tokens.slice(consumed), [Token.LCParen])) {
|
|
1519
|
+
const [subInitializer, subConsumed] = this.parseArrayInitializer(tokens.slice(consumed));
|
|
1520
|
+
values.push(subInitializer);
|
|
1521
|
+
consumed += subConsumed;
|
|
1522
|
+
}
|
|
1523
|
+
else {
|
|
1524
|
+
const [expr, exprConsumed] = this.binaryExpression(tokens.slice(consumed));
|
|
1525
|
+
values.push(expr);
|
|
1526
|
+
consumed += exprConsumed;
|
|
1527
|
+
}
|
|
1528
|
+
if (this.matchNext(tokens.slice(consumed), [Token.Comma])) {
|
|
1529
|
+
consumed++;
|
|
1530
|
+
}
|
|
1531
|
+
else if (!this.matchNext(tokens.slice(consumed), [Token.RCParen])) {
|
|
1532
|
+
throwParserError(BadExpressionError, tokens[consumed], this.index + consumed, "expected comma or } in array initializer");
|
|
1533
|
+
}
|
|
1534
|
+
}
|
|
1535
|
+
consumed++;
|
|
1536
|
+
return [new ArrayInitializer(values), consumed];
|
|
1537
|
+
}
|
|
1538
|
+
/**
|
|
1539
|
+
* Parses an array access.
|
|
1540
|
+
* @param tokens - Tokens to parse.
|
|
1541
|
+
* @return An ArrayAccess node and the number of tokens consumed.
|
|
1542
|
+
*/
|
|
1543
|
+
parseArrayAccess(tokens) {
|
|
1544
|
+
let consumed = 0;
|
|
1545
|
+
if (!this.matchNext(tokens.slice(consumed), [Token.Id])) {
|
|
1546
|
+
throwParserError(BadExpressionError, tokens[consumed], this.index + consumed, "expected identifier for array access");
|
|
1547
|
+
}
|
|
1548
|
+
const identifier = new Identifier(tokens[0][1].toString());
|
|
1549
|
+
consumed++;
|
|
1550
|
+
let indices = [];
|
|
1551
|
+
if (this.matchNext(tokens.slice(consumed), [Token.LSParen])) {
|
|
1552
|
+
consumed++;
|
|
1553
|
+
while (!this.matchNext(tokens.slice(consumed), [Token.RSParen])) {
|
|
1554
|
+
const [index1, index1Consumed] = this.binaryExpression(tokens.slice(consumed));
|
|
1555
|
+
indices.push(index1);
|
|
1556
|
+
consumed += index1Consumed;
|
|
1557
|
+
if (this.matchNext(tokens.slice(consumed), [Token.Comma])) {
|
|
1558
|
+
consumed++;
|
|
1559
|
+
}
|
|
1560
|
+
else if (this.matchNext(tokens.slice(consumed), [Token.Colon])) {
|
|
1561
|
+
consumed++;
|
|
1562
|
+
let step = new IntegerLiteral(1);
|
|
1563
|
+
let stop;
|
|
1564
|
+
const [index2, index2Consumed] = this.binaryExpression(tokens.slice(consumed));
|
|
1565
|
+
consumed += index2Consumed;
|
|
1566
|
+
if (this.matchNext(tokens.slice(consumed), [Token.Colon])) {
|
|
1567
|
+
consumed++;
|
|
1568
|
+
const [index3, index3Consumed] = this.binaryExpression(tokens.slice(consumed));
|
|
1569
|
+
consumed += index3Consumed;
|
|
1570
|
+
stop = index3;
|
|
1571
|
+
step = index2;
|
|
1572
|
+
}
|
|
1573
|
+
else {
|
|
1574
|
+
stop = index2;
|
|
1575
|
+
}
|
|
1576
|
+
indices = new Range(index1, stop, step);
|
|
1577
|
+
break;
|
|
1578
|
+
}
|
|
1579
|
+
else if (!this.matchNext(tokens.slice(consumed), [Token.RSParen])) {
|
|
1580
|
+
throwParserError(BadExpressionError, tokens[consumed], this.index + consumed, "expected comma or closing bracket for array access");
|
|
1581
|
+
}
|
|
1582
|
+
}
|
|
1583
|
+
if (!this.matchNext(tokens.slice(consumed), [Token.RSParen])) {
|
|
1584
|
+
throwParserError(MissingBraceError, tokens[consumed], this.index + consumed, "expected closing bracket ] for array access");
|
|
1585
|
+
}
|
|
1586
|
+
consumed++;
|
|
1587
|
+
}
|
|
1588
|
+
return [new ArrayAccess(identifier, indices), consumed];
|
|
1589
|
+
}
|
|
1590
|
+
/**
|
|
1591
|
+
* Parses a switch statement.
|
|
1592
|
+
* @param tokens - Tokens to parse.
|
|
1593
|
+
* @return A SwitchStatement node and the number of tokens consumed.
|
|
1594
|
+
*/
|
|
1595
|
+
switchStatement(tokens) {
|
|
1596
|
+
let consumed = 1;
|
|
1597
|
+
if (!this.matchNext(tokens.slice(consumed), [Token.LParen])) {
|
|
1598
|
+
throwParserError(MissingBraceError, tokens[consumed], this.index + consumed, "expected opening parenthesis after switch keywork");
|
|
1599
|
+
}
|
|
1600
|
+
consumed++;
|
|
1601
|
+
const [controlExpr, controlExprConsumed] = this.binaryExpression(tokens.slice(consumed));
|
|
1602
|
+
consumed += controlExprConsumed;
|
|
1603
|
+
if (!this.matchNext(tokens.slice(consumed), [Token.RParen])) {
|
|
1604
|
+
throwParserError(MissingBraceError, tokens[consumed], this.index + consumed, "expected closing parenthesis after switch control expression");
|
|
1605
|
+
}
|
|
1606
|
+
consumed++;
|
|
1607
|
+
if (!this.matchNext(tokens.slice(consumed), [Token.LCParen])) {
|
|
1608
|
+
throwParserError(MissingBraceError, tokens[consumed], this.index + consumed, "expected opening curly brace for switch body");
|
|
1609
|
+
}
|
|
1610
|
+
consumed++;
|
|
1611
|
+
const cases = [];
|
|
1612
|
+
let defaultCase = null;
|
|
1613
|
+
while (!this.matchNext(tokens.slice(consumed), [Token.RCParen])) {
|
|
1614
|
+
if (this.matchNext(tokens.slice(consumed), [Token.Case])) {
|
|
1615
|
+
const [caseStmt, caseConsumed] = this.parseCaseStatement(tokens.slice(consumed));
|
|
1616
|
+
cases.push(caseStmt);
|
|
1617
|
+
consumed += caseConsumed;
|
|
1618
|
+
}
|
|
1619
|
+
else if (this.matchNext(tokens.slice(consumed), [Token.Default])) {
|
|
1620
|
+
if (defaultCase !== null) {
|
|
1621
|
+
throwParserError(BadExpressionError, tokens[consumed], this.index + consumed, "multiple default statements not allowed in a swtich statement");
|
|
1622
|
+
}
|
|
1623
|
+
const [defaultStmt, defaultConsumed] = this.parseDefaultStatement(tokens.slice(consumed));
|
|
1624
|
+
defaultCase = defaultStmt;
|
|
1625
|
+
consumed += defaultConsumed;
|
|
1626
|
+
}
|
|
1627
|
+
else {
|
|
1628
|
+
throwParserError(BadExpressionError, tokens[consumed], this.index + consumed, "expected case or default statement in switch body");
|
|
1629
|
+
}
|
|
1630
|
+
}
|
|
1631
|
+
if (cases.length === 0) {
|
|
1632
|
+
throwParserError(BadExpressionError, tokens[consumed], this.index + consumed, "swtich statement must contain at least one case statement");
|
|
1633
|
+
}
|
|
1634
|
+
consumed++;
|
|
1635
|
+
return [new SwitchStatement(controlExpr, cases, defaultCase), consumed];
|
|
1636
|
+
}
|
|
1637
|
+
/**
|
|
1638
|
+
* Parses a case statement.
|
|
1639
|
+
* @param tokens - Tokens to parse.
|
|
1640
|
+
* @return A CaseStatement node and the number of tokens consumed.
|
|
1641
|
+
*/
|
|
1642
|
+
parseCaseStatement(tokens) {
|
|
1643
|
+
let consumed = 1;
|
|
1644
|
+
const labels = [];
|
|
1645
|
+
while (!this.matchNext(tokens.slice(consumed), [Token.LCParen])) {
|
|
1646
|
+
const [label, labelConsumed] = this.binaryExpression(tokens.slice(consumed));
|
|
1647
|
+
labels.push(label);
|
|
1648
|
+
consumed += labelConsumed;
|
|
1649
|
+
if (this.matchNext(tokens.slice(consumed), [Token.Comma])) {
|
|
1650
|
+
consumed++;
|
|
1651
|
+
}
|
|
1652
|
+
else if (!this.matchNext(tokens.slice(consumed), [Token.LCParen])) {
|
|
1653
|
+
throwParserError(BadExpressionError, tokens[consumed], this.index + consumed, "expected comma or opening brace in case statement");
|
|
1654
|
+
}
|
|
1655
|
+
}
|
|
1656
|
+
const [body, bodyConsumed] = this.programBlock(tokens.slice(consumed));
|
|
1657
|
+
consumed += bodyConsumed;
|
|
1658
|
+
return [new CaseStatement(labels, body), consumed];
|
|
1659
|
+
}
|
|
1660
|
+
/**
|
|
1661
|
+
* Parses a default statement.
|
|
1662
|
+
* @param tokens - Tokens to parse.
|
|
1663
|
+
* @return A DefaultStatement node and the number of tokens consumed.
|
|
1664
|
+
*/
|
|
1665
|
+
parseDefaultStatement(tokens) {
|
|
1666
|
+
let consumed = 1;
|
|
1667
|
+
if (!this.matchNext(tokens.slice(consumed), [Token.LCParen])) {
|
|
1668
|
+
throwParserError(MissingBraceError, tokens[consumed], this.index + consumed, "expecting opening brace for default statement");
|
|
1669
|
+
}
|
|
1670
|
+
const [body, bodyConsumed] = this.programBlock(tokens.slice(consumed));
|
|
1671
|
+
consumed += bodyConsumed;
|
|
1672
|
+
return [new DefaultStatement(body), consumed];
|
|
1673
|
+
}
|
|
1674
|
+
/**
|
|
1675
|
+
* Parses a delay statement.
|
|
1676
|
+
* @param tokens - Tokens to parse.
|
|
1677
|
+
* @return A QuantumDelay node and the number of tokens consumed.
|
|
1678
|
+
*/
|
|
1679
|
+
delay(tokens) {
|
|
1680
|
+
let consumed = 1;
|
|
1681
|
+
if (!this.matchNext(tokens.slice(consumed), [Token.LSParen])) {
|
|
1682
|
+
throwParserError(MissingBraceError, tokens[consumed], this.index + consumed, "expected opening bracket [ after delay keyword");
|
|
1683
|
+
}
|
|
1684
|
+
consumed++;
|
|
1685
|
+
const [duration, durationConsumed] = this.binaryExpression(tokens.slice(consumed));
|
|
1686
|
+
consumed += durationConsumed;
|
|
1687
|
+
if (!this.matchNext(tokens.slice(consumed), [Token.RSParen])) {
|
|
1688
|
+
throwParserError(MissingBraceError, tokens[consumed], this.index + consumed, "expected closing bracket ] after delay designator");
|
|
1689
|
+
}
|
|
1690
|
+
consumed++;
|
|
1691
|
+
const [qubits, qubitsConsumed] = this.parseQubitList(tokens.slice(consumed));
|
|
1692
|
+
consumed += qubitsConsumed;
|
|
1693
|
+
if (!this.matchNext(tokens.slice(consumed), [Token.Semicolon])) {
|
|
1694
|
+
throwParserError(MissingSemicolonError, tokens[consumed], this.index + consumed, "expected semicolon");
|
|
1695
|
+
}
|
|
1696
|
+
consumed++;
|
|
1697
|
+
return [new QuantumDelay(duration, qubits), consumed];
|
|
1698
|
+
}
|
|
1699
|
+
/**
|
|
1700
|
+
* Parses a durationof function call.
|
|
1701
|
+
* @param tokens - Tokens to parse.
|
|
1702
|
+
* @return A DurationOf node and the number of tokens consumed.
|
|
1703
|
+
*/
|
|
1704
|
+
durationOf(tokens) {
|
|
1705
|
+
let consumed = 1;
|
|
1706
|
+
if (!this.matchNext(tokens.slice(consumed), [Token.LParen])) {
|
|
1707
|
+
throwParserError(MissingBraceError, tokens[consumed], this.index + consumed, "expected opening parenthesis after durationof");
|
|
1708
|
+
}
|
|
1709
|
+
consumed++;
|
|
1710
|
+
if (!this.matchNext(tokens.slice(consumed), [Token.LCParen])) {
|
|
1711
|
+
throwParserError(MissingBraceError, tokens[consumed], this.index + consumed, "expected opening brace for durationof scope");
|
|
1712
|
+
}
|
|
1713
|
+
const [scope, scopeConsumed] = this.programBlock(tokens.slice(consumed));
|
|
1714
|
+
consumed += scopeConsumed;
|
|
1715
|
+
if (!this.matchNext(tokens.slice(consumed), [Token.RParen])) {
|
|
1716
|
+
throwParserError(MissingBraceError, tokens[consumed], this.index + consumed, "expected closing parenthesis after durationof scope");
|
|
1717
|
+
}
|
|
1718
|
+
consumed++;
|
|
1719
|
+
if (this.matchNext(tokens.slice(consumed), [Token.Semicolon])) {
|
|
1720
|
+
consumed++;
|
|
1721
|
+
}
|
|
1722
|
+
return [new DurationOf(scope), consumed];
|
|
1723
|
+
}
|
|
1724
|
+
/**
|
|
1725
|
+
* Parses a sizeof function call.
|
|
1726
|
+
* @param tokens - Tokens to parse.
|
|
1727
|
+
* @return A SizeOf node and the number of tokens consumed.
|
|
1728
|
+
*/
|
|
1729
|
+
sizeOf(tokens) {
|
|
1730
|
+
let consumed = 1;
|
|
1731
|
+
let dimensions = null;
|
|
1732
|
+
if (!this.matchNext(tokens.slice(consumed), [Token.LParen])) {
|
|
1733
|
+
throwParserError(MissingBraceError, tokens[consumed], this.index + consumed, "expected opening parenthesis for sizeof function call");
|
|
1734
|
+
}
|
|
1735
|
+
consumed++;
|
|
1736
|
+
const [arrayId, arrayIdConsumed] = this.unaryExpression(tokens.slice(consumed));
|
|
1737
|
+
consumed += arrayIdConsumed;
|
|
1738
|
+
if (this.matchNext(tokens.slice(consumed), [Token.Comma])) {
|
|
1739
|
+
consumed++;
|
|
1740
|
+
const [dim, dimConsumed] = this.binaryExpression(tokens.slice(consumed));
|
|
1741
|
+
consumed += dimConsumed;
|
|
1742
|
+
dimensions = dim;
|
|
1743
|
+
}
|
|
1744
|
+
if (!this.matchNext(tokens.slice(consumed), [Token.RParen])) {
|
|
1745
|
+
throwParserError(MissingBraceError, tokens[consumed], this.index + consumed, "expected closing parenthesis for sizeof function call");
|
|
1746
|
+
}
|
|
1747
|
+
consumed++;
|
|
1748
|
+
return [new SizeOf(arrayId, dimensions), consumed];
|
|
1749
|
+
}
|
|
1750
|
+
/**
|
|
1751
|
+
* Parses a program block.
|
|
1752
|
+
* @param tokens - Tokens to parse.
|
|
1753
|
+
* @return A ProgramBlock node and the number of tokens consumed.
|
|
1754
|
+
*/
|
|
1755
|
+
programBlock(tokens) {
|
|
1756
|
+
const statements = [];
|
|
1757
|
+
let consumed = 0;
|
|
1758
|
+
let braceCount = 0;
|
|
1759
|
+
if (this.matchNext(tokens, [Token.LCParen])) {
|
|
1760
|
+
consumed++;
|
|
1761
|
+
braceCount++;
|
|
1762
|
+
while (braceCount > 0 && consumed < tokens.length) {
|
|
1763
|
+
if (this.matchNext(tokens.slice(consumed), [Token.LCParen])) {
|
|
1764
|
+
braceCount++;
|
|
1765
|
+
}
|
|
1766
|
+
else if (this.matchNext(tokens.slice(consumed), [Token.RCParen])) {
|
|
1767
|
+
braceCount--;
|
|
1768
|
+
}
|
|
1769
|
+
if (braceCount === 0) {
|
|
1770
|
+
break;
|
|
1771
|
+
}
|
|
1772
|
+
const [node, nodeConsumed] = this.parseNode(tokens.slice(consumed));
|
|
1773
|
+
if (node && node.length > 0) {
|
|
1774
|
+
statements.push(node[0]);
|
|
1775
|
+
}
|
|
1776
|
+
consumed += nodeConsumed;
|
|
1777
|
+
if (this.matchNext(tokens.slice(consumed), [Token.Semicolon])) {
|
|
1778
|
+
consumed++;
|
|
1779
|
+
}
|
|
1780
|
+
}
|
|
1781
|
+
if (braceCount !== 0) {
|
|
1782
|
+
throwParserError(MissingBraceError, tokens[consumed], this.index + consumed, "mismatched curly braces in program block");
|
|
1783
|
+
}
|
|
1784
|
+
consumed++;
|
|
1785
|
+
}
|
|
1786
|
+
else {
|
|
1787
|
+
const [node, nodeConsumed] = this.parseNode(tokens.slice(consumed));
|
|
1788
|
+
if (node && node.length > 0) {
|
|
1789
|
+
statements.push(node[0]);
|
|
1790
|
+
}
|
|
1791
|
+
consumed += nodeConsumed;
|
|
1792
|
+
if (this.matchNext(tokens.slice(consumed), [Token.Semicolon])) {
|
|
1793
|
+
consumed++;
|
|
1794
|
+
}
|
|
1795
|
+
}
|
|
1796
|
+
return [new ProgramBlock(statements), consumed];
|
|
1797
|
+
}
|
|
1798
|
+
/**
|
|
1799
|
+
* Parses an include statement.
|
|
1800
|
+
* @param tokens - Tokens to parse.
|
|
1801
|
+
* @return An Include node representing the include statement and the number of consumed tokens.
|
|
1802
|
+
*/
|
|
1803
|
+
include(tokens) {
|
|
1804
|
+
let filename;
|
|
1805
|
+
const consumed = 1;
|
|
1806
|
+
if (this.matchNext(tokens.slice(consumed), [Token.String, Token.Semicolon])) {
|
|
1807
|
+
filename = tokens[consumed][1].toString();
|
|
1808
|
+
return [new Include(filename), consumed + 2];
|
|
1809
|
+
}
|
|
1810
|
+
throwParserError(BadStringLiteralError, tokens[consumed], this.index + consumed, "expected string literal following `include` keyword");
|
|
1811
|
+
}
|
|
1812
|
+
/**
|
|
1813
|
+
* Parses the version header and sets the parser version.
|
|
1814
|
+
* @param tokens - Tokens to parse.
|
|
1815
|
+
* @return A Version node representing the version statement and the number of consumed tokens.
|
|
1816
|
+
*/
|
|
1817
|
+
versionHeader(tokens) {
|
|
1818
|
+
let version;
|
|
1819
|
+
let consumed = 1;
|
|
1820
|
+
const slicedTokens = tokens.slice(consumed);
|
|
1821
|
+
if (this.matchNext(slicedTokens, [Token.NNInteger, Token.Semicolon])) {
|
|
1822
|
+
version = new OpenQASMVersion(Number(tokens[0][1]));
|
|
1823
|
+
if (!version.isVersion3()) {
|
|
1824
|
+
throwParserError(UnsupportedOpenQASMVersionError, tokens[consumed], this.index + consumed, "expected QASM version 3");
|
|
1825
|
+
}
|
|
1826
|
+
consumed += 2;
|
|
1827
|
+
return [new Version(version), consumed];
|
|
1828
|
+
}
|
|
1829
|
+
else if (this.matchNext(slicedTokens, [Token.Real, Token.Semicolon])) {
|
|
1830
|
+
const versionSplits = tokens[consumed][1].toString().split(".");
|
|
1831
|
+
version = new OpenQASMVersion(Number(versionSplits[0]), Number(versionSplits[1]));
|
|
1832
|
+
if (!version.isVersion3()) {
|
|
1833
|
+
throwParserError(UnsupportedOpenQASMVersionError, tokens[consumed], this.index + consumed, "expected QASM version 3");
|
|
1834
|
+
}
|
|
1835
|
+
consumed += 2;
|
|
1836
|
+
return [new Version(version), consumed];
|
|
1837
|
+
}
|
|
1838
|
+
throwParserError(UnsupportedOpenQASMVersionError, tokens[consumed], this.index + consumed, "expected QASM version 3");
|
|
1839
|
+
}
|
|
1840
|
+
/**
|
|
1841
|
+
* Parses a classical type.
|
|
1842
|
+
* @param token - The token that represents the type.
|
|
1843
|
+
* @return The ClassicalType and the number of consumed tokens.
|
|
1844
|
+
*/
|
|
1845
|
+
parseClassicalType(tokens) {
|
|
1846
|
+
let typeToken = tokens[0][0];
|
|
1847
|
+
let consumed = 1;
|
|
1848
|
+
let width;
|
|
1849
|
+
let isComplex = false;
|
|
1850
|
+
if (typeToken === Token.Complex) {
|
|
1851
|
+
if (!this.matchNext(tokens.slice(consumed), [Token.LSParen, Token.Float])) {
|
|
1852
|
+
throwParserError(BadClassicalTypeError, tokens[consumed], this.index + consumed, "expected float type for complex number");
|
|
1853
|
+
}
|
|
1854
|
+
isComplex = true;
|
|
1855
|
+
consumed += 2;
|
|
1856
|
+
typeToken = tokens[2][0];
|
|
1857
|
+
}
|
|
1858
|
+
// Check if there's a width or size specification
|
|
1859
|
+
if (this.matchNext(tokens.slice(consumed), [Token.LSParen])) {
|
|
1860
|
+
consumed++;
|
|
1861
|
+
const [widthExpr, widthConsumed] = this.binaryExpression(tokens.slice(consumed));
|
|
1862
|
+
width = widthExpr;
|
|
1863
|
+
consumed += widthConsumed;
|
|
1864
|
+
if (!this.matchNext(tokens.slice(consumed), [Token.RSParen])) {
|
|
1865
|
+
throwParserError(MissingBraceError, tokens[consumed], this.index + consumed, "expected closing bracket ] for type width");
|
|
1866
|
+
}
|
|
1867
|
+
if (isComplex) {
|
|
1868
|
+
consumed++;
|
|
1869
|
+
}
|
|
1870
|
+
consumed++;
|
|
1871
|
+
}
|
|
1872
|
+
const classicalType = this.createClassicalType(typeToken, width);
|
|
1873
|
+
if (isComplex) {
|
|
1874
|
+
return [new ComplexType(classicalType), consumed];
|
|
1875
|
+
}
|
|
1876
|
+
else {
|
|
1877
|
+
return [classicalType, consumed];
|
|
1878
|
+
}
|
|
1879
|
+
}
|
|
1880
|
+
/**
|
|
1881
|
+
* Parses a set declaration.
|
|
1882
|
+
* @param token - The token that represents the type.
|
|
1883
|
+
* @return The resulting Identifier, IndexSet, or Range node and the number of consumed tokens.
|
|
1884
|
+
*/
|
|
1885
|
+
parseSetDeclaration(tokens) {
|
|
1886
|
+
if (this.matchNext(tokens, [Token.Id])) {
|
|
1887
|
+
const [identifier, identifierConsumed] = this.unaryExpression(tokens);
|
|
1888
|
+
return [identifier, identifierConsumed];
|
|
1889
|
+
}
|
|
1890
|
+
else if (this.matchNext(tokens, [Token.LCParen])) {
|
|
1891
|
+
return this.parseIndexSet(tokens);
|
|
1892
|
+
}
|
|
1893
|
+
else if (this.matchNext(tokens, [Token.LSParen])) {
|
|
1894
|
+
return this.parseSubscript(tokens);
|
|
1895
|
+
}
|
|
1896
|
+
else {
|
|
1897
|
+
return this.binaryExpression(tokens);
|
|
1898
|
+
}
|
|
1899
|
+
}
|
|
1900
|
+
/**
|
|
1901
|
+
* Parses an index set.
|
|
1902
|
+
* @param token - The token that represents the type.
|
|
1903
|
+
* @return The resulting Identifier, IndexSet, or Range node and the number of consumed tokens.
|
|
1904
|
+
*/
|
|
1905
|
+
parseIndexSet(tokens) {
|
|
1906
|
+
let consumed = 1;
|
|
1907
|
+
const values = [];
|
|
1908
|
+
while (!this.matchNext(tokens.slice(consumed), [Token.RCParen])) {
|
|
1909
|
+
const [expr, exprConsumed] = this.binaryExpression(tokens.slice(consumed));
|
|
1910
|
+
values.push(expr);
|
|
1911
|
+
consumed += exprConsumed;
|
|
1912
|
+
if (this.matchNext(tokens.slice(consumed), [Token.Comma])) {
|
|
1913
|
+
consumed++;
|
|
1914
|
+
}
|
|
1915
|
+
else if (!this.matchNext(tokens.slice(consumed), [Token.RCParen])) {
|
|
1916
|
+
throwParserError(BadExpressionError, tokens[consumed], this.index + consumed, "expected comma or closing curly brace in index set");
|
|
1917
|
+
}
|
|
1918
|
+
}
|
|
1919
|
+
consumed++;
|
|
1920
|
+
return [new IndexSet(values), consumed];
|
|
1921
|
+
}
|
|
1922
|
+
/**
|
|
1923
|
+
* Creates a classical type.
|
|
1924
|
+
* @param token - The token that represents the type.
|
|
1925
|
+
* @param width - The type's width or size, if applicable.
|
|
1926
|
+
* @return The created ClassicalType.
|
|
1927
|
+
*/
|
|
1928
|
+
createClassicalType(token, width) {
|
|
1929
|
+
switch (token) {
|
|
1930
|
+
case Token.Int:
|
|
1931
|
+
return new IntType(width || this.machineIntSize);
|
|
1932
|
+
case Token.UInt:
|
|
1933
|
+
return new UIntType(width || this.machineIntSize);
|
|
1934
|
+
case Token.Float:
|
|
1935
|
+
return new FloatType(width || this.machineFloatWidth);
|
|
1936
|
+
case Token.Bool:
|
|
1937
|
+
return new BoolType();
|
|
1938
|
+
case Token.Bit:
|
|
1939
|
+
return new BitType(width);
|
|
1940
|
+
case Token.CReg:
|
|
1941
|
+
return new BitType(width);
|
|
1942
|
+
case Token.Stretch:
|
|
1943
|
+
return new StretchType();
|
|
1944
|
+
case Token.Duration:
|
|
1945
|
+
return new DurationType();
|
|
1946
|
+
case Token.Angle:
|
|
1947
|
+
return new AngleType(width);
|
|
1948
|
+
default:
|
|
1949
|
+
throwParserError(BadClassicalTypeError, token, this.index, "unsupported classical type");
|
|
1950
|
+
}
|
|
1951
|
+
}
|
|
1952
|
+
/**
|
|
1953
|
+
* Creates a math or trigonometric function node.
|
|
1954
|
+
* @param token - The token representing the function.
|
|
1955
|
+
* @param expr - The expression to which the function is applied.
|
|
1956
|
+
* @return The created MathFunction or TrigFunction node and the number of consumed tokens.
|
|
1957
|
+
*/
|
|
1958
|
+
createMathOrTrigFunction(token, expr) {
|
|
1959
|
+
switch (token) {
|
|
1960
|
+
case Token.Arccos:
|
|
1961
|
+
return [new TrigFunction(TrigFunctionTypes.ARCCOS, expr), 1];
|
|
1962
|
+
case Token.Arcsin:
|
|
1963
|
+
return [new TrigFunction(TrigFunctionTypes.ARCSIN, expr), 1];
|
|
1964
|
+
case Token.Arctan:
|
|
1965
|
+
return [new TrigFunction(TrigFunctionTypes.ARCTAN, expr), 1];
|
|
1966
|
+
case Token.Cos:
|
|
1967
|
+
return [new TrigFunction(TrigFunctionTypes.COS, expr), 1];
|
|
1968
|
+
case Token.Sin:
|
|
1969
|
+
return [new TrigFunction(TrigFunctionTypes.SIN, expr), 1];
|
|
1970
|
+
case Token.Tan:
|
|
1971
|
+
return [new TrigFunction(TrigFunctionTypes.TAN, expr), 1];
|
|
1972
|
+
case Token.Ceiling:
|
|
1973
|
+
return [new MathFunction(MathFunctionTypes.CEILING, expr), 1];
|
|
1974
|
+
case Token.Exp:
|
|
1975
|
+
return [new MathFunction(MathFunctionTypes.EXP, expr), 1];
|
|
1976
|
+
case Token.Floor:
|
|
1977
|
+
return [new MathFunction(MathFunctionTypes.FLOOR, expr), 1];
|
|
1978
|
+
case Token.Log:
|
|
1979
|
+
return [new MathFunction(MathFunctionTypes.LOG, expr), 1];
|
|
1980
|
+
case Token.Mod:
|
|
1981
|
+
return [new MathFunction(MathFunctionTypes.MOD, expr), 1];
|
|
1982
|
+
case Token.Popcount:
|
|
1983
|
+
return [new MathFunction(MathFunctionTypes.POPCOUNT, expr), 1];
|
|
1984
|
+
case Token.Pow:
|
|
1985
|
+
return [new MathFunction(MathFunctionTypes.POW, expr), 1];
|
|
1986
|
+
case Token.Rotl:
|
|
1987
|
+
return [new MathFunction(MathFunctionTypes.ROTL, expr), 1];
|
|
1988
|
+
case Token.Rotr:
|
|
1989
|
+
return [new MathFunction(MathFunctionTypes.ROTR, expr), 1];
|
|
1990
|
+
case Token.Sqrt:
|
|
1991
|
+
return [new MathFunction(MathFunctionTypes.SQRT, expr), 1];
|
|
1992
|
+
default:
|
|
1993
|
+
throwParserError(BadExpressionError, token, this.index, "unsupported math or trig function");
|
|
1994
|
+
}
|
|
1995
|
+
}
|
|
1996
|
+
/** Checks whether a Bit token precedes a measurement statement. */
|
|
1997
|
+
isMeasurement(tokens) {
|
|
1998
|
+
let i = 0;
|
|
1999
|
+
while (i < tokens.length - 1 &&
|
|
2000
|
+
tokens[i][0] !== Token.LCParen &&
|
|
2001
|
+
tokens[i][0] !== Token.Semicolon &&
|
|
2002
|
+
tokens[i][0] !== Token.EqualsAssmt) {
|
|
2003
|
+
i++;
|
|
2004
|
+
}
|
|
2005
|
+
return (tokens[i][0] === Token.EqualsAssmt && tokens[i + 1][0] === Token.Measure);
|
|
2006
|
+
}
|
|
2007
|
+
/** Checks whether the statement is part of an assignment. */
|
|
2008
|
+
isAssignment(tokens) {
|
|
2009
|
+
let i = 0;
|
|
2010
|
+
while (i < tokens.length - 1 &&
|
|
2011
|
+
tokens[i][0] !== Token.LCParen &&
|
|
2012
|
+
tokens[i][0] !== Token.Semicolon &&
|
|
2013
|
+
tokens[i][0] !== Token.EqualsAssmt) {
|
|
2014
|
+
i++;
|
|
2015
|
+
}
|
|
2016
|
+
return tokens[i][0] === Token.EqualsAssmt;
|
|
2017
|
+
}
|
|
2018
|
+
/** Checks whether an identifier is a quantum gate call. */
|
|
2019
|
+
isQuantumGateCall(tokens) {
|
|
2020
|
+
const gateName = tokens[0][1];
|
|
2021
|
+
return (this.gates.has(gateName) ||
|
|
2022
|
+
this.standardGates.has(gateName) ||
|
|
2023
|
+
this.customGates.has(gateName));
|
|
2024
|
+
}
|
|
2025
|
+
/** Checks whether an identifier is a subroutine call. */
|
|
2026
|
+
isSubroutineCall(tokens) {
|
|
2027
|
+
const subroutineName = tokens[0][1];
|
|
2028
|
+
return this.subroutines.has(subroutineName);
|
|
2029
|
+
}
|
|
2030
|
+
/** Checks whether a number is imaginary. */
|
|
2031
|
+
isImaginary(token) {
|
|
2032
|
+
if (token.length !== 2) {
|
|
2033
|
+
return false;
|
|
2034
|
+
}
|
|
2035
|
+
return token[1].toString() === "im";
|
|
2036
|
+
}
|
|
2037
|
+
/** Checks whether an identifier is an array. */
|
|
2038
|
+
isArray(tokens) {
|
|
2039
|
+
if (tokens[0][0] !== Token.Id) {
|
|
2040
|
+
return false;
|
|
2041
|
+
}
|
|
2042
|
+
const identifierName = tokens[0][1].toString();
|
|
2043
|
+
return this.customArrays.has(identifierName);
|
|
2044
|
+
}
|
|
2045
|
+
/** Checks whether an identifier represents a duration literal unit. */
|
|
2046
|
+
isDuration(tokens) {
|
|
2047
|
+
const checkToken = tokens[0];
|
|
2048
|
+
if (checkToken.length !== 2 || checkToken[0] !== Token.Id) {
|
|
2049
|
+
return false;
|
|
2050
|
+
}
|
|
2051
|
+
if (Object.values(DurationUnit).includes(checkToken[1].toString())) {
|
|
2052
|
+
return true;
|
|
2053
|
+
}
|
|
2054
|
+
return false;
|
|
2055
|
+
}
|
|
2056
|
+
/** TODO : update this
|
|
2057
|
+
* Validates whether a register or gate identifier.
|
|
2058
|
+
* @param identifier - The identifier to validate.
|
|
2059
|
+
* @return Boolean indicating successful validation or not.
|
|
2060
|
+
*/
|
|
2061
|
+
validateIdentifier(identifier) {
|
|
2062
|
+
const firstChar = identifier[0];
|
|
2063
|
+
return /^[a-z]$/.test(firstChar);
|
|
2064
|
+
}
|
|
2065
|
+
}
|
|
2066
|
+
/**
|
|
2067
|
+
* Checks if a token represents a type.
|
|
2068
|
+
*/
|
|
2069
|
+
function isTypeToken(token) {
|
|
2070
|
+
return (token === Token.Float ||
|
|
2071
|
+
token === Token.Int ||
|
|
2072
|
+
token === Token.UInt ||
|
|
2073
|
+
token === Token.Bool ||
|
|
2074
|
+
token === Token.Duration ||
|
|
2075
|
+
token === Token.Angle ||
|
|
2076
|
+
token === Token.Stretch ||
|
|
2077
|
+
token === Token.Bit);
|
|
2078
|
+
}
|
|
2079
|
+
export default Parser;
|