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.
@@ -0,0 +1,667 @@
1
+ /* eslint-disable no-useless-escape */
2
+ /**
3
+ * OpenQASM 3.0 Lexical Analyzer
4
+ *
5
+ * This module implements the lexer for OpenQASM 3.0, which transforms source code
6
+ * into a stream of tokens. The lexer handles the significantly expanded syntax of
7
+ * OpenQASM 3.0, including classical programming constructs, control flow, and
8
+ * advanced quantum features.
9
+ *
10
+ * Key features of the OpenQASM 3.0 lexer:
11
+ * - **Extended token set**: Classical types, control flow, functions
12
+ * - **Complex operators**: Compound assignment, bitwise operations
13
+ * - **Advanced literals**: Scientific notation, binary/hex/octal, durations
14
+ * - **Gate modifiers**: ctrl, negctrl, inv, pow with @ syntax
15
+ * - **Unicode support**: Mathematical constants (π, ℇ, τ)
16
+ * - **Robust error handling**: Detailed syntax error reporting
17
+ *
18
+ * The lexer performs several validation passes:
19
+ * - Semicolon verification for statement termination
20
+ * - Comment handling (single-line // and multi-line /* *\/)
21
+ * - String literal parsing with multiple quote styles
22
+ * - Number format validation and conversion
23
+ *
24
+ * @module
25
+ *
26
+ * @example Basic lexing process
27
+ * ```typescript
28
+ * const lexer = new Lexer('qubit[2] q; h q[0];');
29
+ * const tokens = lexer.lex();
30
+ * // Returns: [
31
+ * // [Token.Id, 'qubit'], [Token.LSParen], [Token.NNInteger, 2],
32
+ * // [Token.RSParen], [Token.Id, 'q'], [Token.Semicolon], ...
33
+ * // ]
34
+ * ```
35
+ */
36
+ import { Token, lookup } from "./token";
37
+ import { MissingSemicolonError, UnsupportedOpenQASMVersionError, } from "../errors";
38
+ /**
39
+ * Handles throwing lexer errors with basic stack trace.
40
+ * @param error - The error to throw.
41
+ * @param number - The line number in the source code.
42
+ * @param code - The source code that the error is about.
43
+ */
44
+ function throwLexerError(error, line, code) {
45
+ throw new error(`Line ${line}: ${code}`);
46
+ }
47
+ /**
48
+ * Returns whether a given character could be an element of a numeric value.
49
+ * @param c - The character.
50
+ * @return Whether the character is numeric.
51
+ */
52
+ function isNumeric(c) {
53
+ return c == "." || c == "e" || c == "_" || !isNaN(parseInt(c));
54
+ }
55
+ /**
56
+ * Returns whether a given character is a letter.
57
+ * @param c - The character.
58
+ * @param matchCase - Whether to check for a letter that is upper case, lower case, or either. (optional)
59
+ * @return Whether the character is a letter.
60
+ */
61
+ function isLetter(c, matchCase) {
62
+ switch (matchCase) {
63
+ case "upper":
64
+ return /^[A-Z]$/.test(c);
65
+ case "lower":
66
+ return /^[a-z]$/.test(c);
67
+ default:
68
+ return /^[A-Za-z]$/.test(c);
69
+ }
70
+ }
71
+ /**
72
+ * Returns whether a given character is unicode.
73
+ * @param c - The character.
74
+ * @param excludePi - Whether to exclude the Pi symbol from consideration.
75
+ * @return - Whether the given character is valid unicode.
76
+ */
77
+ function isUnicode(c, excludePi) {
78
+ const isBasicUnicode = /^\u0000-\u00ff/.test(c);
79
+ switch (excludePi) {
80
+ case true:
81
+ return isBasicUnicode && c !== "\u03C0";
82
+ case false:
83
+ return isBasicUnicode;
84
+ default:
85
+ return isBasicUnicode;
86
+ }
87
+ }
88
+ /**
89
+ * Returns whether a given character is alphanumeric.
90
+ * @param c - The character.
91
+ * @return Whether the character is alphanumeric.
92
+ */
93
+ function isAlpha(c) {
94
+ return /^[0-9a-zA-Z]+$/.test(c);
95
+ }
96
+ /**
97
+ * Returns whether a given character is a newline character.
98
+ * @param c - The character.
99
+ * @return Whether the character is a newline.
100
+ */
101
+ function isNewline(c) {
102
+ return /\n|\r(?!\n)|\u2028|\u2029|\r\n/.test(c);
103
+ }
104
+ /**
105
+ * OpenQASM 3.0 Lexical Analyzer
106
+ *
107
+ * The main lexer class that processes OpenQASM 3.0 source code character by
108
+ * character and produces a stream of tokens for the parser to consume.
109
+ *
110
+ * The lexer maintains state including:
111
+ * - Current cursor position in the input
112
+ * - Input validation status
113
+ * - Error reporting context
114
+ *
115
+ * @example Creating and using a lexer
116
+ * ```typescript
117
+ * const source = `
118
+ * OPENQASM 3.0;
119
+ * include "stdgates.inc";
120
+ * qubit[2] q;
121
+ * h q[0];
122
+ * cx q[0], q[1];
123
+ * `;
124
+ *
125
+ * const lexer = new Lexer(source);
126
+ * const tokens = lexer.lex();
127
+ * ```
128
+ */
129
+ class Lexer {
130
+ /**
131
+ * Creates a lexer.
132
+ * @param input - The string to lex.
133
+ * @param cursor - The starting cursor position.
134
+ */
135
+ constructor(input, cursor = 0) {
136
+ /**
137
+ * Verifies that all appropriate lines end with a semicolon.
138
+ * @return A tuple of the status and if False, returns the problematic line.
139
+ */
140
+ this.verifyInput = () => {
141
+ const lines = this.input.split(/\n|\r(?!\n)|\u2028|\u2029|\r\n/g);
142
+ for (let i = 0; i < lines.length; i++) {
143
+ const trimmedLine = lines[i].trim();
144
+ if (!trimmedLine.startsWith("//") &&
145
+ !trimmedLine.startsWith("/*") &&
146
+ !trimmedLine.startsWith("*") &&
147
+ !trimmedLine.startsWith("*/") &&
148
+ trimmedLine.length > 0 &&
149
+ !trimmedLine.startsWith("gate") &&
150
+ trimmedLine !== "{" &&
151
+ trimmedLine !== "}" &&
152
+ !trimmedLine.includes(";") &&
153
+ !trimmedLine.startsWith("def") &&
154
+ !trimmedLine.startsWith("if") &&
155
+ !trimmedLine.startsWith("else") &&
156
+ !trimmedLine.startsWith("for") &&
157
+ !trimmedLine.startsWith("while") &&
158
+ !trimmedLine.startsWith("switch") &&
159
+ !trimmedLine.startsWith("case") &&
160
+ !trimmedLine.startsWith("box") &&
161
+ !trimmedLine.startsWith("array") &&
162
+ !trimmedLine.startsWith("{") &&
163
+ !trimmedLine.startsWith("default")) {
164
+ return [false, i + 1, lines[i]];
165
+ }
166
+ }
167
+ return [true, null, null];
168
+ };
169
+ /**
170
+ * Calling this method lexes the code represented by the provided string.
171
+ * @return An array of tokens and their corresponding values.
172
+ */
173
+ this.lex = () => {
174
+ const tokens = [];
175
+ let token;
176
+ const verifyInputResult = this.verifyInput();
177
+ if (!verifyInputResult[0]) {
178
+ throwLexerError(MissingSemicolonError, verifyInputResult[1], verifyInputResult[2]);
179
+ }
180
+ while (this.cursor < this.input.length) {
181
+ token = this.nextToken();
182
+ if (token) {
183
+ tokens.push(token);
184
+ }
185
+ }
186
+ return tokens;
187
+ };
188
+ /**
189
+ * Reads a character and advances the cursor.
190
+ * @param num - Optional cursor position modifier.
191
+ */
192
+ this.readChar = (num = 1) => {
193
+ this.cursor += num;
194
+ return this.input[this.cursor - num];
195
+ };
196
+ /**
197
+ * Advances the cusor past the next comment.
198
+ */
199
+ this.skipComment = () => {
200
+ let char = "";
201
+ while (!isNewline(char)) {
202
+ char = this.readChar();
203
+ }
204
+ };
205
+ /**
206
+ * Advances the cursor past a multiline comment.
207
+ */
208
+ this.skipMultiLineComment = () => {
209
+ let char = "";
210
+ let nextChar = "";
211
+ const multiLineCommentTerminator = "*/";
212
+ while (`${char}${nextChar}` !== multiLineCommentTerminator) {
213
+ char = this.readChar();
214
+ nextChar = this.peek();
215
+ }
216
+ this.readChar();
217
+ };
218
+ /**
219
+ * Determines whether the next character to process equals a given character.
220
+ * @param c - The given character.
221
+ * @return Whether the next character equals the given character.
222
+ */
223
+ this.peekEq = (c) => this.peek() == c;
224
+ /**
225
+ * Reads a character without advancing the cursor.
226
+ * @param index - Optional peek position offset.
227
+ */
228
+ this.peek = () => this.input[this.cursor];
229
+ /**
230
+ * Reads a numeric value.
231
+ * @return The numeric value as a string.
232
+ */
233
+ this.readNumeric = () => {
234
+ let num = "";
235
+ let char = this.peek();
236
+ while (isNumeric(char) || char === "e") {
237
+ num += this.readChar();
238
+ if (char === "e" && this.peek() === "-") {
239
+ num += this.readChar();
240
+ }
241
+ char = this.peek();
242
+ }
243
+ return num;
244
+ };
245
+ /**
246
+ * Reads an identifier.
247
+ * @return The identifier as a string.
248
+ */
249
+ this.readIdentifier = () => {
250
+ let id = "";
251
+ let next = this.peek();
252
+ while (isAlpha(next) || next == "_" || isUnicode(next)) {
253
+ id += this.readChar();
254
+ next = this.peek();
255
+ }
256
+ return id;
257
+ };
258
+ /**
259
+ * Reds a keyword or identifier.
260
+ * If the character sequence matches a keyword, returns the corresponding token.
261
+ * Otherwise, treats the sequence as an identifier.
262
+ * @param char - The first character of the keyword or identifier.
263
+ * @return The corresponding token or identifier.
264
+ */
265
+ this.readKeywordOrIdentifier = (char) => {
266
+ const identifier = char + this.readIdentifier();
267
+ return [lookup(identifier), identifier];
268
+ };
269
+ /**
270
+ * Reads a string literal.
271
+ * @param terminator - The literal's termination character.
272
+ * @return The literal as a string.
273
+ */
274
+ this.readStringLiteral = (terminator) => {
275
+ let lit = "";
276
+ let char = "";
277
+ while (!(terminator == char)) {
278
+ char = this.readChar();
279
+ lit += char;
280
+ }
281
+ return lit;
282
+ };
283
+ /**
284
+ * Advances the cusor past the next block of whitespace.
285
+ */
286
+ this.skipWhitespace = () => {
287
+ while (" \t\n\r\v".indexOf(this.peek()) > -1) {
288
+ this.cursor += 1;
289
+ }
290
+ return null;
291
+ };
292
+ /**
293
+ * Lexes the next token.
294
+ * @return The next token and its corresponding value.
295
+ */
296
+ this.nextToken = () => {
297
+ this.skipWhitespace();
298
+ if (this.cursor == this.input.length) {
299
+ return [Token.EndOfFile];
300
+ }
301
+ const char = this.peek();
302
+ this.readChar();
303
+ switch (char) {
304
+ case "π":
305
+ return [Token.Pi];
306
+ case "ℇ":
307
+ return [Token.Euler];
308
+ case "τ":
309
+ return [Token.Tau];
310
+ case "@":
311
+ return [Token.At];
312
+ case "θ":
313
+ return this.readKeywordOrIdentifier(char);
314
+ case "!":
315
+ if (this.peekEq("=")) {
316
+ this.readChar();
317
+ return [Token.BinaryOp, "!="];
318
+ }
319
+ else {
320
+ return [Token.UnaryOp, "!"];
321
+ }
322
+ case "~":
323
+ return [Token.UnaryOp, "~"];
324
+ case "*":
325
+ if (this.peekEq("*")) {
326
+ this.readChar();
327
+ if (this.peekEq("=")) {
328
+ this.readChar();
329
+ return [Token.CompoundArithmeticOp, "**="];
330
+ }
331
+ else {
332
+ return [Token.ArithmeticOp, "**"];
333
+ }
334
+ }
335
+ else if (this.peekEq("=")) {
336
+ this.readChar();
337
+ return [Token.CompoundArithmeticOp, "*="];
338
+ }
339
+ else {
340
+ return [Token.ArithmeticOp, "*"];
341
+ }
342
+ case "/":
343
+ if (this.peekEq("/")) {
344
+ this.skipComment();
345
+ return;
346
+ }
347
+ else if (this.peekEq("*")) {
348
+ this.skipMultiLineComment();
349
+ return;
350
+ }
351
+ else if (this.peekEq("=")) {
352
+ this.readChar();
353
+ return [Token.CompoundArithmeticOp, "/="];
354
+ }
355
+ else {
356
+ return [Token.ArithmeticOp, "/"];
357
+ }
358
+ case "%":
359
+ if (this.peekEq("=")) {
360
+ this.readChar();
361
+ return [Token.CompoundArithmeticOp, "%="];
362
+ }
363
+ else {
364
+ return [Token.ArithmeticOp, "%"];
365
+ }
366
+ case "+":
367
+ if (this.peekEq("=")) {
368
+ this.readChar();
369
+ return [Token.CompoundArithmeticOp, "+="];
370
+ }
371
+ else if (this.peekEq("+")) {
372
+ this.readChar();
373
+ return [Token.ArithmeticOp, "++"];
374
+ }
375
+ return [Token.ArithmeticOp, "+"];
376
+ case "-": {
377
+ if (this.peekEq(">")) {
378
+ this.readChar();
379
+ return [Token.Arrow];
380
+ }
381
+ else if (this.peekEq("=")) {
382
+ this.readChar();
383
+ return [Token.CompoundArithmeticOp, "-="];
384
+ }
385
+ return [Token.UnaryOp, "-"];
386
+ }
387
+ case "&":
388
+ if (this.peekEq("&")) {
389
+ this.readChar();
390
+ return [Token.BinaryOp, "&&"];
391
+ }
392
+ else {
393
+ return [Token.BinaryOp, "&"];
394
+ }
395
+ case "|":
396
+ if (this.peekEq("|")) {
397
+ this.readChar();
398
+ return [Token.BinaryOp, "||"];
399
+ }
400
+ else {
401
+ return [Token.BinaryOp, "|"];
402
+ }
403
+ case "^":
404
+ if (this.peekEq("=")) {
405
+ this.readChar();
406
+ return [Token.CompoundBinaryOp, "^="];
407
+ }
408
+ return [Token.BinaryOp, "^"];
409
+ case "<":
410
+ if (this.peekEq("=")) {
411
+ this.readChar();
412
+ return [Token.BinaryOp, "<="];
413
+ }
414
+ else if (this.input[this.cursor] == "<" &&
415
+ this.input[this.cursor + 1] == "=") {
416
+ this.readChar(2);
417
+ return [Token.CompoundBinaryOp, "<<="];
418
+ }
419
+ else if (this.peekEq("<")) {
420
+ this.readChar();
421
+ return [Token.BinaryOp, "<<"];
422
+ }
423
+ else {
424
+ return [Token.BinaryOp, "<"];
425
+ }
426
+ case ">":
427
+ if (this.peekEq("=")) {
428
+ this.readChar();
429
+ return [Token.BinaryOp, ">="];
430
+ }
431
+ else if (this.input[this.cursor] == ">" &&
432
+ this.input[this.cursor + 1] == "=") {
433
+ this.readChar(2);
434
+ return [Token.CompoundBinaryOp, ">>="];
435
+ }
436
+ else if (this.peekEq(">")) {
437
+ this.readChar();
438
+ return [Token.BinaryOp, ">>"];
439
+ }
440
+ else {
441
+ return [Token.BinaryOp, ">"];
442
+ }
443
+ case "=":
444
+ if (this.peekEq("=")) {
445
+ this.readChar();
446
+ return [Token.BinaryOp, "=="];
447
+ }
448
+ else {
449
+ return [Token.EqualsAssmt];
450
+ }
451
+ case ";":
452
+ return [Token.Semicolon];
453
+ case ",":
454
+ return [Token.Comma];
455
+ case ":":
456
+ return [Token.Colon];
457
+ case "(":
458
+ return [Token.LParen];
459
+ case "[":
460
+ return [Token.LSParen];
461
+ case "{":
462
+ return [Token.LCParen];
463
+ case ")":
464
+ return [Token.RParen];
465
+ case "]":
466
+ return [Token.RSParen];
467
+ case "}":
468
+ return [Token.RCParen];
469
+ case "$":
470
+ return [Token.Dollar];
471
+ case "#":
472
+ if (this.input[this.cursor] == "d" &&
473
+ this.input[this.cursor + 1] == "i" &&
474
+ this.input[this.cursor + 2] == "m") {
475
+ this.readChar(3);
476
+ return [Token.Dim];
477
+ }
478
+ return this.readKeywordOrIdentifier(char);
479
+ case "c":
480
+ if (this.input[this.cursor] == "t" &&
481
+ this.input[this.cursor + 1] == "r" &&
482
+ this.input[this.cursor + 2] == "l") {
483
+ this.readChar(3);
484
+ return this.lexGateModifier("ctrl");
485
+ }
486
+ return this.readKeywordOrIdentifier(char);
487
+ case "n":
488
+ if (this.input[this.cursor] == "e" &&
489
+ this.input[this.cursor + 1] == "g" &&
490
+ this.input[this.cursor + 2] == "c" &&
491
+ this.input[this.cursor + 3] == "t" &&
492
+ this.input[this.cursor + 4] == "r" &&
493
+ this.input[this.cursor + 5] == "l") {
494
+ this.readChar(6);
495
+ return this.lexGateModifier("negctrl");
496
+ }
497
+ return this.readKeywordOrIdentifier(char);
498
+ case "i":
499
+ if (this.input[this.cursor] == "n" &&
500
+ this.input[this.cursor + 1] == "v") {
501
+ this.readChar(2);
502
+ return this.lexGateModifier("inv");
503
+ }
504
+ return this.readKeywordOrIdentifier(char);
505
+ case "p":
506
+ if (this.input[this.cursor] == "o" &&
507
+ this.input[this.cursor + 1] == "w" &&
508
+ !isLetter(this.input[this.cursor + 2])) {
509
+ this.readChar(2);
510
+ return this.lexGateModifier("pow");
511
+ }
512
+ return this.readKeywordOrIdentifier(char);
513
+ case "0":
514
+ if (this.peekEq("b") || this.peekEq("B")) {
515
+ const char = this.readChar(1);
516
+ const binaryLit = this.readIdentifier();
517
+ return [Token.BinaryLiteral, `0${char}${binaryLit}`];
518
+ }
519
+ else if (this.peekEq("o") || this.peekEq("O")) {
520
+ const char = this.readChar(1);
521
+ const octalLit = this.readIdentifier();
522
+ return [Token.OctalLiteral, `0${char}${octalLit}`];
523
+ }
524
+ else if (this.peekEq("x") || this.peekEq("X")) {
525
+ const char = this.readChar(1);
526
+ const hexLit = this.readIdentifier();
527
+ return [Token.HexLiteral, `0${char}${hexLit}`];
528
+ }
529
+ else {
530
+ const num = char + this.readNumeric();
531
+ if (num.indexOf(".") != -1) {
532
+ return [Token.Real, parseFloat(num)];
533
+ }
534
+ else if (num.indexOf("_") != -1) {
535
+ return [Token.Integer, num];
536
+ }
537
+ else {
538
+ return [Token.NNInteger, parseInt(num)];
539
+ }
540
+ }
541
+ return this.readKeywordOrIdentifier(char);
542
+ case "O":
543
+ if (this.input[this.cursor].toLowerCase() == "p" &&
544
+ this.input[this.cursor + 1].toLowerCase() == "e" &&
545
+ this.input[this.cursor + 2].toLowerCase() == "n" &&
546
+ this.input[this.cursor + 3] == "Q" &&
547
+ this.input[this.cursor + 4] == "A" &&
548
+ this.input[this.cursor + 5] == "S" &&
549
+ this.input[this.cursor + 6] == "M") {
550
+ this.readChar(7);
551
+ let offset = 0;
552
+ while (this.cursor + offset < this.input.length &&
553
+ " \t".indexOf(this.input[this.cursor + offset]) > -1) {
554
+ offset++;
555
+ }
556
+ // Read the major version
557
+ let majorVersion = "";
558
+ while (this.cursor + offset < this.input.length &&
559
+ !isNaN(parseInt(this.input[this.cursor + offset], 10))) {
560
+ majorVersion += this.input[this.cursor + offset];
561
+ offset++;
562
+ }
563
+ // Attempt to read the minor version
564
+ let minorVersion = undefined;
565
+ if (this.input[this.cursor + offset] == ".") {
566
+ offset++;
567
+ minorVersion = "";
568
+ while (this.cursor + offset < this.input.length &&
569
+ !isNaN(parseInt(this.input[this.cursor + offset], 10))) {
570
+ minorVersion += this.input[this.cursor + offset];
571
+ offset++;
572
+ }
573
+ }
574
+ // Parse major and minor versions
575
+ const major = parseInt(majorVersion, 10);
576
+ const minor = minorVersion ? parseInt(minorVersion, 10) : undefined;
577
+ if (major !== 3) {
578
+ throw new UnsupportedOpenQASMVersionError(`Unsupported OpenQASM version detected: ${majorVersion}.${minor !== null && minor !== void 0 ? minor : 0}`);
579
+ }
580
+ return [Token.OpenQASM];
581
+ }
582
+ return this.readKeywordOrIdentifier(char);
583
+ case '"': {
584
+ const stringLiteral = char + this.readStringLiteral('"');
585
+ return [Token.String, stringLiteral];
586
+ }
587
+ case "’": {
588
+ const singleStringLiteral = char + this.readStringLiteral("’");
589
+ return [Token.String, singleStringLiteral];
590
+ }
591
+ default:
592
+ if (isLetter(char)) {
593
+ return this.readKeywordOrIdentifier(char);
594
+ }
595
+ else if (isNumeric(char)) {
596
+ const num = char + this.readNumeric();
597
+ if (num.indexOf("e") != -1) {
598
+ return [Token.ScientificNotation, num];
599
+ }
600
+ else if (num.indexOf(".") != -1) {
601
+ return [Token.Real, parseFloat(num)];
602
+ }
603
+ else if (num.indexOf("_") != -1) {
604
+ return [Token.Integer, num];
605
+ }
606
+ else {
607
+ return [Token.NNInteger, parseInt(num)];
608
+ }
609
+ }
610
+ else {
611
+ return [Token.Illegal];
612
+ }
613
+ }
614
+ };
615
+ /**
616
+ * Returns the line number where the current cursor is located.
617
+ * @param cursor - The current cursor position in the input string.
618
+ * @return The line number.
619
+ */
620
+ this.getLineNumber = (cursor) => {
621
+ return this.input
622
+ .substring(0, cursor)
623
+ .split(/\n|\r(?!\n)|\u2028|\u2029|\r\n/).length;
624
+ };
625
+ /**
626
+ * Returns the current line of code where the cursor is located.
627
+ * @param cursor - The current cursor position in the input string.
628
+ * @return The specific line where the cursor is located.
629
+ */
630
+ this.getCurrentLine = (cursor) => {
631
+ const lines = this.input.split(/\n|\r(?!\n)|\u2028|\u2029|\r\n/);
632
+ const lineNumber = this.getLineNumber(cursor);
633
+ return lines[lineNumber - 1];
634
+ };
635
+ /**
636
+ * Retruns an identifier or gate modifier.
637
+ */
638
+ this.lexGateModifier = (keyword) => {
639
+ let offset = 1;
640
+ let currChar = "";
641
+ while (this.cursor + offset < this.input.length &&
642
+ this.input[this.cursor + offset] !== ";") {
643
+ currChar = this.input[this.cursor + offset];
644
+ if (currChar === "@") {
645
+ break;
646
+ }
647
+ offset++;
648
+ }
649
+ if (currChar === "@") {
650
+ switch (keyword) {
651
+ case "ctrl":
652
+ return [Token.Ctrl];
653
+ case "negctrl":
654
+ return [Token.NegCtrl];
655
+ case "inv":
656
+ return [Token.Inv];
657
+ case "pow":
658
+ return [Token.PowM];
659
+ }
660
+ }
661
+ return this.readKeywordOrIdentifier(keyword);
662
+ };
663
+ this.input = input;
664
+ this.cursor = cursor;
665
+ }
666
+ }
667
+ export default Lexer;