Option Explicit
' =========================
' Entry Point
' =========================
Public Sub BuildParseMaterial()
' 元テーブル(必須列):
' [コード].[コードラインID], [コード].[ProcID], [コード].[LineNo], [コード].[TrimCode], [コード].[コメント行フラグ]
' 出力テーブル:
' [構造解析行](既に作成済み)
Dim objDb As DAO.Database
Dim rsSrc As DAO.Recordset
Dim rsDst As DAO.Recordset
Dim lngCurrentProcID As Long
Dim strBuffer As String
Dim blnInContinuation As Boolean
Dim lngTokenSeq As Long ' @txt の通し番号(ジョブ内で一意)
Dim strSql As String
On Error GoTo EH
Set objDb = CurrentDb
' 出力を初期化(必要に応じてコメントアウト可)
objDb.Execute "DELETE FROM [構造解析行]", dbFailOnError
' 解析対象の取得:コメント行は除外し、ProcID→LineNoで安定化
strSql = ""
strSql = strSql & "SELECT [コード].[コードラインID], [コード].[ProcID], [コード].[LineNo], [コード].[TrimCode], Nz([コード].[コメント行フラグ], False) AS コメント行フラグ "
strSql = strSql & "FROM [コード] "
strSql = strSql & "WHERE Nz([コード].[コメント行フラグ], False) = False "
strSql = strSql & "ORDER BY [コード].[ProcID], [コード].[LineNo]"
Set rsSrc = objDb.OpenRecordset(strSql, dbOpenSnapshot)
Set rsDst = objDb.OpenRecordset("[構造解析行]", dbOpenDynaset)
lngCurrentProcID = 0
strBuffer = vbNullString
blnInContinuation = False
lngTokenSeq = 0
Do While Not rsSrc.EOF
Dim lngProcID As Long
Dim lngLineID As Long
Dim strLine As String
Dim blnHasCont As Boolean
lngProcID = Nz(rsSrc.Fields("ProcID").Value, 0&)
lngLineID = Nz(rsSrc.Fields("コードラインID").Value, 0&)
strLine = Nz(rsSrc.Fields("TrimCode").Value, vbNullString)
' プロシージャ切替時:バッファ吐き出し
If (lngCurrentProcID <> 0) And (lngProcID <> lngCurrentProcID) Then
If Len(strBuffer) > 0 Then
Call WriteParsedRows(rsDst, lngCurrentProcID, lngLineID, strBuffer, lngTokenSeq)
strBuffer = vbNullString
blnInContinuation = False
End If
End If
lngCurrentProcID = lngProcID
' 実質空行はスキップ
If Len(Trim$(strLine)) = 0 Then
rsSrc.MoveNext
GoTo ContinueLoop
End If
' 行継続の判定
blnHasCont = HasLineContinuation(strLine)
If blnHasCont Then
' 末尾の「 _」を外して結合
strBuffer = Trim$(strBuffer & " " & StripContinuationSuffix(strLine))
blnInContinuation = True
Else
' 最終行:ここでひと束完成 → 整形して出力
strBuffer = Trim$(strBuffer & " " & strLine)
Call WriteParsedRows(rsDst, lngProcID, lngLineID, strBuffer, lngTokenSeq)
strBuffer = vbNullString
blnInContinuation = False
End If
ContinueLoop:
rsSrc.MoveNext
Loop
' ファイル末尾の残りを出力
If Len(strBuffer) > 0 Then
Call WriteParsedRows(rsDst, lngCurrentProcID, 0&, strBuffer, lngTokenSeq)
End If
rsDst.Close: Set rsDst = Nothing
rsSrc.Close: Set rsSrc = Nothing
Set objDb = Nothing
MsgBox "構造解析の素材作成が完了しました。", vbInformation
Exit Sub
EH:
MsgBox "エラー " & Err.Number & " : " & Err.Description, vbExclamation
On Error Resume Next
If Not rsDst Is Nothing Then rsDst.Close
If Not rsSrc Is Nothing Then rsSrc.Close
Set rsDst = Nothing
Set rsSrc = Nothing
Set objDb = Nothing
End Sub
' =========================
' Core: 1束 → 1文ずつ出力
' =========================
Private Sub WriteParsedRows(ByRef rsDst As DAO.Recordset, _
ByVal lngProcID As Long, _
ByVal lngAnyLineID As Long, _
ByVal strMerged As String, _
ByRef lngTokenSeq As Long)
Dim strWork As String
Dim strTokenMap As String
Dim arrSentences() As String
Dim lngI As Long
Dim lngSentIdx As Long
strWork = Trim$(strMerged)
If Len(strWork) = 0 Then Exit Sub
' 1) 文字列 → @txtN 置換(対応表は文字列で保持)
strTokenMap = vbNullString
strWork = TokenizeStrings(strWork, strTokenMap, lngTokenSeq)
' 2) 行内コメントの削除(' と Rem)
strWork = RemoveInlineComment(strWork)
If Len(Trim$(strWork)) = 0 Then Exit Sub
' 3) コロン(:)で文に分割(@txt に退避済みなので安全)
arrSentences = SplitStatements(strWork)
' 4) 各文を整形して保存
lngSentIdx = 0
For lngI = LBound(arrSentences) To UBound(arrSentences)
Dim strSentence As String
strSentence = NormalizeSpaces(arrSentences(lngI))
If Len(strSentence) = 0 Then GoTo NextI
lngSentIdx = lngSentIdx + 1
rsDst.AddNew
rsDst.Fields("コードラインID").Value = lngAnyLineID
rsDst.Fields("ProcID").Value = lngProcID
rsDst.Fields("文インデックス").Value = lngSentIdx
rsDst.Fields("構造解析用コード").Value = strSentence
rsDst.Fields("文字列トークン一覧").Value = strTokenMap
rsDst.Fields("生成日時").Value = Now
rsDst.Update
NextI:
Next lngI
End Sub
' =========================
' Helpers: 行継続
' =========================
Private Function HasLineContinuation(ByVal strS As String) As Boolean
Dim strT As String
strT = RTrimSpaces(strS)
HasLineContinuation = (Len(strT) > 0 And Right$(strT, 1) = "_")
End Function
Private Function StripContinuationSuffix(ByVal strS As String) As String
Dim strT As String
strT = RTrimSpaces(strS)
If Len(strT) > 0 And Right$(strT, 1) = "_" Then
strT = Left$(strT, Len(strT) - 1)
End If
StripContinuationSuffix = RTrimSpaces(strT)
End Function
Private Function RTrimSpaces(ByVal strS As String) As String
Do While Len(strS) > 0
Dim strCh As String
strCh = Right$(strS, 1)
If (strCh = " ") Or (strCh = vbTab) Then
strS = Left$(strS, Len(strS) - 1)
Else
Exit Do
End If
Loop
RTrimSpaces = strS
End Function
' =========================
' Helpers: 文字列 → @txtN
' =========================
Private Function TokenizeStrings(ByVal strS As String, _
ByRef strTokenMap As String, _
ByRef lngTokenSeq As Long) As String
Dim lngI As Long, lngN As Long
Dim blnInString As Boolean
Dim lngStart As Long
Dim strRes As String
lngN = Len(strS)
lngI = 1
blnInString = False
lngStart = 0
strRes = vbNullString
Do While lngI <= lngN
Dim strC As String
strC = Mid$(strS, lngI, 1)
If Not blnInString Then
If strC = """" Then
blnInString = True
lngStart = lngI
Else
strRes = strRes & strC
End If
lngI = lngI + 1
Else
' 文字列内
If strC = """" Then
If (lngI < lngN) And (Mid$(strS, lngI + 1, 1) = """") Then
' 連続の "" はエスケープ(1文字の ")
lngI = lngI + 2
Else
' 文字列終端
Dim strRaw As String
Dim strTkn As String
strRaw = Mid$(strS, lngStart, lngI - lngStart + 1) ' 引用符込み
lngTokenSeq = lngTokenSeq + 1
strTkn = "@txt" & CStr(lngTokenSeq)
strRes = strRes & strTkn
' マッピング文字列へ追記 (区切りは ; )
If Len(strTokenMap) > 0 Then strTokenMap = strTokenMap & ";"
strTokenMap = strTokenMap & strTkn & "=" & strRaw
blnInString = False
lngI = lngI + 1
End If
Else
lngI = lngI + 1
End If
End If
Loop
' 不正な未終端の " があっても、そのまま返す(後工程で検出可能)
TokenizeStrings = strRes
End Function
' =========================
' Helpers: コメント削除
' =========================
Private Function RemoveInlineComment(ByVal strS As String) As String
Dim lngApo As Long
Dim lngRem As Long
Dim strL As String
Dim strOut As String
strOut = strS
' アポストロフィ(')以降を削除
lngApo = InStr(1, strOut, "'", vbBinaryCompare)
If lngApo > 0 Then
strOut = Left$(strOut, lngApo - 1)
End If
' Rem は行頭または空白の直後を有効とみなす(簡易)
strL = LCase$(strOut)
lngRem = InStr(1, strL, " rem ", vbBinaryCompare)
If lngRem = 1 Then
strOut = ""
ElseIf lngRem > 1 Then
If Mid$(strOut, lngRem - 1, 1) = " " Then
strOut = Left$(strOut, lngRem - 1)
End If
End If
RemoveInlineComment = Trim$(strOut)
End Function
' =========================
' Helpers: 文分割(:)
' =========================
Private Function SplitStatements(ByVal strS As String) As String()
Dim arr() As String
Dim lngI As Long
Dim lngStart As Long
Dim strTmp As String
ReDim arr(0 To 0)
lngStart = 1
For lngI = 1 To Len(strS)
If Mid$(strS, lngI, 1) = ":" Then
strTmp = Trim$(Mid$(strS, lngStart, lngI - lngStart))
If Len(strTmp) > 0 Then Call AppendString(arr, strTmp)
lngStart = lngI + 1
End If
Next lngI
strTmp = Trim$(Mid$(strS, lngStart))
If Len(strTmp) > 0 Then Call AppendString(arr, strTmp)
SplitStatements = arr
End Function
Private Sub AppendString(ByRef arr() As String, ByVal strV As String)
Dim lngN As Long
If (UBound(arr) = 0) And (Len(arr(0)) = 0) Then
arr(0) = strV
Exit Sub
End If
lngN = UBound(arr) + 1
ReDim Preserve arr(0 To lngN)
arr(lngN) = strV
End Sub
' =========================
' Helpers: 空白整形
' =========================
Private Function NormalizeSpaces(ByVal strS As String) As String
Dim strOut As String
strOut = Replace$(strS, vbTab, " ")
Do While InStr(strOut, " ") > 0
strOut = Replace$(strOut, " ", " ")
Loop
NormalizeSpaces = Trim$(strOut)
End Function