vba-runner 0.1.1-alpha.0 → 0.1.1-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/vba-analyzer.cjs +163 -85
- package/dist/bin/vba-formatter.cjs +28 -7
- package/dist/bin/vba-parse-check.cjs +189 -109
- package/dist/bin/vba-run.cjs +368 -229
- package/dist/bin/vba-runner.cjs +12631 -0
- package/dist/lib.cjs +202 -90
- package/package.json +3 -2
|
@@ -41,6 +41,7 @@ __export(vba_analyzer_exports, {
|
|
|
41
41
|
formatWorkspaceOutlineForMcp: () => formatWorkspaceOutline,
|
|
42
42
|
formatWorkspaceReportForMcp: () => formatWorkspaceReportForMcp,
|
|
43
43
|
formatWorkspaceSummary: () => formatWorkspaceSummary,
|
|
44
|
+
main: () => main,
|
|
44
45
|
paramName: () => paramName,
|
|
45
46
|
vbaTypeToTs: () => vbaTypeToTs
|
|
46
47
|
});
|
|
@@ -591,25 +592,46 @@ var Parser = class _Parser {
|
|
|
591
592
|
..._Parser.CONTEXTUAL_KW_STMT_ABSENT,
|
|
592
593
|
..._Parser.CONTEXTUAL_KW_STRUCTURAL
|
|
593
594
|
]);
|
|
594
|
-
/** <statement-keyword> tokens
|
|
595
|
-
*
|
|
596
|
-
*
|
|
597
|
-
*
|
|
595
|
+
/** <statement-keyword> tokens that are <reserved-identifier> per §3.3.5.2 but whose
|
|
596
|
+
* enum values fall in [KeywordBase, KeywordAddressOf].
|
|
597
|
+
* These CANNOT be used as module-level procedure names (parseProcedureDeclaration rejects them).
|
|
598
|
+
* They ARE permitted as member names in expression context (obj.Print, ws.Get, obj.Open) via
|
|
599
|
+
* parsePrimary, which is why they appear in COMPAT_KW_EXPR. */
|
|
600
|
+
static STATEMENT_KW_RESERVED = /* @__PURE__ */ new Set([
|
|
601
|
+
81 /* KeywordOpen */,
|
|
602
|
+
82 /* KeywordClose */,
|
|
603
|
+
84 /* KeywordInput */,
|
|
604
|
+
85 /* KeywordPrint */,
|
|
605
|
+
86 /* KeywordPut */,
|
|
606
|
+
92 /* KeywordWrite */,
|
|
607
|
+
93 /* KeywordLock */,
|
|
608
|
+
98 /* KeywordSeek */,
|
|
609
|
+
100 /* KeywordUnlock */,
|
|
610
|
+
102 /* KeywordEvent */,
|
|
611
|
+
103 /* KeywordRaiseEvent */,
|
|
612
|
+
105 /* KeywordImplements */
|
|
613
|
+
]);
|
|
614
|
+
/** <statement-keyword> tokens permitted as member names in parsePrimary (obj.Print, ws.Get,
|
|
615
|
+
* obj.Open, etc.) per §3.3.5.2 "unrestricted-name" rule.
|
|
616
|
+
* In parseStatementInner these keywords are all dispatched unconditionally before the
|
|
617
|
+
* identifier branch, so the COMPAT_KW_EXPR entries there are never reached — they exist
|
|
618
|
+
* solely for parsePrimary dot-member-access context. */
|
|
598
619
|
static COMPAT_KW_EXPR = /* @__PURE__ */ new Set([
|
|
599
620
|
98 /* KeywordSeek */,
|
|
600
|
-
// <statement-keyword>
|
|
601
621
|
84 /* KeywordInput */,
|
|
602
|
-
// <statement-keyword> / <special-form>
|
|
603
622
|
85 /* KeywordPrint */,
|
|
604
|
-
// <statement-keyword>
|
|
605
623
|
86 /* KeywordPut */,
|
|
606
|
-
// <statement-keyword>
|
|
607
624
|
20 /* KeywordGet */,
|
|
608
|
-
// <
|
|
625
|
+
// enum < KeywordBase so also rejected by range check in parseProcedureDeclaration
|
|
609
626
|
93 /* KeywordLock */,
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
627
|
+
100 /* KeywordUnlock */,
|
|
628
|
+
81 /* KeywordOpen */,
|
|
629
|
+
82 /* KeywordClose */,
|
|
630
|
+
// Non-reserved statement keywords: can be user-defined proc names (e.g. Function Kill()).
|
|
631
|
+
// Included here so "Kill = v", "Reset = v", "Width = v" fall through to identifier branch.
|
|
632
|
+
97 /* KeywordKill */,
|
|
633
|
+
99 /* KeywordReset */,
|
|
634
|
+
109 /* KeywordWidth */
|
|
613
635
|
]);
|
|
614
636
|
errorRecovery;
|
|
615
637
|
/** Returns true if token is a valid IDENTIFIER per §3.3.5.2:
|
|
@@ -626,6 +648,15 @@ var Parser = class _Parser {
|
|
|
626
648
|
isNameToken(token) {
|
|
627
649
|
return token.type === 0 /* Identifier */ || token.type === 138 /* ForeignName */ || token.type >= 3 /* KeywordFor */ && token.type <= 110 /* KeywordAddressOf */;
|
|
628
650
|
}
|
|
651
|
+
/**
|
|
652
|
+
* Returns true if the current token stream contains a file-I/O Open statement pattern
|
|
653
|
+
* ("For <file-mode>") on the current logical line.
|
|
654
|
+
* Open "path" For Input|Output|Append|Random|Binary ... As #n → true
|
|
655
|
+
* Open() / Open = value / Open x (user-defined call) → false
|
|
656
|
+
*
|
|
657
|
+
* This syntactic check lets the parser disambiguate without a pre-scan:
|
|
658
|
+
* VBA §3.3.5.3 — user-defined procedures (priority 2) > built-in statement keywords (priority 3).
|
|
659
|
+
*/
|
|
629
660
|
recordError(message, token) {
|
|
630
661
|
const pos = { line: token.line, column: token.column };
|
|
631
662
|
this._diagnostics.push({ message, loc: { start: pos, end: pos }, severity: "error" });
|
|
@@ -1116,6 +1147,67 @@ var Parser = class _Parser {
|
|
|
1116
1147
|
}
|
|
1117
1148
|
return stmt;
|
|
1118
1149
|
}
|
|
1150
|
+
/**
|
|
1151
|
+
* Parse an identifier, method call, or assignment statement.
|
|
1152
|
+
* Called from the identifier branch of parseStatementInner AND from the ARCH-1
|
|
1153
|
+
* user-proc override path (when a built-in keyword is user-defined as a procedure).
|
|
1154
|
+
*/
|
|
1155
|
+
parseIdentifierOrCallStatement() {
|
|
1156
|
+
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 === 130 /* OperatorColon */) {
|
|
1158
|
+
const labelName = token.value;
|
|
1159
|
+
this.advance();
|
|
1160
|
+
this.advance();
|
|
1161
|
+
return { type: "LabelStatement", label: labelName };
|
|
1162
|
+
}
|
|
1163
|
+
if (token.type === 1 /* Number */) {
|
|
1164
|
+
const labelName = token.value;
|
|
1165
|
+
this.advance();
|
|
1166
|
+
this.match(130 /* OperatorColon */);
|
|
1167
|
+
return { type: "LabelStatement", label: labelName };
|
|
1168
|
+
}
|
|
1169
|
+
const savedPos = this.pos;
|
|
1170
|
+
const expr = this.parsePrimary();
|
|
1171
|
+
if (this.match(120 /* OperatorEquals */)) {
|
|
1172
|
+
return {
|
|
1173
|
+
type: "AssignmentStatement",
|
|
1174
|
+
left: expr,
|
|
1175
|
+
right: this.parseExpression()
|
|
1176
|
+
};
|
|
1177
|
+
} else {
|
|
1178
|
+
if (expr.type === "CallExpression" && this.isBinaryOnlyOperator(this.peek().type)) {
|
|
1179
|
+
this.pos = savedPos;
|
|
1180
|
+
const callee = this.parsePrimary(
|
|
1181
|
+
/* stopBeforeSpacedLParen= */
|
|
1182
|
+
true
|
|
1183
|
+
);
|
|
1184
|
+
const args2 = [this.parseCallArgument()];
|
|
1185
|
+
while (this.match(126 /* OperatorComma */)) {
|
|
1186
|
+
args2.push(this.parseCallArgument());
|
|
1187
|
+
}
|
|
1188
|
+
return { type: "CallStatement", expression: { type: "CallExpression", callee, args: args2 } };
|
|
1189
|
+
}
|
|
1190
|
+
const args = [];
|
|
1191
|
+
if (this.peek().type !== 135 /* Newline */ && this.peek().type !== 136 /* 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
|
+
args.push(this.parseCallArgument());
|
|
1193
|
+
while (this.match(126 /* OperatorComma */)) {
|
|
1194
|
+
args.push(this.parseCallArgument());
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
1197
|
+
if (args.length > 0) {
|
|
1198
|
+
return { type: "CallStatement", expression: { type: "CallExpression", callee: expr, args } };
|
|
1199
|
+
} else if (expr.type === "CallExpression") {
|
|
1200
|
+
const callExpr = expr;
|
|
1201
|
+
if (callExpr.args.length === 0 && callExpr.callee.type === "Identifier" && !callExpr.callee.foreign) {
|
|
1202
|
+
const line = callExpr.loc?.start.line ?? this.peek().line;
|
|
1203
|
+
this.throwError(`Parse error: syntax error at line ${line}`);
|
|
1204
|
+
}
|
|
1205
|
+
return { type: "CallStatement", expression: callExpr };
|
|
1206
|
+
} else {
|
|
1207
|
+
return { type: "CallStatement", expression: { type: "CallExpression", callee: expr, args: [] } };
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
}
|
|
1119
1211
|
parseStatementInner() {
|
|
1120
1212
|
const token = this.peek();
|
|
1121
1213
|
if (_Parser.CONTEXTUAL_KW.has(token.type) && this.pos + 1 < this.tokens.length && this.tokens[this.pos + 1].type === 130 /* OperatorColon */) {
|
|
@@ -1150,6 +1242,9 @@ var Parser = class _Parser {
|
|
|
1150
1242
|
stmt2.scope = scope;
|
|
1151
1243
|
return stmt2;
|
|
1152
1244
|
}
|
|
1245
|
+
if (next.type === 102 /* KeywordEvent */) {
|
|
1246
|
+
return this.parseEventDeclaration(scope);
|
|
1247
|
+
}
|
|
1153
1248
|
const stmt = this.parseDimStatement(false, true);
|
|
1154
1249
|
if (stmt) {
|
|
1155
1250
|
stmt.scope = scope;
|
|
@@ -1271,7 +1366,7 @@ var Parser = class _Parser {
|
|
|
1271
1366
|
return this.parsePrintStatement();
|
|
1272
1367
|
} else if (token.type === 86 /* KeywordPut */) {
|
|
1273
1368
|
return this.parsePutStatement();
|
|
1274
|
-
} else if (token.type === 20 /* KeywordGet */) {
|
|
1369
|
+
} else if (token.type === 20 /* KeywordGet */ && this.peek(1).type === 111 /* OperatorHash */) {
|
|
1275
1370
|
return this.parseGetStatement();
|
|
1276
1371
|
} else if (token.type === 84 /* KeywordInput */) {
|
|
1277
1372
|
return this.parseInputStatement();
|
|
@@ -1291,7 +1386,7 @@ var Parser = class _Parser {
|
|
|
1291
1386
|
return this.parseLockStatement();
|
|
1292
1387
|
} else if (token.type === 100 /* KeywordUnlock */) {
|
|
1293
1388
|
return this.parseUnlockStatement();
|
|
1294
|
-
} else if (token.type === 109 /* KeywordWidth */ && this.peek(1).type
|
|
1389
|
+
} else if (token.type === 109 /* KeywordWidth */ && this.peek(1).type === 111 /* OperatorHash */) {
|
|
1295
1390
|
return this.parseWidthStatement();
|
|
1296
1391
|
} else if (token.type === 69 /* KeywordClass */ && this.peek(1).type !== 120 /* OperatorEquals */) {
|
|
1297
1392
|
return this.parseClassDeclaration();
|
|
@@ -1304,59 +1399,8 @@ var Parser = class _Parser {
|
|
|
1304
1399
|
return { type: "CallStatement", expression: { type: "CallExpression", callee: expr, args: [] } };
|
|
1305
1400
|
}
|
|
1306
1401
|
this.throwError(`Parse error: Expected procedure call after 'Call'`);
|
|
1307
|
-
} else if (token.type === 0 /* Identifier */ || token.type === 138 /* ForeignName */ || token.type === 129 /* OperatorDot */ || token.type === 1 /* Number */ || _Parser.CONTEXTUAL_KW.has(token.type)) {
|
|
1308
|
-
|
|
1309
|
-
const labelName = token.value;
|
|
1310
|
-
this.advance();
|
|
1311
|
-
this.advance();
|
|
1312
|
-
return { type: "LabelStatement", label: labelName };
|
|
1313
|
-
} else if (token.type === 1 /* Number */) {
|
|
1314
|
-
const labelName = token.value;
|
|
1315
|
-
this.advance();
|
|
1316
|
-
this.match(130 /* OperatorColon */);
|
|
1317
|
-
return { type: "LabelStatement", label: labelName };
|
|
1318
|
-
}
|
|
1319
|
-
const savedPos = this.pos;
|
|
1320
|
-
const expr = this.parsePrimary();
|
|
1321
|
-
if (this.match(120 /* OperatorEquals */)) {
|
|
1322
|
-
return {
|
|
1323
|
-
type: "AssignmentStatement",
|
|
1324
|
-
left: expr,
|
|
1325
|
-
right: this.parseExpression()
|
|
1326
|
-
};
|
|
1327
|
-
} else {
|
|
1328
|
-
if (expr.type === "CallExpression" && this.isBinaryOnlyOperator(this.peek().type)) {
|
|
1329
|
-
this.pos = savedPos;
|
|
1330
|
-
const callee = this.parsePrimary(
|
|
1331
|
-
/* stopBeforeSpacedLParen= */
|
|
1332
|
-
true
|
|
1333
|
-
);
|
|
1334
|
-
const args2 = [this.parseCallArgument()];
|
|
1335
|
-
while (this.match(126 /* OperatorComma */)) {
|
|
1336
|
-
args2.push(this.parseCallArgument());
|
|
1337
|
-
}
|
|
1338
|
-
return { type: "CallStatement", expression: { type: "CallExpression", callee, args: args2 } };
|
|
1339
|
-
}
|
|
1340
|
-
const args = [];
|
|
1341
|
-
if (this.peek().type !== 135 /* Newline */ && this.peek().type !== 136 /* 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 */) {
|
|
1342
|
-
args.push(this.parseCallArgument());
|
|
1343
|
-
while (this.match(126 /* OperatorComma */)) {
|
|
1344
|
-
args.push(this.parseCallArgument());
|
|
1345
|
-
}
|
|
1346
|
-
}
|
|
1347
|
-
if (args.length > 0) {
|
|
1348
|
-
return { type: "CallStatement", expression: { type: "CallExpression", callee: expr, args } };
|
|
1349
|
-
} else if (expr.type === "CallExpression") {
|
|
1350
|
-
const callExpr = expr;
|
|
1351
|
-
if (callExpr.args.length === 0 && callExpr.callee.type === "Identifier" && !callExpr.callee.foreign) {
|
|
1352
|
-
const line = callExpr.loc?.start.line ?? this.peek().line;
|
|
1353
|
-
this.throwError(`Parse error: syntax error at line ${line}`);
|
|
1354
|
-
}
|
|
1355
|
-
return { type: "CallStatement", expression: callExpr };
|
|
1356
|
-
} else {
|
|
1357
|
-
return { type: "CallStatement", expression: { type: "CallExpression", callee: expr, args: [] } };
|
|
1358
|
-
}
|
|
1359
|
-
}
|
|
1402
|
+
} else if (token.type === 0 /* Identifier */ || token.type === 138 /* ForeignName */ || token.type === 129 /* OperatorDot */ || token.type === 70 /* KeywordMe */ || token.type === 1 /* Number */ || _Parser.CONTEXTUAL_KW.has(token.type) || _Parser.COMPAT_KW_EXPR.has(token.type)) {
|
|
1403
|
+
return this.parseIdentifierOrCallStatement();
|
|
1360
1404
|
} else if (token.type === 137 /* Unknown */) {
|
|
1361
1405
|
this.throwError(`Parse error: Unknown token '${this.tokenDisplay(token.value)}' at line ${token.line}`);
|
|
1362
1406
|
} else {
|
|
@@ -1364,7 +1408,7 @@ var Parser = class _Parser {
|
|
|
1364
1408
|
}
|
|
1365
1409
|
return null;
|
|
1366
1410
|
}
|
|
1367
|
-
parseProcedureDeclaration(scope, isStatic) {
|
|
1411
|
+
parseProcedureDeclaration(scope, isStatic, isClassMember = false) {
|
|
1368
1412
|
const isFunction = this.peek().type === 18 /* KeywordFunction */;
|
|
1369
1413
|
const isProperty = this.peek().type === 19 /* KeywordProperty */;
|
|
1370
1414
|
this.advance();
|
|
@@ -1382,6 +1426,9 @@ var Parser = class _Parser {
|
|
|
1382
1426
|
if (!this.isIdentifier(idToken) && (idToken.type < 79 /* KeywordBase */ || idToken.type > 110 /* KeywordAddressOf */)) {
|
|
1383
1427
|
this.throwError(`Parse error at line ${idToken.line}: Expected procedure name (Found ${this.tokenDisplay(idToken.value)})`);
|
|
1384
1428
|
}
|
|
1429
|
+
if (!isClassMember && _Parser.STATEMENT_KW_RESERVED.has(idToken.type)) {
|
|
1430
|
+
this.throwError(`Compile error at line ${idToken.line}: '${idToken.value}' is a reserved word and cannot be used as a procedure name`);
|
|
1431
|
+
}
|
|
1385
1432
|
const name = this.makeIdentifier(idToken);
|
|
1386
1433
|
const parameters = [];
|
|
1387
1434
|
let paramsEndColumn;
|
|
@@ -1544,7 +1591,7 @@ var Parser = class _Parser {
|
|
|
1544
1591
|
parseGoToStatement() {
|
|
1545
1592
|
this.advance();
|
|
1546
1593
|
const labelToken = this.advance();
|
|
1547
|
-
if (labelToken
|
|
1594
|
+
if (!this.isIdentifier(labelToken) && labelToken.type !== 1 /* Number */) {
|
|
1548
1595
|
this.throwError(`Parse error: Expected identifier or number after 'GoTo' at line ${labelToken.line}`);
|
|
1549
1596
|
}
|
|
1550
1597
|
return { type: "GoToStatement", label: labelToken.value };
|
|
@@ -1667,7 +1714,7 @@ var Parser = class _Parser {
|
|
|
1667
1714
|
parseClassDeclaration() {
|
|
1668
1715
|
this.advance();
|
|
1669
1716
|
const nameToken = this.advance();
|
|
1670
|
-
if (nameToken
|
|
1717
|
+
if (!this.isIdentifier(nameToken)) {
|
|
1671
1718
|
this.throwError(`Parse error: Expected class name after 'Class' at line ${nameToken.line}`);
|
|
1672
1719
|
}
|
|
1673
1720
|
return this.parseClassBody(nameToken.value, true);
|
|
@@ -1693,7 +1740,7 @@ var Parser = class _Parser {
|
|
|
1693
1740
|
}
|
|
1694
1741
|
const inner = this.peek();
|
|
1695
1742
|
if (inner.type === 17 /* KeywordSub */ || inner.type === 18 /* KeywordFunction */ || inner.type === 19 /* KeywordProperty */) {
|
|
1696
|
-
const proc = this.parseProcedureDeclaration(scope);
|
|
1743
|
+
const proc = this.parseProcedureDeclaration(scope, void 0, true);
|
|
1697
1744
|
proc.moduleName = className;
|
|
1698
1745
|
procedures.push(proc);
|
|
1699
1746
|
body.push(proc);
|
|
@@ -1709,13 +1756,16 @@ var Parser = class _Parser {
|
|
|
1709
1756
|
} else if (inner.type === 102 /* KeywordEvent */) {
|
|
1710
1757
|
const event = this.parseEventDeclaration(scope);
|
|
1711
1758
|
body.push(event);
|
|
1759
|
+
} else if (inner.type === 31 /* KeywordConst */) {
|
|
1760
|
+
const constDecl = this.parseConstDeclaration();
|
|
1761
|
+
body.push(constDecl);
|
|
1712
1762
|
} else if (inner.type === 68 /* KeywordStatic */) {
|
|
1713
1763
|
this.advance();
|
|
1714
1764
|
const field = this.parseDimStatement(true, true);
|
|
1715
1765
|
field.scope = scope ?? "public";
|
|
1716
1766
|
fields.push(field);
|
|
1717
1767
|
body.push(field);
|
|
1718
|
-
} else if (scope !== void 0 && inner.type ===
|
|
1768
|
+
} else if (scope !== void 0 && (this.isIdentifier(inner) || inner.type === 104 /* KeywordWithEvents */)) {
|
|
1719
1769
|
const field = this.parseDimStatement(false, true);
|
|
1720
1770
|
field.scope = scope;
|
|
1721
1771
|
fields.push(field);
|
|
@@ -2194,7 +2244,8 @@ var Parser = class _Parser {
|
|
|
2194
2244
|
} else if (token.type === 0 /* Identifier */ || _Parser.CONTEXTUAL_KW.has(token.type) || _Parser.COMPAT_KW_EXPR.has(token.type)) {
|
|
2195
2245
|
expr = { type: "Identifier", name: token.value };
|
|
2196
2246
|
} else if (token.type === 110 /* KeywordAddressOf */) {
|
|
2197
|
-
const procName = this.
|
|
2247
|
+
const procName = this.advance();
|
|
2248
|
+
if (!this.isIdentifier(procName)) this.throwError(`Parse error at line ${procName.line}: Expected procedure name after 'AddressOf'`);
|
|
2198
2249
|
expr = { type: "AddressOfExpression", procedureName: { type: "Identifier", name: procName.value } };
|
|
2199
2250
|
} else if (token.type === 44 /* KeywordEmpty */) {
|
|
2200
2251
|
expr = { type: "Identifier", name: token.value };
|
|
@@ -2229,7 +2280,7 @@ var Parser = class _Parser {
|
|
|
2229
2280
|
this.throwError(`Parse error: Expected 'Is' after 'TypeOf' at line ${this.peek().line}`);
|
|
2230
2281
|
}
|
|
2231
2282
|
const typeToken = this.advance();
|
|
2232
|
-
if (typeToken
|
|
2283
|
+
if (!this.isIdentifier(typeToken) && typeToken.type !== 25 /* KeywordCollection */) {
|
|
2233
2284
|
this.throwError(`Parse error: Expected type name after 'Is' at line ${typeToken.line}`);
|
|
2234
2285
|
}
|
|
2235
2286
|
expr = { type: "TypeOfIsExpression", expression: expr, typeName: typeToken.value };
|
|
@@ -2318,7 +2369,7 @@ var Parser = class _Parser {
|
|
|
2318
2369
|
const labels = [];
|
|
2319
2370
|
while (true) {
|
|
2320
2371
|
const labelToken = this.advance();
|
|
2321
|
-
if (labelToken
|
|
2372
|
+
if (!this.isIdentifier(labelToken) && labelToken.type !== 1 /* Number */) {
|
|
2322
2373
|
this.throwError(`Parse error: Expected label (identifier or number) in On...GoTo/GoSub at line ${labelToken.line}`);
|
|
2323
2374
|
}
|
|
2324
2375
|
labels.push(labelToken.value);
|
|
@@ -2331,7 +2382,7 @@ var Parser = class _Parser {
|
|
|
2331
2382
|
parseGoSubStatement() {
|
|
2332
2383
|
this.advance();
|
|
2333
2384
|
const labelToken = this.advance();
|
|
2334
|
-
if (labelToken
|
|
2385
|
+
if (!this.isIdentifier(labelToken) && labelToken.type !== 1 /* Number */) {
|
|
2335
2386
|
this.throwError(`Parse error: Expected label after GoSub at line ${labelToken.line}`);
|
|
2336
2387
|
}
|
|
2337
2388
|
return { type: "GoSubStatement", label: labelToken.value };
|
|
@@ -2361,7 +2412,8 @@ var Parser = class _Parser {
|
|
|
2361
2412
|
}
|
|
2362
2413
|
parseEventDeclaration(scope) {
|
|
2363
2414
|
this.advance();
|
|
2364
|
-
const idToken = this.
|
|
2415
|
+
const idToken = this.advance();
|
|
2416
|
+
if (!this.isIdentifier(idToken) && !this.isNameToken(idToken)) this.throwError(`Expected identifier after 'Event' at line ${idToken.line}`);
|
|
2365
2417
|
const name = { type: "Identifier", name: idToken.value };
|
|
2366
2418
|
const parameters = [];
|
|
2367
2419
|
if (this.match(127 /* OperatorLParen */)) {
|
|
@@ -2377,7 +2429,8 @@ var Parser = class _Parser {
|
|
|
2377
2429
|
}
|
|
2378
2430
|
parseRaiseEventStatement() {
|
|
2379
2431
|
this.advance();
|
|
2380
|
-
const idToken = this.
|
|
2432
|
+
const idToken = this.advance();
|
|
2433
|
+
if (!this.isIdentifier(idToken) && !this.isNameToken(idToken)) this.throwError(`Expected identifier after 'RaiseEvent' at line ${idToken.line}`);
|
|
2381
2434
|
const eventName = { type: "Identifier", name: idToken.value };
|
|
2382
2435
|
const args = [];
|
|
2383
2436
|
if (this.match(127 /* OperatorLParen */)) {
|
|
@@ -2393,7 +2446,8 @@ var Parser = class _Parser {
|
|
|
2393
2446
|
}
|
|
2394
2447
|
parseImplementsDirective() {
|
|
2395
2448
|
this.advance();
|
|
2396
|
-
const idToken = this.
|
|
2449
|
+
const idToken = this.advance();
|
|
2450
|
+
if (!this.isIdentifier(idToken)) this.throwError(`Parse error at line ${idToken.line}: Expected interface name after 'Implements'`);
|
|
2397
2451
|
return { type: "ImplementsDirective", interfaceName: idToken.value };
|
|
2398
2452
|
}
|
|
2399
2453
|
parseAppActivateStatement() {
|
|
@@ -2705,6 +2759,11 @@ function evaluateCCExpr(expr, resolve) {
|
|
|
2705
2759
|
// ../../test-libs/vba-analyzer.ts
|
|
2706
2760
|
var fs = __toESM(require("fs"), 1);
|
|
2707
2761
|
var path = __toESM(require("path"), 1);
|
|
2762
|
+
|
|
2763
|
+
// ../../test-libs/version.ts
|
|
2764
|
+
var VERSION = "0.1.1-alpha.1";
|
|
2765
|
+
|
|
2766
|
+
// ../../test-libs/vba-analyzer.ts
|
|
2708
2767
|
function isStatementArray(v) {
|
|
2709
2768
|
return Array.isArray(v);
|
|
2710
2769
|
}
|
|
@@ -4208,7 +4267,8 @@ function analyzeFile(filePath) {
|
|
|
4208
4267
|
definedTypes: /* @__PURE__ */ new Set(),
|
|
4209
4268
|
callsByProc: /* @__PURE__ */ new Map(),
|
|
4210
4269
|
xlVbConstantRefs: /* @__PURE__ */ new Set(),
|
|
4211
|
-
commentedCodeBlocks: detectCommentedCode(src, filePath)
|
|
4270
|
+
commentedCodeBlocks: detectCommentedCode(src, filePath),
|
|
4271
|
+
withEventsVars: /* @__PURE__ */ new Set()
|
|
4212
4272
|
};
|
|
4213
4273
|
}
|
|
4214
4274
|
if (ast.diagnostics && ast.diagnostics.length > 0) {
|
|
@@ -4287,13 +4347,22 @@ function analyzeFile(filePath) {
|
|
|
4287
4347
|
const xlVbConstantRefs = collectExcelConstantRefs(ast);
|
|
4288
4348
|
const gotoGraphs = procs.map((proc) => analyzeGotoInProc(proc));
|
|
4289
4349
|
const loopAnalyses = procs.map((proc, i) => analyzeLoopsInProc(proc, gotoGraphs[i]));
|
|
4350
|
+
const withEventsVars = /* @__PURE__ */ new Set();
|
|
4351
|
+
for (const s of ast.body) {
|
|
4352
|
+
if (s.type === "VariableDeclaration") {
|
|
4353
|
+
for (const decl of s.declarations ?? []) {
|
|
4354
|
+
if (decl.isWithEvents) withEventsVars.add((decl.name?.name ?? "").toLowerCase());
|
|
4355
|
+
}
|
|
4356
|
+
}
|
|
4357
|
+
}
|
|
4290
4358
|
return {
|
|
4291
4359
|
report: { filePath, totalLines: lines.length, procedureCount: procedures.length, procedures, prefixClusters, warnings, gotoGraphs, loopAnalyses },
|
|
4292
4360
|
definedProcs,
|
|
4293
4361
|
definedTypes,
|
|
4294
4362
|
callsByProc,
|
|
4295
4363
|
xlVbConstantRefs,
|
|
4296
|
-
commentedCodeBlocks: detectCommentedCode(src, filePath)
|
|
4364
|
+
commentedCodeBlocks: detectCommentedCode(src, filePath),
|
|
4365
|
+
withEventsVars
|
|
4297
4366
|
};
|
|
4298
4367
|
}
|
|
4299
4368
|
function buildWorkspaceReport(analyses) {
|
|
@@ -4325,6 +4394,10 @@ function buildWorkspaceReport(analyses) {
|
|
|
4325
4394
|
for (const a of analyses) {
|
|
4326
4395
|
for (const p of a.report.procedures) {
|
|
4327
4396
|
if (p.referenceCount === 0) {
|
|
4397
|
+
const nameLower = p.name.toLowerCase();
|
|
4398
|
+
const sep = nameLower.indexOf("_");
|
|
4399
|
+
const isEventHandler = nameLower === "class_initialize" || nameLower === "class_terminate" || sep > 0 && a.withEventsVars.has(nameLower.slice(0, sep));
|
|
4400
|
+
if (isEventHandler) continue;
|
|
4328
4401
|
if (p.scope === "private") {
|
|
4329
4402
|
deadCodeCandidates.push({
|
|
4330
4403
|
file: a.report.filePath,
|
|
@@ -4989,10 +5062,13 @@ function buildDiffJson(baseline, current) {
|
|
|
4989
5062
|
}
|
|
4990
5063
|
};
|
|
4991
5064
|
}
|
|
4992
|
-
function main() {
|
|
4993
|
-
|
|
5065
|
+
function main(args) {
|
|
5066
|
+
if (args.includes("--version") || args.includes("-v")) {
|
|
5067
|
+
console.log(VERSION);
|
|
5068
|
+
process.exit(0);
|
|
5069
|
+
}
|
|
4994
5070
|
if (args.length === 0 || args.includes("--help")) {
|
|
4995
|
-
console.log("Usage:
|
|
5071
|
+
console.log("Usage: vba-runner analyze <file-or-dir> [options]");
|
|
4996
5072
|
console.log("");
|
|
4997
5073
|
console.log(" --json JSON \u5F62\u5F0F\u3067\u51FA\u529B\uFF08\u30D7\u30ED\u30B0\u30E9\u30E0\u9023\u643A\u7528\uFF09");
|
|
4998
5074
|
console.log(" --diff <baseline> baseline JSON \u3068\u306E\u5DEE\u5206\u3092\u8868\u793A\uFF08--json \u3068\u4F75\u7528\u53EF\uFF09");
|
|
@@ -5002,6 +5078,7 @@ function main() {
|
|
|
5002
5078
|
console.log(" --goto-graph GoTo \u6587\u306E\u5236\u5FA1\u30D5\u30ED\u30FC\u30B0\u30E9\u30D5\u3068\u30EA\u30D5\u30A1\u30AF\u30BF\u30EA\u30F3\u30B0\u63D0\u6848\u3092\u8868\u793A");
|
|
5003
5079
|
console.log(" --cross-iter \u30AF\u30ED\u30B9\u30A4\u30C6\u30EC\u30FC\u30B7\u30E7\u30F3\u5909\u6570\uFF08ByRef \u5FC5\u8981\u5909\u6570\uFF09\u306E\u4E00\u89A7\u3092\u8868\u793A");
|
|
5004
5080
|
console.log(" --gen-test-dir <dir> \u30C6\u30B9\u30C8\u7528\u30BD\u30FC\u30B9\u3092\u6307\u5B9A\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u306B\u751F\u6210\uFF08const.ts \u7B49\uFF09");
|
|
5081
|
+
console.log(" --version \u30D0\u30FC\u30B8\u30E7\u30F3\u3092\u8868\u793A");
|
|
5005
5082
|
process.exit(args.length === 0 ? 1 : 0);
|
|
5006
5083
|
}
|
|
5007
5084
|
const wantJson = args.includes("--json");
|
|
@@ -5076,7 +5153,7 @@ function main() {
|
|
|
5076
5153
|
);
|
|
5077
5154
|
if (hasParseErrors) process.exit(1);
|
|
5078
5155
|
}
|
|
5079
|
-
if (process.argv[1]?.includes("vba-analyzer")) main();
|
|
5156
|
+
if (process.argv[1]?.includes("vba-analyzer")) main(process.argv.slice(2));
|
|
5080
5157
|
function analyzeWorkspaceForMcpFromFiles(files) {
|
|
5081
5158
|
const analyses = files.map((f) => analyzeFile(f));
|
|
5082
5159
|
return buildWorkspaceReport(analyses);
|
|
@@ -5100,6 +5177,7 @@ function formatWorkspaceReportForMcp(w) {
|
|
|
5100
5177
|
formatWorkspaceOutlineForMcp,
|
|
5101
5178
|
formatWorkspaceReportForMcp,
|
|
5102
5179
|
formatWorkspaceSummary,
|
|
5180
|
+
main,
|
|
5103
5181
|
paramName,
|
|
5104
5182
|
vbaTypeToTs
|
|
5105
5183
|
});
|
|
@@ -5,6 +5,10 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
|
5
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
6
|
var __getProtoOf = Object.getPrototypeOf;
|
|
7
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
8
12
|
var __copyProps = (to, from, except, desc) => {
|
|
9
13
|
if (from && typeof from === "object" || typeof from === "function") {
|
|
10
14
|
for (let key of __getOwnPropNames(from))
|
|
@@ -21,8 +25,14 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
21
25
|
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
22
26
|
mod
|
|
23
27
|
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
24
29
|
|
|
25
30
|
// ../../test-libs/vba-formatter.ts
|
|
31
|
+
var vba_formatter_exports = {};
|
|
32
|
+
__export(vba_formatter_exports, {
|
|
33
|
+
main: () => main
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(vba_formatter_exports);
|
|
26
36
|
var fs = __toESM(require("fs"), 1);
|
|
27
37
|
var path = __toESM(require("path"), 1);
|
|
28
38
|
|
|
@@ -916,6 +926,9 @@ function applyEdits(source, edits) {
|
|
|
916
926
|
return lines.join("\n");
|
|
917
927
|
}
|
|
918
928
|
|
|
929
|
+
// ../../test-libs/version.ts
|
|
930
|
+
var VERSION = "0.1.1-alpha.1";
|
|
931
|
+
|
|
919
932
|
// ../../test-libs/vba-formatter.ts
|
|
920
933
|
var VBA_EXTENSIONS = /\.(bas|cls|frm)$/i;
|
|
921
934
|
function collectFiles(target) {
|
|
@@ -950,21 +963,25 @@ function buildDiff(original, formatted, filePath) {
|
|
|
950
963
|
}
|
|
951
964
|
return hasDiff ? diffLines.join("\n") : "";
|
|
952
965
|
}
|
|
953
|
-
function main() {
|
|
954
|
-
|
|
966
|
+
function main(args) {
|
|
967
|
+
if (args.includes("--version") || args.includes("-v")) {
|
|
968
|
+
console.log(VERSION);
|
|
969
|
+
process.exit(0);
|
|
970
|
+
}
|
|
955
971
|
if (args.length === 0 || args[0] === "--help" || args[0] === "-h") {
|
|
956
|
-
console.log(`Usage: vba-
|
|
972
|
+
console.log(`Usage: vba-runner format <file-or-dir> [--check | --write] [options]
|
|
957
973
|
|
|
958
974
|
Options:
|
|
959
975
|
--check \u5DEE\u5206\u304C\u3042\u308C\u3070\u8868\u793A\u3057\u3066 exit code 1 \u3067\u7D42\u4E86
|
|
960
976
|
--write \u30D5\u30A1\u30A4\u30EB\u3092\u4E0A\u66F8\u304D\uFF08\u6307\u5B9A\u3057\u306A\u3044\u5834\u5408\u306F stdout \u306B\u51FA\u529B\uFF09
|
|
961
977
|
--indent-size=N \u30A4\u30F3\u30C7\u30F3\u30C8\u5E45\uFF08\u30C7\u30D5\u30A9\u30EB\u30C8: 4\uFF09
|
|
962
978
|
--no-keyword-case \u30AD\u30FC\u30EF\u30FC\u30C9\u306E\u5927\u6587\u5B57\u5316\u3092\u7121\u52B9\u5316
|
|
979
|
+
--version \u30D0\u30FC\u30B8\u30E7\u30F3\u3092\u8868\u793A
|
|
963
980
|
|
|
964
981
|
Example:
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
982
|
+
vba-runner format src/vba/Main.bas
|
|
983
|
+
vba-runner format src/vba/ --check
|
|
984
|
+
vba-runner format src/vba/Main.bas --write --indent-size=2`);
|
|
968
985
|
process.exit(0);
|
|
969
986
|
}
|
|
970
987
|
const target = args.find((a) => !a.startsWith("-"));
|
|
@@ -1015,4 +1032,8 @@ Example:
|
|
|
1015
1032
|
process.exit(1);
|
|
1016
1033
|
}
|
|
1017
1034
|
}
|
|
1018
|
-
main();
|
|
1035
|
+
if (process.argv[1]?.includes("vba-formatter")) main(process.argv.slice(2));
|
|
1036
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
1037
|
+
0 && (module.exports = {
|
|
1038
|
+
main
|
|
1039
|
+
});
|