発端
何気なくAIに作らせたVBAマクロのソースが巨大な1つのSub関数でした。(VBAソースを学習してるとそうなるのかな?)
- 仕方ないので、VBAソースをAIにリファクタリングさせました
- VS Code上でリファクタリングしている過程でコードが壊れていないか不安でした
- VS Code上でVBAを実行できるようにしたくなりました
- AIにVBA評価器も一緒に作らせながらリファクタリングしました
結果、そのVBAマクロの使用機能の範囲で、一晩で評価器ができてしまいました。
しばらくこのプロジェクトは放置していたのですが、趣味の範囲で本格的にVBA評価器を完成させたくなったのでVBA仕様準拠、振る舞い準拠するべく日々(AIが)改修しています。
だいぶ動くようになったと思ったので公開した次第です。
最近、超巨大レガシーコードに出会ってしまいました。この評価器と一緒にAIに丸投げして、勝手に安全にリファクタリングできるようになるといいなと思ってますが、怖いので本番投入はできてません。
リポジトリです
副産物のWeb上でのVBA実行環境です
READMEの一部抜粋です。リンク切れしている箇所は直接GitHubのサイトを見てください。
VBA Runner — VBA実行環境 + リファクタリング支援ツール
Excel 不要で VBA コードを実行・テスト・静的解析できる実行環境です。
VBA実行エンジン(パーサー + AST評価器)と、リファクタリング支援のための静的解析CLI(vba-analyzer)を備えています。
AIによる支援を前提とした設計で、レガシーVBAコードの品質改善を加速します。
VBA Runnerの目的
- Excel 不要での実行・確認: Excel (Windows/Mac) に依存せず、Node.js 環境で直接 VBA の構文とロジックを実行・検証できます。
-
リファクタリングの支援:
vba-analyzerを使って巨大なVBAコードベースの問題箇所(深いネスト・重複ブロック・Excel依存)を特定し、純粋な関数を安全に切り出す検証基盤を提供します。 - ユニットテストの実行: TypeScript のテストランナーを通じて、抽出した VBA 関数にモックデータとアサーションを与え、プログラムによるテスト自動化を実現します。
VS Code などのモダンな環境で、AIの支援を受けながら VBA ソースのリファクタリングとテストを進めることができます。
VSCode拡張機能 (Phase 2)
VBA Runnerにおける第一段階(MS-VBAL 仕様書で名前の付いた構文要素・標準ライブラリ関数の実装)は完了しました。仕様書本文に書かれているが個別セクションを持たない ランタイム挙動 の検証は継続中で、進捗は TODO.md の「VBA ランタイム挙動」を参照してください。
第二段階として、この実行エンジンを組み込んだ VSCode拡張機能 を実装しました。.vba / .bas / .cls / .frm ファイルを開くと、以下のLSP機能が有効になります。詳細は後述の「VSCode拡張機能の使い方」を参照してください。
今後の開発方針については TODO_NEXT.md を参照してください。
基本方針:環境依存操作のスタブ・仮想化
VBA Runnerはリファクタリングとユニットテストの実行を主目的としています。そのため、実行環境(OSやファイルシステム)に副作用を及ぼす、または環境に依存する以下の操作は、原則としてサンドボックス化またはスタブ・フェイク実装で行います。
-
外部コマンドの実行 (
Shell): 実際のコマンドは実行せず、ログ出力と成功ステータスの返却のみを行います(スタブ化)。 -
ファイル操作 (
Open,Kill等): 指定されたサンドボックスディレクトリ (sandbox/) 内に制限されます(仮想化)。 -
環境変数 (
Environ): OSの実環境変数にはアクセスせず、仮想的な環境変数(.env等)を使用します。 -
レジストリ (
GetSetting,SaveSetting): Windowsレジストリにはアクセスせず、メモリ上のマップ等による仮想レジストリを使用します。 -
外部オブジェクト・ネットワーク (
CreateObject,MSXML2.XMLHTTP): 主要なライブラリをスタブ・フェイク実装として提供し、実際のCOMオブジェクト生成やネットワーク通信は行いません。 -
ユーザー対話 (
MsgBox,InputBox): 実行をブロックせず、ログ出力とデフォルト値の返却のみを行います(スタブ化)。
AIを活用する場合、リファクタリングしたいソースコードと同じ階層に VBA Runner を配置し、AIに対して「この vba-runner を使ってリファクタリングおよびテストを実行して」と依頼(プロンプト)することで、スムーズに作業を進めることができます。
VBA言語仕様の拡張機能
クラス定義の拡張構文
実VBAの標準仕様では、クラスはファイル名(.cls ファイル)またはVB_Name属性によってのみ定義されます。Class...End Class という構文は存在しません。
本実行エンジンの拡張機能として、.bas モジュール内に Class キーワードを使用してクラスを定義可能にしています。これにより、テストコード内で複数のクラス定義を1つのファイルにまとめることができ、ユニットテスト環境での利便性が向上します。
' module.bas の例(本エンジンの拡張)
Class MyClass
Public Name As String
Sub Initialize(n As String)
Name = n
End Sub
End Class
Sub Main()
Dim obj As MyClass
Set obj = New MyClass
obj.Initialize "Test"
Debug.Print obj.Name
End Sub
ただし、Excelで実行することを想定したVBAコード(.cls ファイル)では、このクラス定義構文を使用しないでください。.cls ファイルはVBA標準の形式で、ファイル名がクラス名となります。
VBA仕様上の制限
本エンジンで実装されているVBAは、MS-VBAL(MS-VBA Language)仕様に準拠しています。以下はVBA言語仕様で定義されている制限事項です。
モジュール名の長さ制限
VBA仕様により、モジュール名(.cls ファイルの場合はファイル名、.bas ファイルの場合は名前属性)の最大長は 31文字 に制限されています。(MS-VBAL 仕様書 §5.2 参照)
' OK: 31文字以下
Module MyModule ' 8文字
Class VeryLongModuleNameExample ' 27文字
' NG: 32文字以上
' Class ThisIsAVeryLongModuleNameThatExceeds31Chars ' 計50文字以上
ディレクトリ構成とVBAサンプルコード
VBA Runnerでは、巨大なVBAマクロを「テスト可能な単位」にリファクタリングする事例として、以下の構成を採用しています。
project-dir
├── vba-runner ...VBA Runner(`git clone`で配置してください)
│ ├── src/engine/
│ ├── sample/src/vba/
│ ├── sample/src/vba_legacy/
│ ├── sample/tests/ts/
│ ├── tests/spec/
│ ├── tests/test-libs-tests/
│ └── test-libs/
├── node-vX.X.X-win-x64 ...実行用Node.jsの実行環境(Windows64bitスタンドアローン版の例)
│ ├── node_modules/ 普段Node.jsを使わない人はここにスタンドアローン版を配置するのが手軽です
│ └── ...
├── massive-vba-project ...リファクタリングしたいVBAソース
│ ├── Module1.vba
│ ├── Module2.vba
│ ├── ...
│ └── Form1.frm
└── test/ ...VBAソースをテストするためのTypeScriptで書かれたテストコード
├── Module1Test/
│ └── test.ts
└── Module2Test/
└── test.ts
続きは以下を参照してください