vba-runner 0.1.0-alpha.0
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/LICENSE +21 -0
- package/README.ja.md +94 -0
- package/README.md +91 -0
- package/dist/bin/vba-analyzer.cjs +5093 -0
- package/dist/bin/vba-formatter.cjs +1012 -0
- package/dist/bin/vba-parse-check.cjs +2499 -0
- package/dist/bin/vba-run.cjs +9414 -0
- package/dist/lib.cjs +10650 -0
- package/package.json +33 -0
|
@@ -0,0 +1,1012 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __copyProps = (to, from, except, desc) => {
|
|
9
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
10
|
+
for (let key of __getOwnPropNames(from))
|
|
11
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
12
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
13
|
+
}
|
|
14
|
+
return to;
|
|
15
|
+
};
|
|
16
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
17
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
18
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
19
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
20
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
21
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
22
|
+
mod
|
|
23
|
+
));
|
|
24
|
+
|
|
25
|
+
// ../../test-libs/vba-formatter.ts
|
|
26
|
+
var fs = __toESM(require("fs"), 1);
|
|
27
|
+
var path = __toESM(require("path"), 1);
|
|
28
|
+
|
|
29
|
+
// ../../src/engine/lexer.ts
|
|
30
|
+
var LexError = class extends Error {
|
|
31
|
+
constructor(message, line, column) {
|
|
32
|
+
super(`${message} (line ${line})`);
|
|
33
|
+
this.line = line;
|
|
34
|
+
this.column = column;
|
|
35
|
+
this.name = "LexError";
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
var Lexer = class _Lexer {
|
|
39
|
+
input = "";
|
|
40
|
+
pos = 0;
|
|
41
|
+
line = 1;
|
|
42
|
+
column = 1;
|
|
43
|
+
diagnostics = [];
|
|
44
|
+
// MS-VBAL §3.3.5: name-start-character = Unicode-Letter (Lu,Ll,Lt,Lm,Lo,Nl) | "_"
|
|
45
|
+
static reUnicodeNameStart = /^[\p{L}\p{Nl}]$/u;
|
|
46
|
+
// name-continue-character adds Unicode-Combining-Character (Mn,Mc), Digit (Nd), Connector-Punct (Pc)
|
|
47
|
+
static reUnicodeNameContinue = /^[\p{Mn}\p{Mc}\p{Nd}\p{Pc}]$/u;
|
|
48
|
+
constructor(input) {
|
|
49
|
+
this.input = input;
|
|
50
|
+
}
|
|
51
|
+
peek() {
|
|
52
|
+
if (this.pos >= this.input.length) return "\0";
|
|
53
|
+
return this.input[this.pos];
|
|
54
|
+
}
|
|
55
|
+
advance() {
|
|
56
|
+
if (this.pos >= this.input.length) return "\0";
|
|
57
|
+
const char = this.input[this.pos++];
|
|
58
|
+
if (char === "\n") {
|
|
59
|
+
this.line++;
|
|
60
|
+
this.column = 1;
|
|
61
|
+
} else {
|
|
62
|
+
this.column++;
|
|
63
|
+
}
|
|
64
|
+
return char;
|
|
65
|
+
}
|
|
66
|
+
isWhitespace(char) {
|
|
67
|
+
return char === " " || char === " " || char === "\r";
|
|
68
|
+
}
|
|
69
|
+
isAlpha(char) {
|
|
70
|
+
if (char >= "a" && char <= "z" || char >= "A" && char <= "Z" || char === "_") return true;
|
|
71
|
+
const code = char.codePointAt(0) ?? 0;
|
|
72
|
+
return code > 127 && _Lexer.reUnicodeNameStart.test(char);
|
|
73
|
+
}
|
|
74
|
+
isAlphaNumeric(char) {
|
|
75
|
+
if (char >= "a" && char <= "z" || char >= "A" && char <= "Z" || char === "_") return true;
|
|
76
|
+
if (char >= "0" && char <= "9") return true;
|
|
77
|
+
const code = char.codePointAt(0) ?? 0;
|
|
78
|
+
return code > 127 && (_Lexer.reUnicodeNameStart.test(char) || _Lexer.reUnicodeNameContinue.test(char));
|
|
79
|
+
}
|
|
80
|
+
isDigit(char) {
|
|
81
|
+
return char >= "0" && char <= "9";
|
|
82
|
+
}
|
|
83
|
+
skipWhitespace() {
|
|
84
|
+
while (this.isWhitespace(this.peek())) {
|
|
85
|
+
this.advance();
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
getNextToken() {
|
|
89
|
+
while (true) {
|
|
90
|
+
this.skipWhitespace();
|
|
91
|
+
const startLine = this.line;
|
|
92
|
+
const startColumn = this.column;
|
|
93
|
+
if (this.pos >= this.input.length) {
|
|
94
|
+
return { type: 136 /* EOF */, value: "", line: startLine, column: startColumn };
|
|
95
|
+
}
|
|
96
|
+
const char = this.peek();
|
|
97
|
+
if (char === "#") {
|
|
98
|
+
let foundClosingHash = false;
|
|
99
|
+
for (let j = this.pos + 1; j < this.input.length; j++) {
|
|
100
|
+
if (this.input[j] === "#") {
|
|
101
|
+
foundClosingHash = true;
|
|
102
|
+
break;
|
|
103
|
+
}
|
|
104
|
+
if (this.input[j] === "\n") break;
|
|
105
|
+
}
|
|
106
|
+
if (foundClosingHash) {
|
|
107
|
+
let potentialDate = "";
|
|
108
|
+
let k = this.pos + 1;
|
|
109
|
+
while (k < this.input.length && this.input[k] !== "#" && this.input[k] !== "\n") {
|
|
110
|
+
potentialDate += this.input[k];
|
|
111
|
+
k++;
|
|
112
|
+
}
|
|
113
|
+
const dateRegex = /^[0-9\/\-\s:apm,]+$/i;
|
|
114
|
+
const monthsRegex = /jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec/i;
|
|
115
|
+
if (dateRegex.test(potentialDate) || monthsRegex.test(potentialDate)) {
|
|
116
|
+
this.advance();
|
|
117
|
+
let dateValue = "";
|
|
118
|
+
while (this.peek() !== "#" && this.peek() !== "\n" && this.peek() !== "\0") {
|
|
119
|
+
dateValue += this.advance();
|
|
120
|
+
}
|
|
121
|
+
if (this.peek() === "#") {
|
|
122
|
+
this.advance();
|
|
123
|
+
return { type: 134 /* Date */, value: dateValue, line: startLine, column: startColumn };
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
this.advance();
|
|
128
|
+
return { type: 111 /* OperatorHash */, value: "#", line: startLine, column: startColumn };
|
|
129
|
+
}
|
|
130
|
+
if (char === "_") {
|
|
131
|
+
const next = this.pos + 1 < this.input.length ? this.input[this.pos + 1] : "\0";
|
|
132
|
+
const afterCr = next === "\r" && this.pos + 2 < this.input.length ? this.input[this.pos + 2] : "\0";
|
|
133
|
+
if (next === "\n" || next === "\r" && afterCr === "\n") {
|
|
134
|
+
this.advance();
|
|
135
|
+
if (this.peek() === "\r") this.advance();
|
|
136
|
+
if (this.peek() === "\n") this.advance();
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
let lookahead = this.pos + 1;
|
|
140
|
+
while (lookahead < this.input.length && this.input[lookahead] === " ") lookahead++;
|
|
141
|
+
const afterSpaces = lookahead < this.input.length ? this.input[lookahead] : "\0";
|
|
142
|
+
if (afterSpaces === "\n" || afterSpaces === "\r" || afterSpaces === "'") {
|
|
143
|
+
const msg = afterSpaces === "'" ? "\u884C\u7D99\u7D9A\u6587\u5B57 '_' \u306E\u5F8C\u306B\u30B3\u30E1\u30F3\u30C8\u306F\u8A18\u8FF0\u3067\u304D\u307E\u305B\u3093" : "\u884C\u7D99\u7D9A\u6587\u5B57 '_' \u306E\u76F4\u5F8C\u306B\u7A7A\u767D\u3092\u7F6E\u304F\u3053\u3068\u306F\u3067\u304D\u307E\u305B\u3093";
|
|
144
|
+
this.advance();
|
|
145
|
+
while (this.peek() !== "\n" && this.peek() !== "\0") this.advance();
|
|
146
|
+
if (this.peek() === "\n") this.advance();
|
|
147
|
+
throw new LexError(msg, startLine, startColumn);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
if (char === "'") {
|
|
151
|
+
let commentText = "";
|
|
152
|
+
while (this.peek() !== "\n" && this.peek() !== "\0") {
|
|
153
|
+
commentText += this.advance();
|
|
154
|
+
}
|
|
155
|
+
if (/[ \t]*_[ \t]*$/.test(commentText)) {
|
|
156
|
+
this.diagnostics.push({
|
|
157
|
+
message: "\u30B3\u30E1\u30F3\u30C8\u672B\u5C3E\u306E '_' \u306F\u884C\u7D99\u7D9A\u3068\u3057\u3066\u6A5F\u80FD\u3057\u307E\u305B\u3093",
|
|
158
|
+
line: startLine,
|
|
159
|
+
column: startColumn + 1 + commentText.lastIndexOf("_")
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
if (char === "\n") {
|
|
165
|
+
this.advance();
|
|
166
|
+
return { type: 135 /* Newline */, value: "\n", line: startLine, column: startColumn };
|
|
167
|
+
}
|
|
168
|
+
if (char === '"') {
|
|
169
|
+
this.advance();
|
|
170
|
+
let strValue = "";
|
|
171
|
+
while (this.peek() !== "\0") {
|
|
172
|
+
if (this.peek() === '"') {
|
|
173
|
+
this.advance();
|
|
174
|
+
if (this.peek() === '"') {
|
|
175
|
+
strValue += '"';
|
|
176
|
+
this.advance();
|
|
177
|
+
} else {
|
|
178
|
+
return { type: 2 /* String */, value: strValue, line: startLine, column: startColumn };
|
|
179
|
+
}
|
|
180
|
+
} else {
|
|
181
|
+
strValue += this.advance();
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return { type: 2 /* String */, value: strValue, line: startLine, column: startColumn };
|
|
185
|
+
}
|
|
186
|
+
if (char === "=") {
|
|
187
|
+
this.advance();
|
|
188
|
+
return { type: 120 /* OperatorEquals */, value: "=", line: startLine, column: startColumn };
|
|
189
|
+
}
|
|
190
|
+
if (char === "<") {
|
|
191
|
+
this.advance();
|
|
192
|
+
if (this.peek() === ">") {
|
|
193
|
+
this.advance();
|
|
194
|
+
return { type: 121 /* OperatorNotEquals */, value: "<>", line: startLine, column: startColumn };
|
|
195
|
+
}
|
|
196
|
+
if (this.peek() === "=") {
|
|
197
|
+
this.advance();
|
|
198
|
+
return { type: 124 /* OperatorLessThanOrEqual */, value: "<=", line: startLine, column: startColumn };
|
|
199
|
+
}
|
|
200
|
+
return { type: 122 /* OperatorLessThan */, value: "<", line: startLine, column: startColumn };
|
|
201
|
+
}
|
|
202
|
+
if (char === ">") {
|
|
203
|
+
this.advance();
|
|
204
|
+
if (this.peek() === "=") {
|
|
205
|
+
this.advance();
|
|
206
|
+
return { type: 125 /* OperatorGreaterThanOrEqual */, value: ">=", line: startLine, column: startColumn };
|
|
207
|
+
}
|
|
208
|
+
return { type: 123 /* OperatorGreaterThan */, value: ">", line: startLine, column: startColumn };
|
|
209
|
+
}
|
|
210
|
+
if (char === "+") {
|
|
211
|
+
this.advance();
|
|
212
|
+
return { type: 112 /* OperatorPlus */, value: "+", line: startLine, column: startColumn };
|
|
213
|
+
}
|
|
214
|
+
if (char === "-") {
|
|
215
|
+
this.advance();
|
|
216
|
+
return { type: 113 /* OperatorMinus */, value: "-", line: startLine, column: startColumn };
|
|
217
|
+
}
|
|
218
|
+
if (char === "!") {
|
|
219
|
+
this.advance();
|
|
220
|
+
return { type: 132 /* OperatorExclamation */, value: "!", line: startLine, column: startColumn };
|
|
221
|
+
}
|
|
222
|
+
if (char === "&") {
|
|
223
|
+
this.advance();
|
|
224
|
+
const next = this.peek().toLowerCase();
|
|
225
|
+
if (next === "h") {
|
|
226
|
+
this.advance();
|
|
227
|
+
let hexStr = "";
|
|
228
|
+
while (/[0-9a-f]/i.test(this.peek())) {
|
|
229
|
+
hexStr += this.advance();
|
|
230
|
+
}
|
|
231
|
+
return { type: 1 /* Number */, value: "0x" + hexStr, line: startLine, column: startColumn };
|
|
232
|
+
} else if (next === "o" || this.isDigit(next)) {
|
|
233
|
+
if (next === "o") this.advance();
|
|
234
|
+
let octStr = "";
|
|
235
|
+
while (/[0-7]/.test(this.peek())) {
|
|
236
|
+
octStr += this.advance();
|
|
237
|
+
}
|
|
238
|
+
return { type: 1 /* Number */, value: "0o" + octStr, line: startLine, column: startColumn };
|
|
239
|
+
}
|
|
240
|
+
return { type: 117 /* OperatorAmpersand */, value: "&", line: startLine, column: startColumn };
|
|
241
|
+
}
|
|
242
|
+
if (char === ",") {
|
|
243
|
+
this.advance();
|
|
244
|
+
return { type: 126 /* OperatorComma */, value: ",", line: startLine, column: startColumn };
|
|
245
|
+
}
|
|
246
|
+
if (char === "#") {
|
|
247
|
+
this.advance();
|
|
248
|
+
return { type: 111 /* OperatorHash */, value: "#", line: startLine, column: startColumn };
|
|
249
|
+
}
|
|
250
|
+
if (char === "(") {
|
|
251
|
+
this.advance();
|
|
252
|
+
return { type: 127 /* OperatorLParen */, value: "(", line: startLine, column: startColumn };
|
|
253
|
+
}
|
|
254
|
+
if (char === ")") {
|
|
255
|
+
this.advance();
|
|
256
|
+
return { type: 128 /* OperatorRParen */, value: ")", line: startLine, column: startColumn };
|
|
257
|
+
}
|
|
258
|
+
if (char === "[") {
|
|
259
|
+
this.advance();
|
|
260
|
+
let foreignStr = "";
|
|
261
|
+
while (this.peek() !== "]" && this.peek() !== "\n" && this.peek() !== "\r" && this.peek() !== "\0") {
|
|
262
|
+
foreignStr += this.advance();
|
|
263
|
+
}
|
|
264
|
+
if (this.peek() === "]") this.advance();
|
|
265
|
+
return { type: 138 /* ForeignName */, value: foreignStr, line: startLine, column: startColumn };
|
|
266
|
+
}
|
|
267
|
+
if (char === ".") {
|
|
268
|
+
this.advance();
|
|
269
|
+
return { type: 129 /* OperatorDot */, value: ".", line: startLine, column: startColumn };
|
|
270
|
+
}
|
|
271
|
+
if (char === ":") {
|
|
272
|
+
this.advance();
|
|
273
|
+
if (this.peek() === "=") {
|
|
274
|
+
this.advance();
|
|
275
|
+
return { type: 131 /* OperatorColonEquals */, value: ":=", line: startLine, column: startColumn };
|
|
276
|
+
}
|
|
277
|
+
return { type: 130 /* OperatorColon */, value: ":", line: startLine, column: startColumn };
|
|
278
|
+
}
|
|
279
|
+
if (char === ";") {
|
|
280
|
+
this.advance();
|
|
281
|
+
return { type: 133 /* OperatorSemicolon */, value: ";", line: startLine, column: startColumn };
|
|
282
|
+
}
|
|
283
|
+
if (this.isDigit(char)) {
|
|
284
|
+
let numStr = "";
|
|
285
|
+
while (this.isDigit(this.peek())) {
|
|
286
|
+
numStr += this.advance();
|
|
287
|
+
}
|
|
288
|
+
if (this.peek() === ".") {
|
|
289
|
+
numStr += this.advance();
|
|
290
|
+
while (this.isDigit(this.peek())) {
|
|
291
|
+
numStr += this.advance();
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
const nextChar = this.peek().toLowerCase();
|
|
295
|
+
if (nextChar === "e") {
|
|
296
|
+
numStr += this.advance();
|
|
297
|
+
if (this.peek() === "+" || this.peek() === "-") {
|
|
298
|
+
numStr += this.advance();
|
|
299
|
+
}
|
|
300
|
+
while (this.isDigit(this.peek())) {
|
|
301
|
+
numStr += this.advance();
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
const peekChar = this.peek();
|
|
305
|
+
if (["%", "&", "@", "!", "#", "^"].indexOf(peekChar) !== -1) {
|
|
306
|
+
numStr += this.advance();
|
|
307
|
+
}
|
|
308
|
+
return { type: 1 /* Number */, value: numStr, line: startLine, column: startColumn };
|
|
309
|
+
}
|
|
310
|
+
if (this.isAlpha(char)) {
|
|
311
|
+
let idStr = "";
|
|
312
|
+
while (this.isAlphaNumeric(this.peek())) {
|
|
313
|
+
idStr += this.advance();
|
|
314
|
+
}
|
|
315
|
+
const typeHints = ["$", "%", "&", "#", "@"];
|
|
316
|
+
if (typeHints.includes(this.peek())) {
|
|
317
|
+
idStr += this.advance();
|
|
318
|
+
}
|
|
319
|
+
const lowerId = idStr.toLowerCase();
|
|
320
|
+
const lowerBase = lowerId.replace(/[$%&#@]$/, "");
|
|
321
|
+
if (lowerBase === "rem") {
|
|
322
|
+
while (this.peek() !== "\n" && this.peek() !== "\0") {
|
|
323
|
+
this.advance();
|
|
324
|
+
}
|
|
325
|
+
continue;
|
|
326
|
+
}
|
|
327
|
+
if (lowerBase === "for") return { type: 3 /* KeywordFor */, value: idStr, line: startLine, column: startColumn };
|
|
328
|
+
if (lowerBase === "to") return { type: 4 /* KeywordTo */, value: idStr, line: startLine, column: startColumn };
|
|
329
|
+
if (lowerBase === "next") return { type: 5 /* KeywordNext */, value: idStr, line: startLine, column: startColumn };
|
|
330
|
+
if (lowerBase === "if") return { type: 6 /* KeywordIf */, value: idStr, line: startLine, column: startColumn };
|
|
331
|
+
if (lowerBase === "then") return { type: 7 /* KeywordThen */, value: idStr, line: startLine, column: startColumn };
|
|
332
|
+
if (lowerBase === "elseif") return { type: 8 /* KeywordElseIf */, value: idStr, line: startLine, column: startColumn };
|
|
333
|
+
if (lowerBase === "else") return { type: 9 /* KeywordElse */, value: idStr, line: startLine, column: startColumn };
|
|
334
|
+
if (lowerBase === "end") return { type: 10 /* KeywordEnd */, value: idStr, line: startLine, column: startColumn };
|
|
335
|
+
if (lowerBase === "do") return { type: 11 /* KeywordDo */, value: idStr, line: startLine, column: startColumn };
|
|
336
|
+
if (lowerBase === "while") return { type: 12 /* KeywordWhile */, value: idStr, line: startLine, column: startColumn };
|
|
337
|
+
if (lowerBase === "wend") return { type: 13 /* KeywordWend */, value: idStr, line: startLine, column: startColumn };
|
|
338
|
+
if (lowerBase === "loop") return { type: 14 /* KeywordLoop */, value: idStr, line: startLine, column: startColumn };
|
|
339
|
+
if (lowerBase === "until") return { type: 15 /* KeywordUntil */, value: idStr, line: startLine, column: startColumn };
|
|
340
|
+
if (lowerBase === "gosub") return { type: 37 /* KeywordGoSub */, value: idStr, line: startLine, column: startColumn };
|
|
341
|
+
if (lowerBase === "return") return { type: 38 /* KeywordReturn */, value: idStr, line: startLine, column: startColumn };
|
|
342
|
+
if (lowerBase === "lset") return { type: 39 /* KeywordLSet */, value: idStr, line: startLine, column: startColumn };
|
|
343
|
+
if (lowerBase === "rset") return { type: 40 /* KeywordRSet */, value: idStr, line: startLine, column: startColumn };
|
|
344
|
+
if (lowerBase === "stop") return { type: 16 /* KeywordStop */, value: idStr, line: startLine, column: startColumn };
|
|
345
|
+
if (lowerBase === "sub") return { type: 17 /* KeywordSub */, value: idStr, line: startLine, column: startColumn };
|
|
346
|
+
if (lowerBase === "function") return { type: 18 /* KeywordFunction */, value: idStr, line: startLine, column: startColumn };
|
|
347
|
+
if (lowerBase === "property") return { type: 19 /* KeywordProperty */, value: idStr, line: startLine, column: startColumn };
|
|
348
|
+
if (lowerBase === "get") return { type: 20 /* KeywordGet */, value: idStr, line: startLine, column: startColumn };
|
|
349
|
+
if (lowerBase === "let") return { type: 21 /* KeywordLet */, value: idStr, line: startLine, column: startColumn };
|
|
350
|
+
if (lowerBase === "dim") return { type: 22 /* KeywordDim */, value: idStr, line: startLine, column: startColumn };
|
|
351
|
+
if (lowerBase === "as") return { type: 23 /* KeywordAs */, value: idStr, line: startLine, column: startColumn };
|
|
352
|
+
if (lowerBase === "new") return { type: 24 /* KeywordNew */, value: idStr, line: startLine, column: startColumn };
|
|
353
|
+
if (lowerBase === "collection") return { type: 25 /* KeywordCollection */, value: idStr, line: startLine, column: startColumn };
|
|
354
|
+
if (lowerBase === "like") return { type: 63 /* KeywordLike */, value: idStr, line: startLine, column: startColumn };
|
|
355
|
+
if (lowerBase === "and") return { type: 26 /* KeywordAnd */, value: idStr, line: startLine, column: startColumn };
|
|
356
|
+
if (lowerBase === "or") return { type: 27 /* KeywordOr */, value: idStr, line: startLine, column: startColumn };
|
|
357
|
+
if (lowerBase === "xor") return { type: 64 /* KeywordXor */, value: idStr, line: startLine, column: startColumn };
|
|
358
|
+
if (lowerBase === "eqv") return { type: 65 /* KeywordEqv */, value: idStr, line: startLine, column: startColumn };
|
|
359
|
+
if (lowerBase === "imp") return { type: 66 /* KeywordImp */, value: idStr, line: startLine, column: startColumn };
|
|
360
|
+
if (lowerBase === "not") return { type: 28 /* KeywordNot */, value: idStr, line: startLine, column: startColumn };
|
|
361
|
+
if (lowerBase === "option") return { type: 29 /* KeywordOption */, value: idStr, line: startLine, column: startColumn };
|
|
362
|
+
if (lowerBase === "explicit") return { type: 30 /* KeywordExplicit */, value: idStr, line: startLine, column: startColumn };
|
|
363
|
+
if (lowerBase === "const") return { type: 31 /* KeywordConst */, value: idStr, line: startLine, column: startColumn };
|
|
364
|
+
if (lowerBase === "set") return { type: 32 /* KeywordSet */, value: idStr, line: startLine, column: startColumn };
|
|
365
|
+
if (lowerBase === "call") return { type: 33 /* KeywordCall */, value: idStr, line: startLine, column: startColumn };
|
|
366
|
+
if (lowerBase === "on") return { type: 34 /* KeywordOn */, value: idStr, line: startLine, column: startColumn };
|
|
367
|
+
if (lowerBase === "error") return { type: 35 /* KeywordError */, value: idStr, line: startLine, column: startColumn };
|
|
368
|
+
if (lowerBase === "goto") return { type: 36 /* KeywordGoTo */, value: idStr, line: startLine, column: startColumn };
|
|
369
|
+
if (lowerBase === "erase") return { type: 41 /* KeywordErase */, value: idStr, line: startLine, column: startColumn };
|
|
370
|
+
if (lowerBase === "redim") return { type: 42 /* KeywordReDim */, value: idStr, line: startLine, column: startColumn };
|
|
371
|
+
if (lowerBase === "step") return { type: 43 /* KeywordStep */, value: idStr, line: startLine, column: startColumn };
|
|
372
|
+
if (lowerBase === "empty") return { type: 44 /* KeywordEmpty */, value: idStr, line: startLine, column: startColumn };
|
|
373
|
+
if (lowerBase === "exit") return { type: 45 /* KeywordExit */, value: idStr, line: startLine, column: startColumn };
|
|
374
|
+
if (lowerBase === "byref") return { type: 46 /* KeywordByRef */, value: idStr, line: startLine, column: startColumn };
|
|
375
|
+
if (lowerBase === "byval") return { type: 47 /* KeywordByVal */, value: idStr, line: startLine, column: startColumn };
|
|
376
|
+
if (lowerBase === "mod") return { type: 118 /* KeywordMod */, value: idStr, line: startLine, column: startColumn };
|
|
377
|
+
if (lowerBase === "type") return { type: 48 /* KeywordType */, value: idStr, line: startLine, column: startColumn };
|
|
378
|
+
if (lowerBase === "nothing") return { type: 49 /* KeywordNothing */, value: idStr, line: startLine, column: startColumn };
|
|
379
|
+
if (lowerBase === "optional") return { type: 50 /* KeywordOptional */, value: idStr, line: startLine, column: startColumn };
|
|
380
|
+
if (lowerBase === "is") return { type: 51 /* KeywordIs */, value: idStr, line: startLine, column: startColumn };
|
|
381
|
+
if (lowerBase === "resume") return { type: 52 /* KeywordResume */, value: idStr, line: startLine, column: startColumn };
|
|
382
|
+
if (lowerBase === "select") return { type: 53 /* KeywordSelect */, value: idStr, line: startLine, column: startColumn };
|
|
383
|
+
if (lowerBase === "case") return { type: 54 /* KeywordCase */, value: idStr, line: startLine, column: startColumn };
|
|
384
|
+
if (lowerBase === "each") return { type: 55 /* KeywordEach */, value: idStr, line: startLine, column: startColumn };
|
|
385
|
+
if (lowerBase === "in") return { type: 56 /* KeywordIn */, value: idStr, line: startLine, column: startColumn };
|
|
386
|
+
if (lowerBase === "public") return { type: 57 /* KeywordPublic */, value: idStr, line: startLine, column: startColumn };
|
|
387
|
+
if (lowerBase === "private") return { type: 58 /* KeywordPrivate */, value: idStr, line: startLine, column: startColumn };
|
|
388
|
+
if (lowerBase === "enum") return { type: 59 /* KeywordEnum */, value: idStr, line: startLine, column: startColumn };
|
|
389
|
+
if (lowerBase === "typeof") return { type: 62 /* KeywordTypeOf */, value: idStr, line: startLine, column: startColumn };
|
|
390
|
+
if (lowerBase === "friend") return { type: 60 /* KeywordFriend */, value: idStr, line: startLine, column: startColumn };
|
|
391
|
+
if (lowerBase === "with") return { type: 61 /* KeywordWith */, value: idStr, line: startLine, column: startColumn };
|
|
392
|
+
if (lowerBase === "null") return { type: 67 /* KeywordNull */, value: idStr, line: startLine, column: startColumn };
|
|
393
|
+
if (lowerBase === "static") return { type: 68 /* KeywordStatic */, value: idStr, line: startLine, column: startColumn };
|
|
394
|
+
if (lowerBase === "class") return { type: 69 /* KeywordClass */, value: idStr, line: startLine, column: startColumn };
|
|
395
|
+
if (lowerBase === "me") return { type: 70 /* KeywordMe */, value: idStr, line: startLine, column: startColumn };
|
|
396
|
+
if (lowerBase === "compare") return { type: 71 /* KeywordCompare */, value: idStr, line: startLine, column: startColumn };
|
|
397
|
+
if (lowerBase === "binary") return { type: 72 /* KeywordBinary */, value: idStr, line: startLine, column: startColumn };
|
|
398
|
+
if (lowerBase === "text") return { type: 73 /* KeywordText */, value: idStr, line: startLine, column: startColumn };
|
|
399
|
+
if (lowerBase === "attribute") return { type: 74 /* KeywordAttribute */, value: idStr, line: startLine, column: startColumn };
|
|
400
|
+
if (lowerBase === "declare") return { type: 75 /* KeywordDeclare */, value: idStr, line: startLine, column: startColumn };
|
|
401
|
+
if (lowerBase === "lib") return { type: 76 /* KeywordLib */, value: idStr, line: startLine, column: startColumn };
|
|
402
|
+
if (lowerBase === "alias") return { type: 77 /* KeywordAlias */, value: idStr, line: startLine, column: startColumn };
|
|
403
|
+
if (lowerBase === "ptrsafe") return { type: 78 /* KeywordPtrSafe */, value: idStr, line: startLine, column: startColumn };
|
|
404
|
+
if (lowerBase === "open") return { type: 81 /* KeywordOpen */, value: idStr, line: startLine, column: startColumn };
|
|
405
|
+
if (lowerBase === "close") return { type: 82 /* KeywordClose */, value: idStr, line: startLine, column: startColumn };
|
|
406
|
+
if (lowerBase === "line") return { type: 83 /* KeywordLine */, value: idStr, line: startLine, column: startColumn };
|
|
407
|
+
if (lowerBase === "input") return { type: 84 /* KeywordInput */, value: idStr, line: startLine, column: startColumn };
|
|
408
|
+
if (lowerBase === "print") return { type: 85 /* KeywordPrint */, value: idStr, line: startLine, column: startColumn };
|
|
409
|
+
if (lowerBase === "put") return { type: 86 /* KeywordPut */, value: idStr, line: startLine, column: startColumn };
|
|
410
|
+
if (lowerBase === "output") return { type: 87 /* KeywordOutput */, value: idStr, line: startLine, column: startColumn };
|
|
411
|
+
if (lowerBase === "append") return { type: 88 /* KeywordAppend */, value: idStr, line: startLine, column: startColumn };
|
|
412
|
+
if (lowerBase === "random") return { type: 89 /* KeywordRandom */, value: idStr, line: startLine, column: startColumn };
|
|
413
|
+
if (lowerBase === "access") return { type: 90 /* KeywordAccess */, value: idStr, line: startLine, column: startColumn };
|
|
414
|
+
if (lowerBase === "read") return { type: 91 /* KeywordRead */, value: idStr, line: startLine, column: startColumn };
|
|
415
|
+
if (lowerBase === "write") return { type: 92 /* KeywordWrite */, value: idStr, line: startLine, column: startColumn };
|
|
416
|
+
if (lowerBase === "lock") return { type: 93 /* KeywordLock */, value: idStr, line: startLine, column: startColumn };
|
|
417
|
+
if (lowerBase === "shared") return { type: 94 /* KeywordShared */, value: idStr, line: startLine, column: startColumn };
|
|
418
|
+
if (lowerBase === "spc") return { type: 95 /* KeywordSpc */, value: idStr, line: startLine, column: startColumn };
|
|
419
|
+
if (lowerBase === "tab") return { type: 96 /* KeywordTab */, value: idStr, line: startLine, column: startColumn };
|
|
420
|
+
if (lowerBase === "kill") return { type: 97 /* KeywordKill */, value: idStr, line: startLine, column: startColumn };
|
|
421
|
+
if (lowerBase === "get") return { type: 20 /* KeywordGet */, value: idStr, line: startLine, column: startColumn };
|
|
422
|
+
if (lowerBase === "seek") return { type: 98 /* KeywordSeek */, value: idStr, line: startLine, column: startColumn };
|
|
423
|
+
if (lowerBase === "reset") return { type: 99 /* KeywordReset */, value: idStr, line: startLine, column: startColumn };
|
|
424
|
+
if (lowerBase === "unlock") return { type: 100 /* KeywordUnlock */, value: idStr, line: startLine, column: startColumn };
|
|
425
|
+
if (lowerBase === "paramarray") return { type: 101 /* KeywordParamArray */, value: idStr, line: startLine, column: startColumn };
|
|
426
|
+
if (lowerBase === "module") return { type: 80 /* KeywordModule */, value: idStr, line: startLine, column: startColumn };
|
|
427
|
+
if (lowerBase === "event") return { type: 102 /* KeywordEvent */, value: idStr, line: startLine, column: startColumn };
|
|
428
|
+
if (lowerBase === "raiseevent") return { type: 103 /* KeywordRaiseEvent */, value: idStr, line: startLine, column: startColumn };
|
|
429
|
+
if (lowerBase === "withevents") return { type: 104 /* KeywordWithEvents */, value: idStr, line: startLine, column: startColumn };
|
|
430
|
+
if (lowerBase === "implements") return { type: 105 /* KeywordImplements */, value: idStr, line: startLine, column: startColumn };
|
|
431
|
+
if (lowerBase === "appactivate") return { type: 106 /* KeywordAppActivate */, value: idStr, line: startLine, column: startColumn };
|
|
432
|
+
if (lowerBase === "sendkeys") return { type: 107 /* KeywordSendKeys */, value: idStr, line: startLine, column: startColumn };
|
|
433
|
+
if (lowerBase === "mid") return { type: 108 /* KeywordMid */, value: idStr, line: startLine, column: startColumn };
|
|
434
|
+
if (lowerBase === "midb") return { type: 108 /* KeywordMid */, value: idStr, line: startLine, column: startColumn };
|
|
435
|
+
if (lowerBase === "width") return { type: 109 /* KeywordWidth */, value: idStr, line: startLine, column: startColumn };
|
|
436
|
+
if (lowerBase === "addressof") return { type: 110 /* KeywordAddressOf */, value: idStr, line: startLine, column: startColumn };
|
|
437
|
+
return { type: 0 /* Identifier */, value: idStr, line: startLine, column: startColumn };
|
|
438
|
+
}
|
|
439
|
+
if (char === "*") {
|
|
440
|
+
this.advance();
|
|
441
|
+
return { type: 114 /* OperatorMultiply */, value: "*", line: startLine, column: startColumn };
|
|
442
|
+
}
|
|
443
|
+
if (char === "/") {
|
|
444
|
+
this.advance();
|
|
445
|
+
return { type: 115 /* OperatorDivide */, value: "/", line: startLine, column: startColumn };
|
|
446
|
+
}
|
|
447
|
+
if (char === "\\") {
|
|
448
|
+
this.advance();
|
|
449
|
+
return { type: 116 /* OperatorIntDivide */, value: "\\", line: startLine, column: startColumn };
|
|
450
|
+
}
|
|
451
|
+
if (char === "^") {
|
|
452
|
+
this.advance();
|
|
453
|
+
return { type: 119 /* OperatorPower */, value: "^", line: startLine, column: startColumn };
|
|
454
|
+
}
|
|
455
|
+
const unknownChar = this.advance();
|
|
456
|
+
return { type: 137 /* Unknown */, value: unknownChar, line: startLine, column: startColumn };
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
tokenize() {
|
|
460
|
+
const tokens = [];
|
|
461
|
+
let token;
|
|
462
|
+
do {
|
|
463
|
+
token = this.getNextToken();
|
|
464
|
+
tokens.push(token);
|
|
465
|
+
} while (token.type !== 136 /* EOF */);
|
|
466
|
+
return tokens;
|
|
467
|
+
}
|
|
468
|
+
};
|
|
469
|
+
|
|
470
|
+
// ../../src/lsp/formatter.ts
|
|
471
|
+
var KEYWORD_CANONICAL = /* @__PURE__ */ new Map([
|
|
472
|
+
[22 /* KeywordDim */, "Dim"],
|
|
473
|
+
[17 /* KeywordSub */, "Sub"],
|
|
474
|
+
[18 /* KeywordFunction */, "Function"],
|
|
475
|
+
[19 /* KeywordProperty */, "Property"],
|
|
476
|
+
[20 /* KeywordGet */, "Get"],
|
|
477
|
+
[21 /* KeywordLet */, "Let"],
|
|
478
|
+
[32 /* KeywordSet */, "Set"],
|
|
479
|
+
[10 /* KeywordEnd */, "End"],
|
|
480
|
+
[6 /* KeywordIf */, "If"],
|
|
481
|
+
[7 /* KeywordThen */, "Then"],
|
|
482
|
+
[9 /* KeywordElse */, "Else"],
|
|
483
|
+
[8 /* KeywordElseIf */, "ElseIf"],
|
|
484
|
+
[3 /* KeywordFor */, "For"],
|
|
485
|
+
[4 /* KeywordTo */, "To"],
|
|
486
|
+
[43 /* KeywordStep */, "Step"],
|
|
487
|
+
[5 /* KeywordNext */, "Next"],
|
|
488
|
+
[55 /* KeywordEach */, "Each"],
|
|
489
|
+
[56 /* KeywordIn */, "In"],
|
|
490
|
+
[11 /* KeywordDo */, "Do"],
|
|
491
|
+
[14 /* KeywordLoop */, "Loop"],
|
|
492
|
+
[12 /* KeywordWhile */, "While"],
|
|
493
|
+
[13 /* KeywordWend */, "Wend"],
|
|
494
|
+
[15 /* KeywordUntil */, "Until"],
|
|
495
|
+
[61 /* KeywordWith */, "With"],
|
|
496
|
+
[53 /* KeywordSelect */, "Select"],
|
|
497
|
+
[54 /* KeywordCase */, "Case"],
|
|
498
|
+
[48 /* KeywordType */, "Type"],
|
|
499
|
+
[59 /* KeywordEnum */, "Enum"],
|
|
500
|
+
[69 /* KeywordClass */, "Class"],
|
|
501
|
+
[57 /* KeywordPublic */, "Public"],
|
|
502
|
+
[58 /* KeywordPrivate */, "Private"],
|
|
503
|
+
[60 /* KeywordFriend */, "Friend"],
|
|
504
|
+
[68 /* KeywordStatic */, "Static"],
|
|
505
|
+
[31 /* KeywordConst */, "Const"],
|
|
506
|
+
[23 /* KeywordAs */, "As"],
|
|
507
|
+
[24 /* KeywordNew */, "New"],
|
|
508
|
+
[47 /* KeywordByVal */, "ByVal"],
|
|
509
|
+
[46 /* KeywordByRef */, "ByRef"],
|
|
510
|
+
[50 /* KeywordOptional */, "Optional"],
|
|
511
|
+
[101 /* KeywordParamArray */, "ParamArray"],
|
|
512
|
+
[33 /* KeywordCall */, "Call"],
|
|
513
|
+
[45 /* KeywordExit */, "Exit"],
|
|
514
|
+
[38 /* KeywordReturn */, "Return"],
|
|
515
|
+
[36 /* KeywordGoTo */, "GoTo"],
|
|
516
|
+
[37 /* KeywordGoSub */, "GoSub"],
|
|
517
|
+
[34 /* KeywordOn */, "On"],
|
|
518
|
+
[35 /* KeywordError */, "Error"],
|
|
519
|
+
[52 /* KeywordResume */, "Resume"],
|
|
520
|
+
[42 /* KeywordReDim */, "ReDim"],
|
|
521
|
+
[41 /* KeywordErase */, "Erase"],
|
|
522
|
+
[28 /* KeywordNot */, "Not"],
|
|
523
|
+
[26 /* KeywordAnd */, "And"],
|
|
524
|
+
[27 /* KeywordOr */, "Or"],
|
|
525
|
+
[64 /* KeywordXor */, "Xor"],
|
|
526
|
+
[65 /* KeywordEqv */, "Eqv"],
|
|
527
|
+
[66 /* KeywordImp */, "Imp"],
|
|
528
|
+
[118 /* KeywordMod */, "Mod"],
|
|
529
|
+
[51 /* KeywordIs */, "Is"],
|
|
530
|
+
[63 /* KeywordLike */, "Like"],
|
|
531
|
+
[49 /* KeywordNothing */, "Nothing"],
|
|
532
|
+
[44 /* KeywordEmpty */, "Empty"],
|
|
533
|
+
[67 /* KeywordNull */, "Null"],
|
|
534
|
+
[70 /* KeywordMe */, "Me"],
|
|
535
|
+
[29 /* KeywordOption */, "Option"],
|
|
536
|
+
[30 /* KeywordExplicit */, "Explicit"],
|
|
537
|
+
[79 /* KeywordBase */, "Base"],
|
|
538
|
+
[71 /* KeywordCompare */, "Compare"],
|
|
539
|
+
[16 /* KeywordStop */, "Stop"],
|
|
540
|
+
[81 /* KeywordOpen */, "Open"],
|
|
541
|
+
[82 /* KeywordClose */, "Close"],
|
|
542
|
+
[85 /* KeywordPrint */, "Print"],
|
|
543
|
+
[84 /* KeywordInput */, "Input"],
|
|
544
|
+
[87 /* KeywordOutput */, "Output"],
|
|
545
|
+
[88 /* KeywordAppend */, "Append"],
|
|
546
|
+
[89 /* KeywordRandom */, "Random"],
|
|
547
|
+
[90 /* KeywordAccess */, "Access"],
|
|
548
|
+
[91 /* KeywordRead */, "Read"],
|
|
549
|
+
[92 /* KeywordWrite */, "Write"],
|
|
550
|
+
[93 /* KeywordLock */, "Lock"],
|
|
551
|
+
[94 /* KeywordShared */, "Shared"],
|
|
552
|
+
[75 /* KeywordDeclare */, "Declare"],
|
|
553
|
+
[76 /* KeywordLib */, "Lib"],
|
|
554
|
+
[77 /* KeywordAlias */, "Alias"],
|
|
555
|
+
[78 /* KeywordPtrSafe */, "PtrSafe"],
|
|
556
|
+
[105 /* KeywordImplements */, "Implements"],
|
|
557
|
+
[102 /* KeywordEvent */, "Event"],
|
|
558
|
+
[103 /* KeywordRaiseEvent */, "RaiseEvent"],
|
|
559
|
+
[104 /* KeywordWithEvents */, "WithEvents"],
|
|
560
|
+
[62 /* KeywordTypeOf */, "TypeOf"],
|
|
561
|
+
[110 /* KeywordAddressOf */, "AddressOf"],
|
|
562
|
+
[25 /* KeywordCollection */, "Collection"],
|
|
563
|
+
[39 /* KeywordLSet */, "LSet"],
|
|
564
|
+
[40 /* KeywordRSet */, "RSet"],
|
|
565
|
+
[108 /* KeywordMid */, "Mid"],
|
|
566
|
+
[97 /* KeywordKill */, "Kill"],
|
|
567
|
+
[98 /* KeywordSeek */, "Seek"],
|
|
568
|
+
[99 /* KeywordReset */, "Reset"],
|
|
569
|
+
[100 /* KeywordUnlock */, "Unlock"],
|
|
570
|
+
[109 /* KeywordWidth */, "Width"],
|
|
571
|
+
[83 /* KeywordLine */, "Line"],
|
|
572
|
+
[86 /* KeywordPut */, "Put"],
|
|
573
|
+
[74 /* KeywordAttribute */, "Attribute"],
|
|
574
|
+
[80 /* KeywordModule */, "Module"],
|
|
575
|
+
[95 /* KeywordSpc */, "Spc"],
|
|
576
|
+
[96 /* KeywordTab */, "Tab"],
|
|
577
|
+
[106 /* KeywordAppActivate */, "AppActivate"],
|
|
578
|
+
[107 /* KeywordSendKeys */, "SendKeys"],
|
|
579
|
+
[72 /* KeywordBinary */, "Binary"],
|
|
580
|
+
[73 /* KeywordText */, "Text"]
|
|
581
|
+
]);
|
|
582
|
+
var PARAM_INTRO = /* @__PURE__ */ new Set([
|
|
583
|
+
127 /* OperatorLParen */,
|
|
584
|
+
126 /* OperatorComma */,
|
|
585
|
+
46 /* KeywordByRef */,
|
|
586
|
+
47 /* KeywordByVal */,
|
|
587
|
+
50 /* KeywordOptional */,
|
|
588
|
+
101 /* KeywordParamArray */
|
|
589
|
+
]);
|
|
590
|
+
function collectParamNames(tokens, startIdx, define) {
|
|
591
|
+
let i = startIdx;
|
|
592
|
+
while (i < tokens.length && tokens[i].type !== 127 /* OperatorLParen */) {
|
|
593
|
+
if (i > startIdx + 2) return;
|
|
594
|
+
i++;
|
|
595
|
+
}
|
|
596
|
+
i++;
|
|
597
|
+
let depth = 1;
|
|
598
|
+
while (i < tokens.length && depth > 0) {
|
|
599
|
+
const t = tokens[i];
|
|
600
|
+
if (t.type === 127 /* OperatorLParen */) {
|
|
601
|
+
depth++;
|
|
602
|
+
i++;
|
|
603
|
+
continue;
|
|
604
|
+
}
|
|
605
|
+
if (t.type === 128 /* OperatorRParen */) {
|
|
606
|
+
depth--;
|
|
607
|
+
i++;
|
|
608
|
+
continue;
|
|
609
|
+
}
|
|
610
|
+
if (depth === 1 && t.type === 0 /* Identifier */) {
|
|
611
|
+
const prev = tokens[i - 1];
|
|
612
|
+
if (prev && PARAM_INTRO.has(prev.type)) define(t.value);
|
|
613
|
+
}
|
|
614
|
+
i++;
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
function collectDefinitions(source) {
|
|
618
|
+
const moduleDefs = /* @__PURE__ */ new Map();
|
|
619
|
+
const procs = [];
|
|
620
|
+
let tokens;
|
|
621
|
+
try {
|
|
622
|
+
tokens = new Lexer(source).tokenize().filter((t) => t.type !== 135 /* Newline */ && t.type !== 136 /* EOF */);
|
|
623
|
+
} catch {
|
|
624
|
+
return { moduleDefs, procs };
|
|
625
|
+
}
|
|
626
|
+
const defModule = (name) => {
|
|
627
|
+
if (!moduleDefs.has(name.toLowerCase())) moduleDefs.set(name.toLowerCase(), name);
|
|
628
|
+
};
|
|
629
|
+
let currentProc = null;
|
|
630
|
+
const defLocal = (name) => {
|
|
631
|
+
const map = currentProc?.defs ?? moduleDefs;
|
|
632
|
+
if (!map.has(name.toLowerCase())) map.set(name.toLowerCase(), name);
|
|
633
|
+
};
|
|
634
|
+
let inTypeOrEnum = false;
|
|
635
|
+
let prevLine = -1;
|
|
636
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
637
|
+
const t = tokens[i];
|
|
638
|
+
const next = tokens[i + 1];
|
|
639
|
+
if (inTypeOrEnum) {
|
|
640
|
+
if (t.type === 10 /* KeywordEnd */) {
|
|
641
|
+
if (next?.type === 48 /* KeywordType */ || next?.type === 59 /* KeywordEnum */) {
|
|
642
|
+
inTypeOrEnum = false;
|
|
643
|
+
}
|
|
644
|
+
prevLine = t.line;
|
|
645
|
+
continue;
|
|
646
|
+
}
|
|
647
|
+
if (t.type === 0 /* Identifier */ && t.line !== prevLine) {
|
|
648
|
+
defModule(t.value);
|
|
649
|
+
}
|
|
650
|
+
prevLine = t.line;
|
|
651
|
+
continue;
|
|
652
|
+
}
|
|
653
|
+
if (ACCESS_MODIFIERS.has(t.type)) continue;
|
|
654
|
+
if (t.type === 10 /* KeywordEnd */) {
|
|
655
|
+
if (next?.type === 17 /* KeywordSub */ || next?.type === 18 /* KeywordFunction */ || next?.type === 19 /* KeywordProperty */) {
|
|
656
|
+
if (currentProc) {
|
|
657
|
+
procs.push({ startLine: currentProc.startLine, endLine: next.line, defs: currentProc.defs });
|
|
658
|
+
currentProc = null;
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
if (next?.type === 48 /* KeywordType */ || next?.type === 59 /* KeywordEnum */) {
|
|
662
|
+
inTypeOrEnum = false;
|
|
663
|
+
}
|
|
664
|
+
continue;
|
|
665
|
+
}
|
|
666
|
+
switch (t.type) {
|
|
667
|
+
case 22 /* KeywordDim */:
|
|
668
|
+
case 31 /* KeywordConst */:
|
|
669
|
+
case 68 /* KeywordStatic */: {
|
|
670
|
+
if (next?.type === 0 /* Identifier */) defLocal(next.value);
|
|
671
|
+
break;
|
|
672
|
+
}
|
|
673
|
+
case 42 /* KeywordReDim */: {
|
|
674
|
+
const skip = next?.value?.toLowerCase() === "preserve" ? tokens[i + 2] : next;
|
|
675
|
+
if (skip?.type === 0 /* Identifier */) defLocal(skip.value);
|
|
676
|
+
break;
|
|
677
|
+
}
|
|
678
|
+
case 17 /* KeywordSub */:
|
|
679
|
+
case 18 /* KeywordFunction */: {
|
|
680
|
+
if (next?.type === 0 /* Identifier */) {
|
|
681
|
+
defModule(next.value);
|
|
682
|
+
currentProc = { startLine: t.line, defs: /* @__PURE__ */ new Map() };
|
|
683
|
+
collectParamNames(tokens, i + 2, (name) => defLocal(name));
|
|
684
|
+
}
|
|
685
|
+
break;
|
|
686
|
+
}
|
|
687
|
+
case 19 /* KeywordProperty */: {
|
|
688
|
+
let nameIdx = i + 1;
|
|
689
|
+
const verb = tokens[nameIdx]?.value?.toLowerCase();
|
|
690
|
+
if (verb === "get" || verb === "let" || verb === "set") nameIdx++;
|
|
691
|
+
if (tokens[nameIdx]?.type === 0 /* Identifier */) {
|
|
692
|
+
defModule(tokens[nameIdx].value);
|
|
693
|
+
currentProc = { startLine: t.line, defs: /* @__PURE__ */ new Map() };
|
|
694
|
+
collectParamNames(tokens, nameIdx + 1, (name) => defLocal(name));
|
|
695
|
+
}
|
|
696
|
+
break;
|
|
697
|
+
}
|
|
698
|
+
case 48 /* KeywordType */:
|
|
699
|
+
case 59 /* KeywordEnum */: {
|
|
700
|
+
if (next?.type === 0 /* Identifier */) defModule(next.value);
|
|
701
|
+
inTypeOrEnum = true;
|
|
702
|
+
break;
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
return { moduleDefs, procs };
|
|
707
|
+
}
|
|
708
|
+
function format(source, options = {}) {
|
|
709
|
+
const indentSize = options.indentSize ?? 4;
|
|
710
|
+
const indentChar = options.indentChar ?? " ";
|
|
711
|
+
const doKeywordCase = options.keywordCase !== false;
|
|
712
|
+
const unit = indentChar.repeat(indentSize);
|
|
713
|
+
const physicalLines = source.split("\n");
|
|
714
|
+
const scopedDefs = collectDefinitions(source);
|
|
715
|
+
const keywordChangesByLine = /* @__PURE__ */ new Map();
|
|
716
|
+
{
|
|
717
|
+
let tokens;
|
|
718
|
+
try {
|
|
719
|
+
tokens = new Lexer(source).tokenize();
|
|
720
|
+
} catch {
|
|
721
|
+
tokens = [];
|
|
722
|
+
}
|
|
723
|
+
for (const token of tokens) {
|
|
724
|
+
let canonical;
|
|
725
|
+
if (doKeywordCase) {
|
|
726
|
+
canonical = KEYWORD_CANONICAL.get(token.type);
|
|
727
|
+
}
|
|
728
|
+
if (!canonical && token.type === 0 /* Identifier */) {
|
|
729
|
+
const lv = token.value.toLowerCase();
|
|
730
|
+
const proc = scopedDefs.procs.find(
|
|
731
|
+
(p) => token.line >= p.startLine && token.line <= p.endLine
|
|
732
|
+
);
|
|
733
|
+
canonical = proc ? proc.defs.get(lv) ?? scopedDefs.moduleDefs.get(lv) : scopedDefs.moduleDefs.get(lv);
|
|
734
|
+
}
|
|
735
|
+
if (!canonical) continue;
|
|
736
|
+
const value = token.value;
|
|
737
|
+
if (value === canonical) continue;
|
|
738
|
+
const lineIdx = token.line - 1;
|
|
739
|
+
if (!keywordChangesByLine.has(lineIdx)) keywordChangesByLine.set(lineIdx, []);
|
|
740
|
+
keywordChangesByLine.get(lineIdx).push({
|
|
741
|
+
startCol: token.column - 1,
|
|
742
|
+
endCol: token.column - 1 + value.length,
|
|
743
|
+
text: canonical
|
|
744
|
+
});
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
const edits = [];
|
|
748
|
+
let level = 0;
|
|
749
|
+
let selectDepth = 0;
|
|
750
|
+
let continuationAccum = [];
|
|
751
|
+
for (let i = 0; i < physicalLines.length; i++) {
|
|
752
|
+
const rawLine = physicalLines[i];
|
|
753
|
+
const hasCR = rawLine.endsWith("\r");
|
|
754
|
+
const line = hasCR ? rawLine.slice(0, -1) : rawLine;
|
|
755
|
+
const trimmed = line.trim();
|
|
756
|
+
if (trimmed === "") continue;
|
|
757
|
+
const prevLine = i > 0 ? physicalLines[i - 1].replace(/\r$/, "").trimEnd() : "";
|
|
758
|
+
const isContinuation = prevLine.endsWith(" _") || prevLine.endsWith(" _");
|
|
759
|
+
if (!isContinuation) {
|
|
760
|
+
continuationAccum = [trimmed];
|
|
761
|
+
} else {
|
|
762
|
+
continuationAccum.push(trimmed);
|
|
763
|
+
}
|
|
764
|
+
const isLastContinuation = isContinuation && !trimmed.endsWith(" _") && !trimmed.endsWith(" _");
|
|
765
|
+
const lineTokens = tokenizeLine(trimmed);
|
|
766
|
+
const first = lineTokens[0]?.type;
|
|
767
|
+
const second = lineTokens[1]?.type;
|
|
768
|
+
const isLabel = lineTokens.length >= 2 && lineTokens[0].type === 0 /* Identifier */ && lineTokens[1].type === 130 /* OperatorColon */;
|
|
769
|
+
let expectedLevel = level;
|
|
770
|
+
if (!isContinuation && !isLabel) {
|
|
771
|
+
const startsMultiLine = trimmed.endsWith(" _") || trimmed.endsWith(" _");
|
|
772
|
+
const { closerBefore, openerAfter } = classifyLine(lineTokens, selectDepth);
|
|
773
|
+
if (closerBefore) {
|
|
774
|
+
level = Math.max(0, level - 1);
|
|
775
|
+
if (first === 10 /* KeywordEnd */ && second === 53 /* KeywordSelect */) {
|
|
776
|
+
selectDepth = Math.max(0, selectDepth - 1);
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
expectedLevel = level;
|
|
780
|
+
if (openerAfter && !startsMultiLine) {
|
|
781
|
+
level++;
|
|
782
|
+
if (first === 53 /* KeywordSelect */ && second === 54 /* KeywordCase */) {
|
|
783
|
+
selectDepth++;
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
} else if (isLastContinuation && !isLabel) {
|
|
787
|
+
const logicalLine = continuationAccum.map((l) => l.replace(/[ \t]+_[ \t]*$/, "")).join(" ");
|
|
788
|
+
const logTokens = tokenizeLine(logicalLine);
|
|
789
|
+
const logFirst = logTokens[0]?.type;
|
|
790
|
+
const logSecond = logTokens[1]?.type;
|
|
791
|
+
const { openerAfter } = classifyLine(logTokens, selectDepth);
|
|
792
|
+
if (openerAfter) {
|
|
793
|
+
level++;
|
|
794
|
+
if (logFirst === 53 /* KeywordSelect */ && logSecond === 54 /* KeywordCase */) {
|
|
795
|
+
selectDepth++;
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
const expectedIndent = isContinuation ? "" : isLabel ? "" : unit.repeat(expectedLevel);
|
|
800
|
+
const actualIndent = leadingWhitespace(line);
|
|
801
|
+
const contentStart = actualIndent.length;
|
|
802
|
+
const changes = keywordChangesByLine.get(i) ?? [];
|
|
803
|
+
let content = line.slice(contentStart);
|
|
804
|
+
if (changes.length > 0) {
|
|
805
|
+
const contentChanges = changes.map((c) => ({ start: c.startCol - contentStart, end: c.endCol - contentStart, text: c.text })).filter((c) => c.start >= 0).sort((a, b) => b.start - a.start);
|
|
806
|
+
for (const c of contentChanges) {
|
|
807
|
+
content = content.slice(0, c.start) + c.text + content.slice(c.end);
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
const newLine = (isContinuation ? actualIndent : expectedIndent) + content + (hasCR ? "\r" : "");
|
|
811
|
+
if (newLine !== rawLine) {
|
|
812
|
+
edits.push({
|
|
813
|
+
range: {
|
|
814
|
+
start: { line: i, character: 0 },
|
|
815
|
+
end: { line: i, character: rawLine.length }
|
|
816
|
+
},
|
|
817
|
+
newText: newLine
|
|
818
|
+
});
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
return edits;
|
|
822
|
+
}
|
|
823
|
+
var BLOCK_OPEN = /* @__PURE__ */ new Set([
|
|
824
|
+
17 /* KeywordSub */,
|
|
825
|
+
18 /* KeywordFunction */,
|
|
826
|
+
19 /* KeywordProperty */,
|
|
827
|
+
3 /* KeywordFor */,
|
|
828
|
+
11 /* KeywordDo */,
|
|
829
|
+
12 /* KeywordWhile */,
|
|
830
|
+
61 /* KeywordWith */,
|
|
831
|
+
48 /* KeywordType */,
|
|
832
|
+
59 /* KeywordEnum */,
|
|
833
|
+
69 /* KeywordClass */
|
|
834
|
+
]);
|
|
835
|
+
var BLOCK_CLOSE_SINGLE = /* @__PURE__ */ new Set([
|
|
836
|
+
5 /* KeywordNext */,
|
|
837
|
+
13 /* KeywordWend */,
|
|
838
|
+
14 /* KeywordLoop */
|
|
839
|
+
]);
|
|
840
|
+
var ACCESS_MODIFIERS = /* @__PURE__ */ new Set([
|
|
841
|
+
57 /* KeywordPublic */,
|
|
842
|
+
58 /* KeywordPrivate */,
|
|
843
|
+
60 /* KeywordFriend */,
|
|
844
|
+
68 /* KeywordStatic */
|
|
845
|
+
]);
|
|
846
|
+
function classifyLine(tokens, selectDepth) {
|
|
847
|
+
if (tokens.length === 0) return { closerBefore: false, openerAfter: false };
|
|
848
|
+
let offset = 0;
|
|
849
|
+
while (offset < tokens.length && ACCESS_MODIFIERS.has(tokens[offset].type)) {
|
|
850
|
+
offset++;
|
|
851
|
+
}
|
|
852
|
+
const first = tokens[offset]?.type ?? tokens[0].type;
|
|
853
|
+
const second = tokens[offset + 1]?.type;
|
|
854
|
+
if (first === 10 /* KeywordEnd */ && second !== void 0) {
|
|
855
|
+
const isEndProc = second === 17 /* KeywordSub */ || second === 18 /* KeywordFunction */ || second === 19 /* KeywordProperty */;
|
|
856
|
+
const isEndBlock = second === 6 /* KeywordIf */ || second === 61 /* KeywordWith */ || second === 48 /* KeywordType */ || second === 59 /* KeywordEnum */ || second === 69 /* KeywordClass */ || second === 53 /* KeywordSelect */;
|
|
857
|
+
if (isEndProc || isEndBlock) {
|
|
858
|
+
return { closerBefore: true, openerAfter: false };
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
if (BLOCK_CLOSE_SINGLE.has(first)) {
|
|
862
|
+
return { closerBefore: true, openerAfter: false };
|
|
863
|
+
}
|
|
864
|
+
if (first === 9 /* KeywordElse */ || first === 8 /* KeywordElseIf */) {
|
|
865
|
+
return { closerBefore: true, openerAfter: true };
|
|
866
|
+
}
|
|
867
|
+
if (first === 54 /* KeywordCase */ && selectDepth > 0) {
|
|
868
|
+
return { closerBefore: true, openerAfter: true };
|
|
869
|
+
}
|
|
870
|
+
if (BLOCK_OPEN.has(first)) {
|
|
871
|
+
return { closerBefore: false, openerAfter: true };
|
|
872
|
+
}
|
|
873
|
+
if (first === 53 /* KeywordSelect */ && second === 54 /* KeywordCase */) {
|
|
874
|
+
return { closerBefore: false, openerAfter: true };
|
|
875
|
+
}
|
|
876
|
+
if (first === 6 /* KeywordIf */) {
|
|
877
|
+
const thenIdx = tokens.findIndex((t) => t.type === 7 /* KeywordThen */);
|
|
878
|
+
if (thenIdx !== -1) {
|
|
879
|
+
const afterThen = tokens.slice(thenIdx + 1).filter((t) => t.type !== 130 /* OperatorColon */);
|
|
880
|
+
if (afterThen.length === 0) {
|
|
881
|
+
return { closerBefore: false, openerAfter: true };
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
return { closerBefore: false, openerAfter: false };
|
|
885
|
+
}
|
|
886
|
+
return { closerBefore: false, openerAfter: false };
|
|
887
|
+
}
|
|
888
|
+
function tokenizeLine(trimmed) {
|
|
889
|
+
try {
|
|
890
|
+
return new Lexer(trimmed).tokenize().filter(
|
|
891
|
+
(t) => t.type !== 135 /* Newline */ && t.type !== 136 /* EOF */
|
|
892
|
+
);
|
|
893
|
+
} catch {
|
|
894
|
+
return [];
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
function leadingWhitespace(line) {
|
|
898
|
+
const match = line.match(/^[ \t]*/);
|
|
899
|
+
return match ? match[0] : "";
|
|
900
|
+
}
|
|
901
|
+
function applyEdits(source, edits) {
|
|
902
|
+
if (edits.length === 0) return source;
|
|
903
|
+
const lines = source.split("\n");
|
|
904
|
+
const sorted = [...edits].sort((a, b) => b.range.start.line - a.range.start.line);
|
|
905
|
+
for (const edit of sorted) {
|
|
906
|
+
const lineIdx = edit.range.start.line;
|
|
907
|
+
const origLine = lines[lineIdx];
|
|
908
|
+
lines[lineIdx] = origLine.slice(0, edit.range.start.character) + edit.newText + origLine.slice(edit.range.end.character);
|
|
909
|
+
}
|
|
910
|
+
return lines.join("\n");
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
// ../../test-libs/vba-formatter.ts
|
|
914
|
+
var VBA_EXTENSIONS = /\.(bas|cls|frm)$/i;
|
|
915
|
+
function collectFiles(target) {
|
|
916
|
+
const stat = fs.statSync(target);
|
|
917
|
+
if (stat.isFile()) return [target];
|
|
918
|
+
if (stat.isDirectory()) {
|
|
919
|
+
return fs.readdirSync(target).flatMap((entry) => {
|
|
920
|
+
const full = path.join(target, entry);
|
|
921
|
+
const s = fs.statSync(full);
|
|
922
|
+
if (s.isDirectory()) return collectFiles(full);
|
|
923
|
+
if (VBA_EXTENSIONS.test(entry)) return [full];
|
|
924
|
+
return [];
|
|
925
|
+
});
|
|
926
|
+
}
|
|
927
|
+
return [];
|
|
928
|
+
}
|
|
929
|
+
function buildDiff(original, formatted, filePath) {
|
|
930
|
+
const origLines = original.split("\n");
|
|
931
|
+
const fmtLines = formatted.split("\n");
|
|
932
|
+
const maxLen = Math.max(origLines.length, fmtLines.length);
|
|
933
|
+
const diffLines = [`--- ${filePath} (original)`, `+++ ${filePath} (formatted)`];
|
|
934
|
+
let hasDiff = false;
|
|
935
|
+
for (let i = 0; i < maxLen; i++) {
|
|
936
|
+
const orig = origLines[i] ?? "";
|
|
937
|
+
const fmtd = fmtLines[i] ?? "";
|
|
938
|
+
if (orig !== fmtd) {
|
|
939
|
+
hasDiff = true;
|
|
940
|
+
diffLines.push(`@@ line ${i + 1} @@`);
|
|
941
|
+
diffLines.push(`-${orig}`);
|
|
942
|
+
diffLines.push(`+${fmtd}`);
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
return hasDiff ? diffLines.join("\n") : "";
|
|
946
|
+
}
|
|
947
|
+
function main() {
|
|
948
|
+
const args = process.argv.slice(2);
|
|
949
|
+
if (args.length === 0 || args[0] === "--help" || args[0] === "-h") {
|
|
950
|
+
console.log(`Usage: vba-formatter <file-or-dir> [--check | --write] [options]
|
|
951
|
+
|
|
952
|
+
Options:
|
|
953
|
+
--check \u5DEE\u5206\u304C\u3042\u308C\u3070\u8868\u793A\u3057\u3066 exit code 1 \u3067\u7D42\u4E86
|
|
954
|
+
--write \u30D5\u30A1\u30A4\u30EB\u3092\u4E0A\u66F8\u304D\uFF08\u6307\u5B9A\u3057\u306A\u3044\u5834\u5408\u306F stdout \u306B\u51FA\u529B\uFF09
|
|
955
|
+
--indent-size=N \u30A4\u30F3\u30C7\u30F3\u30C8\u5E45\uFF08\u30C7\u30D5\u30A9\u30EB\u30C8: 4\uFF09
|
|
956
|
+
--no-keyword-case \u30AD\u30FC\u30EF\u30FC\u30C9\u306E\u5927\u6587\u5B57\u5316\u3092\u7121\u52B9\u5316
|
|
957
|
+
|
|
958
|
+
Example:
|
|
959
|
+
npx tsx test-libs/vba-formatter.ts src/vba/Main.bas
|
|
960
|
+
npx tsx test-libs/vba-formatter.ts src/vba/ --check
|
|
961
|
+
npx tsx test-libs/vba-formatter.ts src/vba/Main.bas --write --indent-size=2`);
|
|
962
|
+
process.exit(0);
|
|
963
|
+
}
|
|
964
|
+
const target = args.find((a) => !a.startsWith("-"));
|
|
965
|
+
const isCheck = args.includes("--check");
|
|
966
|
+
const isWrite = args.includes("--write");
|
|
967
|
+
const noKeywordCase = args.includes("--no-keyword-case");
|
|
968
|
+
const indentSizeArg = args.find((a) => a.startsWith("--indent-size="));
|
|
969
|
+
const indentSize = indentSizeArg ? parseInt(indentSizeArg.split("=")[1], 10) : 4;
|
|
970
|
+
if (!target || !fs.existsSync(target)) {
|
|
971
|
+
console.error(`Error: "${target ?? "(\u6307\u5B9A\u306A\u3057)"}" \u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093`);
|
|
972
|
+
process.exit(1);
|
|
973
|
+
}
|
|
974
|
+
const options = {
|
|
975
|
+
indentSize,
|
|
976
|
+
keywordCase: noKeywordCase ? false : "pascal"
|
|
977
|
+
};
|
|
978
|
+
const files = collectFiles(target);
|
|
979
|
+
if (files.length === 0) {
|
|
980
|
+
console.error(`Error: VBA\u30D5\u30A1\u30A4\u30EB\uFF08.bas/.cls/.frm\uFF09\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093: ${target}`);
|
|
981
|
+
process.exit(1);
|
|
982
|
+
}
|
|
983
|
+
let hasAnyDiff = false;
|
|
984
|
+
for (const filePath of files) {
|
|
985
|
+
const original = fs.readFileSync(filePath, "utf8");
|
|
986
|
+
const edits = format(original, options);
|
|
987
|
+
const formatted = applyEdits(original, edits);
|
|
988
|
+
const hasDiff = formatted !== original;
|
|
989
|
+
if (isCheck) {
|
|
990
|
+
if (hasDiff) {
|
|
991
|
+
hasAnyDiff = true;
|
|
992
|
+
const diff = buildDiff(original, formatted, filePath);
|
|
993
|
+
console.log(diff);
|
|
994
|
+
}
|
|
995
|
+
} else if (isWrite) {
|
|
996
|
+
if (hasDiff) {
|
|
997
|
+
fs.writeFileSync(filePath, formatted, "utf8");
|
|
998
|
+
console.log(`\u30D5\u30A9\u30FC\u30DE\u30C3\u30C8\u6E08\u307F: ${filePath}`);
|
|
999
|
+
}
|
|
1000
|
+
} else {
|
|
1001
|
+
if (files.length > 1) {
|
|
1002
|
+
console.error("Error: stdout \u30E2\u30FC\u30C9\u306F\u5358\u4E00\u30D5\u30A1\u30A4\u30EB\u306E\u307F\u5BFE\u5FDC\u3057\u3066\u3044\u307E\u3059\u3002\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u306B\u306F --check \u307E\u305F\u306F --write \u3092\u4F7F\u7528\u3057\u3066\u304F\u3060\u3055\u3044\u3002");
|
|
1003
|
+
process.exit(1);
|
|
1004
|
+
}
|
|
1005
|
+
process.stdout.write(formatted);
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
if (isCheck && hasAnyDiff) {
|
|
1009
|
+
process.exit(1);
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
main();
|