qasm-ts 1.1.1
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/ast.js +267 -0
- package/dist/errors.js +148 -0
- package/dist/example.js +22 -0
- package/dist/lexer.js +300 -0
- package/dist/main.js +26 -0
- package/dist/parser.js +489 -0
- package/dist/token.js +89 -0
- package/package.json +36 -0
- package/readme.md +122 -0
- package/src/ast.ts +192 -0
- package/src/errors.ts +102 -0
- package/src/lexer.ts +319 -0
- package/src/main.ts +27 -0
- package/src/parser.ts +531 -0
- package/src/token.ts +92 -0
- package/tsconfig.json +14 -0
package/src/parser.ts
ADDED
|
@@ -0,0 +1,531 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Token,
|
|
3
|
+
notParam
|
|
4
|
+
} from './token';
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
BadArgumentError,
|
|
8
|
+
BadCregError,
|
|
9
|
+
BadQregError,
|
|
10
|
+
BadConditionalError,
|
|
11
|
+
BadBarrierError,
|
|
12
|
+
BadMeasurementError,
|
|
13
|
+
BadGateError,
|
|
14
|
+
BadParameterError
|
|
15
|
+
} from './errors';
|
|
16
|
+
|
|
17
|
+
import {
|
|
18
|
+
AstNode,
|
|
19
|
+
QReg,
|
|
20
|
+
CReg,
|
|
21
|
+
Barrier,
|
|
22
|
+
Measure,
|
|
23
|
+
Gate,
|
|
24
|
+
If,
|
|
25
|
+
ApplyGate,
|
|
26
|
+
Divide,
|
|
27
|
+
Power,
|
|
28
|
+
Times,
|
|
29
|
+
Plus,
|
|
30
|
+
Minus,
|
|
31
|
+
Sin,
|
|
32
|
+
Cos,
|
|
33
|
+
Tan,
|
|
34
|
+
Exp,
|
|
35
|
+
Ln,
|
|
36
|
+
Sqrt,
|
|
37
|
+
Pi,
|
|
38
|
+
NNInteger,
|
|
39
|
+
Real,
|
|
40
|
+
Variable
|
|
41
|
+
} from './ast';
|
|
42
|
+
|
|
43
|
+
/** Class representing a token parser. */
|
|
44
|
+
class Parser {
|
|
45
|
+
|
|
46
|
+
/** The tokens to parse. */
|
|
47
|
+
tokens:Array<[Token, (number | String)?]>;
|
|
48
|
+
|
|
49
|
+
/** The allowed gates. */
|
|
50
|
+
gates:Array<string>;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Creates a parser.
|
|
54
|
+
* @param tokens - Tokens to parse.
|
|
55
|
+
*/
|
|
56
|
+
constructor(tokens:Array<[Token, (number | String)?]>) {
|
|
57
|
+
this.tokens = tokens;
|
|
58
|
+
this.gates = [
|
|
59
|
+
'x',
|
|
60
|
+
'y',
|
|
61
|
+
'z',
|
|
62
|
+
'u1',
|
|
63
|
+
'u2',
|
|
64
|
+
'u3',
|
|
65
|
+
's',
|
|
66
|
+
'sdg',
|
|
67
|
+
'h',
|
|
68
|
+
'tdg',
|
|
69
|
+
'cx',
|
|
70
|
+
'cy',
|
|
71
|
+
'cz',
|
|
72
|
+
't',
|
|
73
|
+
'ccx',
|
|
74
|
+
'reset',
|
|
75
|
+
'cu1',
|
|
76
|
+
'ccy',
|
|
77
|
+
'ccz'
|
|
78
|
+
]
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Calling this method parses the code represented by the provided tokens.
|
|
83
|
+
* @return The abstract syntax tree.
|
|
84
|
+
*/
|
|
85
|
+
parse(): Array<AstNode> {
|
|
86
|
+
let ast:Array<AstNode> = [];
|
|
87
|
+
let i = 0;
|
|
88
|
+
while (i < (this.tokens.length - 1)) {
|
|
89
|
+
let nodes = this.parseNode(this.tokens.slice(i));
|
|
90
|
+
ast = ast.concat(
|
|
91
|
+
nodes ? nodes : []
|
|
92
|
+
);
|
|
93
|
+
while (!(this.matchNext(this.tokens.slice(i), [Token.Semicolon]))) {
|
|
94
|
+
if (this.matchNext(this.tokens.slice(i), [Token.LCParen])) {
|
|
95
|
+
while (!(this.matchNext(this.tokens.slice(i), [Token.RCParen]))) {
|
|
96
|
+
i++;
|
|
97
|
+
}
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
i++;
|
|
101
|
+
}
|
|
102
|
+
i++;
|
|
103
|
+
}
|
|
104
|
+
return ast;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Delegates the parsing of the next set of tokens to the appropriate method.
|
|
109
|
+
* @param tokens - Remaining tokens to parse.
|
|
110
|
+
* @param allowVariables - Whether encountered identifiers should be consider
|
|
111
|
+
variable initializations or references.
|
|
112
|
+
* @return A set of AST nodes.
|
|
113
|
+
*/
|
|
114
|
+
parseNode(tokens:Array<[Token, (number | String)?]>, allowVariables=false): Array<AstNode> {
|
|
115
|
+
const token = tokens[0];
|
|
116
|
+
switch(token[0]) {
|
|
117
|
+
case Token.QReg:
|
|
118
|
+
return [this.qreg(tokens.slice(1))];
|
|
119
|
+
case Token.CReg:
|
|
120
|
+
return [this.creg(tokens.slice(1))];
|
|
121
|
+
case Token.Barrier:
|
|
122
|
+
return [this.barrier(tokens.slice(1))];
|
|
123
|
+
case Token.Measure:
|
|
124
|
+
return [this.measure(tokens.slice(1))];
|
|
125
|
+
case Token.Id:
|
|
126
|
+
if (!(token[1].toString().indexOf('QASM') != -1) &&
|
|
127
|
+
!(token[1].toString().indexOf('include') != -1)) {
|
|
128
|
+
if (this.gates.includes(token[1].toString())) {
|
|
129
|
+
return [this.application(tokens, token[1].toString())];
|
|
130
|
+
} else if (allowVariables) {
|
|
131
|
+
return [new Variable(token[1].toString())];
|
|
132
|
+
} else {
|
|
133
|
+
throw BadGateError;
|
|
134
|
+
}
|
|
135
|
+
} else {
|
|
136
|
+
return [];
|
|
137
|
+
}
|
|
138
|
+
case Token.Gate:
|
|
139
|
+
return [this.gate(tokens.slice(1))];
|
|
140
|
+
case Token.If:
|
|
141
|
+
return [this.conditional(tokens.slice(1))];
|
|
142
|
+
case Token.Power:
|
|
143
|
+
return [new Power()];
|
|
144
|
+
case Token.Divide:
|
|
145
|
+
return [new Divide()];
|
|
146
|
+
case Token.Times:
|
|
147
|
+
return [new Times()];
|
|
148
|
+
case Token.Plus:
|
|
149
|
+
return [new Plus()];
|
|
150
|
+
case Token.Minus:
|
|
151
|
+
return [new Minus()];
|
|
152
|
+
case Token.Pi:
|
|
153
|
+
return [new Pi()];
|
|
154
|
+
case Token.Sin:
|
|
155
|
+
return [new Sin()];
|
|
156
|
+
case Token.Cos:
|
|
157
|
+
return [new Cos()];
|
|
158
|
+
case Token.Exp:
|
|
159
|
+
return [new Exp()];
|
|
160
|
+
case Token.Ln:
|
|
161
|
+
return [new Ln()];
|
|
162
|
+
case Token.Sqrt:
|
|
163
|
+
return [new Sqrt()];
|
|
164
|
+
case Token.Tan:
|
|
165
|
+
return [new Tan()];
|
|
166
|
+
case Token.NNInteger:
|
|
167
|
+
return [new NNInteger(Number(token[1]))];
|
|
168
|
+
case Token.Real:
|
|
169
|
+
return [new Real(Number(token[1]))];
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Checks if the next tokens match those expected.
|
|
175
|
+
* @param tokens - Remaining tokens to parse.
|
|
176
|
+
* @param expectedTokens - Expected tokens.
|
|
177
|
+
* @return Whether these is a match.
|
|
178
|
+
*/
|
|
179
|
+
matchNext(tokens:Array<[Token, (number | String)?]>, expectedTokens:Array<Token>): boolean {
|
|
180
|
+
let matches = true;
|
|
181
|
+
let i = 0;
|
|
182
|
+
if (tokens.length == 0) {
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
while (i < expectedTokens.length) {
|
|
186
|
+
if (tokens[i][0] != expectedTokens[i]) {
|
|
187
|
+
matches = false;
|
|
188
|
+
break;
|
|
189
|
+
}
|
|
190
|
+
i++;
|
|
191
|
+
}
|
|
192
|
+
return matches;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Parses a quantum register.
|
|
197
|
+
* @param tokens - Remaining tokens to parse.
|
|
198
|
+
* @return An AST node representing the quantum register.
|
|
199
|
+
*/
|
|
200
|
+
qreg(tokens:Array<[Token, (number | String)?]>): AstNode {
|
|
201
|
+
if (this.matchNext(tokens, [Token.Id, Token.LSParen, Token.NNInteger, Token.RSParen, Token.Semicolon])) {
|
|
202
|
+
const id = tokens[0][1];
|
|
203
|
+
const size = tokens[2][1];
|
|
204
|
+
return new QReg(id.toString(), Number(size));
|
|
205
|
+
} else {
|
|
206
|
+
throw BadQregError;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Parses a classical register.
|
|
212
|
+
* @param tokens - Remaining tokens to parse.
|
|
213
|
+
* @return An AST node representing the classical register.
|
|
214
|
+
*/
|
|
215
|
+
creg(tokens:Array<[Token, (number | String)?]>): AstNode {
|
|
216
|
+
if (this.matchNext(tokens, [Token.Id, Token.LSParen, Token.NNInteger,
|
|
217
|
+
Token.RSParen, Token.Semicolon])) {
|
|
218
|
+
const id = tokens[0][1];
|
|
219
|
+
const size = tokens[2][1];
|
|
220
|
+
return new CReg(id.toString(), Number(size));
|
|
221
|
+
} else {
|
|
222
|
+
throw BadCregError;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Parses a conditional.
|
|
228
|
+
* @param tokens - Remaining tokens to parse.
|
|
229
|
+
* @return An AST node representing the conditional.
|
|
230
|
+
*/
|
|
231
|
+
conditional(tokens:Array<[Token, (number | String)?]>): AstNode {
|
|
232
|
+
if (this.matchNext(tokens, [Token.LParen, Token.Id, Token.Equals, Token.NNInteger, Token.RParen])) {
|
|
233
|
+
let id = tokens[1][1];
|
|
234
|
+
let val = tokens[3][1];
|
|
235
|
+
let node = this.parseNode(tokens.slice(5));
|
|
236
|
+
return new If(id.toString(), Number(val), node);
|
|
237
|
+
} else {
|
|
238
|
+
throw BadConditionalError;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Parses a barrier.
|
|
244
|
+
* @param tokens - Remaining tokens to parse.
|
|
245
|
+
* @return An AST node representing the barrier.
|
|
246
|
+
*/
|
|
247
|
+
barrier(tokens:Array<[Token, (number | String)?]>): AstNode {
|
|
248
|
+
if (this.matchNext(tokens, [Token.Id, Token.Semicolon])) {
|
|
249
|
+
let id = tokens[0][1];
|
|
250
|
+
return new Barrier(id.toString());
|
|
251
|
+
} else if (this.matchNext(tokens, [Token.Id, Token.LParen,
|
|
252
|
+
Token.NNInteger, Token.RParen, Token.Semicolon])) {
|
|
253
|
+
let id = tokens[0][1];
|
|
254
|
+
let index = tokens[2][1];
|
|
255
|
+
return new Barrier(id.toString(), Number(index));
|
|
256
|
+
} else {
|
|
257
|
+
throw BadBarrierError;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Parses a measurement.
|
|
263
|
+
* @param tokens - Remaining tokens to parse.
|
|
264
|
+
* @return An AST node representing the measurement.
|
|
265
|
+
*/
|
|
266
|
+
measure(tokens:Array<[Token, (number | String)?]>): AstNode {
|
|
267
|
+
let first_id:string;
|
|
268
|
+
let second_id:string;
|
|
269
|
+
let first_index:number;
|
|
270
|
+
let second_index:number;
|
|
271
|
+
if (this.matchNext(tokens, [Token.Id, Token.Arrow])) {
|
|
272
|
+
first_id = tokens[0][1].toString();
|
|
273
|
+
tokens = tokens.slice(2);
|
|
274
|
+
} else if (this.matchNext(tokens, [Token.Id, Token.LSParen, Token.NNInteger, Token.RSParen, Token.Arrow])) {
|
|
275
|
+
first_id = tokens[0][1].toString();
|
|
276
|
+
first_index = Number(tokens[2][1]);
|
|
277
|
+
tokens = tokens.slice(5);
|
|
278
|
+
} else {
|
|
279
|
+
throw BadMeasurementError;
|
|
280
|
+
}
|
|
281
|
+
if (this.matchNext(tokens, [Token.Id, Token.Semicolon])) {
|
|
282
|
+
second_id = tokens[0][1].toString();
|
|
283
|
+
tokens = tokens.slice(2);
|
|
284
|
+
} else if (this.matchNext(tokens, [Token.Id, Token.LSParen, Token.NNInteger, Token.RSParen, Token.Semicolon])) {
|
|
285
|
+
second_id = tokens[0][1].toString();
|
|
286
|
+
second_index = Number(tokens[2][1]);
|
|
287
|
+
tokens = tokens.slice(5);
|
|
288
|
+
} else {
|
|
289
|
+
throw BadMeasurementError;
|
|
290
|
+
}
|
|
291
|
+
if (first_index != undefined && second_index != undefined) {
|
|
292
|
+
return new Measure(first_id, second_id, first_index, second_index);
|
|
293
|
+
} else if (first_index != undefined) {
|
|
294
|
+
return new Measure(first_id, second_id, first_index=first_index);
|
|
295
|
+
} else if (second_index != undefined) {
|
|
296
|
+
return new Measure(first_id, second_id, second_index=second_index);
|
|
297
|
+
}
|
|
298
|
+
return new Measure(first_id, second_id);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Parses an application of one of the allowed gates.
|
|
303
|
+
* @param tokens - Remaining tokens to parse.
|
|
304
|
+
* @return An AST node representing the gate application.
|
|
305
|
+
*/
|
|
306
|
+
application(tokens:Array<[Token, (number | String)?]>, op:string): Array<AstNode> {
|
|
307
|
+
let params:Array<AstNode> = [];
|
|
308
|
+
let list:Array<[string, number?]> = [];
|
|
309
|
+
let applications:Array<AstNode> = [];
|
|
310
|
+
let id:string;
|
|
311
|
+
if (tokens[0][1] == op) {
|
|
312
|
+
tokens = tokens.slice(1);
|
|
313
|
+
}
|
|
314
|
+
if (this.matchNext(tokens, [Token.LParen])) {
|
|
315
|
+
if (this.matchNext(tokens.slice(1), [Token.RParen])) {
|
|
316
|
+
params = [];
|
|
317
|
+
tokens = tokens.slice(2);
|
|
318
|
+
} else {
|
|
319
|
+
params = this.matchParamList(tokens.slice(1));
|
|
320
|
+
let count = 0;
|
|
321
|
+
let commas = 0;
|
|
322
|
+
for (let i in params) {
|
|
323
|
+
commas += 1;
|
|
324
|
+
for (let j in params[i]) {
|
|
325
|
+
count++;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
tokens = tokens.slice(count + (commas - 1) + 2);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
let args = this.matchArgList(tokens);
|
|
333
|
+
for (let arg in args) {
|
|
334
|
+
id = args[arg][0];
|
|
335
|
+
list.push(args[arg]);
|
|
336
|
+
}
|
|
337
|
+
applications.push(new ApplyGate(op, list, params));
|
|
338
|
+
return applications;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Parses a subroutine used in a custom gate definition.
|
|
343
|
+
* @param tokens - Expression tokens to parse.
|
|
344
|
+
* @return A parsed subroutine.
|
|
345
|
+
*/
|
|
346
|
+
sub(tokens:Array<[Token, (number | String)?]>): Array<AstNode> {
|
|
347
|
+
let ast:Array<AstNode> = [];
|
|
348
|
+
let i = 0;
|
|
349
|
+
if (this.matchNext(tokens.slice(i), [Token.LCParen])) {
|
|
350
|
+
tokens = tokens.slice(1);
|
|
351
|
+
}
|
|
352
|
+
while ((i < (tokens.length - 1)) && (tokens[i][0] != Token.RCParen)) {
|
|
353
|
+
let nodes = this.parseNode(tokens.slice(i));
|
|
354
|
+
ast = ast.concat(
|
|
355
|
+
nodes ? nodes : []
|
|
356
|
+
);
|
|
357
|
+
while ((!this.matchNext(tokens.slice(i), [Token.Semicolon])) &&
|
|
358
|
+
(!this.matchNext(tokens.slice(i), [Token.RCParen]))) {
|
|
359
|
+
i++;
|
|
360
|
+
}
|
|
361
|
+
if (this.matchNext(tokens.slice(i), [Token.RCParen])) {
|
|
362
|
+
break;
|
|
363
|
+
}
|
|
364
|
+
i++;
|
|
365
|
+
}
|
|
366
|
+
return ast;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* Parses a parameter value.
|
|
371
|
+
* @param tokens - Tokens to parse.
|
|
372
|
+
* @return An AST node representing the parameter value.
|
|
373
|
+
*/
|
|
374
|
+
matchParam(tokens:Array<[Token, (number | String)?]>): AstNode {
|
|
375
|
+
let param:AstNode;
|
|
376
|
+
if (!(notParam(tokens[0][0]))) {
|
|
377
|
+
param = this.parseNode([tokens[0]], true);
|
|
378
|
+
} else {
|
|
379
|
+
throw BadParameterError;
|
|
380
|
+
}
|
|
381
|
+
return param;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* Parses a list of parameter values.
|
|
386
|
+
* @param tokens - Tokens to parse.
|
|
387
|
+
* @return An array of AST nodes representing the parameter values.
|
|
388
|
+
*/
|
|
389
|
+
matchParamList(tokens:Array<[Token, (number | String)?]>): Array<Array<AstNode>> {
|
|
390
|
+
let args:Array<Array<AstNode>> = [];
|
|
391
|
+
let i:number = 0;
|
|
392
|
+
let j:number = 0;
|
|
393
|
+
args[0] = [];
|
|
394
|
+
while(!this.matchNext(tokens.slice(j), [Token.RParen])) {
|
|
395
|
+
while(!this.matchNext(tokens.slice(j), [Token.Comma]) &&
|
|
396
|
+
!this.matchNext(tokens.slice(j), [Token.RParen])) {
|
|
397
|
+
if (notParam(tokens[j][0])) {
|
|
398
|
+
throw BadParameterError;
|
|
399
|
+
}
|
|
400
|
+
let next = this.matchParam(tokens.slice(j));
|
|
401
|
+
args[i].push(next);
|
|
402
|
+
j++;
|
|
403
|
+
}
|
|
404
|
+
if (this.matchNext(tokens.slice(j), [Token.RParen])) {
|
|
405
|
+
break;
|
|
406
|
+
}
|
|
407
|
+
i++;
|
|
408
|
+
j++;
|
|
409
|
+
args[i] = [];
|
|
410
|
+
}
|
|
411
|
+
return args;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* Parses an argument value.
|
|
416
|
+
* @param tokens - Tokens to parse.
|
|
417
|
+
* @return An AST node representing the argument value.
|
|
418
|
+
*/
|
|
419
|
+
matchArg(tokens:Array<[Token, (number | String)?]>): number {
|
|
420
|
+
let index:number;
|
|
421
|
+
if (this.matchNext(tokens, [Token.LSParen])) {
|
|
422
|
+
tokens = tokens.slice(1);
|
|
423
|
+
if (this.matchNext(tokens, [Token.NNInteger])) {
|
|
424
|
+
index = Number(tokens[0][1]);
|
|
425
|
+
tokens = tokens.slice(1);
|
|
426
|
+
} else {
|
|
427
|
+
throw BadArgumentError;
|
|
428
|
+
}
|
|
429
|
+
if (this.matchNext(tokens, [Token.RSParen])) {
|
|
430
|
+
return index;
|
|
431
|
+
} else {
|
|
432
|
+
throw BadArgumentError;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
* Parses a list of argument values.
|
|
439
|
+
* @param tokens - Tokens to parse.
|
|
440
|
+
* @return An array of AST nodes representing the argument values.
|
|
441
|
+
*/
|
|
442
|
+
matchArgList(tokens:Array<[Token, (number | String)?]>): Array<[string,number?]> {
|
|
443
|
+
let args:Array<[string, number?]> = [];
|
|
444
|
+
let next:[string, number?];
|
|
445
|
+
let id:string;
|
|
446
|
+
let j:number = 0;
|
|
447
|
+
while(j < tokens.length && !this.matchNext(tokens.slice(j),[Token.Semicolon])) {
|
|
448
|
+
if (this.matchNext(tokens.slice(j), [Token.Id])) {
|
|
449
|
+
id = tokens[j][1].toString();
|
|
450
|
+
let index = this.matchArg(tokens.slice(j + 1));
|
|
451
|
+
next = [id, index];
|
|
452
|
+
args.push(next);
|
|
453
|
+
if (index != undefined) {
|
|
454
|
+
j += 4;
|
|
455
|
+
} else {
|
|
456
|
+
j++;
|
|
457
|
+
}
|
|
458
|
+
if (this.matchNext(tokens.slice(j), [Token.Comma])) {
|
|
459
|
+
j++;
|
|
460
|
+
}
|
|
461
|
+
} else {
|
|
462
|
+
throw BadArgumentError;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
return args;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
/**
|
|
469
|
+
* Parses a list of identifiers.
|
|
470
|
+
* @param tokens - Tokens to parse.
|
|
471
|
+
* @return An array of AST nodes representing the identifiers.
|
|
472
|
+
*/
|
|
473
|
+
matchIdList(tokens:Array<[Token, (number | String)?]>): Array<string> {
|
|
474
|
+
let args:Array<string> = [];
|
|
475
|
+
let head:string;
|
|
476
|
+
if (this.matchNext(tokens, [Token.Id])) {
|
|
477
|
+
head = tokens[0][1].toString();
|
|
478
|
+
}
|
|
479
|
+
tokens = tokens.slice(1);
|
|
480
|
+
args.push(head);
|
|
481
|
+
if (this.matchNext(tokens, [Token.Comma])) {
|
|
482
|
+
tokens = tokens.slice(1);
|
|
483
|
+
let sub = this.matchIdList(tokens);
|
|
484
|
+
args = args.concat(sub);
|
|
485
|
+
}
|
|
486
|
+
return args;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
/**
|
|
490
|
+
* Parses a gate.
|
|
491
|
+
* @param tokens - Remaining tokens to parse.
|
|
492
|
+
* @return An AST node representing the gate.
|
|
493
|
+
*/
|
|
494
|
+
gate(tokens:Array<[Token, (number | String)?]>): AstNode {
|
|
495
|
+
let name:string;
|
|
496
|
+
let params:Array<string>;
|
|
497
|
+
let registers:Array<string>;
|
|
498
|
+
let applications:Array<AstNode>;
|
|
499
|
+
if (this.matchNext(tokens, [Token.Id])) {
|
|
500
|
+
name = tokens[0][1].toString();
|
|
501
|
+
} else {
|
|
502
|
+
throw BadGateError;
|
|
503
|
+
}
|
|
504
|
+
tokens = tokens.slice(1);
|
|
505
|
+
if (this.matchNext(tokens, [Token.LParen])) {
|
|
506
|
+
tokens = tokens.slice(1);
|
|
507
|
+
if (this.matchNext(tokens, [Token.RParen])) {
|
|
508
|
+
params = [];
|
|
509
|
+
tokens = tokens.slice(1);
|
|
510
|
+
} else {
|
|
511
|
+
params = this.matchIdList(tokens);
|
|
512
|
+
let count = 0;
|
|
513
|
+
for (let i in params) {
|
|
514
|
+
count++;
|
|
515
|
+
}
|
|
516
|
+
tokens = tokens.slice(count + 1);
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
registers = this.matchIdList(tokens);
|
|
520
|
+
let count = 0;
|
|
521
|
+
for (let i in registers) {
|
|
522
|
+
count++;
|
|
523
|
+
}
|
|
524
|
+
tokens = tokens.slice(count + (count - 1));
|
|
525
|
+
applications = this.sub(tokens);
|
|
526
|
+
this.gates.push(name);
|
|
527
|
+
return new Gate(name, registers, params, applications);
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
export default Parser;
|
package/src/token.ts
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
enum Token {
|
|
2
|
+
Illegal,
|
|
3
|
+
EndOfFile,
|
|
4
|
+
Real,
|
|
5
|
+
NNInteger,
|
|
6
|
+
Id,
|
|
7
|
+
OpenQASM,
|
|
8
|
+
Semicolon,
|
|
9
|
+
Comma,
|
|
10
|
+
LParen,
|
|
11
|
+
LSParen,
|
|
12
|
+
LCParen,
|
|
13
|
+
RParen,
|
|
14
|
+
RSParen,
|
|
15
|
+
RCParen,
|
|
16
|
+
Arrow,
|
|
17
|
+
Equals,
|
|
18
|
+
Plus,
|
|
19
|
+
Minus,
|
|
20
|
+
Times,
|
|
21
|
+
Divide,
|
|
22
|
+
Power,
|
|
23
|
+
Sin,
|
|
24
|
+
Cos,
|
|
25
|
+
Tan,
|
|
26
|
+
Exp,
|
|
27
|
+
Ln,
|
|
28
|
+
Sqrt,
|
|
29
|
+
Pi,
|
|
30
|
+
QReg,
|
|
31
|
+
CReg,
|
|
32
|
+
Barrier,
|
|
33
|
+
Gate,
|
|
34
|
+
Measure,
|
|
35
|
+
Reset,
|
|
36
|
+
Include,
|
|
37
|
+
If,
|
|
38
|
+
String
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const lookupMap:object = {
|
|
42
|
+
'if': Token.If,
|
|
43
|
+
'sin': Token.Sin,
|
|
44
|
+
'cos': Token.Cos,
|
|
45
|
+
'tan': Token.Tan,
|
|
46
|
+
'exp': Token.Exp,
|
|
47
|
+
'ln': Token.Ln,
|
|
48
|
+
'sqrt': Token.Sqrt,
|
|
49
|
+
'pi': Token.Pi,
|
|
50
|
+
'+': Token.Plus,
|
|
51
|
+
'-': Token.Minus,
|
|
52
|
+
'/': Token.Divide,
|
|
53
|
+
'*': Token.Times,
|
|
54
|
+
'^': Token.Power
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Returns the token that represents a given string.
|
|
59
|
+
* @param ident - The string.
|
|
60
|
+
* @return The corresponding token.
|
|
61
|
+
*/
|
|
62
|
+
function lookup(ident: string): Token {
|
|
63
|
+
return ident in lookupMap ? lookupMap[ident]: Token.Id;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Returns the string representation of a token.
|
|
68
|
+
* @param tokens - The token.
|
|
69
|
+
* @return The string representation of the token.
|
|
70
|
+
*/
|
|
71
|
+
function inverseLookup(token:Token): string {
|
|
72
|
+
return Object.keys(lookupMap).find((ident) => lookupMap[ident] == token);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Determines whether a token denotes a parameter.
|
|
77
|
+
* @param tokens - The token.
|
|
78
|
+
* @return Whether the token does NOT denote a parameter.
|
|
79
|
+
*/
|
|
80
|
+
function notParam(token:Token): boolean {
|
|
81
|
+
if (token == Token.NNInteger || token == Token.Real || token == Token.Id || this.inverseLookup(token)) {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export {
|
|
88
|
+
Token,
|
|
89
|
+
notParam,
|
|
90
|
+
lookup,
|
|
91
|
+
inverseLookup
|
|
92
|
+
};
|