vba-runner 0.1.1-alpha.1 → 0.1.1-alpha.2
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/bin/vba-analyzer.cjs +320 -193
- package/dist/bin/vba-formatter.cjs +56 -40
- package/dist/bin/vba-parse-check.cjs +302 -193
- package/dist/bin/vba-run.cjs +412 -200
- package/dist/bin/vba-runner.cjs +440 -210
- package/dist/lib.cjs +412 -200
- package/package.json +1 -1
|
@@ -114,7 +114,7 @@ var Lexer = class _Lexer {
|
|
|
114
114
|
const startLine = this.line;
|
|
115
115
|
const startColumn = this.column;
|
|
116
116
|
if (this.pos >= this.input.length) {
|
|
117
|
-
return { type:
|
|
117
|
+
return { type: 137 /* EOF */, value: "", line: startLine, column: startColumn };
|
|
118
118
|
}
|
|
119
119
|
const char = this.peek();
|
|
120
120
|
if (char === "#") {
|
|
@@ -143,12 +143,12 @@ var Lexer = class _Lexer {
|
|
|
143
143
|
}
|
|
144
144
|
if (this.peek() === "#") {
|
|
145
145
|
this.advance();
|
|
146
|
-
return { type:
|
|
146
|
+
return { type: 135 /* Date */, value: dateValue, line: startLine, column: startColumn };
|
|
147
147
|
}
|
|
148
148
|
}
|
|
149
149
|
}
|
|
150
150
|
this.advance();
|
|
151
|
-
return { type:
|
|
151
|
+
return { type: 112 /* OperatorHash */, value: "#", line: startLine, column: startColumn };
|
|
152
152
|
}
|
|
153
153
|
if (char === "_") {
|
|
154
154
|
const next = this.pos + 1 < this.input.length ? this.input[this.pos + 1] : "\0";
|
|
@@ -186,7 +186,7 @@ var Lexer = class _Lexer {
|
|
|
186
186
|
}
|
|
187
187
|
if (char === "\n") {
|
|
188
188
|
this.advance();
|
|
189
|
-
return { type:
|
|
189
|
+
return { type: 136 /* Newline */, value: "\n", line: startLine, column: startColumn };
|
|
190
190
|
}
|
|
191
191
|
if (char === '"') {
|
|
192
192
|
this.advance();
|
|
@@ -208,39 +208,39 @@ var Lexer = class _Lexer {
|
|
|
208
208
|
}
|
|
209
209
|
if (char === "=") {
|
|
210
210
|
this.advance();
|
|
211
|
-
return { type:
|
|
211
|
+
return { type: 121 /* OperatorEquals */, value: "=", line: startLine, column: startColumn };
|
|
212
212
|
}
|
|
213
213
|
if (char === "<") {
|
|
214
214
|
this.advance();
|
|
215
215
|
if (this.peek() === ">") {
|
|
216
216
|
this.advance();
|
|
217
|
-
return { type:
|
|
217
|
+
return { type: 122 /* OperatorNotEquals */, value: "<>", line: startLine, column: startColumn };
|
|
218
218
|
}
|
|
219
219
|
if (this.peek() === "=") {
|
|
220
220
|
this.advance();
|
|
221
|
-
return { type:
|
|
221
|
+
return { type: 125 /* OperatorLessThanOrEqual */, value: "<=", line: startLine, column: startColumn };
|
|
222
222
|
}
|
|
223
|
-
return { type:
|
|
223
|
+
return { type: 123 /* OperatorLessThan */, value: "<", line: startLine, column: startColumn };
|
|
224
224
|
}
|
|
225
225
|
if (char === ">") {
|
|
226
226
|
this.advance();
|
|
227
227
|
if (this.peek() === "=") {
|
|
228
228
|
this.advance();
|
|
229
|
-
return { type:
|
|
229
|
+
return { type: 126 /* OperatorGreaterThanOrEqual */, value: ">=", line: startLine, column: startColumn };
|
|
230
230
|
}
|
|
231
|
-
return { type:
|
|
231
|
+
return { type: 124 /* OperatorGreaterThan */, value: ">", line: startLine, column: startColumn };
|
|
232
232
|
}
|
|
233
233
|
if (char === "+") {
|
|
234
234
|
this.advance();
|
|
235
|
-
return { type:
|
|
235
|
+
return { type: 113 /* OperatorPlus */, value: "+", line: startLine, column: startColumn };
|
|
236
236
|
}
|
|
237
237
|
if (char === "-") {
|
|
238
238
|
this.advance();
|
|
239
|
-
return { type:
|
|
239
|
+
return { type: 114 /* OperatorMinus */, value: "-", line: startLine, column: startColumn };
|
|
240
240
|
}
|
|
241
241
|
if (char === "!") {
|
|
242
242
|
this.advance();
|
|
243
|
-
return { type:
|
|
243
|
+
return { type: 133 /* OperatorExclamation */, value: "!", line: startLine, column: startColumn };
|
|
244
244
|
}
|
|
245
245
|
if (char === "&") {
|
|
246
246
|
this.advance();
|
|
@@ -262,23 +262,23 @@ var Lexer = class _Lexer {
|
|
|
262
262
|
if (NUMERIC_TYPE_SUFFIXES.has(this.peek())) this.advance();
|
|
263
263
|
return { type: 1 /* Number */, value: "0o" + octStr, line: startLine, column: startColumn };
|
|
264
264
|
}
|
|
265
|
-
return { type:
|
|
265
|
+
return { type: 118 /* OperatorAmpersand */, value: "&", line: startLine, column: startColumn };
|
|
266
266
|
}
|
|
267
267
|
if (char === ",") {
|
|
268
268
|
this.advance();
|
|
269
|
-
return { type:
|
|
269
|
+
return { type: 127 /* OperatorComma */, value: ",", line: startLine, column: startColumn };
|
|
270
270
|
}
|
|
271
271
|
if (char === "#") {
|
|
272
272
|
this.advance();
|
|
273
|
-
return { type:
|
|
273
|
+
return { type: 112 /* OperatorHash */, value: "#", line: startLine, column: startColumn };
|
|
274
274
|
}
|
|
275
275
|
if (char === "(") {
|
|
276
276
|
this.advance();
|
|
277
|
-
return { type:
|
|
277
|
+
return { type: 128 /* OperatorLParen */, value: "(", line: startLine, column: startColumn };
|
|
278
278
|
}
|
|
279
279
|
if (char === ")") {
|
|
280
280
|
this.advance();
|
|
281
|
-
return { type:
|
|
281
|
+
return { type: 129 /* OperatorRParen */, value: ")", line: startLine, column: startColumn };
|
|
282
282
|
}
|
|
283
283
|
if (char === "[") {
|
|
284
284
|
this.advance();
|
|
@@ -287,23 +287,23 @@ var Lexer = class _Lexer {
|
|
|
287
287
|
foreignStr += this.advance();
|
|
288
288
|
}
|
|
289
289
|
if (this.peek() === "]") this.advance();
|
|
290
|
-
return { type:
|
|
290
|
+
return { type: 139 /* ForeignName */, value: foreignStr, line: startLine, column: startColumn };
|
|
291
291
|
}
|
|
292
292
|
if (char === ".") {
|
|
293
293
|
this.advance();
|
|
294
|
-
return { type:
|
|
294
|
+
return { type: 130 /* OperatorDot */, value: ".", line: startLine, column: startColumn };
|
|
295
295
|
}
|
|
296
296
|
if (char === ":") {
|
|
297
297
|
this.advance();
|
|
298
298
|
if (this.peek() === "=") {
|
|
299
299
|
this.advance();
|
|
300
|
-
return { type:
|
|
300
|
+
return { type: 132 /* OperatorColonEquals */, value: ":=", line: startLine, column: startColumn };
|
|
301
301
|
}
|
|
302
|
-
return { type:
|
|
302
|
+
return { type: 131 /* OperatorColon */, value: ":", line: startLine, column: startColumn };
|
|
303
303
|
}
|
|
304
304
|
if (char === ";") {
|
|
305
305
|
this.advance();
|
|
306
|
-
return { type:
|
|
306
|
+
return { type: 134 /* OperatorSemicolon */, value: ";", line: startLine, column: startColumn };
|
|
307
307
|
}
|
|
308
308
|
if (this.isDigit(char)) {
|
|
309
309
|
let numStr = "";
|
|
@@ -400,7 +400,7 @@ var Lexer = class _Lexer {
|
|
|
400
400
|
if (lowerBase === "exit") return { type: 45 /* KeywordExit */, value: idStr, line: startLine, column: startColumn };
|
|
401
401
|
if (lowerBase === "byref") return { type: 46 /* KeywordByRef */, value: idStr, line: startLine, column: startColumn };
|
|
402
402
|
if (lowerBase === "byval") return { type: 47 /* KeywordByVal */, value: idStr, line: startLine, column: startColumn };
|
|
403
|
-
if (lowerBase === "mod") return { type:
|
|
403
|
+
if (lowerBase === "mod") return { type: 119 /* KeywordMod */, value: idStr, line: startLine, column: startColumn };
|
|
404
404
|
if (lowerBase === "type") return { type: 48 /* KeywordType */, value: idStr, line: startLine, column: startColumn };
|
|
405
405
|
if (lowerBase === "nothing") return { type: 49 /* KeywordNothing */, value: idStr, line: startLine, column: startColumn };
|
|
406
406
|
if (lowerBase === "optional") return { type: 50 /* KeywordOptional */, value: idStr, line: startLine, column: startColumn };
|
|
@@ -461,26 +461,42 @@ var Lexer = class _Lexer {
|
|
|
461
461
|
if (lowerBase === "midb") return { type: 108 /* KeywordMid */, value: idStr, line: startLine, column: startColumn };
|
|
462
462
|
if (lowerBase === "width") return { type: 109 /* KeywordWidth */, value: idStr, line: startLine, column: startColumn };
|
|
463
463
|
if (lowerBase === "addressof") return { type: 110 /* KeywordAddressOf */, value: idStr, line: startLine, column: startColumn };
|
|
464
|
+
if ([
|
|
465
|
+
"defbool",
|
|
466
|
+
"defbyte",
|
|
467
|
+
"defint",
|
|
468
|
+
"deflng",
|
|
469
|
+
"deflnglng",
|
|
470
|
+
"deflngptr",
|
|
471
|
+
"defsng",
|
|
472
|
+
"defdbl",
|
|
473
|
+
"defcur",
|
|
474
|
+
"defdate",
|
|
475
|
+
"defstr",
|
|
476
|
+
"defobj",
|
|
477
|
+
"defvar"
|
|
478
|
+
].includes(lowerBase))
|
|
479
|
+
return { type: 111 /* KeywordDef */, value: idStr, line: startLine, column: startColumn };
|
|
464
480
|
return { type: 0 /* Identifier */, value: idStr, line: startLine, column: startColumn };
|
|
465
481
|
}
|
|
466
482
|
if (char === "*") {
|
|
467
483
|
this.advance();
|
|
468
|
-
return { type:
|
|
484
|
+
return { type: 115 /* OperatorMultiply */, value: "*", line: startLine, column: startColumn };
|
|
469
485
|
}
|
|
470
486
|
if (char === "/") {
|
|
471
487
|
this.advance();
|
|
472
|
-
return { type:
|
|
488
|
+
return { type: 116 /* OperatorDivide */, value: "/", line: startLine, column: startColumn };
|
|
473
489
|
}
|
|
474
490
|
if (char === "\\") {
|
|
475
491
|
this.advance();
|
|
476
|
-
return { type:
|
|
492
|
+
return { type: 117 /* OperatorIntDivide */, value: "\\", line: startLine, column: startColumn };
|
|
477
493
|
}
|
|
478
494
|
if (char === "^") {
|
|
479
495
|
this.advance();
|
|
480
|
-
return { type:
|
|
496
|
+
return { type: 120 /* OperatorPower */, value: "^", line: startLine, column: startColumn };
|
|
481
497
|
}
|
|
482
498
|
const unknownChar = this.advance();
|
|
483
|
-
return { type:
|
|
499
|
+
return { type: 138 /* Unknown */, value: unknownChar, line: startLine, column: startColumn };
|
|
484
500
|
}
|
|
485
501
|
}
|
|
486
502
|
tokenize() {
|
|
@@ -489,7 +505,7 @@ var Lexer = class _Lexer {
|
|
|
489
505
|
do {
|
|
490
506
|
token = this.getNextToken();
|
|
491
507
|
tokens.push(token);
|
|
492
|
-
} while (token.type !==
|
|
508
|
+
} while (token.type !== 137 /* EOF */);
|
|
493
509
|
return tokens;
|
|
494
510
|
}
|
|
495
511
|
};
|
|
@@ -637,7 +653,7 @@ var Parser = class _Parser {
|
|
|
637
653
|
/** Returns true if token is a valid IDENTIFIER per §3.3.5.2:
|
|
638
654
|
* either a plain lex-identifier or a contextual keyword that is not reserved. */
|
|
639
655
|
isIdentifier(token) {
|
|
640
|
-
return token.type === 0 /* Identifier */ || token.type ===
|
|
656
|
+
return token.type === 0 /* Identifier */ || token.type === 139 /* ForeignName */ || _Parser.CONTEXTUAL_KW.has(token.type);
|
|
641
657
|
}
|
|
642
658
|
constructor(tokens, options = {}) {
|
|
643
659
|
this.tokens = tokens;
|
|
@@ -646,7 +662,7 @@ var Parser = class _Parser {
|
|
|
646
662
|
}
|
|
647
663
|
// Keywords can appear as property/class names in VBA (e.g. obj.Property, New Collection)
|
|
648
664
|
isNameToken(token) {
|
|
649
|
-
return token.type === 0 /* Identifier */ || token.type ===
|
|
665
|
+
return token.type === 0 /* Identifier */ || token.type === 139 /* ForeignName */ || token.type >= 3 /* KeywordFor */ && token.type <= 110 /* KeywordAddressOf */;
|
|
650
666
|
}
|
|
651
667
|
/**
|
|
652
668
|
* Returns true if the current token stream contains a file-I/O Open statement pattern
|
|
@@ -663,7 +679,7 @@ var Parser = class _Parser {
|
|
|
663
679
|
}
|
|
664
680
|
throwMissingRParen() {
|
|
665
681
|
const peek = this.peek();
|
|
666
|
-
if (peek.type ===
|
|
682
|
+
if (peek.type === 136 /* Newline */) {
|
|
667
683
|
const prevToken = this.tokens[Math.max(0, this.pos - 1)];
|
|
668
684
|
if (prevToken && this.isContinuationEndToken(prevToken.type)) {
|
|
669
685
|
this.throwError(
|
|
@@ -675,24 +691,24 @@ var Parser = class _Parser {
|
|
|
675
691
|
this.throwError(`Parse error: Expected ')' at line ${peek.line} `);
|
|
676
692
|
}
|
|
677
693
|
isContinuationEndToken(type) {
|
|
678
|
-
return type ===
|
|
694
|
+
return type === 113 /* OperatorPlus */ || type === 114 /* OperatorMinus */ || type === 115 /* OperatorMultiply */ || type === 116 /* OperatorDivide */ || type === 117 /* OperatorIntDivide */ || type === 120 /* OperatorPower */ || type === 118 /* OperatorAmpersand */ || type === 121 /* OperatorEquals */ || type === 122 /* OperatorNotEquals */ || type === 123 /* OperatorLessThan */ || type === 124 /* OperatorGreaterThan */ || type === 125 /* OperatorLessThanOrEqual */ || type === 126 /* OperatorGreaterThanOrEqual */ || type === 127 /* OperatorComma */ || type === 128 /* OperatorLParen */ || type === 26 /* KeywordAnd */ || type === 27 /* KeywordOr */ || type === 64 /* KeywordXor */ || type === 65 /* KeywordEqv */ || type === 66 /* KeywordImp */ || type === 119 /* KeywordMod */ || type === 63 /* KeywordLike */ || type === 51 /* KeywordIs */ || type === 28 /* KeywordNot */;
|
|
679
695
|
}
|
|
680
696
|
tokenDisplay(value) {
|
|
681
697
|
return value.replace(/\n/g, "<\u6539\u884C>").replace(/\r/g, "<CR>").replace(/\t/g, "<\u30BF\u30D6>");
|
|
682
698
|
}
|
|
683
699
|
throwError(message, token) {
|
|
684
700
|
const peek = this.peek();
|
|
685
|
-
const t = token ?? (peek.type !==
|
|
701
|
+
const t = token ?? (peek.type !== 137 /* EOF */ ? peek : this.tokens[Math.max(0, this.pos - 1)]);
|
|
686
702
|
throw new ParseError(message, t.line, t.column);
|
|
687
703
|
}
|
|
688
704
|
syncToNextTopLevelStatement() {
|
|
689
|
-
while (this.peek().type !==
|
|
705
|
+
while (this.peek().type !== 137 /* EOF */ && this.peek().type !== 136 /* Newline */) {
|
|
690
706
|
this.advance();
|
|
691
707
|
}
|
|
692
708
|
while (this.isAtEndTerminator()) {
|
|
693
709
|
this.advance();
|
|
694
710
|
this.advance();
|
|
695
|
-
while (this.peek().type !==
|
|
711
|
+
while (this.peek().type !== 137 /* EOF */ && this.peek().type !== 136 /* Newline */) {
|
|
696
712
|
this.advance();
|
|
697
713
|
}
|
|
698
714
|
}
|
|
@@ -720,6 +736,18 @@ var Parser = class _Parser {
|
|
|
720
736
|
}
|
|
721
737
|
return { type: "OptionCompareStatement", mode };
|
|
722
738
|
}
|
|
739
|
+
validateParameterOrder(params) {
|
|
740
|
+
let seenOptional = false;
|
|
741
|
+
for (const p of params) {
|
|
742
|
+
if (p.isParamArray) break;
|
|
743
|
+
if (p.isOptional) {
|
|
744
|
+
seenOptional = true;
|
|
745
|
+
} else if (seenOptional) {
|
|
746
|
+
const line = p.loc?.start?.line ?? this.peek().line;
|
|
747
|
+
this.throwError(`Compile error at line ${line}: Non-optional parameter '${p.name}' cannot follow an Optional parameter`);
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
}
|
|
723
751
|
parseParameter() {
|
|
724
752
|
let isByVal = false;
|
|
725
753
|
let isOptional = false;
|
|
@@ -741,20 +769,20 @@ var Parser = class _Parser {
|
|
|
741
769
|
}
|
|
742
770
|
const nameToken = this.advance();
|
|
743
771
|
let isArray = false;
|
|
744
|
-
if (this.match(
|
|
745
|
-
this.consume(
|
|
772
|
+
if (this.match(128 /* OperatorLParen */)) {
|
|
773
|
+
this.consume(129 /* OperatorRParen */, "Expected ')' after parameter name");
|
|
746
774
|
isArray = true;
|
|
747
775
|
}
|
|
748
776
|
let paramType;
|
|
749
777
|
if (this.match(23 /* KeywordAs */)) {
|
|
750
778
|
paramType = this.advance().value;
|
|
751
|
-
if (this.peek().type ===
|
|
779
|
+
if (this.peek().type === 130 /* OperatorDot */) {
|
|
752
780
|
this.advance();
|
|
753
781
|
paramType += "." + this.advance().value;
|
|
754
782
|
}
|
|
755
783
|
}
|
|
756
784
|
let defaultValue;
|
|
757
|
-
if (this.match(
|
|
785
|
+
if (this.match(121 /* OperatorEquals */)) {
|
|
758
786
|
defaultValue = this.parseExpression();
|
|
759
787
|
}
|
|
760
788
|
return {
|
|
@@ -770,10 +798,43 @@ var Parser = class _Parser {
|
|
|
770
798
|
loc: { start: { line: nameToken.line, column: nameToken.column }, end: { line: nameToken.line, column: nameToken.column + nameToken.value.length } }
|
|
771
799
|
};
|
|
772
800
|
}
|
|
801
|
+
parseDefDirective() {
|
|
802
|
+
const defToken = this.advance();
|
|
803
|
+
const keyword = defToken.value.toLowerCase().replace(/[$%&#@!^]$/, "");
|
|
804
|
+
const typeMap = {
|
|
805
|
+
defbool: "Boolean",
|
|
806
|
+
defbyte: "Byte",
|
|
807
|
+
defint: "Integer",
|
|
808
|
+
deflng: "Long",
|
|
809
|
+
deflnglng: "LongLong",
|
|
810
|
+
deflngptr: "LongPtr",
|
|
811
|
+
defsng: "Single",
|
|
812
|
+
defdbl: "Double",
|
|
813
|
+
defcur: "Currency",
|
|
814
|
+
defdate: "Date",
|
|
815
|
+
defstr: "String",
|
|
816
|
+
defobj: "Object",
|
|
817
|
+
defvar: "Variant"
|
|
818
|
+
};
|
|
819
|
+
const vbaType = typeMap[keyword] ?? "Variant";
|
|
820
|
+
const ranges = [];
|
|
821
|
+
do {
|
|
822
|
+
const fromTok = this.advance();
|
|
823
|
+
const from = fromTok.value.charAt(0).toLowerCase();
|
|
824
|
+
let to = from;
|
|
825
|
+
if (this.peek().type === 114 /* OperatorMinus */) {
|
|
826
|
+
this.advance();
|
|
827
|
+
const toTok = this.advance();
|
|
828
|
+
to = toTok.value.charAt(0).toLowerCase();
|
|
829
|
+
}
|
|
830
|
+
ranges.push({ from, to });
|
|
831
|
+
} while (this.match(127 /* OperatorComma */));
|
|
832
|
+
return { type: "DefDirective", vbaType, ranges };
|
|
833
|
+
}
|
|
773
834
|
parseAttributeStatement() {
|
|
774
835
|
this.advance();
|
|
775
836
|
const name = this.advance().value;
|
|
776
|
-
this.consume(
|
|
837
|
+
this.consume(121 /* OperatorEquals */, "Expected '=' after Attribute name");
|
|
777
838
|
const value = this.parseExpression();
|
|
778
839
|
return { type: "AttributeStatement", name, value };
|
|
779
840
|
}
|
|
@@ -831,7 +892,7 @@ var Parser = class _Parser {
|
|
|
831
892
|
}
|
|
832
893
|
}
|
|
833
894
|
this.consume(23 /* KeywordAs */, "Expected 'As' in Open statement");
|
|
834
|
-
this.match(
|
|
895
|
+
this.match(112 /* OperatorHash */);
|
|
835
896
|
const fileNumber = this.parseExpression();
|
|
836
897
|
return { type: "OpenStatement", path: path2, mode, access, lock, fileNumber };
|
|
837
898
|
}
|
|
@@ -839,34 +900,34 @@ var Parser = class _Parser {
|
|
|
839
900
|
this.advance();
|
|
840
901
|
const fileNumbers = [];
|
|
841
902
|
while (!this.isAtTerminator()) {
|
|
842
|
-
this.match(
|
|
903
|
+
this.match(112 /* OperatorHash */);
|
|
843
904
|
fileNumbers.push(this.parseExpression());
|
|
844
|
-
if (!this.match(
|
|
905
|
+
if (!this.match(127 /* OperatorComma */)) break;
|
|
845
906
|
}
|
|
846
907
|
return { type: "CloseStatement", fileNumbers };
|
|
847
908
|
}
|
|
848
909
|
parsePrintStatement() {
|
|
849
910
|
this.advance();
|
|
850
|
-
this.consume(
|
|
911
|
+
this.consume(112 /* OperatorHash */, "Expected '#' in Print statement");
|
|
851
912
|
const fileNumber = this.parseExpression();
|
|
852
|
-
this.consume(
|
|
913
|
+
this.consume(127 /* OperatorComma */, "Expected ',' after file number in Print statement");
|
|
853
914
|
const expressions = [];
|
|
854
915
|
while (!this.isAtTerminator()) {
|
|
855
916
|
if (this.match(95 /* KeywordSpc */)) {
|
|
856
|
-
this.consume(
|
|
917
|
+
this.consume(128 /* OperatorLParen */, "Expected '(' after Spc");
|
|
857
918
|
expressions.push({ type: "Spc", val: this.parseExpression() });
|
|
858
|
-
this.consume(
|
|
919
|
+
this.consume(129 /* OperatorRParen */, "Expected ')' after Spc");
|
|
859
920
|
} else if (this.match(96 /* KeywordTab */)) {
|
|
860
|
-
if (this.match(
|
|
921
|
+
if (this.match(128 /* OperatorLParen */)) {
|
|
861
922
|
expressions.push({ type: "Tab", val: this.parseExpression() });
|
|
862
|
-
this.consume(
|
|
923
|
+
this.consume(129 /* OperatorRParen */, "Expected ')' after Tab");
|
|
863
924
|
} else {
|
|
864
925
|
expressions.push("Tab");
|
|
865
926
|
}
|
|
866
|
-
} else if (this.peek().type ===
|
|
927
|
+
} else if (this.peek().type === 127 /* OperatorComma */) {
|
|
867
928
|
this.advance();
|
|
868
929
|
expressions.push("Comma");
|
|
869
|
-
} else if (this.peek().type ===
|
|
930
|
+
} else if (this.peek().type === 134 /* OperatorSemicolon */) {
|
|
870
931
|
this.advance();
|
|
871
932
|
expressions.push("Semicolon");
|
|
872
933
|
} else {
|
|
@@ -879,9 +940,9 @@ var Parser = class _Parser {
|
|
|
879
940
|
parseLineInputStatement() {
|
|
880
941
|
this.advance();
|
|
881
942
|
this.consume(84 /* KeywordInput */, "Expected 'Input' after 'Line'");
|
|
882
|
-
this.consume(
|
|
943
|
+
this.consume(112 /* OperatorHash */, "Expected '#' in Line Input statement");
|
|
883
944
|
const fileNumber = this.parseExpression();
|
|
884
|
-
this.consume(
|
|
945
|
+
this.consume(127 /* OperatorComma */, "Expected ',' after file number");
|
|
885
946
|
const variable = this.parsePrimary();
|
|
886
947
|
if (variable.type !== "Identifier") {
|
|
887
948
|
this.throwError(`Parse error: Expected variable name in Line Input at line ${this.peek().line}`);
|
|
@@ -890,14 +951,14 @@ var Parser = class _Parser {
|
|
|
890
951
|
}
|
|
891
952
|
parsePutStatement() {
|
|
892
953
|
this.advance();
|
|
893
|
-
this.consume(
|
|
954
|
+
this.consume(112 /* OperatorHash */, "Expected '#' in Put statement");
|
|
894
955
|
const fileNumber = this.parseExpression();
|
|
895
|
-
this.consume(
|
|
956
|
+
this.consume(127 /* OperatorComma */, "Expected ',' after file number");
|
|
896
957
|
let recordNumber = void 0;
|
|
897
|
-
if (this.peek().type !==
|
|
958
|
+
if (this.peek().type !== 127 /* OperatorComma */) {
|
|
898
959
|
recordNumber = this.parseExpression();
|
|
899
960
|
}
|
|
900
|
-
this.consume(
|
|
961
|
+
this.consume(127 /* OperatorComma */, "Expected second ',' in Put statement");
|
|
901
962
|
const data = this.parseExpression();
|
|
902
963
|
return { type: "PutStatement", fileNumber, recordNumber, data };
|
|
903
964
|
}
|
|
@@ -908,47 +969,47 @@ var Parser = class _Parser {
|
|
|
908
969
|
}
|
|
909
970
|
parseWriteStatement() {
|
|
910
971
|
this.advance();
|
|
911
|
-
this.consume(
|
|
972
|
+
this.consume(112 /* OperatorHash */, "Expected '#' in Write statement");
|
|
912
973
|
const fileNumber = this.parseExpression();
|
|
913
974
|
const items = [];
|
|
914
|
-
if (this.match(
|
|
975
|
+
if (this.match(127 /* OperatorComma */)) {
|
|
915
976
|
while (!this.isAtTerminator()) {
|
|
916
977
|
items.push(this.parseExpression());
|
|
917
|
-
if (!this.match(
|
|
978
|
+
if (!this.match(127 /* OperatorComma */)) break;
|
|
918
979
|
}
|
|
919
980
|
}
|
|
920
981
|
return { type: "WriteStatement", fileNumber, items };
|
|
921
982
|
}
|
|
922
983
|
parseInputStatement() {
|
|
923
984
|
this.advance();
|
|
924
|
-
this.consume(
|
|
985
|
+
this.consume(112 /* OperatorHash */, "Expected '#' in Input statement");
|
|
925
986
|
const fileNumber = this.parseExpression();
|
|
926
|
-
this.consume(
|
|
987
|
+
this.consume(127 /* OperatorComma */, "Expected ',' in Input statement");
|
|
927
988
|
const variables = [];
|
|
928
989
|
while (!this.isAtTerminator()) {
|
|
929
990
|
variables.push(this.parseExpression());
|
|
930
|
-
if (!this.match(
|
|
991
|
+
if (!this.match(127 /* OperatorComma */)) break;
|
|
931
992
|
}
|
|
932
993
|
return { type: "InputStatement", fileNumber, variables };
|
|
933
994
|
}
|
|
934
995
|
parseGetStatement() {
|
|
935
996
|
this.advance();
|
|
936
|
-
this.consume(
|
|
997
|
+
this.consume(112 /* OperatorHash */, "Expected '#' in Get statement");
|
|
937
998
|
const fileNumber = this.parseExpression();
|
|
938
|
-
this.consume(
|
|
999
|
+
this.consume(127 /* OperatorComma */, "Expected ',' in Get statement");
|
|
939
1000
|
let recordNumber = void 0;
|
|
940
|
-
if (this.peek().type !==
|
|
1001
|
+
if (this.peek().type !== 127 /* OperatorComma */) {
|
|
941
1002
|
recordNumber = this.parseExpression();
|
|
942
1003
|
}
|
|
943
|
-
this.consume(
|
|
1004
|
+
this.consume(127 /* OperatorComma */, "Expected ',' in Get statement");
|
|
944
1005
|
const variable = this.parseExpression();
|
|
945
1006
|
return { type: "GetStatement", fileNumber, recordNumber, variable };
|
|
946
1007
|
}
|
|
947
1008
|
parseSeekStatement() {
|
|
948
1009
|
this.advance();
|
|
949
|
-
this.consume(
|
|
1010
|
+
this.consume(112 /* OperatorHash */, "Expected '#' in Seek statement");
|
|
950
1011
|
const fileNumber = this.parseExpression();
|
|
951
|
-
this.consume(
|
|
1012
|
+
this.consume(127 /* OperatorComma */, "Expected ',' in Seek statement");
|
|
952
1013
|
const position = this.parseExpression();
|
|
953
1014
|
return { type: "SeekStatement", fileNumber, position };
|
|
954
1015
|
}
|
|
@@ -985,15 +1046,16 @@ var Parser = class _Parser {
|
|
|
985
1046
|
aliasName = this.advance().value.replace(/^"|"$/g, "");
|
|
986
1047
|
}
|
|
987
1048
|
let parameters = [];
|
|
988
|
-
if (this.peek().type ===
|
|
1049
|
+
if (this.peek().type === 128 /* OperatorLParen */) {
|
|
989
1050
|
this.advance();
|
|
990
|
-
while (this.peek().type !==
|
|
1051
|
+
while (this.peek().type !== 129 /* OperatorRParen */ && this.peek().type !== 137 /* EOF */) {
|
|
991
1052
|
parameters.push(this.parseParameter());
|
|
992
|
-
if (this.peek().type ===
|
|
1053
|
+
if (this.peek().type === 127 /* OperatorComma */) {
|
|
993
1054
|
this.advance();
|
|
994
1055
|
}
|
|
995
1056
|
}
|
|
996
|
-
this.consume(
|
|
1057
|
+
this.consume(129 /* OperatorRParen */, "Expected ')' after declare parameters");
|
|
1058
|
+
this.validateParameterOrder(parameters);
|
|
997
1059
|
}
|
|
998
1060
|
let returnType;
|
|
999
1061
|
if (!isSub) {
|
|
@@ -1030,7 +1092,7 @@ var Parser = class _Parser {
|
|
|
1030
1092
|
// argument starters). When one of these follows a greedily-parsed CallExpression in a
|
|
1031
1093
|
// statement context, it signals that the `(args)` was actually part of the argument expression.
|
|
1032
1094
|
isBinaryOnlyOperator(type) {
|
|
1033
|
-
return type ===
|
|
1095
|
+
return type === 115 /* OperatorMultiply */ || type === 116 /* OperatorDivide */ || type === 117 /* OperatorIntDivide */ || type === 120 /* OperatorPower */ || type === 113 /* OperatorPlus */ || type === 114 /* OperatorMinus */ || type === 118 /* OperatorAmpersand */ || type === 122 /* OperatorNotEquals */ || type === 123 /* OperatorLessThan */ || type === 124 /* OperatorGreaterThan */ || type === 125 /* OperatorLessThanOrEqual */ || type === 126 /* OperatorGreaterThanOrEqual */ || type === 119 /* KeywordMod */;
|
|
1034
1096
|
}
|
|
1035
1097
|
// Returns true if there is whitespace between the previous token and the current token (same line).
|
|
1036
1098
|
// Used to distinguish `Foo(arg)` (no space = function-call postfix) from
|
|
@@ -1044,7 +1106,7 @@ var Parser = class _Parser {
|
|
|
1044
1106
|
}
|
|
1045
1107
|
isAtTerminator() {
|
|
1046
1108
|
const type = this.peek().type;
|
|
1047
|
-
return type ===
|
|
1109
|
+
return type === 136 /* Newline */ || type === 137 /* EOF */ || type === 131 /* OperatorColon */;
|
|
1048
1110
|
}
|
|
1049
1111
|
consume(expectedType, message) {
|
|
1050
1112
|
if (this.peek().type === expectedType) {
|
|
@@ -1053,7 +1115,7 @@ var Parser = class _Parser {
|
|
|
1053
1115
|
this.throwError(`Parse error at line ${this.peek().line}: ${message}`);
|
|
1054
1116
|
}
|
|
1055
1117
|
skipNewlines() {
|
|
1056
|
-
while (this.match(
|
|
1118
|
+
while (this.match(136 /* Newline */)) {
|
|
1057
1119
|
}
|
|
1058
1120
|
}
|
|
1059
1121
|
// Build a SourceLocation spanning from start token to end token (inclusive).
|
|
@@ -1091,7 +1153,7 @@ var Parser = class _Parser {
|
|
|
1091
1153
|
diagnostics: this._diagnostics
|
|
1092
1154
|
};
|
|
1093
1155
|
this.skipNewlines();
|
|
1094
|
-
while (this.peek().type !==
|
|
1156
|
+
while (this.peek().type !== 137 /* EOF */) {
|
|
1095
1157
|
const startPos = this.pos;
|
|
1096
1158
|
const startToken = this.peek();
|
|
1097
1159
|
try {
|
|
@@ -1110,7 +1172,7 @@ var Parser = class _Parser {
|
|
|
1110
1172
|
}
|
|
1111
1173
|
this.syncToNextTopLevelStatement();
|
|
1112
1174
|
}
|
|
1113
|
-
if (this.pos === startPos && this.peek().type !==
|
|
1175
|
+
if (this.pos === startPos && this.peek().type !== 137 /* EOF */) {
|
|
1114
1176
|
if (this.isAtEndTerminator()) {
|
|
1115
1177
|
this.advance();
|
|
1116
1178
|
this.advance();
|
|
@@ -1154,7 +1216,7 @@ var Parser = class _Parser {
|
|
|
1154
1216
|
*/
|
|
1155
1217
|
parseIdentifierOrCallStatement() {
|
|
1156
1218
|
const token = this.peek();
|
|
1157
|
-
if ((token.type === 0 /* Identifier */ || _Parser.CONTEXTUAL_KW.has(token.type)) && this.pos + 1 < this.tokens.length && this.tokens[this.pos + 1].type ===
|
|
1219
|
+
if ((token.type === 0 /* Identifier */ || _Parser.CONTEXTUAL_KW.has(token.type)) && this.pos + 1 < this.tokens.length && this.tokens[this.pos + 1].type === 131 /* OperatorColon */) {
|
|
1158
1220
|
const labelName = token.value;
|
|
1159
1221
|
this.advance();
|
|
1160
1222
|
this.advance();
|
|
@@ -1163,12 +1225,12 @@ var Parser = class _Parser {
|
|
|
1163
1225
|
if (token.type === 1 /* Number */) {
|
|
1164
1226
|
const labelName = token.value;
|
|
1165
1227
|
this.advance();
|
|
1166
|
-
this.match(
|
|
1228
|
+
this.match(131 /* OperatorColon */);
|
|
1167
1229
|
return { type: "LabelStatement", label: labelName };
|
|
1168
1230
|
}
|
|
1169
1231
|
const savedPos = this.pos;
|
|
1170
1232
|
const expr = this.parsePrimary();
|
|
1171
|
-
if (this.match(
|
|
1233
|
+
if (this.match(121 /* OperatorEquals */)) {
|
|
1172
1234
|
return {
|
|
1173
1235
|
type: "AssignmentStatement",
|
|
1174
1236
|
left: expr,
|
|
@@ -1182,15 +1244,15 @@ var Parser = class _Parser {
|
|
|
1182
1244
|
true
|
|
1183
1245
|
);
|
|
1184
1246
|
const args2 = [this.parseCallArgument()];
|
|
1185
|
-
while (this.match(
|
|
1247
|
+
while (this.match(127 /* OperatorComma */)) {
|
|
1186
1248
|
args2.push(this.parseCallArgument());
|
|
1187
1249
|
}
|
|
1188
1250
|
return { type: "CallStatement", expression: { type: "CallExpression", callee, args: args2 } };
|
|
1189
1251
|
}
|
|
1190
1252
|
const args = [];
|
|
1191
|
-
if (this.peek().type !==
|
|
1253
|
+
if (this.peek().type !== 136 /* Newline */ && this.peek().type !== 137 /* EOF */ && this.peek().type !== 9 /* KeywordElse */ && this.peek().type !== 8 /* KeywordElseIf */ && this.peek().type !== 10 /* KeywordEnd */ && this.peek().type !== 5 /* KeywordNext */ && this.peek().type !== 14 /* KeywordLoop */) {
|
|
1192
1254
|
args.push(this.parseCallArgument());
|
|
1193
|
-
while (this.match(
|
|
1255
|
+
while (this.match(127 /* OperatorComma */)) {
|
|
1194
1256
|
args.push(this.parseCallArgument());
|
|
1195
1257
|
}
|
|
1196
1258
|
}
|
|
@@ -1210,7 +1272,7 @@ var Parser = class _Parser {
|
|
|
1210
1272
|
}
|
|
1211
1273
|
parseStatementInner() {
|
|
1212
1274
|
const token = this.peek();
|
|
1213
|
-
if (_Parser.CONTEXTUAL_KW.has(token.type) && this.pos + 1 < this.tokens.length && this.tokens[this.pos + 1].type ===
|
|
1275
|
+
if (_Parser.CONTEXTUAL_KW.has(token.type) && this.pos + 1 < this.tokens.length && this.tokens[this.pos + 1].type === 131 /* OperatorColon */) {
|
|
1214
1276
|
const labelName = token.value;
|
|
1215
1277
|
this.advance();
|
|
1216
1278
|
this.advance();
|
|
@@ -1259,7 +1321,7 @@ var Parser = class _Parser {
|
|
|
1259
1321
|
return this.parseDoWhileStatement();
|
|
1260
1322
|
} else if (token.type === 12 /* KeywordWhile */) {
|
|
1261
1323
|
return this.parseWhileStatement();
|
|
1262
|
-
} else if (token.type === 17 /* KeywordSub */ || token.type === 18 /* KeywordFunction */ || token.type === 19 /* KeywordProperty */ && this.peek(1).type !==
|
|
1324
|
+
} else if (token.type === 17 /* KeywordSub */ || token.type === 18 /* KeywordFunction */ || token.type === 19 /* KeywordProperty */ && this.peek(1).type !== 121 /* OperatorEquals */) {
|
|
1263
1325
|
return this.parseProcedureDeclaration();
|
|
1264
1326
|
} else if (token.type === 68 /* KeywordStatic */) {
|
|
1265
1327
|
this.advance();
|
|
@@ -1298,13 +1360,15 @@ var Parser = class _Parser {
|
|
|
1298
1360
|
} else {
|
|
1299
1361
|
return this.parseOnGoToSubStatement();
|
|
1300
1362
|
}
|
|
1301
|
-
} else if (token.type === 35 /* KeywordError */ && this.peek(1).type !==
|
|
1363
|
+
} else if (token.type === 35 /* KeywordError */ && this.peek(1).type !== 121 /* OperatorEquals */) {
|
|
1302
1364
|
return this.parseErrorStatement();
|
|
1303
1365
|
} else if (token.type === 37 /* KeywordGoSub */) {
|
|
1304
1366
|
return this.parseGoSubStatement();
|
|
1305
1367
|
} else if (token.type === 38 /* KeywordReturn */) {
|
|
1306
1368
|
this.advance();
|
|
1307
1369
|
return { type: "ReturnStatement" };
|
|
1370
|
+
} else if (token.type === 108 /* KeywordMid */ && this.hasMidAssignmentAhead()) {
|
|
1371
|
+
return this.parseMidStatement();
|
|
1308
1372
|
} else if (token.type === 39 /* KeywordLSet */) {
|
|
1309
1373
|
return this.parseLSetStatement();
|
|
1310
1374
|
} else if (token.type === 40 /* KeywordRSet */) {
|
|
@@ -1319,9 +1383,9 @@ var Parser = class _Parser {
|
|
|
1319
1383
|
return this.parseResumeStatement();
|
|
1320
1384
|
} else if (token.type === 105 /* KeywordImplements */) {
|
|
1321
1385
|
return this.parseImplementsDirective();
|
|
1322
|
-
} else if (token.type === 106 /* KeywordAppActivate */ && this.peek(1).type !==
|
|
1386
|
+
} else if (token.type === 106 /* KeywordAppActivate */ && this.peek(1).type !== 121 /* OperatorEquals */) {
|
|
1323
1387
|
return this.parseAppActivateStatement();
|
|
1324
|
-
} else if (token.type === 107 /* KeywordSendKeys */ && this.peek(1).type !==
|
|
1388
|
+
} else if (token.type === 107 /* KeywordSendKeys */ && this.peek(1).type !== 121 /* OperatorEquals */) {
|
|
1325
1389
|
return this.parseSendKeysStatement();
|
|
1326
1390
|
} else if (token.type === 29 /* KeywordOption */) {
|
|
1327
1391
|
this.advance();
|
|
@@ -1344,6 +1408,8 @@ var Parser = class _Parser {
|
|
|
1344
1408
|
return { type: "OptionPrivateModuleStatement" };
|
|
1345
1409
|
}
|
|
1346
1410
|
return null;
|
|
1411
|
+
} else if (token.type === 111 /* KeywordDef */) {
|
|
1412
|
+
return this.parseDefDirective();
|
|
1347
1413
|
} else if (token.type === 74 /* KeywordAttribute */) {
|
|
1348
1414
|
return this.parseAttributeStatement();
|
|
1349
1415
|
} else if (token.type === 75 /* KeywordDeclare */) {
|
|
@@ -1360,13 +1426,13 @@ var Parser = class _Parser {
|
|
|
1360
1426
|
return this.parseOpenStatement();
|
|
1361
1427
|
} else if (token.type === 82 /* KeywordClose */) {
|
|
1362
1428
|
return this.parseCloseStatement();
|
|
1363
|
-
} else if (token.type === 83 /* KeywordLine */ && this.peek(1).type !==
|
|
1429
|
+
} else if (token.type === 83 /* KeywordLine */ && this.peek(1).type !== 121 /* OperatorEquals */) {
|
|
1364
1430
|
return this.parseLineInputStatement();
|
|
1365
1431
|
} else if (token.type === 85 /* KeywordPrint */) {
|
|
1366
1432
|
return this.parsePrintStatement();
|
|
1367
1433
|
} else if (token.type === 86 /* KeywordPut */) {
|
|
1368
1434
|
return this.parsePutStatement();
|
|
1369
|
-
} else if (token.type === 20 /* KeywordGet */ && this.peek(1).type ===
|
|
1435
|
+
} else if (token.type === 20 /* KeywordGet */ && this.peek(1).type === 112 /* OperatorHash */) {
|
|
1370
1436
|
return this.parseGetStatement();
|
|
1371
1437
|
} else if (token.type === 84 /* KeywordInput */) {
|
|
1372
1438
|
return this.parseInputStatement();
|
|
@@ -1374,9 +1440,9 @@ var Parser = class _Parser {
|
|
|
1374
1440
|
return this.parseWriteStatement();
|
|
1375
1441
|
} else if (token.type === 98 /* KeywordSeek */) {
|
|
1376
1442
|
return this.parseSeekStatement();
|
|
1377
|
-
} else if (token.type === 99 /* KeywordReset */ && this.peek(1).type !==
|
|
1443
|
+
} else if (token.type === 99 /* KeywordReset */ && this.peek(1).type !== 121 /* OperatorEquals */) {
|
|
1378
1444
|
return this.parseResetStatement();
|
|
1379
|
-
} else if (token.type === 97 /* KeywordKill */ && this.peek(1).type !==
|
|
1445
|
+
} else if (token.type === 97 /* KeywordKill */ && this.peek(1).type !== 121 /* OperatorEquals */) {
|
|
1380
1446
|
return this.parseKillStatement();
|
|
1381
1447
|
} else if (token.type === 102 /* KeywordEvent */) {
|
|
1382
1448
|
return this.parseEventDeclaration();
|
|
@@ -1386,9 +1452,9 @@ var Parser = class _Parser {
|
|
|
1386
1452
|
return this.parseLockStatement();
|
|
1387
1453
|
} else if (token.type === 100 /* KeywordUnlock */) {
|
|
1388
1454
|
return this.parseUnlockStatement();
|
|
1389
|
-
} else if (token.type === 109 /* KeywordWidth */ && this.peek(1).type ===
|
|
1455
|
+
} else if (token.type === 109 /* KeywordWidth */ && this.peek(1).type === 112 /* OperatorHash */) {
|
|
1390
1456
|
return this.parseWidthStatement();
|
|
1391
|
-
} else if (token.type === 69 /* KeywordClass */ && this.peek(1).type !==
|
|
1457
|
+
} else if (token.type === 69 /* KeywordClass */ && this.peek(1).type !== 121 /* OperatorEquals */) {
|
|
1392
1458
|
return this.parseClassDeclaration();
|
|
1393
1459
|
} else if (token.type === 33 /* KeywordCall */) {
|
|
1394
1460
|
this.advance();
|
|
@@ -1399,9 +1465,9 @@ var Parser = class _Parser {
|
|
|
1399
1465
|
return { type: "CallStatement", expression: { type: "CallExpression", callee: expr, args: [] } };
|
|
1400
1466
|
}
|
|
1401
1467
|
this.throwError(`Parse error: Expected procedure call after 'Call'`);
|
|
1402
|
-
} else if (token.type === 0 /* Identifier */ || token.type ===
|
|
1468
|
+
} else if (token.type === 0 /* Identifier */ || token.type === 139 /* ForeignName */ || token.type === 130 /* OperatorDot */ || token.type === 70 /* KeywordMe */ || token.type === 1 /* Number */ || _Parser.CONTEXTUAL_KW.has(token.type) || _Parser.COMPAT_KW_EXPR.has(token.type)) {
|
|
1403
1469
|
return this.parseIdentifierOrCallStatement();
|
|
1404
|
-
} else if (token.type ===
|
|
1470
|
+
} else if (token.type === 138 /* Unknown */) {
|
|
1405
1471
|
this.throwError(`Parse error: Unknown token '${this.tokenDisplay(token.value)}' at line ${token.line}`);
|
|
1406
1472
|
} else {
|
|
1407
1473
|
this.advance();
|
|
@@ -1432,20 +1498,21 @@ var Parser = class _Parser {
|
|
|
1432
1498
|
const name = this.makeIdentifier(idToken);
|
|
1433
1499
|
const parameters = [];
|
|
1434
1500
|
let paramsEndColumn;
|
|
1435
|
-
if (this.match(
|
|
1436
|
-
if (this.peek().type !==
|
|
1501
|
+
if (this.match(128 /* OperatorLParen */)) {
|
|
1502
|
+
if (this.peek().type !== 129 /* OperatorRParen */) {
|
|
1437
1503
|
parameters.push(this.parseParameter());
|
|
1438
|
-
while (this.match(
|
|
1504
|
+
while (this.match(127 /* OperatorComma */)) {
|
|
1439
1505
|
parameters.push(this.parseParameter());
|
|
1440
1506
|
}
|
|
1441
1507
|
}
|
|
1442
|
-
const rParen = this.consume(
|
|
1508
|
+
const rParen = this.consume(129 /* OperatorRParen */, "Expected ')' after procedure parameters");
|
|
1443
1509
|
paramsEndColumn = rParen.column + 1;
|
|
1510
|
+
this.validateParameterOrder(parameters);
|
|
1444
1511
|
}
|
|
1445
1512
|
let returnType;
|
|
1446
1513
|
if (this.match(23 /* KeywordAs */)) {
|
|
1447
1514
|
returnType = this.advance().value;
|
|
1448
|
-
if (this.peek().type ===
|
|
1515
|
+
if (this.peek().type === 130 /* OperatorDot */) {
|
|
1449
1516
|
this.advance();
|
|
1450
1517
|
returnType += "." + this.advance().value;
|
|
1451
1518
|
}
|
|
@@ -1457,7 +1524,7 @@ var Parser = class _Parser {
|
|
|
1457
1524
|
this.skipNewlines();
|
|
1458
1525
|
const body = [];
|
|
1459
1526
|
const expectedEndStr = isFunction ? "Function" : isProperty ? "Property" : "Sub";
|
|
1460
|
-
while (!this.isAtEndTerminator() && this.peek().type !==
|
|
1527
|
+
while (!this.isAtEndTerminator() && this.peek().type !== 137 /* EOF */) {
|
|
1461
1528
|
const stmt = this.parseStatement();
|
|
1462
1529
|
if (stmt) body.push(stmt);
|
|
1463
1530
|
this.skipNewlines();
|
|
@@ -1468,7 +1535,7 @@ var Parser = class _Parser {
|
|
|
1468
1535
|
if (endToken.value.toLowerCase() !== expectedEndStr.toLowerCase()) {
|
|
1469
1536
|
this.throwError(`Parse error: Expected '${expectedEndStr}' after 'End' at line ${endToken.line}`);
|
|
1470
1537
|
}
|
|
1471
|
-
} else if (this.peek().type ===
|
|
1538
|
+
} else if (this.peek().type === 137 /* EOF */) {
|
|
1472
1539
|
this.throwError(`Parse error: Expected 'End ${expectedEndStr}'`);
|
|
1473
1540
|
}
|
|
1474
1541
|
return { type: "ProcedureDeclaration", isFunction, isProperty, propertyType, name, parameters, returnType, body, scope: scope || "public", isStatic, paramsEndColumn };
|
|
@@ -1491,9 +1558,9 @@ var Parser = class _Parser {
|
|
|
1491
1558
|
let isNew = false;
|
|
1492
1559
|
let objectType;
|
|
1493
1560
|
let arrayEndColumn;
|
|
1494
|
-
if (this.match(
|
|
1561
|
+
if (this.match(128 /* OperatorLParen */)) {
|
|
1495
1562
|
isArray = true;
|
|
1496
|
-
if (this.peek().type !==
|
|
1563
|
+
if (this.peek().type !== 129 /* OperatorRParen */) {
|
|
1497
1564
|
arrayBounds = [];
|
|
1498
1565
|
while (true) {
|
|
1499
1566
|
let lower;
|
|
@@ -1503,13 +1570,13 @@ var Parser = class _Parser {
|
|
|
1503
1570
|
upper = this.parseExpression();
|
|
1504
1571
|
}
|
|
1505
1572
|
arrayBounds.push({ lower, upper });
|
|
1506
|
-
if (!this.match(
|
|
1573
|
+
if (!this.match(127 /* OperatorComma */)) {
|
|
1507
1574
|
break;
|
|
1508
1575
|
}
|
|
1509
1576
|
}
|
|
1510
1577
|
}
|
|
1511
1578
|
const rParenTok = this.peek();
|
|
1512
|
-
if (this.match(
|
|
1579
|
+
if (this.match(129 /* OperatorRParen */)) {
|
|
1513
1580
|
arrayEndColumn = rParenTok.column + 1;
|
|
1514
1581
|
}
|
|
1515
1582
|
}
|
|
@@ -1520,14 +1587,14 @@ var Parser = class _Parser {
|
|
|
1520
1587
|
const typeToken = this.peek();
|
|
1521
1588
|
if (this.isNameToken(typeToken)) {
|
|
1522
1589
|
objectType = this.advance().value;
|
|
1523
|
-
if (this.peek().type ===
|
|
1590
|
+
if (this.peek().type === 130 /* OperatorDot */) {
|
|
1524
1591
|
this.advance();
|
|
1525
1592
|
objectType += "." + this.advance().value;
|
|
1526
1593
|
}
|
|
1527
1594
|
}
|
|
1528
1595
|
}
|
|
1529
1596
|
declarations.push({ name, isArray, arrayBounds, isNew, isWithEvents, objectType, arrayEndColumn });
|
|
1530
|
-
if (this.match(
|
|
1597
|
+
if (this.match(127 /* OperatorComma */)) {
|
|
1531
1598
|
continue;
|
|
1532
1599
|
} else {
|
|
1533
1600
|
break;
|
|
@@ -1543,14 +1610,14 @@ var Parser = class _Parser {
|
|
|
1543
1610
|
if (this.match(23 /* KeywordAs */)) {
|
|
1544
1611
|
this.advance();
|
|
1545
1612
|
}
|
|
1546
|
-
if (!this.match(
|
|
1613
|
+
if (!this.match(121 /* OperatorEquals */)) this.throwError(`Parse error: Expected '=' in Const at line ${this.peek().line}`);
|
|
1547
1614
|
const value = this.parseExpression();
|
|
1548
1615
|
return { type: "ConstDeclaration", name, value };
|
|
1549
1616
|
}
|
|
1550
1617
|
parseSetStatement() {
|
|
1551
1618
|
this.advance();
|
|
1552
1619
|
const left = this.parsePrimary();
|
|
1553
|
-
if (!this.match(
|
|
1620
|
+
if (!this.match(121 /* OperatorEquals */)) this.throwError(`Parse error: Expected '=' in Set statement at line ${this.peek().line}`);
|
|
1554
1621
|
const right = this.parseExpression();
|
|
1555
1622
|
return { type: "SetStatement", left, right };
|
|
1556
1623
|
}
|
|
@@ -1562,7 +1629,7 @@ var Parser = class _Parser {
|
|
|
1562
1629
|
const labelToken = this.advance();
|
|
1563
1630
|
label = labelToken.value;
|
|
1564
1631
|
} else {
|
|
1565
|
-
while (this.peek().type !==
|
|
1632
|
+
while (this.peek().type !== 136 /* Newline */ && this.peek().type !== 137 /* EOF */) {
|
|
1566
1633
|
label += this.advance().value + " ";
|
|
1567
1634
|
}
|
|
1568
1635
|
label = label.trim();
|
|
@@ -1599,7 +1666,7 @@ var Parser = class _Parser {
|
|
|
1599
1666
|
parseResumeStatement() {
|
|
1600
1667
|
this.advance();
|
|
1601
1668
|
let target = "";
|
|
1602
|
-
while (this.peek().type !==
|
|
1669
|
+
while (this.peek().type !== 136 /* Newline */ && this.peek().type !== 137 /* EOF */) {
|
|
1603
1670
|
target += this.advance().value;
|
|
1604
1671
|
}
|
|
1605
1672
|
return { type: "ResumeStatement", target: target.trim() };
|
|
@@ -1620,8 +1687,8 @@ var Parser = class _Parser {
|
|
|
1620
1687
|
const idToken = this.advance();
|
|
1621
1688
|
const name = this.makeIdentifier(idToken);
|
|
1622
1689
|
const bounds = [];
|
|
1623
|
-
if (this.match(
|
|
1624
|
-
if (this.peek().type !==
|
|
1690
|
+
if (this.match(128 /* OperatorLParen */)) {
|
|
1691
|
+
if (this.peek().type !== 129 /* OperatorRParen */) {
|
|
1625
1692
|
while (true) {
|
|
1626
1693
|
let lower;
|
|
1627
1694
|
let upper = this.parseExpression();
|
|
@@ -1630,12 +1697,12 @@ var Parser = class _Parser {
|
|
|
1630
1697
|
upper = this.parseExpression();
|
|
1631
1698
|
}
|
|
1632
1699
|
bounds.push({ lower, upper });
|
|
1633
|
-
if (!this.match(
|
|
1700
|
+
if (!this.match(127 /* OperatorComma */)) {
|
|
1634
1701
|
break;
|
|
1635
1702
|
}
|
|
1636
1703
|
}
|
|
1637
1704
|
}
|
|
1638
|
-
this.match(
|
|
1705
|
+
this.match(129 /* OperatorRParen */);
|
|
1639
1706
|
}
|
|
1640
1707
|
let objectType;
|
|
1641
1708
|
if (this.match(23 /* KeywordAs */)) {
|
|
@@ -1652,18 +1719,18 @@ var Parser = class _Parser {
|
|
|
1652
1719
|
const typeName = nameToken.value;
|
|
1653
1720
|
const members = [];
|
|
1654
1721
|
this.skipNewlines();
|
|
1655
|
-
while (this.peek().type !== 10 /* KeywordEnd */ && this.peek().type !==
|
|
1722
|
+
while (this.peek().type !== 10 /* KeywordEnd */ && this.peek().type !== 137 /* EOF */) {
|
|
1656
1723
|
const memberNameToken = this.advance();
|
|
1657
1724
|
if (!this.isWordToken(memberNameToken)) {
|
|
1658
1725
|
this.throwError(`Parse error: Expected member name in Type at line ${memberNameToken.line}`);
|
|
1659
1726
|
}
|
|
1660
|
-
if (this.peek().type ===
|
|
1727
|
+
if (this.peek().type === 128 /* OperatorLParen */) {
|
|
1661
1728
|
this.advance();
|
|
1662
1729
|
let depth = 1;
|
|
1663
|
-
while (depth > 0 && this.peek().type !==
|
|
1730
|
+
while (depth > 0 && this.peek().type !== 137 /* EOF */ && this.peek().type !== 136 /* Newline */) {
|
|
1664
1731
|
const t = this.advance();
|
|
1665
|
-
if (t.type ===
|
|
1666
|
-
else if (t.type ===
|
|
1732
|
+
if (t.type === 128 /* OperatorLParen */) depth++;
|
|
1733
|
+
else if (t.type === 129 /* OperatorRParen */) depth--;
|
|
1667
1734
|
}
|
|
1668
1735
|
}
|
|
1669
1736
|
if (!this.match(23 /* KeywordAs */)) {
|
|
@@ -1690,14 +1757,14 @@ var Parser = class _Parser {
|
|
|
1690
1757
|
const name = this.makeIdentifier(nameToken);
|
|
1691
1758
|
const members = [];
|
|
1692
1759
|
this.skipNewlines();
|
|
1693
|
-
while (this.peek().type !== 10 /* KeywordEnd */ && this.peek().type !==
|
|
1760
|
+
while (this.peek().type !== 10 /* KeywordEnd */ && this.peek().type !== 137 /* EOF */) {
|
|
1694
1761
|
const memberNameToken = this.advance();
|
|
1695
1762
|
if (!this.isWordToken(memberNameToken)) {
|
|
1696
1763
|
this.throwError(`Parse error: Expected member name in Enum at line ${memberNameToken.line}`);
|
|
1697
1764
|
}
|
|
1698
1765
|
const memberName = this.makeIdentifier(memberNameToken);
|
|
1699
1766
|
let value;
|
|
1700
|
-
if (this.match(
|
|
1767
|
+
if (this.match(121 /* OperatorEquals */)) {
|
|
1701
1768
|
value = this.parseExpression();
|
|
1702
1769
|
}
|
|
1703
1770
|
members.push({ name: memberName, value });
|
|
@@ -1724,12 +1791,12 @@ var Parser = class _Parser {
|
|
|
1724
1791
|
const procedures = [];
|
|
1725
1792
|
const body = [];
|
|
1726
1793
|
this.skipNewlines();
|
|
1727
|
-
while (this.peek().type !==
|
|
1794
|
+
while (this.peek().type !== 137 /* EOF */) {
|
|
1728
1795
|
if (untilEndClass && this.peek().type === 10 /* KeywordEnd */ && this.peek(1).type === 69 /* KeywordClass */) {
|
|
1729
1796
|
break;
|
|
1730
1797
|
}
|
|
1731
1798
|
const tok = this.peek();
|
|
1732
|
-
if (tok.type ===
|
|
1799
|
+
if (tok.type === 136 /* Newline */) {
|
|
1733
1800
|
this.skipNewlines();
|
|
1734
1801
|
continue;
|
|
1735
1802
|
}
|
|
@@ -1793,7 +1860,7 @@ var Parser = class _Parser {
|
|
|
1793
1860
|
this.throwError(`Parse error: Expected identifier after 'For' at line ${idToken.line} `);
|
|
1794
1861
|
}
|
|
1795
1862
|
const identifier = this.makeIdentifier(idToken);
|
|
1796
|
-
if (!this.match(
|
|
1863
|
+
if (!this.match(121 /* OperatorEquals */)) {
|
|
1797
1864
|
this.throwError(`Parse error: Expected '=' in For statement at line ${this.peek().line} `);
|
|
1798
1865
|
}
|
|
1799
1866
|
const startExpr = this.parseExpression();
|
|
@@ -1807,7 +1874,7 @@ var Parser = class _Parser {
|
|
|
1807
1874
|
}
|
|
1808
1875
|
this.skipNewlines();
|
|
1809
1876
|
const body = [];
|
|
1810
|
-
while (this.peek().type !== 5 /* KeywordNext */ && this.peek().type !==
|
|
1877
|
+
while (this.peek().type !== 5 /* KeywordNext */ && this.peek().type !== 137 /* EOF */ && !this.isAtEndTerminator()) {
|
|
1811
1878
|
const stmt = this.parseStatement();
|
|
1812
1879
|
if (stmt) body.push(stmt);
|
|
1813
1880
|
this.skipNewlines();
|
|
@@ -1849,7 +1916,7 @@ var Parser = class _Parser {
|
|
|
1849
1916
|
const collection = this.parseExpression();
|
|
1850
1917
|
this.skipNewlines();
|
|
1851
1918
|
const body = [];
|
|
1852
|
-
while (this.peek().type !== 5 /* KeywordNext */ && this.peek().type !==
|
|
1919
|
+
while (this.peek().type !== 5 /* KeywordNext */ && this.peek().type !== 137 /* EOF */ && !this.isAtEndTerminator()) {
|
|
1853
1920
|
const stmt = this.parseStatement();
|
|
1854
1921
|
if (stmt) body.push(stmt);
|
|
1855
1922
|
this.skipNewlines();
|
|
@@ -1882,18 +1949,18 @@ var Parser = class _Parser {
|
|
|
1882
1949
|
if (!this.match(7 /* KeywordThen */)) {
|
|
1883
1950
|
this.throwError(`Parse error: Expected 'Then' after condition at line ${this.peek().line}`);
|
|
1884
1951
|
}
|
|
1885
|
-
const isMultiLine = this.peek().type ===
|
|
1952
|
+
const isMultiLine = this.peek().type === 136 /* Newline */;
|
|
1886
1953
|
const consequent = [];
|
|
1887
1954
|
let alternate = null;
|
|
1888
1955
|
if (!isMultiLine) {
|
|
1889
|
-
while (this.peek().type !==
|
|
1956
|
+
while (this.peek().type !== 136 /* Newline */ && this.peek().type !== 137 /* EOF */ && this.peek().type !== 9 /* KeywordElse */) {
|
|
1890
1957
|
const stmt = this.parseStatement(false);
|
|
1891
1958
|
if (stmt) consequent.push(stmt);
|
|
1892
1959
|
}
|
|
1893
1960
|
if (this.peek().type === 9 /* KeywordElse */) {
|
|
1894
1961
|
this.advance();
|
|
1895
1962
|
alternate = [];
|
|
1896
|
-
while (this.peek().type !==
|
|
1963
|
+
while (this.peek().type !== 136 /* Newline */ && this.peek().type !== 137 /* EOF */) {
|
|
1897
1964
|
const stmt = this.parseStatement(false);
|
|
1898
1965
|
if (stmt) alternate.push(stmt);
|
|
1899
1966
|
}
|
|
@@ -1906,7 +1973,7 @@ var Parser = class _Parser {
|
|
|
1906
1973
|
};
|
|
1907
1974
|
}
|
|
1908
1975
|
this.skipNewlines();
|
|
1909
|
-
while (!this.isAtEndTerminator() && this.peek().type !== 9 /* KeywordElse */ && this.peek().type !== 8 /* KeywordElseIf */ && this.peek().type !==
|
|
1976
|
+
while (!this.isAtEndTerminator() && this.peek().type !== 9 /* KeywordElse */ && this.peek().type !== 8 /* KeywordElseIf */ && this.peek().type !== 137 /* EOF */) {
|
|
1910
1977
|
const stmt = this.parseStatement();
|
|
1911
1978
|
if (stmt) consequent.push(stmt);
|
|
1912
1979
|
this.skipNewlines();
|
|
@@ -1916,7 +1983,7 @@ var Parser = class _Parser {
|
|
|
1916
1983
|
} else if (this.match(9 /* KeywordElse */)) {
|
|
1917
1984
|
this.skipNewlines();
|
|
1918
1985
|
alternate = [];
|
|
1919
|
-
while (!this.isAtEndTerminator() && this.peek().type !==
|
|
1986
|
+
while (!this.isAtEndTerminator() && this.peek().type !== 137 /* EOF */) {
|
|
1920
1987
|
const stmt = this.parseStatement();
|
|
1921
1988
|
if (stmt) alternate.push(stmt);
|
|
1922
1989
|
this.skipNewlines();
|
|
@@ -1953,7 +2020,7 @@ var Parser = class _Parser {
|
|
|
1953
2020
|
}
|
|
1954
2021
|
this.skipNewlines();
|
|
1955
2022
|
const body = [];
|
|
1956
|
-
while (this.peek().type !== 14 /* KeywordLoop */ && this.peek().type !==
|
|
2023
|
+
while (this.peek().type !== 14 /* KeywordLoop */ && this.peek().type !== 137 /* EOF */ && !this.isAtEndTerminator()) {
|
|
1957
2024
|
const stmt = this.parseStatement();
|
|
1958
2025
|
if (stmt) body.push(stmt);
|
|
1959
2026
|
this.skipNewlines();
|
|
@@ -1985,7 +2052,7 @@ var Parser = class _Parser {
|
|
|
1985
2052
|
const condition = this.parseExpression();
|
|
1986
2053
|
this.skipNewlines();
|
|
1987
2054
|
const body = [];
|
|
1988
|
-
while (this.peek().type !== 13 /* KeywordWend */ && this.peek().type !==
|
|
2055
|
+
while (this.peek().type !== 13 /* KeywordWend */ && this.peek().type !== 137 /* EOF */ && !this.isAtEndTerminator()) {
|
|
1989
2056
|
const stmt = this.parseStatement();
|
|
1990
2057
|
if (stmt) body.push(stmt);
|
|
1991
2058
|
this.skipNewlines();
|
|
@@ -2004,7 +2071,7 @@ var Parser = class _Parser {
|
|
|
2004
2071
|
this.skipNewlines();
|
|
2005
2072
|
const cases = [];
|
|
2006
2073
|
let elseBody = null;
|
|
2007
|
-
while (!this.isAtEndTerminator() && this.peek().type !==
|
|
2074
|
+
while (!this.isAtEndTerminator() && this.peek().type !== 137 /* EOF */) {
|
|
2008
2075
|
if (this.peek().type !== 54 /* KeywordCase */) {
|
|
2009
2076
|
this.throwError(`Parse error: Expected 'Case' in Select Case at line ${this.peek().line}`);
|
|
2010
2077
|
}
|
|
@@ -2013,7 +2080,7 @@ var Parser = class _Parser {
|
|
|
2013
2080
|
this.advance();
|
|
2014
2081
|
this.skipNewlines();
|
|
2015
2082
|
elseBody = [];
|
|
2016
|
-
while (!this.isAtEndTerminator() && this.peek().type !== 54 /* KeywordCase */ && this.peek().type !==
|
|
2083
|
+
while (!this.isAtEndTerminator() && this.peek().type !== 54 /* KeywordCase */ && this.peek().type !== 137 /* EOF */) {
|
|
2017
2084
|
const stmt = this.parseStatement();
|
|
2018
2085
|
if (stmt) elseBody.push(stmt);
|
|
2019
2086
|
this.skipNewlines();
|
|
@@ -2022,12 +2089,12 @@ var Parser = class _Parser {
|
|
|
2022
2089
|
}
|
|
2023
2090
|
const ranges = [];
|
|
2024
2091
|
ranges.push(this.parseRangeClause());
|
|
2025
|
-
while (this.match(
|
|
2092
|
+
while (this.match(127 /* OperatorComma */)) {
|
|
2026
2093
|
ranges.push(this.parseRangeClause());
|
|
2027
2094
|
}
|
|
2028
2095
|
this.skipNewlines();
|
|
2029
2096
|
const body = [];
|
|
2030
|
-
while (!this.isAtEndTerminator() && this.peek().type !== 54 /* KeywordCase */ && this.peek().type !==
|
|
2097
|
+
while (!this.isAtEndTerminator() && this.peek().type !== 54 /* KeywordCase */ && this.peek().type !== 137 /* EOF */) {
|
|
2031
2098
|
const stmt = this.parseStatement();
|
|
2032
2099
|
if (stmt) body.push(stmt);
|
|
2033
2100
|
this.skipNewlines();
|
|
@@ -2047,7 +2114,7 @@ var Parser = class _Parser {
|
|
|
2047
2114
|
const expression = this.parseExpression();
|
|
2048
2115
|
this.skipNewlines();
|
|
2049
2116
|
const body = [];
|
|
2050
|
-
while (!this.isAtEndTerminator() && this.peek().type !==
|
|
2117
|
+
while (!this.isAtEndTerminator() && this.peek().type !== 137 /* EOF */) {
|
|
2051
2118
|
const stmt = this.parseStatement();
|
|
2052
2119
|
if (stmt) body.push(stmt);
|
|
2053
2120
|
this.skipNewlines();
|
|
@@ -2064,7 +2131,7 @@ var Parser = class _Parser {
|
|
|
2064
2131
|
const isKeyword = this.peek().type === 51 /* KeywordIs */;
|
|
2065
2132
|
if (isKeyword) this.advance();
|
|
2066
2133
|
const compOp = this.peek();
|
|
2067
|
-
if (compOp.type ===
|
|
2134
|
+
if (compOp.type === 121 /* OperatorEquals */ || compOp.type === 122 /* OperatorNotEquals */ || compOp.type === 123 /* OperatorLessThan */ || compOp.type === 124 /* OperatorGreaterThan */ || compOp.type === 125 /* OperatorLessThanOrEqual */ || compOp.type === 126 /* OperatorGreaterThanOrEqual */) {
|
|
2068
2135
|
this.advance();
|
|
2069
2136
|
const value = this.parseExpression();
|
|
2070
2137
|
return { kind: "comparison", operator: compOp.value, value };
|
|
@@ -2144,7 +2211,7 @@ var Parser = class _Parser {
|
|
|
2144
2211
|
}
|
|
2145
2212
|
parseEquality() {
|
|
2146
2213
|
let left = this.parseRelational();
|
|
2147
|
-
while (this.peek().type ===
|
|
2214
|
+
while (this.peek().type === 121 /* OperatorEquals */ || this.peek().type === 122 /* OperatorNotEquals */ || this.peek().type === 51 /* KeywordIs */ || this.peek().type === 63 /* KeywordLike */) {
|
|
2148
2215
|
const operator = this.advance().value;
|
|
2149
2216
|
const right = this.parseRelational();
|
|
2150
2217
|
left = { type: "BinaryExpression", operator, left, right, loc: this.makeBinaryLoc(left, right) };
|
|
@@ -2153,7 +2220,7 @@ var Parser = class _Parser {
|
|
|
2153
2220
|
}
|
|
2154
2221
|
parseRelational() {
|
|
2155
2222
|
let left = this.parseConcatenation();
|
|
2156
|
-
while (this.peek().type ===
|
|
2223
|
+
while (this.peek().type === 123 /* OperatorLessThan */ || this.peek().type === 124 /* OperatorGreaterThan */ || this.peek().type === 125 /* OperatorLessThanOrEqual */ || this.peek().type === 126 /* OperatorGreaterThanOrEqual */) {
|
|
2157
2224
|
const operator = this.advance().value;
|
|
2158
2225
|
const right = this.parseConcatenation();
|
|
2159
2226
|
left = { type: "BinaryExpression", operator, left, right, loc: this.makeBinaryLoc(left, right) };
|
|
@@ -2162,7 +2229,7 @@ var Parser = class _Parser {
|
|
|
2162
2229
|
}
|
|
2163
2230
|
parseConcatenation() {
|
|
2164
2231
|
let left = this.parseAdditive();
|
|
2165
|
-
while (this.peek().type ===
|
|
2232
|
+
while (this.peek().type === 118 /* OperatorAmpersand */) {
|
|
2166
2233
|
const operator = this.advance().value;
|
|
2167
2234
|
const right = this.parseAdditive();
|
|
2168
2235
|
left = { type: "BinaryExpression", operator, left, right, loc: this.makeBinaryLoc(left, right) };
|
|
@@ -2171,7 +2238,7 @@ var Parser = class _Parser {
|
|
|
2171
2238
|
}
|
|
2172
2239
|
parseAdditive() {
|
|
2173
2240
|
let left = this.parseModulo();
|
|
2174
|
-
while (this.peek().type ===
|
|
2241
|
+
while (this.peek().type === 113 /* OperatorPlus */ || this.peek().type === 114 /* OperatorMinus */) {
|
|
2175
2242
|
const operator = this.advance().value;
|
|
2176
2243
|
const right = this.parseModulo();
|
|
2177
2244
|
left = { type: "BinaryExpression", operator, left, right, loc: this.makeBinaryLoc(left, right) };
|
|
@@ -2180,7 +2247,7 @@ var Parser = class _Parser {
|
|
|
2180
2247
|
}
|
|
2181
2248
|
parseModulo() {
|
|
2182
2249
|
let left = this.parseIntDivision();
|
|
2183
|
-
while (this.peek().type ===
|
|
2250
|
+
while (this.peek().type === 119 /* KeywordMod */) {
|
|
2184
2251
|
const operator = this.advance().value;
|
|
2185
2252
|
const right = this.parseIntDivision();
|
|
2186
2253
|
left = { type: "BinaryExpression", operator, left, right, loc: this.makeBinaryLoc(left, right) };
|
|
@@ -2189,7 +2256,7 @@ var Parser = class _Parser {
|
|
|
2189
2256
|
}
|
|
2190
2257
|
parseIntDivision() {
|
|
2191
2258
|
let left = this.parseMultiplicative();
|
|
2192
|
-
while (this.peek().type ===
|
|
2259
|
+
while (this.peek().type === 117 /* OperatorIntDivide */) {
|
|
2193
2260
|
const operator = this.advance().value;
|
|
2194
2261
|
const right = this.parseMultiplicative();
|
|
2195
2262
|
left = { type: "BinaryExpression", operator, left, right, loc: this.makeBinaryLoc(left, right) };
|
|
@@ -2198,7 +2265,7 @@ var Parser = class _Parser {
|
|
|
2198
2265
|
}
|
|
2199
2266
|
parseMultiplicative() {
|
|
2200
2267
|
let left = this.parseUnary();
|
|
2201
|
-
while (this.peek().type ===
|
|
2268
|
+
while (this.peek().type === 115 /* OperatorMultiply */ || this.peek().type === 116 /* OperatorDivide */) {
|
|
2202
2269
|
const operator = this.advance().value;
|
|
2203
2270
|
const right = this.parseUnary();
|
|
2204
2271
|
left = { type: "BinaryExpression", operator, left, right, loc: this.makeBinaryLoc(left, right) };
|
|
@@ -2206,7 +2273,7 @@ var Parser = class _Parser {
|
|
|
2206
2273
|
return left;
|
|
2207
2274
|
}
|
|
2208
2275
|
parseUnary() {
|
|
2209
|
-
if (this.peek().type ===
|
|
2276
|
+
if (this.peek().type === 114 /* OperatorMinus */ || this.peek().type === 113 /* OperatorPlus */) {
|
|
2210
2277
|
const opTok = this.tokens[this.pos];
|
|
2211
2278
|
const operator = this.advance().value;
|
|
2212
2279
|
const argument = this.parseUnary();
|
|
@@ -2218,7 +2285,7 @@ var Parser = class _Parser {
|
|
|
2218
2285
|
}
|
|
2219
2286
|
parseExponentiation() {
|
|
2220
2287
|
let left = this.parsePrimary();
|
|
2221
|
-
while (this.peek().type ===
|
|
2288
|
+
while (this.peek().type === 120 /* OperatorPower */) {
|
|
2222
2289
|
const operator = this.advance().value;
|
|
2223
2290
|
const right = this.parsePrimary();
|
|
2224
2291
|
left = { type: "BinaryExpression", operator, left, right, loc: this.makeBinaryLoc(left, right) };
|
|
@@ -2237,16 +2304,24 @@ var Parser = class _Parser {
|
|
|
2237
2304
|
expr = { type: "NumberLiteral", value: Number(cleanVal), typeSuffix, isFloat };
|
|
2238
2305
|
} else if (token.type === 2 /* String */) {
|
|
2239
2306
|
expr = { type: "StringLiteral", value: token.value };
|
|
2240
|
-
} else if (token.type ===
|
|
2307
|
+
} else if (token.type === 135 /* Date */) {
|
|
2241
2308
|
expr = { type: "DateLiteral", value: token.value };
|
|
2242
|
-
} else if (token.type ===
|
|
2309
|
+
} else if (token.type === 139 /* ForeignName */) {
|
|
2243
2310
|
expr = { type: "Identifier", name: token.value, foreign: true };
|
|
2244
2311
|
} else if (token.type === 0 /* Identifier */ || _Parser.CONTEXTUAL_KW.has(token.type) || _Parser.COMPAT_KW_EXPR.has(token.type)) {
|
|
2245
2312
|
expr = { type: "Identifier", name: token.value };
|
|
2246
2313
|
} else if (token.type === 110 /* KeywordAddressOf */) {
|
|
2247
|
-
const
|
|
2248
|
-
if (!this.isIdentifier(
|
|
2249
|
-
|
|
2314
|
+
const firstTok = this.advance();
|
|
2315
|
+
if (!this.isIdentifier(firstTok)) this.throwError(`Parse error at line ${firstTok.line}: Expected procedure name after 'AddressOf'`);
|
|
2316
|
+
let moduleName;
|
|
2317
|
+
let procTok = firstTok;
|
|
2318
|
+
if (this.peek().type === 130 /* OperatorDot */) {
|
|
2319
|
+
this.advance();
|
|
2320
|
+
moduleName = firstTok.value;
|
|
2321
|
+
procTok = this.advance();
|
|
2322
|
+
if (!this.isIdentifier(procTok)) this.throwError(`Parse error at line ${procTok.line}: Expected procedure name after 'AddressOf ${moduleName}.'`);
|
|
2323
|
+
}
|
|
2324
|
+
expr = { type: "AddressOfExpression", procedureName: { type: "Identifier", name: procTok.value }, moduleName };
|
|
2250
2325
|
} else if (token.type === 44 /* KeywordEmpty */) {
|
|
2251
2326
|
expr = { type: "Identifier", name: token.value };
|
|
2252
2327
|
} else if (token.type === 49 /* KeywordNothing */) {
|
|
@@ -2263,14 +2338,14 @@ var Parser = class _Parser {
|
|
|
2263
2338
|
this.throwError(`Parse error: Expected class name after 'New' at line ${classNameToken.line}`);
|
|
2264
2339
|
}
|
|
2265
2340
|
let className = classNameToken.value;
|
|
2266
|
-
if (this.peek().type ===
|
|
2341
|
+
if (this.peek().type === 130 /* OperatorDot */) {
|
|
2267
2342
|
this.advance();
|
|
2268
2343
|
className += "." + this.advance().value;
|
|
2269
2344
|
}
|
|
2270
2345
|
expr = { type: "NewExpression", className };
|
|
2271
|
-
} else if (token.type ===
|
|
2346
|
+
} else if (token.type === 128 /* OperatorLParen */) {
|
|
2272
2347
|
const innerExpr = this.parseExpression();
|
|
2273
|
-
if (!this.match(
|
|
2348
|
+
if (!this.match(129 /* OperatorRParen */)) {
|
|
2274
2349
|
this.throwMissingRParen();
|
|
2275
2350
|
}
|
|
2276
2351
|
expr = { type: "ParenthesizedExpression", expression: innerExpr };
|
|
@@ -2284,14 +2359,14 @@ var Parser = class _Parser {
|
|
|
2284
2359
|
this.throwError(`Parse error: Expected type name after 'Is' at line ${typeToken.line}`);
|
|
2285
2360
|
}
|
|
2286
2361
|
expr = { type: "TypeOfIsExpression", expression: expr, typeName: typeToken.value };
|
|
2287
|
-
} else if (token.type ===
|
|
2362
|
+
} else if (token.type === 130 /* OperatorDot */) {
|
|
2288
2363
|
const propToken = this.advance();
|
|
2289
2364
|
if (!this.isNameToken(propToken)) {
|
|
2290
2365
|
this.throwError(`Parse error: Expected identifier after '.' at line ${propToken.line}`);
|
|
2291
2366
|
}
|
|
2292
2367
|
const property = { type: "Identifier", name: propToken.value };
|
|
2293
2368
|
expr = { type: "ImplicitWithObjectExpression", property };
|
|
2294
|
-
} else if (token.type ===
|
|
2369
|
+
} else if (token.type === 136 /* Newline */) {
|
|
2295
2370
|
const prevToken = this.tokens[Math.max(0, this.pos - 2)];
|
|
2296
2371
|
if (prevToken && this.isContinuationEndToken(prevToken.type)) {
|
|
2297
2372
|
this.throwError(
|
|
@@ -2306,31 +2381,31 @@ var Parser = class _Parser {
|
|
|
2306
2381
|
}
|
|
2307
2382
|
expr.loc = this.exprLoc(startTok, this.tokens[this.pos - 1]);
|
|
2308
2383
|
while (true) {
|
|
2309
|
-
if (this.match(
|
|
2310
|
-
if (this.peek().type ===
|
|
2384
|
+
if (this.match(130 /* OperatorDot */)) {
|
|
2385
|
+
if (this.peek().type === 137 /* EOF */) this.throwError("Expected property name after '.'");
|
|
2311
2386
|
const propToken = this.advance();
|
|
2312
2387
|
const property = { type: "Identifier", name: propToken.value };
|
|
2313
2388
|
expr = { type: "MemberExpression", object: expr, property };
|
|
2314
2389
|
expr.loc = this.exprLoc(startTok, this.tokens[this.pos - 1]);
|
|
2315
|
-
} else if (this.match(
|
|
2316
|
-
if (this.peek().type ===
|
|
2390
|
+
} else if (this.match(133 /* OperatorExclamation */)) {
|
|
2391
|
+
if (this.peek().type === 137 /* EOF */) this.throwError("Expected identifier after '!'");
|
|
2317
2392
|
const propToken = this.advance();
|
|
2318
2393
|
const property = { type: "Identifier", name: propToken.value };
|
|
2319
2394
|
expr = { type: "DictionaryAccessExpression", object: expr, property };
|
|
2320
2395
|
expr.loc = this.exprLoc(startTok, this.tokens[this.pos - 1]);
|
|
2321
|
-
} else if (this.peek().type ===
|
|
2396
|
+
} else if (this.peek().type === 128 /* OperatorLParen */) {
|
|
2322
2397
|
if (stopBeforeSpacedLParen && this.hasSpaceBeforeCurrentToken()) {
|
|
2323
2398
|
break;
|
|
2324
2399
|
}
|
|
2325
2400
|
this.advance();
|
|
2326
2401
|
const args = [];
|
|
2327
|
-
if (this.peek().type !==
|
|
2402
|
+
if (this.peek().type !== 129 /* OperatorRParen */) {
|
|
2328
2403
|
args.push(this.parseCallArgument());
|
|
2329
|
-
while (this.match(
|
|
2404
|
+
while (this.match(127 /* OperatorComma */)) {
|
|
2330
2405
|
args.push(this.parseCallArgument());
|
|
2331
2406
|
}
|
|
2332
2407
|
}
|
|
2333
|
-
if (!this.match(
|
|
2408
|
+
if (!this.match(129 /* OperatorRParen */)) {
|
|
2334
2409
|
this.throwMissingRParen();
|
|
2335
2410
|
}
|
|
2336
2411
|
expr = { type: "CallExpression", callee: expr, args };
|
|
@@ -2344,10 +2419,10 @@ var Parser = class _Parser {
|
|
|
2344
2419
|
// Parse a call argument, handling named arguments (e.g., shift:=xlUp)
|
|
2345
2420
|
parseCallArgument() {
|
|
2346
2421
|
const next = this.peek().type;
|
|
2347
|
-
if (next ===
|
|
2422
|
+
if (next === 127 /* OperatorComma */ || next === 129 /* OperatorRParen */ || this.isAtTerminator()) {
|
|
2348
2423
|
return { type: "MissingArgument" };
|
|
2349
2424
|
}
|
|
2350
|
-
if (this.pos + 1 < this.tokens.length && this.tokens[this.pos + 1].type ===
|
|
2425
|
+
if (this.pos + 1 < this.tokens.length && this.tokens[this.pos + 1].type === 132 /* OperatorColonEquals */ && typeof this.peek().value === "string" && /^[A-Za-z_]\w*$/.test(this.peek().value)) {
|
|
2351
2426
|
const nameToken = this.advance();
|
|
2352
2427
|
this.advance();
|
|
2353
2428
|
const value = this.parseExpression();
|
|
@@ -2373,7 +2448,7 @@ var Parser = class _Parser {
|
|
|
2373
2448
|
this.throwError(`Parse error: Expected label (identifier or number) in On...GoTo/GoSub at line ${labelToken.line}`);
|
|
2374
2449
|
}
|
|
2375
2450
|
labels.push(labelToken.value);
|
|
2376
|
-
if (!this.match(
|
|
2451
|
+
if (!this.match(127 /* OperatorComma */)) {
|
|
2377
2452
|
break;
|
|
2378
2453
|
}
|
|
2379
2454
|
}
|
|
@@ -2390,7 +2465,7 @@ var Parser = class _Parser {
|
|
|
2390
2465
|
parseLSetStatement() {
|
|
2391
2466
|
this.advance();
|
|
2392
2467
|
const left = this.parsePrimary();
|
|
2393
|
-
if (!this.match(
|
|
2468
|
+
if (!this.match(121 /* OperatorEquals */)) {
|
|
2394
2469
|
this.throwError(`Parse error: Expected '=' in LSet statement at line ${this.peek().line}`);
|
|
2395
2470
|
}
|
|
2396
2471
|
const right = this.parseExpression();
|
|
@@ -2399,12 +2474,45 @@ var Parser = class _Parser {
|
|
|
2399
2474
|
parseRSetStatement() {
|
|
2400
2475
|
this.advance();
|
|
2401
2476
|
const left = this.parsePrimary();
|
|
2402
|
-
if (!this.match(
|
|
2477
|
+
if (!this.match(121 /* OperatorEquals */)) {
|
|
2403
2478
|
this.throwError(`Parse error: Expected '=' in RSet statement at line ${this.peek().line}`);
|
|
2404
2479
|
}
|
|
2405
2480
|
const right = this.parseExpression();
|
|
2406
2481
|
return { type: "RSetStatement", left, right };
|
|
2407
2482
|
}
|
|
2483
|
+
hasMidAssignmentAhead() {
|
|
2484
|
+
if (this.peek(1).type !== 128 /* OperatorLParen */) return false;
|
|
2485
|
+
let depth = 0;
|
|
2486
|
+
let i = 1;
|
|
2487
|
+
while (this.pos + i < this.tokens.length) {
|
|
2488
|
+
const t = this.tokens[this.pos + i];
|
|
2489
|
+
if (t.type === 128 /* OperatorLParen */) depth++;
|
|
2490
|
+
else if (t.type === 129 /* OperatorRParen */) {
|
|
2491
|
+
depth--;
|
|
2492
|
+
if (depth === 0) {
|
|
2493
|
+
return this.tokens[this.pos + i + 1]?.type === 121 /* OperatorEquals */;
|
|
2494
|
+
}
|
|
2495
|
+
} else if (t.type === 136 /* Newline */ || t.type === 137 /* EOF */) break;
|
|
2496
|
+
i++;
|
|
2497
|
+
}
|
|
2498
|
+
return false;
|
|
2499
|
+
}
|
|
2500
|
+
parseMidStatement() {
|
|
2501
|
+
const midToken = this.advance();
|
|
2502
|
+
const isByte = midToken.value.toLowerCase().startsWith("midb");
|
|
2503
|
+
this.consume(128 /* OperatorLParen */, "Expected '(' after Mid");
|
|
2504
|
+
const target = this.parseExpression();
|
|
2505
|
+
this.consume(127 /* OperatorComma */, "Expected ',' after Mid target");
|
|
2506
|
+
const start = this.parseExpression();
|
|
2507
|
+
let length = null;
|
|
2508
|
+
if (this.match(127 /* OperatorComma */)) {
|
|
2509
|
+
length = this.parseExpression();
|
|
2510
|
+
}
|
|
2511
|
+
this.consume(129 /* OperatorRParen */, "Expected ')' after Mid arguments");
|
|
2512
|
+
this.consume(121 /* OperatorEquals */, "Expected '=' in Mid statement");
|
|
2513
|
+
const value = this.parseExpression();
|
|
2514
|
+
return { type: "MidStatement", target, start, length, value, isByte };
|
|
2515
|
+
}
|
|
2408
2516
|
parseErrorStatement() {
|
|
2409
2517
|
this.advance();
|
|
2410
2518
|
const errorNumber = this.parseExpression();
|
|
@@ -2416,14 +2524,15 @@ var Parser = class _Parser {
|
|
|
2416
2524
|
if (!this.isIdentifier(idToken) && !this.isNameToken(idToken)) this.throwError(`Expected identifier after 'Event' at line ${idToken.line}`);
|
|
2417
2525
|
const name = { type: "Identifier", name: idToken.value };
|
|
2418
2526
|
const parameters = [];
|
|
2419
|
-
if (this.match(
|
|
2420
|
-
if (this.peek().type !==
|
|
2527
|
+
if (this.match(128 /* OperatorLParen */)) {
|
|
2528
|
+
if (this.peek().type !== 129 /* OperatorRParen */) {
|
|
2421
2529
|
parameters.push(this.parseParameter());
|
|
2422
|
-
while (this.match(
|
|
2530
|
+
while (this.match(127 /* OperatorComma */)) {
|
|
2423
2531
|
parameters.push(this.parseParameter());
|
|
2424
2532
|
}
|
|
2425
2533
|
}
|
|
2426
|
-
this.consume(
|
|
2534
|
+
this.consume(129 /* OperatorRParen */, "Expected ')' after event parameters");
|
|
2535
|
+
this.validateParameterOrder(parameters);
|
|
2427
2536
|
}
|
|
2428
2537
|
return { type: "EventDeclaration", name, parameters, scope };
|
|
2429
2538
|
}
|
|
@@ -2433,14 +2542,14 @@ var Parser = class _Parser {
|
|
|
2433
2542
|
if (!this.isIdentifier(idToken) && !this.isNameToken(idToken)) this.throwError(`Expected identifier after 'RaiseEvent' at line ${idToken.line}`);
|
|
2434
2543
|
const eventName = { type: "Identifier", name: idToken.value };
|
|
2435
2544
|
const args = [];
|
|
2436
|
-
if (this.match(
|
|
2437
|
-
if (this.peek().type !==
|
|
2545
|
+
if (this.match(128 /* OperatorLParen */)) {
|
|
2546
|
+
if (this.peek().type !== 129 /* OperatorRParen */) {
|
|
2438
2547
|
args.push(this.parseExpression());
|
|
2439
|
-
while (this.match(
|
|
2548
|
+
while (this.match(127 /* OperatorComma */)) {
|
|
2440
2549
|
args.push(this.parseExpression());
|
|
2441
2550
|
}
|
|
2442
2551
|
}
|
|
2443
|
-
this.consume(
|
|
2552
|
+
this.consume(129 /* OperatorRParen */, "Expected ')' after RaiseEvent arguments");
|
|
2444
2553
|
}
|
|
2445
2554
|
return { type: "RaiseEventStatement", eventName, args };
|
|
2446
2555
|
}
|
|
@@ -2454,7 +2563,7 @@ var Parser = class _Parser {
|
|
|
2454
2563
|
this.advance();
|
|
2455
2564
|
const title = this.parseExpression();
|
|
2456
2565
|
let wait;
|
|
2457
|
-
if (this.match(
|
|
2566
|
+
if (this.match(127 /* OperatorComma */)) {
|
|
2458
2567
|
wait = this.parseExpression();
|
|
2459
2568
|
}
|
|
2460
2569
|
return { type: "AppActivateStatement", title, wait };
|
|
@@ -2463,17 +2572,17 @@ var Parser = class _Parser {
|
|
|
2463
2572
|
this.advance();
|
|
2464
2573
|
const keys = this.parseExpression();
|
|
2465
2574
|
let wait;
|
|
2466
|
-
if (this.match(
|
|
2575
|
+
if (this.match(127 /* OperatorComma */)) {
|
|
2467
2576
|
wait = this.parseExpression();
|
|
2468
2577
|
}
|
|
2469
2578
|
return { type: "SendKeysStatement", keys, wait };
|
|
2470
2579
|
}
|
|
2471
2580
|
parseLockStatement() {
|
|
2472
2581
|
this.advance();
|
|
2473
|
-
this.match(
|
|
2582
|
+
this.match(112 /* OperatorHash */);
|
|
2474
2583
|
const fileNumber = this.parseExpression();
|
|
2475
2584
|
let recordRange;
|
|
2476
|
-
if (this.match(
|
|
2585
|
+
if (this.match(127 /* OperatorComma */)) {
|
|
2477
2586
|
const start = this.parseExpression();
|
|
2478
2587
|
let end;
|
|
2479
2588
|
if (this.match(4 /* KeywordTo */)) {
|
|
@@ -2485,10 +2594,10 @@ var Parser = class _Parser {
|
|
|
2485
2594
|
}
|
|
2486
2595
|
parseUnlockStatement() {
|
|
2487
2596
|
this.advance();
|
|
2488
|
-
this.match(
|
|
2597
|
+
this.match(112 /* OperatorHash */);
|
|
2489
2598
|
const fileNumber = this.parseExpression();
|
|
2490
2599
|
let recordRange;
|
|
2491
|
-
if (this.match(
|
|
2600
|
+
if (this.match(127 /* OperatorComma */)) {
|
|
2492
2601
|
const start = this.parseExpression();
|
|
2493
2602
|
let end;
|
|
2494
2603
|
if (this.match(4 /* KeywordTo */)) {
|
|
@@ -2500,9 +2609,9 @@ var Parser = class _Parser {
|
|
|
2500
2609
|
}
|
|
2501
2610
|
parseWidthStatement() {
|
|
2502
2611
|
this.advance();
|
|
2503
|
-
this.consume(
|
|
2612
|
+
this.consume(112 /* OperatorHash */, "Expected '#' after Width");
|
|
2504
2613
|
const fileNumber = this.parseExpression();
|
|
2505
|
-
this.consume(
|
|
2614
|
+
this.consume(127 /* OperatorComma */, "Expected ',' after file number");
|
|
2506
2615
|
const width = this.parseExpression();
|
|
2507
2616
|
return { type: "WidthStatement", fileNumber, width };
|
|
2508
2617
|
}
|
|
@@ -3787,6 +3896,18 @@ function collectVarRefsInBody(stmts, writes, reads, skipVars) {
|
|
|
3787
3896
|
collectReadsInExpr(stmt.right, reads);
|
|
3788
3897
|
break;
|
|
3789
3898
|
}
|
|
3899
|
+
case "MidStatement": {
|
|
3900
|
+
const w = extractWriteName(stmt.target);
|
|
3901
|
+
if (w && !skipVars.has(w)) {
|
|
3902
|
+
if (!writes.has(w)) writes.set(w, []);
|
|
3903
|
+
writes.get(w).push(line);
|
|
3904
|
+
}
|
|
3905
|
+
collectReadsInExpr(stmt.target, reads);
|
|
3906
|
+
collectReadsInExpr(stmt.start, reads);
|
|
3907
|
+
if (stmt.length) collectReadsInExpr(stmt.length, reads);
|
|
3908
|
+
collectReadsInExpr(stmt.value, reads);
|
|
3909
|
+
break;
|
|
3910
|
+
}
|
|
3790
3911
|
case "ReDimStatement": {
|
|
3791
3912
|
const w = stmt.name?.name?.toLowerCase() ?? null;
|
|
3792
3913
|
if (w && !skipVars.has(w)) {
|
|
@@ -3959,6 +4080,12 @@ function collectWriteAttrs(stmts, targets, selfRefs, boolOnly, arrayWrites) {
|
|
|
3959
4080
|
if (stmt.left?.type === "CallExpression") arrayWrites.add(w);
|
|
3960
4081
|
}
|
|
3961
4082
|
}
|
|
4083
|
+
if (stmt.type === "MidStatement") {
|
|
4084
|
+
const w = extractWriteName(stmt.target);
|
|
4085
|
+
if (w && targets.has(w)) {
|
|
4086
|
+
selfRefs.add(w);
|
|
4087
|
+
}
|
|
4088
|
+
}
|
|
3962
4089
|
const sub = (s) => collectWriteAttrs(Array.isArray(s) ? s : [s], targets, selfRefs, boolOnly, arrayWrites);
|
|
3963
4090
|
if (stmt.body) sub(stmt.body);
|
|
3964
4091
|
if (stmt.consequent) sub(stmt.consequent);
|