vba-runner 0.1.1-alpha.2 → 0.1.1-alpha.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2521,7 +2521,7 @@ var Parser = class _Parser {
2521
2521
  parseEventDeclaration(scope) {
2522
2522
  this.advance();
2523
2523
  const idToken = this.advance();
2524
- if (!this.isIdentifier(idToken) && !this.isNameToken(idToken)) this.throwError(`Expected identifier after 'Event' at line ${idToken.line}`);
2524
+ if (!this.isIdentifier(idToken)) this.throwError(`Expected identifier after 'Event' at line ${idToken.line}`);
2525
2525
  const name = { type: "Identifier", name: idToken.value };
2526
2526
  const parameters = [];
2527
2527
  if (this.match(128 /* OperatorLParen */)) {
@@ -2539,7 +2539,7 @@ var Parser = class _Parser {
2539
2539
  parseRaiseEventStatement() {
2540
2540
  this.advance();
2541
2541
  const idToken = this.advance();
2542
- if (!this.isIdentifier(idToken) && !this.isNameToken(idToken)) this.throwError(`Expected identifier after 'RaiseEvent' at line ${idToken.line}`);
2542
+ if (!this.isIdentifier(idToken)) this.throwError(`Expected identifier after 'RaiseEvent' at line ${idToken.line}`);
2543
2543
  const eventName = { type: "Identifier", name: idToken.value };
2544
2544
  const args = [];
2545
2545
  if (this.match(128 /* OperatorLParen */)) {
@@ -2864,13 +2864,39 @@ function evaluateCCExpr(expr, resolve) {
2864
2864
  }
2865
2865
  return parseOr();
2866
2866
  }
2867
+ function stripVBAFileHeader(source) {
2868
+ const lines = source.split("\n");
2869
+ if (!lines[0]?.trimEnd().toUpperCase().startsWith("VERSION")) return source;
2870
+ const result = [...lines];
2871
+ let i = 0;
2872
+ result[i] = "";
2873
+ i++;
2874
+ while (i < result.length) {
2875
+ const trimmed = result[i].trimEnd().toUpperCase();
2876
+ result[i] = "";
2877
+ i++;
2878
+ if (trimmed === "END") break;
2879
+ }
2880
+ return result.join("\n");
2881
+ }
2867
2882
 
2868
2883
  // ../../test-libs/vba-analyzer.ts
2869
2884
  var fs = __toESM(require("fs"), 1);
2870
2885
  var path = __toESM(require("path"), 1);
2871
2886
 
2872
2887
  // ../../test-libs/version.ts
2873
- var VERSION = "0.1.1-alpha.1";
2888
+ var import_fs = require("fs");
2889
+ var import_path = require("path");
2890
+ function readVersion() {
2891
+ for (const rel of ["../build/runner/package.json", "../../package.json"]) {
2892
+ try {
2893
+ return JSON.parse((0, import_fs.readFileSync)((0, import_path.join)(__dirname, rel), "utf8")).version;
2894
+ } catch {
2895
+ }
2896
+ }
2897
+ return "unknown";
2898
+ }
2899
+ var VERSION = readVersion();
2874
2900
 
2875
2901
  // ../../test-libs/vba-analyzer.ts
2876
2902
  function isStatementArray(v) {
@@ -4379,7 +4405,9 @@ function analyzeGotoInProc(proc) {
4379
4405
  };
4380
4406
  }
4381
4407
  function analyzeFile(filePath) {
4382
- const src = fs.readFileSync(filePath, "utf-8");
4408
+ const ext = path.extname(filePath).toLowerCase();
4409
+ const raw = fs.readFileSync(filePath, "utf-8");
4410
+ const src = stripVBAFileHeader(raw);
4383
4411
  const lines = src.split("\n");
4384
4412
  const warnings = [];
4385
4413
  let ast;
@@ -943,7 +943,18 @@ function applyEdits(source, edits) {
943
943
  }
944
944
 
945
945
  // ../../test-libs/version.ts
946
- var VERSION = "0.1.1-alpha.1";
946
+ var import_fs = require("fs");
947
+ var import_path = require("path");
948
+ function readVersion() {
949
+ for (const rel of ["../build/runner/package.json", "../../package.json"]) {
950
+ try {
951
+ return JSON.parse((0, import_fs.readFileSync)((0, import_path.join)(__dirname, rel), "utf8")).version;
952
+ } catch {
953
+ }
954
+ }
955
+ return "unknown";
956
+ }
957
+ var VERSION = readVersion();
947
958
 
948
959
  // ../../test-libs/vba-formatter.ts
949
960
  var VBA_EXTENSIONS = /\.(bas|cls|frm)$/i;
@@ -2508,7 +2508,7 @@ var Parser = class _Parser {
2508
2508
  parseEventDeclaration(scope) {
2509
2509
  this.advance();
2510
2510
  const idToken = this.advance();
2511
- if (!this.isIdentifier(idToken) && !this.isNameToken(idToken)) this.throwError(`Expected identifier after 'Event' at line ${idToken.line}`);
2511
+ if (!this.isIdentifier(idToken)) this.throwError(`Expected identifier after 'Event' at line ${idToken.line}`);
2512
2512
  const name = { type: "Identifier", name: idToken.value };
2513
2513
  const parameters = [];
2514
2514
  if (this.match(128 /* OperatorLParen */)) {
@@ -2526,7 +2526,7 @@ var Parser = class _Parser {
2526
2526
  parseRaiseEventStatement() {
2527
2527
  this.advance();
2528
2528
  const idToken = this.advance();
2529
- if (!this.isIdentifier(idToken) && !this.isNameToken(idToken)) this.throwError(`Expected identifier after 'RaiseEvent' at line ${idToken.line}`);
2529
+ if (!this.isIdentifier(idToken)) this.throwError(`Expected identifier after 'RaiseEvent' at line ${idToken.line}`);
2530
2530
  const eventName = { type: "Identifier", name: idToken.value };
2531
2531
  const args = [];
2532
2532
  if (this.match(128 /* OperatorLParen */)) {
@@ -2604,16 +2604,46 @@ var Parser = class _Parser {
2604
2604
  }
2605
2605
  };
2606
2606
 
2607
+ // ../../src/engine/preprocessor.ts
2608
+ function stripVBAFileHeader(source) {
2609
+ const lines = source.split("\n");
2610
+ if (!lines[0]?.trimEnd().toUpperCase().startsWith("VERSION")) return source;
2611
+ const result = [...lines];
2612
+ let i = 0;
2613
+ result[i] = "";
2614
+ i++;
2615
+ while (i < result.length) {
2616
+ const trimmed = result[i].trimEnd().toUpperCase();
2617
+ result[i] = "";
2618
+ i++;
2619
+ if (trimmed === "END") break;
2620
+ }
2621
+ return result.join("\n");
2622
+ }
2623
+
2607
2624
  // ../../test-libs/vba-parse-check.ts
2608
2625
  var fs = __toESM(require("fs"), 1);
2609
2626
  var path = __toESM(require("path"), 1);
2610
2627
 
2611
2628
  // ../../test-libs/version.ts
2612
- var VERSION = "0.1.1-alpha.1";
2629
+ var import_fs = require("fs");
2630
+ var import_path = require("path");
2631
+ function readVersion() {
2632
+ for (const rel of ["../build/runner/package.json", "../../package.json"]) {
2633
+ try {
2634
+ return JSON.parse((0, import_fs.readFileSync)((0, import_path.join)(__dirname, rel), "utf8")).version;
2635
+ } catch {
2636
+ }
2637
+ }
2638
+ return "unknown";
2639
+ }
2640
+ var VERSION = readVersion();
2613
2641
 
2614
2642
  // ../../test-libs/vba-parse-check.ts
2615
2643
  function checkFile(filePath) {
2616
- const src = fs.readFileSync(filePath, "utf8");
2644
+ const ext = path.extname(filePath).toLowerCase();
2645
+ const raw = fs.readFileSync(filePath, "utf8");
2646
+ const src = stripVBAFileHeader(raw);
2617
2647
  const diags = [];
2618
2648
  const lexer = new Lexer(src);
2619
2649
  let tokens;
@@ -2510,7 +2510,7 @@ var Parser = class _Parser {
2510
2510
  parseEventDeclaration(scope) {
2511
2511
  this.advance();
2512
2512
  const idToken = this.advance();
2513
- if (!this.isIdentifier(idToken) && !this.isNameToken(idToken)) this.throwError(`Expected identifier after 'Event' at line ${idToken.line}`);
2513
+ if (!this.isIdentifier(idToken)) this.throwError(`Expected identifier after 'Event' at line ${idToken.line}`);
2514
2514
  const name = { type: "Identifier", name: idToken.value };
2515
2515
  const parameters = [];
2516
2516
  if (this.match(128 /* OperatorLParen */)) {
@@ -2528,7 +2528,7 @@ var Parser = class _Parser {
2528
2528
  parseRaiseEventStatement() {
2529
2529
  this.advance();
2530
2530
  const idToken = this.advance();
2531
- if (!this.isIdentifier(idToken) && !this.isNameToken(idToken)) this.throwError(`Expected identifier after 'RaiseEvent' at line ${idToken.line}`);
2531
+ if (!this.isIdentifier(idToken)) this.throwError(`Expected identifier after 'RaiseEvent' at line ${idToken.line}`);
2532
2532
  const eventName = { type: "Identifier", name: idToken.value };
2533
2533
  const args = [];
2534
2534
  if (this.match(128 /* OperatorLParen */)) {
@@ -4294,6 +4294,16 @@ var Evaluator = class _Evaluator {
4294
4294
  inConstEval = false;
4295
4295
  vbaCallStack = [];
4296
4296
  debugHook = null;
4297
+ /**
4298
+ * resolveIdentifiers 完了後に true になる。
4299
+ * evaluateModule はこのフラグを見て、バッチロード中(false)はモジュールレベル
4300
+ * 実行文を pendingTopLevel に退避し、インタラクティブ呼び出し(true)では即時実行する。
4301
+ */
4302
+ resolveIdentifiersDone = false;
4303
+ /** Pass 1 で配列境界を評価できなかった VariableDeclaration。Pass 2 で再評価。 */
4304
+ pendingArrayDecls = [];
4305
+ /** Pass 1 でシンボルテーブルに退避したモジュールレベル実行文。Pass 2 後に実行。 */
4306
+ pendingTopLevel = [];
4297
4307
  constructor(onPrint, config = {}) {
4298
4308
  this.builtinEnv = new Environment();
4299
4309
  this.env = this.builtinEnv;
@@ -5715,22 +5725,70 @@ var Evaluator = class _Evaluator {
5715
5725
  }
5716
5726
  }
5717
5727
  /**
5718
- * Pass 1: 1モジュール分の AST を登録する。
5719
- * 手続き・変数を env に登録する。
5720
- * モジュールレベル ConstDeclaration はスキップし、Pass 2(resolveIdentifiers)で評価する。
5728
+ * モジュールレベルでシンボルテーブルに登録すべき宣言文かどうかを返す。
5729
+ * false の場合はモジュールレベル実行文として pendingTopLevel に退避する。
5730
+ */
5731
+ isModuleLevelDeclaration(stmt) {
5732
+ switch (stmt.type) {
5733
+ case "ProcedureDeclaration":
5734
+ case "VariableDeclaration":
5735
+ case "ConstDeclaration":
5736
+ case "TypeDeclaration":
5737
+ case "ClassDeclaration":
5738
+ case "EnumDeclaration":
5739
+ case "OptionExplicitStatement":
5740
+ case "OptionBaseStatement":
5741
+ case "OptionCompareStatement":
5742
+ case "OptionPrivateModuleStatement":
5743
+ case "DefDirective":
5744
+ case "AttributeStatement":
5745
+ case "DeclareStatement":
5746
+ case "EventDeclaration":
5747
+ case "ImplementsDirective":
5748
+ case "LabelStatement":
5749
+ return true;
5750
+ default:
5751
+ return false;
5752
+ }
5753
+ }
5754
+ /**
5755
+ * Pass 1: 1モジュール分の AST を登録する(シンボルテーブル構築)。
5721
5756
  *
5722
- * Option Explicit チェックは Pass 2(resolveIdentifiers)に一本化している。
5723
- * Pass 2 は全モジュール名を知った精密モードで実行され、Pass 1 の保守的チェックの
5724
- * 厳密な上位集合なので、ここでは行わない。
5757
+ * - ConstDeclaration → スキップ。Pass 2(resolveIdentifiers)で依存順に評価。
5758
+ * - VariableDeclaration → 配列境界なしは即時登録。配列境界ありは pendingArrayDecls
5759
+ * に退避し Pass 2 で Const 解決後に評価。
5760
+ * - モジュールレベル実行文 → resolveIdentifiers 完了前(バッチロード中)は
5761
+ * pendingTopLevel に退避。Pass 2 後に実行。
5762
+ * resolveIdentifiers 完了後(インタラクティブ呼び出し)は
5763
+ * 即時実行(evalExpression や追加モジュールロード等)。
5725
5764
  */
5726
5765
  evaluateModule(program) {
5727
5766
  if (this.currentSourceModule && !this.env.hasVariable(this.currentSourceModule.toLowerCase())) {
5728
5767
  this.env.set(this.currentSourceModule.toLowerCase(), new VbaNamespaceRef(this.currentSourceModule, "module"));
5729
5768
  }
5769
+ const batchMode = !this.resolveIdentifiersDone;
5770
+ const topLevelStmts = [];
5730
5771
  for (const stmt of program.body) {
5731
- if (stmt.type === "ConstDeclaration") continue;
5772
+ if (stmt.type === "ConstDeclaration") {
5773
+ if (!batchMode) this.evaluateConstDeclaration(stmt);
5774
+ continue;
5775
+ }
5776
+ if (batchMode && stmt.type === "VariableDeclaration") {
5777
+ const varDecl = stmt;
5778
+ if (varDecl.declarations.some((d) => d.isArray && d.arrayBounds && d.arrayBounds.length > 0)) {
5779
+ this.pendingArrayDecls.push({ stmt: varDecl, moduleName: this.currentSourceModule || "" });
5780
+ continue;
5781
+ }
5782
+ }
5783
+ if (batchMode && !this.isModuleLevelDeclaration(stmt)) {
5784
+ topLevelStmts.push(stmt);
5785
+ continue;
5786
+ }
5732
5787
  this.evaluateStatement(stmt);
5733
5788
  }
5789
+ if (batchMode && topLevelStmts.length > 0) {
5790
+ this.pendingTopLevel.push({ moduleName: this.currentSourceModule || "", stmts: topLevelStmts });
5791
+ }
5734
5792
  }
5735
5793
  /** @deprecated Use {@link evaluateModule} instead. */
5736
5794
  evaluate(program) {
@@ -7001,6 +7059,13 @@ var Evaluator = class _Evaluator {
7001
7059
  this.evaluateConstDeclaration(stmt);
7002
7060
  this.currentSourceModule = prev;
7003
7061
  }
7062
+ for (const { stmt, moduleName } of this.pendingArrayDecls) {
7063
+ const prev = this.currentSourceModule;
7064
+ this.currentSourceModule = moduleName;
7065
+ this.evaluateVariableDeclaration(stmt);
7066
+ this.currentSourceModule = prev;
7067
+ }
7068
+ this.pendingArrayDecls = [];
7004
7069
  for (const { ast, moduleName } of modules) {
7005
7070
  const seen = /* @__PURE__ */ new Set();
7006
7071
  for (const stmt of ast.body) {
@@ -7026,6 +7091,16 @@ var Evaluator = class _Evaluator {
7026
7091
  }
7027
7092
  }
7028
7093
  }
7094
+ this.resolveIdentifiersDone = true;
7095
+ for (const { moduleName, stmts } of this.pendingTopLevel) {
7096
+ const prev = this.currentSourceModule;
7097
+ this.currentSourceModule = moduleName;
7098
+ for (const stmt of stmts) {
7099
+ this.evaluateStatement(stmt);
7100
+ }
7101
+ this.currentSourceModule = prev;
7102
+ }
7103
+ this.pendingTopLevel = [];
7029
7104
  }
7030
7105
  /** @deprecated Use {@link resolveIdentifiers} instead. */
7031
7106
  reEvaluateModuleConstsAll(modules) {
@@ -9707,9 +9782,35 @@ function evaluateCCExpr(expr, resolve2) {
9707
9782
  }
9708
9783
  return parseOr();
9709
9784
  }
9785
+ function stripVBAFileHeader(source) {
9786
+ const lines = source.split("\n");
9787
+ if (!lines[0]?.trimEnd().toUpperCase().startsWith("VERSION")) return source;
9788
+ const result = [...lines];
9789
+ let i = 0;
9790
+ result[i] = "";
9791
+ i++;
9792
+ while (i < result.length) {
9793
+ const trimmed = result[i].trimEnd().toUpperCase();
9794
+ result[i] = "";
9795
+ i++;
9796
+ if (trimmed === "END") break;
9797
+ }
9798
+ return result.join("\n");
9799
+ }
9710
9800
 
9711
9801
  // ../../test-libs/version.ts
9712
- var VERSION = "0.1.1-alpha.1";
9802
+ var import_fs = require("fs");
9803
+ var import_path2 = require("path");
9804
+ function readVersion() {
9805
+ for (const rel of ["../build/runner/package.json", "../../package.json"]) {
9806
+ try {
9807
+ return JSON.parse((0, import_fs.readFileSync)((0, import_path2.join)(__dirname, rel), "utf8")).version;
9808
+ } catch {
9809
+ }
9810
+ }
9811
+ return "unknown";
9812
+ }
9813
+ var VERSION = readVersion();
9713
9814
 
9714
9815
  // ../../test-libs/vba-run.ts
9715
9816
  var VBA_EXTENSIONS = /* @__PURE__ */ new Set([".bas", ".cls", ".frm"]);
@@ -9754,8 +9855,10 @@ function main(args) {
9754
9855
  for (const file of files) {
9755
9856
  const moduleName = path4.basename(file, path4.extname(file));
9756
9857
  ev.setSourceModule(moduleName);
9757
- const src = preprocess(fs2.readFileSync(file, "utf-8"));
9758
9858
  const ext = path4.extname(file).toLowerCase();
9859
+ let src = fs2.readFileSync(file, "utf-8");
9860
+ src = stripVBAFileHeader(src);
9861
+ src = preprocess(src);
9759
9862
  const isRawCls = ext === ".cls" && !src.trim().toLowerCase().startsWith("class ") && !src.toLowerCase().includes("end class");
9760
9863
  const ast = new Parser(new Lexer(src).tokenize(), isRawCls ? { parseAsClass: moduleName } : {}).parse();
9761
9864
  ev.evaluateModule(ast);
@@ -2500,7 +2500,7 @@ var Parser = class _Parser {
2500
2500
  parseEventDeclaration(scope) {
2501
2501
  this.advance();
2502
2502
  const idToken = this.advance();
2503
- if (!this.isIdentifier(idToken) && !this.isNameToken(idToken)) this.throwError(`Expected identifier after 'Event' at line ${idToken.line}`);
2503
+ if (!this.isIdentifier(idToken)) this.throwError(`Expected identifier after 'Event' at line ${idToken.line}`);
2504
2504
  const name = { type: "Identifier", name: idToken.value };
2505
2505
  const parameters = [];
2506
2506
  if (this.match(128 /* OperatorLParen */)) {
@@ -2518,7 +2518,7 @@ var Parser = class _Parser {
2518
2518
  parseRaiseEventStatement() {
2519
2519
  this.advance();
2520
2520
  const idToken = this.advance();
2521
- if (!this.isIdentifier(idToken) && !this.isNameToken(idToken)) this.throwError(`Expected identifier after 'RaiseEvent' at line ${idToken.line}`);
2521
+ if (!this.isIdentifier(idToken)) this.throwError(`Expected identifier after 'RaiseEvent' at line ${idToken.line}`);
2522
2522
  const eventName = { type: "Identifier", name: idToken.value };
2523
2523
  const args = [];
2524
2524
  if (this.match(128 /* OperatorLParen */)) {
@@ -4284,6 +4284,16 @@ var Evaluator = class _Evaluator {
4284
4284
  inConstEval = false;
4285
4285
  vbaCallStack = [];
4286
4286
  debugHook = null;
4287
+ /**
4288
+ * resolveIdentifiers 完了後に true になる。
4289
+ * evaluateModule はこのフラグを見て、バッチロード中(false)はモジュールレベル
4290
+ * 実行文を pendingTopLevel に退避し、インタラクティブ呼び出し(true)では即時実行する。
4291
+ */
4292
+ resolveIdentifiersDone = false;
4293
+ /** Pass 1 で配列境界を評価できなかった VariableDeclaration。Pass 2 で再評価。 */
4294
+ pendingArrayDecls = [];
4295
+ /** Pass 1 でシンボルテーブルに退避したモジュールレベル実行文。Pass 2 後に実行。 */
4296
+ pendingTopLevel = [];
4287
4297
  constructor(onPrint, config = {}) {
4288
4298
  this.builtinEnv = new Environment();
4289
4299
  this.env = this.builtinEnv;
@@ -5705,22 +5715,70 @@ var Evaluator = class _Evaluator {
5705
5715
  }
5706
5716
  }
5707
5717
  /**
5708
- * Pass 1: 1モジュール分の AST を登録する。
5709
- * 手続き・変数を env に登録する。
5710
- * モジュールレベル ConstDeclaration はスキップし、Pass 2(resolveIdentifiers)で評価する。
5718
+ * モジュールレベルでシンボルテーブルに登録すべき宣言文かどうかを返す。
5719
+ * false の場合はモジュールレベル実行文として pendingTopLevel に退避する。
5720
+ */
5721
+ isModuleLevelDeclaration(stmt) {
5722
+ switch (stmt.type) {
5723
+ case "ProcedureDeclaration":
5724
+ case "VariableDeclaration":
5725
+ case "ConstDeclaration":
5726
+ case "TypeDeclaration":
5727
+ case "ClassDeclaration":
5728
+ case "EnumDeclaration":
5729
+ case "OptionExplicitStatement":
5730
+ case "OptionBaseStatement":
5731
+ case "OptionCompareStatement":
5732
+ case "OptionPrivateModuleStatement":
5733
+ case "DefDirective":
5734
+ case "AttributeStatement":
5735
+ case "DeclareStatement":
5736
+ case "EventDeclaration":
5737
+ case "ImplementsDirective":
5738
+ case "LabelStatement":
5739
+ return true;
5740
+ default:
5741
+ return false;
5742
+ }
5743
+ }
5744
+ /**
5745
+ * Pass 1: 1モジュール分の AST を登録する(シンボルテーブル構築)。
5711
5746
  *
5712
- * Option Explicit チェックは Pass 2(resolveIdentifiers)に一本化している。
5713
- * Pass 2 は全モジュール名を知った精密モードで実行され、Pass 1 の保守的チェックの
5714
- * 厳密な上位集合なので、ここでは行わない。
5747
+ * - ConstDeclaration → スキップ。Pass 2(resolveIdentifiers)で依存順に評価。
5748
+ * - VariableDeclaration → 配列境界なしは即時登録。配列境界ありは pendingArrayDecls
5749
+ * に退避し Pass 2 で Const 解決後に評価。
5750
+ * - モジュールレベル実行文 → resolveIdentifiers 完了前(バッチロード中)は
5751
+ * pendingTopLevel に退避。Pass 2 後に実行。
5752
+ * resolveIdentifiers 完了後(インタラクティブ呼び出し)は
5753
+ * 即時実行(evalExpression や追加モジュールロード等)。
5715
5754
  */
5716
5755
  evaluateModule(program) {
5717
5756
  if (this.currentSourceModule && !this.env.hasVariable(this.currentSourceModule.toLowerCase())) {
5718
5757
  this.env.set(this.currentSourceModule.toLowerCase(), new VbaNamespaceRef(this.currentSourceModule, "module"));
5719
5758
  }
5759
+ const batchMode = !this.resolveIdentifiersDone;
5760
+ const topLevelStmts = [];
5720
5761
  for (const stmt of program.body) {
5721
- if (stmt.type === "ConstDeclaration") continue;
5762
+ if (stmt.type === "ConstDeclaration") {
5763
+ if (!batchMode) this.evaluateConstDeclaration(stmt);
5764
+ continue;
5765
+ }
5766
+ if (batchMode && stmt.type === "VariableDeclaration") {
5767
+ const varDecl = stmt;
5768
+ if (varDecl.declarations.some((d) => d.isArray && d.arrayBounds && d.arrayBounds.length > 0)) {
5769
+ this.pendingArrayDecls.push({ stmt: varDecl, moduleName: this.currentSourceModule || "" });
5770
+ continue;
5771
+ }
5772
+ }
5773
+ if (batchMode && !this.isModuleLevelDeclaration(stmt)) {
5774
+ topLevelStmts.push(stmt);
5775
+ continue;
5776
+ }
5722
5777
  this.evaluateStatement(stmt);
5723
5778
  }
5779
+ if (batchMode && topLevelStmts.length > 0) {
5780
+ this.pendingTopLevel.push({ moduleName: this.currentSourceModule || "", stmts: topLevelStmts });
5781
+ }
5724
5782
  }
5725
5783
  /** @deprecated Use {@link evaluateModule} instead. */
5726
5784
  evaluate(program) {
@@ -6991,6 +7049,13 @@ var Evaluator = class _Evaluator {
6991
7049
  this.evaluateConstDeclaration(stmt);
6992
7050
  this.currentSourceModule = prev;
6993
7051
  }
7052
+ for (const { stmt, moduleName } of this.pendingArrayDecls) {
7053
+ const prev = this.currentSourceModule;
7054
+ this.currentSourceModule = moduleName;
7055
+ this.evaluateVariableDeclaration(stmt);
7056
+ this.currentSourceModule = prev;
7057
+ }
7058
+ this.pendingArrayDecls = [];
6994
7059
  for (const { ast, moduleName } of modules) {
6995
7060
  const seen = /* @__PURE__ */ new Set();
6996
7061
  for (const stmt of ast.body) {
@@ -7016,6 +7081,16 @@ var Evaluator = class _Evaluator {
7016
7081
  }
7017
7082
  }
7018
7083
  }
7084
+ this.resolveIdentifiersDone = true;
7085
+ for (const { moduleName, stmts } of this.pendingTopLevel) {
7086
+ const prev = this.currentSourceModule;
7087
+ this.currentSourceModule = moduleName;
7088
+ for (const stmt of stmts) {
7089
+ this.evaluateStatement(stmt);
7090
+ }
7091
+ this.currentSourceModule = prev;
7092
+ }
7093
+ this.pendingTopLevel = [];
7019
7094
  }
7020
7095
  /** @deprecated Use {@link resolveIdentifiers} instead. */
7021
7096
  reEvaluateModuleConstsAll(modules) {
@@ -9697,9 +9772,35 @@ function evaluateCCExpr(expr, resolve2) {
9697
9772
  }
9698
9773
  return parseOr();
9699
9774
  }
9775
+ function stripVBAFileHeader(source) {
9776
+ const lines = source.split("\n");
9777
+ if (!lines[0]?.trimEnd().toUpperCase().startsWith("VERSION")) return source;
9778
+ const result = [...lines];
9779
+ let i = 0;
9780
+ result[i] = "";
9781
+ i++;
9782
+ while (i < result.length) {
9783
+ const trimmed = result[i].trimEnd().toUpperCase();
9784
+ result[i] = "";
9785
+ i++;
9786
+ if (trimmed === "END") break;
9787
+ }
9788
+ return result.join("\n");
9789
+ }
9700
9790
 
9701
9791
  // ../../test-libs/version.ts
9702
- var VERSION = "0.1.1-alpha.1";
9792
+ var import_fs = require("fs");
9793
+ var import_path2 = require("path");
9794
+ function readVersion() {
9795
+ for (const rel of ["../build/runner/package.json", "../../package.json"]) {
9796
+ try {
9797
+ return JSON.parse((0, import_fs.readFileSync)((0, import_path2.join)(__dirname, rel), "utf8")).version;
9798
+ } catch {
9799
+ }
9800
+ }
9801
+ return "unknown";
9802
+ }
9803
+ var VERSION = readVersion();
9703
9804
 
9704
9805
  // ../../test-libs/vba-run.ts
9705
9806
  var VBA_EXTENSIONS = /* @__PURE__ */ new Set([".bas", ".cls", ".frm"]);
@@ -9744,8 +9845,10 @@ function main(args) {
9744
9845
  for (const file of files) {
9745
9846
  const moduleName = path4.basename(file, path4.extname(file));
9746
9847
  ev.setSourceModule(moduleName);
9747
- const src = preprocess(fs2.readFileSync(file, "utf-8"));
9748
9848
  const ext = path4.extname(file).toLowerCase();
9849
+ let src = fs2.readFileSync(file, "utf-8");
9850
+ src = stripVBAFileHeader(src);
9851
+ src = preprocess(src);
9749
9852
  const isRawCls = ext === ".cls" && !src.trim().toLowerCase().startsWith("class ") && !src.toLowerCase().includes("end class");
9750
9853
  const ast = new Parser(new Lexer(src).tokenize(), isRawCls ? { parseAsClass: moduleName } : {}).parse();
9751
9854
  ev.evaluateModule(ast);
@@ -11271,7 +11374,9 @@ function analyzeGotoInProc(proc) {
11271
11374
  };
11272
11375
  }
11273
11376
  function analyzeFile(filePath) {
11274
- const src = fs3.readFileSync(filePath, "utf-8");
11377
+ const ext = path5.extname(filePath).toLowerCase();
11378
+ const raw = fs3.readFileSync(filePath, "utf-8");
11379
+ const src = stripVBAFileHeader(raw);
11275
11380
  const lines = src.split("\n");
11276
11381
  const warnings = [];
11277
11382
  let ast;
@@ -12730,7 +12835,9 @@ if (process.argv[1]?.includes("vba-formatter")) main3(process.argv.slice(2));
12730
12835
  var fs5 = __toESM(require("fs"), 1);
12731
12836
  var path7 = __toESM(require("path"), 1);
12732
12837
  function checkFile(filePath) {
12733
- const src = fs5.readFileSync(filePath, "utf8");
12838
+ const ext = path7.extname(filePath).toLowerCase();
12839
+ const raw = fs5.readFileSync(filePath, "utf8");
12840
+ const src = stripVBAFileHeader(raw);
12734
12841
  const diags = [];
12735
12842
  const lexer = new Lexer(src);
12736
12843
  let tokens;
package/dist/lib.cjs CHANGED
@@ -2522,7 +2522,7 @@ var Parser = class _Parser {
2522
2522
  parseEventDeclaration(scope) {
2523
2523
  this.advance();
2524
2524
  const idToken = this.advance();
2525
- if (!this.isIdentifier(idToken) && !this.isNameToken(idToken)) this.throwError(`Expected identifier after 'Event' at line ${idToken.line}`);
2525
+ if (!this.isIdentifier(idToken)) this.throwError(`Expected identifier after 'Event' at line ${idToken.line}`);
2526
2526
  const name = { type: "Identifier", name: idToken.value };
2527
2527
  const parameters = [];
2528
2528
  if (this.match(128 /* OperatorLParen */)) {
@@ -2540,7 +2540,7 @@ var Parser = class _Parser {
2540
2540
  parseRaiseEventStatement() {
2541
2541
  this.advance();
2542
2542
  const idToken = this.advance();
2543
- if (!this.isIdentifier(idToken) && !this.isNameToken(idToken)) this.throwError(`Expected identifier after 'RaiseEvent' at line ${idToken.line}`);
2543
+ if (!this.isIdentifier(idToken)) this.throwError(`Expected identifier after 'RaiseEvent' at line ${idToken.line}`);
2544
2544
  const eventName = { type: "Identifier", name: idToken.value };
2545
2545
  const args = [];
2546
2546
  if (this.match(128 /* OperatorLParen */)) {
@@ -4306,6 +4306,16 @@ var Evaluator = class _Evaluator {
4306
4306
  inConstEval = false;
4307
4307
  vbaCallStack = [];
4308
4308
  debugHook = null;
4309
+ /**
4310
+ * resolveIdentifiers 完了後に true になる。
4311
+ * evaluateModule はこのフラグを見て、バッチロード中(false)はモジュールレベル
4312
+ * 実行文を pendingTopLevel に退避し、インタラクティブ呼び出し(true)では即時実行する。
4313
+ */
4314
+ resolveIdentifiersDone = false;
4315
+ /** Pass 1 で配列境界を評価できなかった VariableDeclaration。Pass 2 で再評価。 */
4316
+ pendingArrayDecls = [];
4317
+ /** Pass 1 でシンボルテーブルに退避したモジュールレベル実行文。Pass 2 後に実行。 */
4318
+ pendingTopLevel = [];
4309
4319
  constructor(onPrint, config = {}) {
4310
4320
  this.builtinEnv = new Environment();
4311
4321
  this.env = this.builtinEnv;
@@ -5727,22 +5737,70 @@ var Evaluator = class _Evaluator {
5727
5737
  }
5728
5738
  }
5729
5739
  /**
5730
- * Pass 1: 1モジュール分の AST を登録する。
5731
- * 手続き・変数を env に登録する。
5732
- * モジュールレベル ConstDeclaration はスキップし、Pass 2(resolveIdentifiers)で評価する。
5740
+ * モジュールレベルでシンボルテーブルに登録すべき宣言文かどうかを返す。
5741
+ * false の場合はモジュールレベル実行文として pendingTopLevel に退避する。
5742
+ */
5743
+ isModuleLevelDeclaration(stmt) {
5744
+ switch (stmt.type) {
5745
+ case "ProcedureDeclaration":
5746
+ case "VariableDeclaration":
5747
+ case "ConstDeclaration":
5748
+ case "TypeDeclaration":
5749
+ case "ClassDeclaration":
5750
+ case "EnumDeclaration":
5751
+ case "OptionExplicitStatement":
5752
+ case "OptionBaseStatement":
5753
+ case "OptionCompareStatement":
5754
+ case "OptionPrivateModuleStatement":
5755
+ case "DefDirective":
5756
+ case "AttributeStatement":
5757
+ case "DeclareStatement":
5758
+ case "EventDeclaration":
5759
+ case "ImplementsDirective":
5760
+ case "LabelStatement":
5761
+ return true;
5762
+ default:
5763
+ return false;
5764
+ }
5765
+ }
5766
+ /**
5767
+ * Pass 1: 1モジュール分の AST を登録する(シンボルテーブル構築)。
5733
5768
  *
5734
- * Option Explicit チェックは Pass 2(resolveIdentifiers)に一本化している。
5735
- * Pass 2 は全モジュール名を知った精密モードで実行され、Pass 1 の保守的チェックの
5736
- * 厳密な上位集合なので、ここでは行わない。
5769
+ * - ConstDeclaration → スキップ。Pass 2(resolveIdentifiers)で依存順に評価。
5770
+ * - VariableDeclaration → 配列境界なしは即時登録。配列境界ありは pendingArrayDecls
5771
+ * に退避し Pass 2 で Const 解決後に評価。
5772
+ * - モジュールレベル実行文 → resolveIdentifiers 完了前(バッチロード中)は
5773
+ * pendingTopLevel に退避。Pass 2 後に実行。
5774
+ * resolveIdentifiers 完了後(インタラクティブ呼び出し)は
5775
+ * 即時実行(evalExpression や追加モジュールロード等)。
5737
5776
  */
5738
5777
  evaluateModule(program) {
5739
5778
  if (this.currentSourceModule && !this.env.hasVariable(this.currentSourceModule.toLowerCase())) {
5740
5779
  this.env.set(this.currentSourceModule.toLowerCase(), new VbaNamespaceRef(this.currentSourceModule, "module"));
5741
5780
  }
5781
+ const batchMode = !this.resolveIdentifiersDone;
5782
+ const topLevelStmts = [];
5742
5783
  for (const stmt of program.body) {
5743
- if (stmt.type === "ConstDeclaration") continue;
5784
+ if (stmt.type === "ConstDeclaration") {
5785
+ if (!batchMode) this.evaluateConstDeclaration(stmt);
5786
+ continue;
5787
+ }
5788
+ if (batchMode && stmt.type === "VariableDeclaration") {
5789
+ const varDecl = stmt;
5790
+ if (varDecl.declarations.some((d) => d.isArray && d.arrayBounds && d.arrayBounds.length > 0)) {
5791
+ this.pendingArrayDecls.push({ stmt: varDecl, moduleName: this.currentSourceModule || "" });
5792
+ continue;
5793
+ }
5794
+ }
5795
+ if (batchMode && !this.isModuleLevelDeclaration(stmt)) {
5796
+ topLevelStmts.push(stmt);
5797
+ continue;
5798
+ }
5744
5799
  this.evaluateStatement(stmt);
5745
5800
  }
5801
+ if (batchMode && topLevelStmts.length > 0) {
5802
+ this.pendingTopLevel.push({ moduleName: this.currentSourceModule || "", stmts: topLevelStmts });
5803
+ }
5746
5804
  }
5747
5805
  /** @deprecated Use {@link evaluateModule} instead. */
5748
5806
  evaluate(program) {
@@ -7013,6 +7071,13 @@ var Evaluator = class _Evaluator {
7013
7071
  this.evaluateConstDeclaration(stmt);
7014
7072
  this.currentSourceModule = prev;
7015
7073
  }
7074
+ for (const { stmt, moduleName } of this.pendingArrayDecls) {
7075
+ const prev = this.currentSourceModule;
7076
+ this.currentSourceModule = moduleName;
7077
+ this.evaluateVariableDeclaration(stmt);
7078
+ this.currentSourceModule = prev;
7079
+ }
7080
+ this.pendingArrayDecls = [];
7016
7081
  for (const { ast, moduleName } of modules) {
7017
7082
  const seen = /* @__PURE__ */ new Set();
7018
7083
  for (const stmt of ast.body) {
@@ -7038,6 +7103,16 @@ var Evaluator = class _Evaluator {
7038
7103
  }
7039
7104
  }
7040
7105
  }
7106
+ this.resolveIdentifiersDone = true;
7107
+ for (const { moduleName, stmts } of this.pendingTopLevel) {
7108
+ const prev = this.currentSourceModule;
7109
+ this.currentSourceModule = moduleName;
7110
+ for (const stmt of stmts) {
7111
+ this.evaluateStatement(stmt);
7112
+ }
7113
+ this.currentSourceModule = prev;
7114
+ }
7115
+ this.pendingTopLevel = [];
7041
7116
  }
7042
7117
  /** @deprecated Use {@link resolveIdentifiers} instead. */
7043
7118
  reEvaluateModuleConstsAll(modules) {
@@ -9666,6 +9741,21 @@ function evaluateCCExpr(expr, resolve2) {
9666
9741
  }
9667
9742
  return parseOr();
9668
9743
  }
9744
+ function stripVBAFileHeader(source) {
9745
+ const lines = source.split("\n");
9746
+ if (!lines[0]?.trimEnd().toUpperCase().startsWith("VERSION")) return source;
9747
+ const result = [...lines];
9748
+ let i = 0;
9749
+ result[i] = "";
9750
+ i++;
9751
+ while (i < result.length) {
9752
+ const trimmed = result[i].trimEnd().toUpperCase();
9753
+ result[i] = "";
9754
+ i++;
9755
+ if (trimmed === "END") break;
9756
+ }
9757
+ return result.join("\n");
9758
+ }
9669
9759
 
9670
9760
  // ../../test-libs/mock-loader.ts
9671
9761
  var fs = __toESM(require("fs"), 1);
@@ -9720,7 +9810,8 @@ function loadVbaMock(file, evaluator) {
9720
9810
  const source = fs.readFileSync(file, "utf-8");
9721
9811
  const ext = path4.extname(file).toLowerCase();
9722
9812
  const moduleName = path4.basename(file, ext);
9723
- const processed = preprocess(source);
9813
+ const stripped = stripVBAFileHeader(source);
9814
+ const processed = preprocess(stripped);
9724
9815
  const isRawCls = ext === ".cls" && !processed.trim().toLowerCase().startsWith("class ") && !processed.toLowerCase().includes("end class");
9725
9816
  const parseOpts = isRawCls ? { parseAsClass: moduleName } : {};
9726
9817
  const classesBefore = evaluator.getRegisteredClassNames();
@@ -10577,8 +10668,9 @@ var VBARunner = class {
10577
10668
  try {
10578
10669
  const moduleName = path5.basename(file, path5.extname(file));
10579
10670
  this.evaluator.setSourceModule(moduleName);
10580
- source = preprocess(source, config.compilerConstants);
10581
10671
  const ext = path5.extname(file).toLowerCase();
10672
+ source = stripVBAFileHeader(source);
10673
+ source = preprocess(source, config.compilerConstants);
10582
10674
  const isRawCls = ext === ".cls" && !source.trim().toLowerCase().startsWith("class ") && !source.toLowerCase().includes("end class");
10583
10675
  const parseOpts = isRawCls ? { parseAsClass: moduleName } : {};
10584
10676
  const ast = new Parser(new Lexer(source).tokenize(), parseOpts).parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vba-runner",
3
- "version": "0.1.1-alpha.2",
3
+ "version": "0.1.1-alpha.4",
4
4
  "description": "VBA execution engine with test runner and CLI tools (analyzer, formatter, parse-check)",
5
5
  "type": "module",
6
6
  "license": "MIT",