コメントトークン化

Option Explicit
Option Base 1

'--------------------------------------------------
'プロシージャの概要:
'処理名:コメントトークン化処理
'概要:t01c_連結コードStep1テーブルのコメント部分をトークンに置換してt02d_トークン化テーブルを作成する
'--------------------------------------------------
Public Sub prcCreateコメントトークン化()
    Const C_TABLE_SOURCE As String = "t01c_連結コードStep1"
    Const C_TABLE_TARGET As String = "t02d_トークン化"
    Const C_TABLE_TOKEN As String = "t02e_トークン管理"
    
    Dim dbs As DAO.Database
    Dim rstSource As DAO.Recordset
    Dim rstTarget As DAO.Recordset
    Dim rstToken As DAO.Recordset
    Dim strSQL As String
    Dim lngCurrentLineID As Long
    Dim strCurrentCode As String
    Dim strTokenizedCode As String
    Dim lngTokenCounter As Long
    Dim lngProcessedCount As Long
    
    ' カレントデータベースを取得
    Set dbs = CurrentDb
    
    ' トークンカウンターを初期化
    lngTokenCounter = 0
    lngProcessedCount = 0
    
    ' 既存データをクリア
    dbs.Execute "DELETE FROM " & C_TABLE_TARGET
    dbs.Execute "DELETE FROM " & C_TABLE_TOKEN
    
    ' レコードセットを開く
    strSQL = ""
    strSQL = strSQL & "SELECT * FROM " & C_TABLE_SOURCE
    strSQL = strSQL & " ORDER BY 連結コードラインID"
    Set rstSource = dbs.OpenRecordset(strSQL)
    
    Set rstTarget = dbs.OpenRecordset(C_TABLE_TARGET)
    Set rstToken = dbs.OpenRecordset(C_TABLE_TOKEN)
    
    ' 各レコードを処理
    Do Until rstSource.EOF
        lngCurrentLineID = rstSource!連結コードラインID
        strCurrentCode = Nz(rstSource!連結コード, "")
        
        ' コメントトークン化処理
        strTokenizedCode = fnctokenizeコメント(strCurrentCode, _
                                             lngCurrentLineID, _
                                             lngTokenCounter, _
                                             rstToken)
        
        ' t02d_トークン化テーブルにレコード追加
        rstTarget.AddNew
        rstTarget!連結コードラインID = lngCurrentLineID
        rstTarget!トークン化コード = strTokenizedCode
        rstTarget.Update
        lngProcessedCount = lngProcessedCount + 1
        
        rstSource.MoveNext
    Loop
    
    ' リソース解放
    rstSource.Close
    rstTarget.Close
    rstToken.Close
    Set rstSource = Nothing
    Set rstTarget = Nothing
    Set rstToken = Nothing
    Set dbs = Nothing
    
    MsgBox "コメントトークン化処理が完了しました。" & vbCrLf & _
           "処理件数: " & lngProcessedCount & vbCrLf & _
           "トークン数: " & lngTokenCounter, vbInformation
End Sub

'--------------------------------------------------
'プロシージャの概要:
'処理名:コメントトークン化関数
'概要:1行のコードからコメント部分を検出してトークンに置換する
'strCode:処理対象のコード行
'lngLineID:元の連結コードラインID
'lngTokenCounter:トークンカウンター(参照渡し)
'rstToken:トークン管理テーブルのレコードセット
'戻り値:トークン化されたコード
'--------------------------------------------------
Private Function fnctokenizeコメント(strCode As String, lngLineID As Long, _
                                   ByRef lngTokenCounter As Long, _
                                   ByRef rstToken As DAO.Recordset) As String
    Dim strResult As String
    Dim intCommentPos As Integer
    Dim strCommentPart As String
    Dim strCodePart As String
    Dim strTokenName As String
    Dim blnInString As Boolean
    Dim i As Integer
    
    strResult = strCode
    
    ' 文字列リテラル内のシングルクォートを考慮してコメント位置を検索
    ' 【重要】文字列内の'はコメントではないため、ダブルクォートの内外を判定しながら処理する
    ' 例:"Don't worry"  ' これはコメント
    '     ↑この'はコメントではない  ↑この'がコメント開始位置
    blnInString = False
    For i = 1 To Len(strCode)
        If Mid(strCode, i, 1) = Chr(34) Then ' ダブルクォート(")を検出
            ' 文字列の開始/終了を切り替え
            blnInString = Not blnInString
        ElseIf Mid(strCode, i, 1) = "'" And Not blnInString Then
            ' シングルクォート(')かつ文字列外の場合のみコメント開始と判定
            intCommentPos = i
            Exit For
        End If
    Next i
    
    ' コメントが見つかった場合
    If intCommentPos > 0 Then
        ' コード部分とコメント部分を分離
        strCodePart = Trim(Left(strCode, intCommentPos - 1))
        strCommentPart = Mid(strCode, intCommentPos)
        
        ' トークン名を生成
        lngTokenCounter = lngTokenCounter + 1
        strTokenName = "@cmt" & lngTokenCounter
        
        ' トークン管理テーブルにレコード追加
        rstToken.AddNew
        rstToken!トークンID = lngTokenCounter
        rstToken!元連結コードラインID = lngLineID
        rstToken!トークン文字列 = strTokenName
        rstToken!元コメント内容 = strCommentPart
        
        ' コメント種別を判定
        If Len(strCodePart) > 0 Then
            rstToken!コメント種別 = "行末コメント"
        Else
            rstToken!コメント種別 = "単独行コメント"
        End If
        
        rstToken.Update
        
        ' トークン化されたコードを作成
        If Len(strCodePart) > 0 Then
            strResult = strCodePart & " " & strTokenName
        Else
            strResult = strTokenName
        End If
    End If
    
    fnctokenizeコメント = strResult
End Function