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/src/lexer.ts ADDED
@@ -0,0 +1,319 @@
1
+ import { Token, lookup } from './token';
2
+ import { BadEqualsError, MissingSemicolonError } from './errors';
3
+
4
+ /**
5
+ * Returns whether a given character could be an element of a numeric value.
6
+ * @param c - The character.
7
+ * @return Whether the character is numeric.
8
+ */
9
+ function isNumeric(c:string): boolean {
10
+ return (c == '.') || !isNaN(parseInt(c));
11
+ }
12
+
13
+ /**
14
+ * Returns whether a given character is a letter.
15
+ * @param c - The character.
16
+ * @return Whether the character is a letter.
17
+ */
18
+ function isLetter(c:string): boolean {
19
+ if (c.match(/[a-z]/i)) {
20
+ return true;
21
+ }
22
+
23
+ return false;
24
+ }
25
+
26
+ /**
27
+ * Returns whether a given character is alphanumeric.
28
+ * @param c - The character.
29
+ * @return Whether the character is alphanumeric.
30
+ */
31
+ function isAlpha(c:string): boolean {
32
+ if (c.match(/^[0-9a-zA-Z]+$/)) {
33
+ return true;
34
+ }
35
+ return false;
36
+ }
37
+
38
+ /**
39
+ * Returns whether a given character is a newline character.
40
+ * @param c - The character.
41
+ * @return Whether the character is a newline.
42
+ */
43
+ function isNewline(c:string): boolean {
44
+ if (c.match(/\n|\r(?!\n)|\u2028|\u2029|\r\n/g)) {
45
+ return true;
46
+ }
47
+ return false;
48
+ }
49
+
50
+ /** Class representing a lexer. */
51
+ class Lexer {
52
+ /** The string to lex. */
53
+ input:string;
54
+ /** The lexer's current cursor location. */
55
+ cursor:number;
56
+ /**
57
+ * Creates a lexer.
58
+ * @param input - The string to lex.
59
+ * @param cursor - The starting cursor position.
60
+ */
61
+ constructor(input:string, cursor:number = 0) {
62
+ this.input = input;
63
+ this.cursor = cursor;
64
+ }
65
+ verifyInput = (): boolean => {
66
+ const lines = this.input.split(/\n|\r(?!\n)|\u2028|\u2029|\r\n/g);
67
+ for(let i = 0; i < lines.length; i++) {
68
+ if (
69
+ !lines[i].startsWith('//')
70
+ && !(lines[i].length == 0)
71
+ && !(lines[i].includes('gate'))
72
+ && !(lines[i].trim() == '{' || lines[i].trim() == '}')
73
+ && !lines[i].includes(';')
74
+ ){
75
+ return false;
76
+ }
77
+ }
78
+ return true;
79
+ }
80
+
81
+ /**
82
+ * Calling this method lexes the code represented by the provided string.
83
+ * @return An array of tokens and their corresponding values.
84
+ */
85
+ lex = (): Array<[Token, (number | String)?]> => {
86
+ let tokens:Array<[Token, (number | String)?]> = [];
87
+ let token:[Token, (number | String)?];
88
+ if (!this.verifyInput()) {
89
+ throw MissingSemicolonError;
90
+ }
91
+ while (this.cursor < this.input.length) {
92
+ token = this.nextToken();
93
+ if (token) {
94
+ tokens.push(token);
95
+ }
96
+ }
97
+ return tokens;
98
+ }
99
+
100
+ /**
101
+ * Reads a character and advances the cursor.
102
+ * @param num - Optional cursor position modifier.
103
+ */
104
+ readChar = (num:number=1): string => {
105
+ this.cursor += num;
106
+ return this.input[this.cursor - num];
107
+ }
108
+
109
+ /**
110
+ * Advances the cusor past the next comment.
111
+ */
112
+ skipComment = () => {
113
+ let char = '';
114
+ while(!isNewline(char)) {
115
+ char = this.readChar();
116
+ }
117
+ }
118
+
119
+ /**
120
+ * Determines whether the next character to process equals a given character.
121
+ * @param c - The given character.
122
+ * @return Whether the next character equals the given character.
123
+ */
124
+ peekEq = (c:string): boolean => (this.peek() == c);
125
+
126
+ /**
127
+ * Reads a character without advancing the cursor.
128
+ * @param index - Optional peek position offset.
129
+ */
130
+ peek = (): string => this.input[this.cursor]
131
+
132
+ /**
133
+ * Reads a numeric value.
134
+ * @return The numeric value as a string.
135
+ */
136
+ readNumeric = (): string => {
137
+ let num = '';
138
+ while (isNumeric(this.peek())) {
139
+ num += this.readChar();
140
+ }
141
+ return num;
142
+ }
143
+
144
+ /**
145
+ * Reads an identifier.
146
+ * @return The identifier as a string.
147
+ */
148
+ readIdentifier = (): string => {
149
+ let id = '';
150
+ while (isAlpha(this.peek())) {
151
+ id += this.readChar();
152
+ }
153
+ return id;
154
+ }
155
+
156
+ /**
157
+ * Reads a string literal.
158
+ * @param terminator - The literal's termination character.
159
+ * @return The literal as a string.
160
+ */
161
+ readStringLiteral = (terminator:string): string => {
162
+ let lit = '';
163
+ let char = '';
164
+ while (!(terminator == char)) {
165
+ char = this.readChar();
166
+ lit += char;
167
+ }
168
+ return lit;
169
+ }
170
+
171
+ /**
172
+ * Advances the cusor past the next block of whitespace.
173
+ */
174
+ skipWhitespace = (): null => {
175
+ while (' \t\n\r\v'.indexOf(this.peek()) > -1) {
176
+ this.cursor += 1;
177
+ }
178
+ return null;
179
+ }
180
+
181
+ /**
182
+ * Lexes the next token.
183
+ * @return The next token and its corresponding value.
184
+ */
185
+ nextToken = (): [Token, (number | String)?] => {
186
+ this.skipWhitespace();
187
+
188
+ if (this.cursor == this.input.length) {
189
+ return [Token.EndOfFile];
190
+ }
191
+
192
+ let char = this.peek();
193
+ this.readChar();
194
+
195
+ switch(char) {
196
+ case '=':
197
+ if (this.peekEq('=')) {
198
+ this.readChar();
199
+ return [Token.Equals];
200
+ } else {
201
+ throw BadEqualsError;
202
+ }
203
+ case '-':
204
+ if (this.peekEq('>')) {
205
+ this.readChar();
206
+ return [Token.Arrow];
207
+ } else {
208
+ return [Token.Minus];
209
+ }
210
+ case '+':
211
+ return [Token.Plus];
212
+ case '*':
213
+ return [Token.Times];
214
+ case '^':
215
+ return [Token.Power];
216
+ case ';':
217
+ return [Token.Semicolon];
218
+ case ',':
219
+ return [Token.Comma];
220
+ case '(':
221
+ return [Token.LParen];
222
+ case '[':
223
+ return [Token.LSParen];
224
+ case '{':
225
+ return [Token.LCParen];
226
+ case ')':
227
+ return [Token.RParen];
228
+ case ']':
229
+ return [Token.RSParen];
230
+ case '}':
231
+ return [Token.RCParen];
232
+ case '/':
233
+ if (this.peekEq('/')) {
234
+ this.skipComment();
235
+ return;
236
+ } else {
237
+ return [Token.Divide];
238
+ }
239
+ case 'g':
240
+ if ((this.input[this.cursor] == 'a')
241
+ && (this.input[this.cursor + 1] == 't')
242
+ && (this.input[this.cursor + 2] == 'e')
243
+ ){
244
+ this.readChar(3);
245
+ return [Token.Gate];
246
+ }
247
+ let literal = char + this.readIdentifier();
248
+ return [lookup(literal), new String(literal)];
249
+ case 'q':
250
+ if ((this.input[this.cursor] == 'r')
251
+ && (this.input[this.cursor + 1] == 'e')
252
+ && (this.input[this.cursor + 2] == 'g')
253
+ ){
254
+ this.readChar(3);
255
+ return [Token.QReg];
256
+ }
257
+ let qregLit = char + this.readIdentifier();
258
+ return [lookup(qregLit), new String(qregLit)];
259
+ case 'c':
260
+ if ((this.input[this.cursor] == 'r')
261
+ && (this.input[this.cursor + 1] == 'e')
262
+ && (this.input[this.cursor + 2] == 'g')
263
+ ){
264
+ this.readChar(3);
265
+ return [Token.QReg];
266
+ }
267
+ let cregLit = char + this.readIdentifier();
268
+ return [lookup(cregLit), new String(cregLit)];
269
+ case 'b':
270
+ if ((this.input[this.cursor] == 'a')
271
+ && (this.input[this.cursor + 1] == 'r')
272
+ && (this.input[this.cursor + 2] == 'r')
273
+ && (this.input[this.cursor + 3] == 'i')
274
+ && (this.input[this.cursor + 4] == 'e')
275
+ && (this.input[this.cursor + 5] == 'r')
276
+ ){
277
+ this.readChar(6);
278
+ return [Token.Barrier];
279
+ }
280
+ let barLit = char + this.readIdentifier();
281
+ return [lookup(barLit), new String(barLit)];
282
+ case 'm':
283
+ if ((this.input[this.cursor] == 'e')
284
+ && (this.input[this.cursor + 1] == 'a')
285
+ && (this.input[this.cursor + 2] == 's')
286
+ && (this.input[this.cursor + 3] == 'u')
287
+ && (this.input[this.cursor + 4] == 'r')
288
+ && (this.input[this.cursor + 5] == 'e')
289
+ ){
290
+ this.readChar(6);
291
+ return [Token.Measure];
292
+ }
293
+ let measureLit = char + this.readIdentifier();
294
+ return [lookup(measureLit), new String(measureLit)];
295
+ case '\"':
296
+ let stringLiteral = char + this.readStringLiteral('\"');
297
+ return [Token.String, new String(stringLiteral)];
298
+ case '\’':
299
+ let singleStringLiteral = char + this.readStringLiteral('\’');
300
+ return [Token.String, new String(singleStringLiteral)];
301
+ default:
302
+ if (isLetter(char)) {
303
+ let literal = char + this.readIdentifier();
304
+ return [lookup(literal), new String(literal)];
305
+ } else if (isNumeric(char)) {
306
+ let num = char + this.readNumeric();
307
+ if (num.indexOf('.') != -1) {
308
+ return [Token.Real, parseFloat(num)];
309
+ } else {
310
+ return [Token.NNInteger, parseFloat(num)];
311
+ }
312
+ } else {
313
+ return [Token.Illegal];
314
+ }
315
+ }
316
+ }
317
+ }
318
+ export default Lexer;
319
+
package/src/main.ts ADDED
@@ -0,0 +1,27 @@
1
+ import Parser from './parser';
2
+ import Lexer from './lexer';
3
+ import * as fs from 'fs';
4
+
5
+ /**
6
+ * Returns the abstract syntax tree for a given string of QASM code.
7
+ * @param qasm - The code string.
8
+ * @return The corresponding AST.
9
+ */
10
+ function parseString(qasm:string) {
11
+ const lexer = new Lexer(qasm, 0);
12
+ const tokens = lexer.lex();
13
+ const parser = new Parser(tokens);
14
+ const ast = parser.parse();
15
+ return ast;
16
+ }
17
+
18
+ /**
19
+ * Returns the abstract syntax tree for a given QASM file.
20
+ * @param file - The file location.
21
+ * @return The corresponding AST.
22
+ */
23
+ exports.parse = function(file:string) {
24
+ return parseString(fs.readFileSync(file, 'utf8'));
25
+ }
26
+
27
+ exports.parseString = parseString;