Excel VBAをJavaScriptに翻訳 その５

# はじめに
[**前回**](https://qiita.com/pooshikin/items/2cf33056b7e58cf7fe48)は、VBAを字句解析する簡単なサンプルプログラムを作成しました。
今回は、VBAの構文解析する前提としてVBAの予約語を定義したいと思います。
あわせてVBA翻訳のサンプルVBAを提示します。

# 前提
* OS : Windows7以上
* PoweShellのターミナルで実行
* VSCodeでコード編集
* node.js環境構築済み

# VBEのオブジェクトブラウザーからVBAの予約語を取り出す
VBEとはVBAを編集するためのExcelに同梱されたエディターです。
Excelから<kBd>Atl+F11</kBd>で起動できます。
VBAのライブラリの定義をここから参照して予約語をまとめます。

赤丸のボタンをマウスでクリックします。
![VBE.png](https://qiita-image-store.s3.amazonaws.com/0/239597/bdab872f-a5bc-7b2b-9dbc-61b705cbe6d4.png)
**すべてのライブラリ**と表示のあるドロップボックスのリストから**VBA**を選択します。
![VBE2.png](https://qiita-image-store.s3.amazonaws.com/0/239597/50c4633e-b464-4b3e-410c-257005ad7bee.png)
![VBE3.png](https://qiita-image-store.s3.amazonaws.com/0/239597/91a21def-7c6d-a387-3197-35f08e5042e9.png)
**クラス**のリストボックスの**Collection**を選択します。**Collectionのメンバー**のリストが表示されます。
![VBE4.png](https://qiita-image-store.s3.amazonaws.com/0/239597/a88ecb77-93b1-50dd-a844-5ecfa7098b13.png)
この内容を以下のように定義してみます。

```JavaScript
var vba_Module_Collection = [Add, Count, Item, Remove];
```

同様にクラスのリストに含まれる、**モジュール**、**クラス**、**Enum**の各メンバーを定義したのが
以下となります。

```JavaScript
var vba_Module_Collection = [Add, Count, Item, Remove];
var vba_Module_ColorConstans = [vbBlack, vbBlue, vbCyan, vbGreen, vbMagenta, vbRed,vbWhite, vbYellow];
var vba_Module_Constans = [vbBack, vbCr,vbCrLf, vbFormFeed, vbLf, vbNewLine, vbNullChar, vbNullString, vbObjectError, vbTab, vbVerticalTab];
var vba_Module_Conversion = [Cbool, CByte, Ccur, CDate, CDbl, CDec, Cint, Clng, CLngPtr, CSng, CStr, CVar, CVDate, CVErr, Error, Error$, Fix, Hex, Hex$, Int, Oct, Oct$, Str, Str$, Val];
var vba_Module_DateTime = [Calendar, Date, Date$, DateAdd, DateDiff, DatePart, DteSerial, DateValue, Day, Hour, Minute, Month, Now, Second, Time, Time$, Timer, TimeSerial, TimeValue, Weekday, Year];
var vba_Class_ErrObject = [Clear, Description, HelpContext, HelpFile, LastDllError, Number, Raise, Source];
var vba_Module_FileSystem = [ChrDir, ChrDrive, CurDrive, CurDir, CurDir$, Dir, EOF, FileAttr, FileCopy, FileDateTime, FileLen, GetAttr, Kill, Loc, LOF, MkDir, Reset, RmDir, Seek, SetAttr];
var vba_Class_Financial = [DDB, FV, Ipmt, IRR, MIRR, NPer, NPV, Pmt, PPmt, PV, Rate, SLN, SYD];
var vba_Enum_FromShowConstants = [vbModal, vbModeless];
var vba_Class_Global = [Load, Unload, UserForms];
var vba_Module_Information = [Err, IMEState, IsArray, IsDate, IsEmpty, IsError, IsMissing, IsNull, IsNumeric, IsObject, QBClor, RGB, TypeName, VarType];
var vba_Module_Interaction = [AppActivate, Beep, CallByName, Choose, Command, Command$,CreateObject, DeleteSetting, DoEvents, Environ, Environ$, GetAllSettings, GetObject, GetSetting, IIF, InputBox, MsgBox, Patition, SaveSetting, SendKeys, Shell, Switch];
var vba_Module_KeyCodeConstants = [vbKey0, vbKey1, vbKey2, vbKey3, vbKey4, vbKey5, vbKey6, vbKey7, vbKey8, vbKey9, vbKeyA, vbKeyAdd, vbKeyB, vbKeyBack, vbKeyC, vbKeyCancel, vbKeyCapital, vbKeyClear, vbKeyControl, vbKeyD, vbKeyDecimal, vbKeyDelete, vbKeyDivide, vbKeyDown, vbKeyE, vbKeyEnd, vbKeyEscape, vbKeyExecute, vbKeyF, vbKeyF1, vbKeyF10, vbKeyF11, vbKeyF12, vbKeyF13, vbKeyF14, vbKeyF15, vbKeyF16, vbKeyF2, vbKeyF3, vbKeyF4, vbKeyF5, vbKeyF6, vbKeyF7, vbKeyF8, vbKeyF9, vbKeyG, vbKeyH, vbKeyHelp, vbKeyHome, vbKeyI, vbKeyInsert, vbKeyJ, vbKeyK, vbKeyL, vbKeyLButton, vbKeyLeft, vbKeyM, vbKeyMButton, vbKeyMenu, vbKeyMultiply, vbKeyN, vbKeyNumLock, vbKeyNumpad0, vbKeynumpad1,vbKeynumpad2, vbKeynumpad3, vbKeynumpad4, vbKeynumpad5, vbKeynumpad6, vbKeynumpad7, vbKeynumpad8, vbKeynumpad9, vbKeyO, vbKeyP, vbKeyPageDown, vbKeyPageUp, vbKeyPause, vbKeyPrint, vbKeyQ, vbKeyR, vbKeyRButton, vbKeyReturn, vbKeyRight, vbKeyS, vbKeySelect, vbKeySeparator, vbKeyShift, vbKeySnapshot, vbKeySpace, vbKeySubtract, vbKeyT, vbKeyTab, vbKeyU, vbKeyUp, vbKeyV, vbKeyW, vbKeyX, vbKeyY, vbKeyZ];
var vba_Module_Math = [Abs, Atn, Cos, Exp, Log, Randomize, Rnd, Round, Sgn, Sin, Sqr, Tan];
var vba_Module_Strings = [Asc, AscB, AscW, Chr, Chr$, ChrB, ChrB$, ChrW, ChrW$, Filter, Format, Format$, FormatCurrncy, FormatDateTime, FormatNumber, FormatPercent, InStr, InStrB, InStrRev, join, LCase, LCase$, Left, Left$, LeftB, LeftB$, Len, LenB, LTrim, LTrim$, Mid, Mid$, MidB, MidB$, MonthName, Replace, Right, Right$, RightB, RightB$, RTrim, RTrim$, Space, Space$, Split, StrComp, StrConv, String, String$, StrReverse, Trim, Trim$, UCase, UCase$, WeekdayName];
var vba_Module_SystemColorConstants = [vb3DDKShadow, vb3DFace, vb3DHighlight, vb3DLight, vb3DShadow, vbActiveBorder, vbActiveTitleBar, vbApplicationWorkspace, vbButtonFace, vbButtonShadow, vbButtonText, vbDesktop, vbGrayText, vbHiglight, vbHiglightText, vbInactiveBorder, vbInactiveCaptionText, vbInactiveTitleBar, vbInfoBackground, vbInfoText, vbMenuBar, vbMenuText, vbMsgBox, vbMsgBoxText, vbScrollBars, vbTitleBarText, vbWindowBackground, vbWindowFrame, vbWindowText];
var vba_Enum_vbAppWinStyle = [vbHide, vbMaximizedFocus, vbMinimizedFocus, vbMinimizedNoFocus, vbNormalFocus, vbNormalNoFocus];
var vba_Enum_vbCalendar = [vbCalGreg, vbCalHijri];
var vba_Enum_vbCallType = [vbGet, vbLet, vbMethod, vbSet];
var vba_Enum_vbComperMrthod = [vbBinaryCompare, vbDatabaseCompare, vbTextCompare];
var vba_Enum_vbDateTimeFormat = [vbGreneralDate, vbLongDate, vbLongTime, vbShortDate, vbShortTime];
var vba_Enum_vbDayOfWeek = [vbFriday, vbMonday, vbSturday, vbSunday, vbThursday,vbTuesday,vbUseSystemDayOfWeek, vbWendnesday];
var vba_Enum_vbFileAttribute = [vbAlias, vbArchive, vbDirectory, vbHidden, vbNormal, vbReadOnly, vbSystem, vbVolume];
var vba_Enum_vbFirstWeekOfYear = [vbFirstFourDays, vbFirstFullWeek, vbFirstJan1, vbUseSystem];
var vba_Enum_vbIMEStatus = [vbIMEAlphaDbl, vbIMEAlphaSng, vbIMEDisable, vbIMEHiragana, vbIMEKatakanaDbl, vbIMEKatakanaSng, vbIMEModeAlpha, vbIMEModeAlphaFull, vbIMEModeDisable, vbIMEModeHangul, vbIMEModeHangulFull, vbIMEModeHiragana, vbIMEModeKatakana, vbIMEModeKatakanaHalf, vbIMEModeNoControl, vbIMENodeOff, vbIMENodeOn, vbIMENoOp, vbIMEOff, vbIMEOn];
var vba_Enum_vbMsgBoxResult = [vbAbout, vbCancel, vbIgnore, vbNo, vbOK, vbRetry, vbYes]
var vba_Enum_vbMsgBoxStyle = [vbAboortRetryIgnore, vbApplicationModal, vbCritical, vbDefaultButton1, vbDefaultButton2, vbDefaultButton3, vbDefaultButton4, vbExclamation, vbInformation, vbMsgBoxHelpButton, vbMsgBoxRight, vbMsgBoxRtlReading, vbMsgBoxSerForeground, vbOKCancel, vbOKOnly, vbQuestion, vbRetryCancel, vbSystemModal, vbYesNo,vbYesNoCancel];
var vba_Enum_vbQueryClose = [vbAppTaskManager, vbAPPWindows, vbFormCode, vbFormControlMenu, vbFormMDIForm];
var vba_Enum_vbStrConv = [vbFormUnicode, vbHiragana, vbKatakana, vbLowerCase, vbNorrow, vbProperCase, vbUnicode, vbUpperCase, vbWide];
var vba_Enum_vbTriState = [vbFalse, vbTrue, vbUseDefault];
var vba_Enum_vbVarType = [vbArray, vbBoolean, vbByte, vbCurrency, vbDataObject, vbDate, vbDecimal, vbDouble, vbEmpty, vbError, vbInteger, vbLong, vbNull, vbObject, vbSingle, vbUserDifinedType, vbVariant];
```

# オブジェクトライブラリ以外の予約語
VBAを構成する構文解析で登場するキーワードをざっくりと定義します。
もちろん上記のVBAオブジェクトライブラリ定義以外となります。
悪戦苦闘で以下のように定義しました。（最終的には完結予定）

```JavaScript
// 予約語
var vba_reserved_word = [
        AddressOfAnd, Any, Array, As, Attribute, Append,
        Base, Binary, Boolean, ByRef, Byte, ByVal,
        Call, Case, Circle, Close, Compare, Const,
        Debug, Decimal, Declare, DefBool, DefByte, DefCur, 
        DefDate, DefDbl, DefDec, DefInt, DefLng, DefLngLng, 
        DefLngPtr, DefObj, DefSng, DefStr, DefVar, Dim, Do, Double,
        Each, Else, ElseIf, Empty, End, EndIf, Enum, Eqv, Erase, 
        Event, Exit, Explicit,
        FALSE, For, Friend, Function,
        Get, Global, GoSub, GoTo,
        If, Imp, Implements, In, Input, InputB, Integer, Is,
        Lbound, Let, Like, LINEINPUT, Lock, Long, LongLong, 
        LongPtr, Loop, Lset,
        Me, Mod,
        New, Next, Not, Nothing, Null,
        On, Open, Option, Optional, Or, Output,
        ParamArray, Preserve, Print, Private, Property, PSet, 
        Public, Put,
        Random, RaiseEvent, ReDim, Rem, Resume, Return, Rset,
        Scale, Selec, Set, Shared, Single, Spc, Static, Step, Stop, Sub,
        Tab, Then, To, TRUE, Type, TypeOf,
        Ubound, Unlock, Until,
        Variant, VB_Base, VB_Control, VB_Creatable, VB_Customizable,
        VB_Description, VB_Exposed, VB_Ext_KEY, VB_GlobalNameSpace, 
        VB_HelpID, VB_Invoke_Func, VB_Invoke_Property, VB_Invoke_PropertyPut,
        VB_Invoke_PropertyPutRef, VB_MemberFlags, VB_Name, VB_PredeclaredId,
        VB_ProcData, VB_TemplateDerived, VB_UserMemId, VB_VarDescription,
        VB_VarHelpID, VB_VarMemberFlags, VB_VarProcData, VB_VarUserMemId,
        Wend, While, With, WithEvents, Write,
        Xor
];        
// VBA演算子        
var vba_operator = ["+", "-", "*", "/", "\", "^"];
// VBA比較演算子
var vba_comparison_operator = ["<", "<=", ">", ">=", "=", "<>"];
```

# 予約語について
VBAで登場する予約語をまとめました。
たぶん抜けや誤字・脱字がありそうです。調整しながら進んでいきます。

# 最初のExcelと翻訳対象VBAコード
構文解析ロジックを組み立てる上で元になるExcelとVBAのサンプルを用意します。
これを最初にJavaScriptへ翻訳して構文解析ロジックの正規化を図りたいと思います。

## 問題集を作るExcel
### ① 元文
問題のテキスト(wikiなどからコピペ)。
穴埋め問題にしたいテキストを赤文字に変更します。
### ②問題
セルA5に**=AnzColor(A1,3)**を入力します。
※カラーインデックス=3は赤

|関数|引数|機能|
|:--|:--|:--|
|AnzColor|セルアドレス、カラーインデックス|元文の指定したカラー文字を□に置換します|
### ③解答
セルA8に**=AnsColor(A1,3)**を入力します。
※カラーインデックス=3は赤

|関数|引数|機能|
|:--|:--|:--|
|AnsColor|セルアドレス、カラーインデックス|元文の指定したカラー文字を抽出します|

![VBE5.png](https://qiita-image-store.s3.amazonaws.com/0/239597/19e84e73-e622-1e8e-009a-223ee58e9a57.png)

## 問題集を作るVBAコード

```VisualBasic
'
' 特定範囲の文字色を判定し、□に変更して返す
' adrs : Cell
' clr  : ColorIndex
'
Function AnzColor(adrs, clr) As String

    Dim iLen    As Integer
    Dim ix      As Integer
    Dim Buf     As String
    
    Sheets("Question").Select
    
    Buf = ""
    For Each ad In adrs
        iLen = Len(ad)
        For ix = 1 To iLen
            
            ' 指定した色の場合
            If ad.Characters(ix, 1).Font.ColorIndex = clr Then
                Buf = Buf & "□"
            Else
                Buf = Buf & Mid(ad, ix, 1)
            End If
        Next ix
    Next
    
    AnzColor = Buf
        
End Function

'
' 特定範囲の文字色を判定し、文字色に一致する文字列群を返す
' adrs : Cell
' clr  : ColorIndex
'
Function AnsColor(adrs, clr) As String

    Dim iLen    As Integer
    Dim ix      As Integer
    Dim Buf     As String
    Dim sw      As Boolean
    
    Sheets("Question").Select
    Buf = ""
    sw = True
    
    For Each ad In adrs
        iLen = Len(ad)
        For ix = 1 To iLen
            
            ' 指定した色の場合
            If ad.Characters(ix, 1).Font.ColorIndex = clr Then
                Buf = Buf & IIf(sw = False, ",", "") & Mid(ad, ix, 1)
                sw = True
            Else
                sw = False
            End If
        Next ix
    Next
    
    AnsColor = IIf(Left(Buf, 1) = ",", Mid(Buf, 2), Buf)
        
End Function
```

# まとめ
ばたばたと予約語と翻訳対象のVBAを作成しました。
途中で路線変更しそうな予感です。
このあたりが一番退屈なところかもしれません。
予約語はこんな感じぐらいで認識しましょう。