﻿RC_Output 自然言語仕様

【この文書の目的】
この自然言語仕様に従い、Windowsのデスクトップ版Wordで動くVBAの標準モジュールを作成する。対象モジュール名はこの文書の先頭に示す。関数名、引数の順序、辞書キーを変更しない。変数宣言を必須にし、手動で標準モジュールへ貼り付ける形式で作成する。属性行やThisDocument用のイベントは不要。
他モジュールの公開処理を呼び出す部分は、その処理の存在を前提とする。同じ公開関数を各モジュールへ重複実装しない。不明な会社書式は確認してから記入し、勝手に推測しない。

【ツール全体の契約】
・旧規程と新規程を読み取り専用で解析し、新欄が左、旧欄が右の編集可能なWord表を別ファイルへ保存する。入力を上書きしない。
・外部AI、外部通信、追加ソフト、外部テンプレートを実行時に使わない。Wordの標準オブジェクトと、遅延バインディングによるScripting.Dictionary、Scripting.FileSystemObject、VBScript.RegExpを使う。追加の参照設定を要求しない。
・通常の成功時はdocxだけを出力する。確認レポート、対応表TSV、手動対応を読み込んで再生成する機能は作らない。RC_Reportモジュールも作らない。
・issuesは処理中だけの内部診断。致命的な失敗時の画面表示と実行失敗TXTは残す。RC_Testsを明示実行した場合のテストTXTは別扱い。
・フォントや余白の変更は本文の変更とみなさない。原文の数字、否定語、句読点、空白、タブを言い換えたり削除したりしない。照合専用の正規化を原文や下線位置へ適用しない。
・Office、Wordのマクロ設定、OSのセキュリティ設定は変更しない。自分が開いた入力や作成した文書だけを閉じる。

【共通データ構造】
以下のキーは大文字小文字を含めて統一する。辞書中のオブジェクトはオブジェクトとして格納・差し替えし、文字列化しない。存在しない片側ノードは空文字ではなくNothingとする。Collectionの添字と文字位置は1始まり。

1. 解析文書：sourceは入力パスまたは識別文字列、titleは文書名、articlesは条ノードのCollection、historyは末尾履歴ノードのCollection、extrasは条に属さない保全ノードのCollection。
2. ノード：id、kind、prefix、gap、body、raw、heading、line、children。
  idは解析順ID。条はA001等、項はA001.P001等、号はA001.P001.I001等、履歴はH001等、未知段落はUに元段落番号を続ける。
  kindはarticle、paragraph、item、history、unknownのいずれか。prefixは元の番号文字列、gapは番号と本文の間の元の空白等、bodyは番号を除く本文、rawはprefix・gap・bodyをそのまま連結したもの。headingは条の直前の括弧見出し、lineは元入力の段落位置、childrenは子ノードのCollection。
  条ノード自身のbodyは空文字。その条の先頭本文は第1項ノードP001へ入れる。P001のprefixには元の第n条の番号を入れ、項番号「1」を新たに補わない。条ノードのrawは元番号とgapだけとなる。条にはheadingLineも加え、見出しの元段落位置を記録する。
3. 対応ペア：newとoldは新旧ノードまたはNothing、scoreはDoubleの類似度、statusはauto、review、new、deletedのいずれか、childrenは子対応ペアのCollection。
4. 内部診断：kind、location、reason、rawの4文字列を持つ辞書をissuesのCollectionへ追加する。利用者向けの確認レポートへ書き出さない。
5. 差分結果：newとoldは各側の下線区間Collection、fallbackは長文の代替計算を使ったBoolean。区間はstartという1始まり文字位置とlengthという文字数を持つ辞書。改行や空白を含む原文字列上の位置を使う。
6. 出力行：newTextとoldText、newHeadingとoldHeading、newHeadingBlankとoldHeadingBlank、newPrefixとoldPrefix、newArticleとoldArticle、newNoteとoldNote、historyGap、newSpansとoldSpans。
  TextとNoteは文字列。HeadingとPrefixは文字数のLong。HeadingBlank、Article、historyGapはBoolean。Spansは上記の下線区間Collection。初期値は文字列が空、文字数が0、BooleanがFalse、Collectionは空。

【モジュール間の公開処理】
RC_Configは設定定数、RC_Color、RC_Navy、RC_HeaderFill、RC_Regex、RC_TrimSpace、RC_MatchText、RC_NumberKeyを提供する。
RC_ParseはRC_Dict、RC_Node、RC_ParseText、RC_ReadDocument、RC_ParseLines、RC_NodeText、RC_AddIssueを提供する。
RC_MatchはRC_Similarity、RC_MatchScore、RC_NewPair、RC_Align、RC_PairChangedを提供する。
RC_CharDiffはRC_Diff、RC_AddSpanを提供する。
RC_OutputはRC_OutputDocumentを提供する。
RC_MainはCreateComparisonTable、RC_ShowOutput、RC_RunFiles、RC_UniqueBase、RC_WriteTextを提供する。
RC_Testsは任意の開発用RC_TestAllを提供する。通常の入口からテストを実行しない。
主な呼出し：MainがParseで旧・新を読み、Matchへ新articles・旧articles・issues・locationの順で渡し、Outputへpairs・新解析文書・旧解析文書・issuesの順で渡す。OutputがCharDiffを呼び、各処理がConfigとParseの共通処理を使う。

【他モジュールを呼ぶ際の引数契約】
次の一覧は呼出し互換性のために示す。この文書の対象以外のモジュールの実装を重複して作らない。
RC_Main
・CreateComparisonTable：公開。引数はなし。戻り値なし。
・RC_ShowOutput：公開。引数はoutput（String、値渡し）。戻り値なし。
・RC_RunFiles：公開。引数はoldPath（String、値渡し）、newPath（String、値渡し）、outputFolder（String、値渡し）。戻り値はString。
・RC_UniqueBase：公開。引数はfolder（String、値渡し）、stem（String、値渡し）。戻り値はString。
・RC_WriteText：公開。引数はpath（String、値渡し）、text（String、値渡し）。戻り値なし。
RC_Config
・RC_Color：公開。引数はhexRGB（String、値渡し）。戻り値はLong。
・RC_Navy：公開。引数はなし。戻り値はLong。
・RC_HeaderFill：公開。引数はなし。戻り値はLong。
・RC_Regex：公開。引数はpattern（String、値渡し）。戻り値はObject。
・RC_TrimSpace：公開。引数はtext（String、値渡し）。戻り値はString。
・RC_MatchText：公開。引数はtext（String、値渡し）。戻り値はString。
・RC_NumberKey：公開。引数はprefix（String、値渡し）。戻り値はString。
RC_Parse
・RC_Dict：公開。引数はなし。戻り値はObject。
・RC_Node：公開。引数はnodeId（String、値渡し）、kind（String、値渡し）、prefix（String、値渡し）、gap（String、値渡し）、body（String、値渡し）、line（Long、値渡し）。戻り値はObject。
・RC_ParseText：公開。引数はtext（String、値渡し）、source（String、値渡し）、issues（Collection、値渡し）。戻り値はObject。
・RC_ReadDocument：公開。引数はpath（String、値渡し）、issues（Collection、値渡し）。戻り値はObject。
・RC_ParseLines：公開。引数はlines（Variant、値渡し）、source（String、値渡し）、issues（Collection、値渡し）。戻り値はObject。
・RC_NodeText：公開。引数はn（Object、値渡し）、includePrefix（Boolean、値渡し、省略可能・既定値False）。戻り値はString。
・RC_AddIssue：公開。引数はissues（Collection、値渡し）、kind（String、値渡し）、location（String、値渡し）、reason（String、値渡し）、raw（String、値渡し）。戻り値なし。
RC_Match
・RC_Similarity：公開。引数はleftText（String、値渡し）、rightText（String、値渡し）。戻り値はDouble。
・RC_MatchScore：公開。引数はnn（Object、値渡し）、oo（Object、値渡し）、ni（Long、値渡し）、oi（Long、値渡し）、nc（Long、値渡し）、oc（Long、値渡し）。戻り値はDouble。
・RC_NewPair：公開。引数はnn（Object、値渡し）、oo（Object、値渡し）、score（Double、値渡し）、status（String、値渡し）。戻り値はObject。
・RC_Align：公開。引数はnews（Collection、値渡し）、olds（Collection、値渡し）、issues（Collection、値渡し）、location（String、値渡し）。戻り値はCollection。
・RC_PairChanged：公開。引数はpair（Object、値渡し）。戻り値はBoolean。
RC_CharDiff
・RC_Diff：公開。引数はnews（String、値渡し）、olds（String、値渡し）。戻り値はObject。
・RC_AddSpan：公開。引数はspans（Collection、値渡し）、first（Long、値渡し）、length（Long、値渡し）。戻り値なし。
RC_Output
・RC_OutputDocument：公開。引数はpairs（Collection、値渡し）、newDoc（Object、値渡し）、oldDoc（Object、値渡し）、issues（Collection、値渡し）。戻り値はDocument。
RC_Tests
・RC_TestAll：公開。引数はoutputFolder（String、値渡し）。戻り値はString。

【共通の境界条件】
自動採番、複雑な表、画像、脚注、本文外の図形、変更履歴の解釈は全面対応しない。内部診断と入力原本を保全するが、抽出できないものを架空の条文へ変換しない。
分割・統合の意味を自動で確定しない。残った原文を新設・削除として保全する。一般的な1対多分割の検出を保証しない。条の順序逆転を見やすく示す改善も別課題であり、この再構築で勝手に照合方式を変えない。
この文書は現行処理を再実装するための仕様。AIが生成したVBAのコンパイル・動作・原文保全は、Wordで別途確認する。

【このモジュールの入口・引数・戻り値】
・RC_OutputDocument：公開。引数はpairs（Collection、値渡し）、newDoc（Object、値渡し）、oldDoc（Object、値渡し）、issues（Collection、値渡し）。戻り値はDocument。
・RC_HistoryRows：モジュール内専用。引数はnews（Collection、値渡し）、olds（Collection、値渡し）、rows（Collection、値渡し）、issues（Collection、値渡し）。戻り値なし。
・RC_ExtraRows：モジュール内専用。引数はnews（Collection、値渡し）、olds（Collection、値渡し）、rows（Collection、値渡し）、issues（Collection、値渡し）。戻り値なし。
・RC_SetBorder：モジュール内専用。引数はborder（border、値渡し）、enabled（Boolean、値渡し）。戻り値なし。
・RC_BlankRow：モジュール内専用。引数はなし。戻り値はObject。
・RC_TextRow：モジュール内専用。引数はnews（String、値渡し）、olds（String、値渡し）、allNew（Boolean、値渡し）、allOld（Boolean、値渡し）、issues（Collection、値渡し）。戻り値はObject。
・RC_ArticleRows：モジュール内専用。引数はpair（Object、値渡し）、rows（Collection、値渡し）、issues（Collection、値渡し）。戻り値なし。
・RC_WholeRows：モジュール内専用。引数はnode（Object、値渡し）、rows（Collection、値渡し）、newSide（Boolean、値渡し）、first（Boolean、値渡し）、issues（Collection、値渡し）。戻り値なし。
・RC_DescendantCount：モジュール内専用。引数はnode（Object、値渡し）。戻り値はLong。
・RC_UnitRows：モジュール内専用。引数はpair（Object、値渡し）、rows（Collection、値渡し）、nh（String、値渡し）、oh（String、値渡し）、whole（Boolean、値渡し）、issues（Collection、値渡し）。戻り値なし。
・RC_RenderSide：モジュール内専用。引数はrow（Object、値渡し）、key（String、値渡し）、node（Object、値渡し）、fullBody（Boolean、値渡し）、note（String、値渡し）。戻り値なし。
・RC_CopySpans：モジュール内専用。引数はsource（Collection、値渡し）、target（Collection、値渡し）、offset（Long、値渡し）。戻り値なし。
・RC_PrependHeading：モジュール内専用。引数はrow（Object、値渡し）、key（String、値渡し）、heading（String、値渡し）、underlined（Boolean、値渡し）。戻り値なし。
・RC_FillCell：モジュール内専用。引数はcell（cell、値渡し）、row（Object、値渡し）、key（String、値渡し）。戻り値なし。

【担当範囲】
対応ペアから掲載行を作り、新旧2列のWord文書へ変換する。会社の字体・色・余白・罫線などはすべてRC_Configの記入値を使う。このモジュールへ会社別の値を直接埋め込まない。

【RC_OutputDocument：行を作る順序】
1. 各条ペアについてRC_PairChangedがTrueの場合だけRC_ArticleRowsで行を追加する。全く変わっていない条は全文を掲載しない。
2. 新旧titleが異なる場合は両方の元文書名をRC_TextRowで比較した行を先頭へ入れる。
3. RC_ExtraRowsで条に属さない保全段落を追加する。ここまでの行数をbodyRowsとして記録する。
4. RC_HistoryRowsで新規程の一番下の最新改定・施行履歴を末尾へ加える。新旧共通でも省かない。

【出力行の初期化と文字差分】
RC_BlankRowは共通契約のすべての行キーを初期値付きで作る。
RC_TextRowは両側の文字列をそのままTextへ入れる。allNewまたはallOldがTrueなら、指定された側の全文へ下線区間を作る。どちらもFalseならRC_Diffで両側を比較してSpansへ移す。fallbackの場合は原文と暫定下線であることを内部診断に残す。
RC_CopySpansは各区間を新しい辞書として複写し、startへ指定offsetを加える。元区間Collectionを変更しない。

【変更条の項・号の行】
RC_ArticleRowsは条全体の新設ならRC_WholeRowsを新側、削除なら旧側で呼ぶ。両側があれば子の項を順にRC_UnitRowsへ渡す。条の新旧headingは最初に生成する項行へだけ付け、以後は空にする。
RC_UnitRowsで片側のない単位はRC_WholeRowsでその側とすべての子を全文保全する。
両側がある単位では、本文が原文比較で異なる、kindがhistoryまたはunknown、子に変更がある、のいずれかならfullBody=True。それ以外はFalse。
RC_RenderSideはfullBody=Trueならrawをそのまま出す。Falseなら元prefix・タブ・注記を連結する。新はRC_UNCHANGED_NOTE、旧はRC_OMITTED_NOTE。Prefixに元prefixの文字数、Articleに元rawが条開始形式かを入れ、省略時だけNoteへ注記文字列を入れる。
番号prefix同士をRC_Diffし、その区間を各Spansへ位置補正なしで加える。fullBody=Trueの場合だけbody同士も比較し、prefixとgapの文字数だけ位置をずらして加える。本文差分のfallbackは内部診断へ残す。
親行の後に子ペアを再帰的に行へする。子に変更がある親の柱書は省略しない。号の開始位置は対応する新旧を同じ表の行へ置く。番号だけの変更は本文を省略し、変わった番号文字だけへ下線を付ける。
最後に、この呼出しが生成した最初の行へ新旧のheadingを付ける。heading同士の差分は見出し上の位置0補正でSpansへ追加する。whole引数は現在の呼出し契約として保持するが、行の意味を新たに変える条件にはしない。

【新設・削除の全文行】
RC_WholeRowsは存在する側のraw全文を1行へ入れ、その側の全文字へ下線を付ける。元prefix文字数と条形式の判定を記録する。
最初の行だけ反対側へ新設または削除の注記を1回入れ、反対側Noteへ同じ文字列を記録する。子の行を作るときはfirst=Falseにして注記を繰り返さない。
articleノードでは子の項を順に再帰処理する。最初の項で生成された先頭行を、その子の全子孫数RC_DescendantCountを用いて特定し、存在する側の条headingを付け、headingも全文下線とする。
その条に独立したheadingがある場合、反対側の先頭行へCR1つを追加して最初の段落を空にする。HeadingBlank=Trueにし、2段落目の注記を本文開始位置へそろえる。項・号単独の新設削除ではこの空見出しを作らない。
RC_DescendantCountは全子孫ノード数を再帰的に数え、現在ノード自身を含めない。
RC_PrependHeadingはheadingが空なら何もしない。非空ならheading・CR・既存Textへ組み替え、Heading文字数を設定し、既存の下線startをheading文字数+1だけずらす。underlined=Trueの場合だけheadingの全文区間も加える。

【条に属さない原文と履歴】
RC_ExtraRowsは新旧extrasを位置順に対にし、ない側は空にする。同じrawなら掲載せず、異なる場合はRC_TextRowで行を追加する。条として意味的に照合したことにはしない。
RC_HistoryRowsは原文完全一致の順序付きLCSで新旧履歴を比べ、旧の削除・変更に相当する段落を元ID・原文とともに内部診断へ残す。同じ履歴が複数ある場合も件数を保持する。
履歴LCSの同点は新側を先に進める方を採る。旧側を飛ばす場合と、新が終わって旧が残る場合に旧原文の診断を加える。新追加は次へ進む。
新historyが空なら表に履歴を足さない。新historyを順に調べ、RC_HISTORY_PATTERNへ最後に一致する位置を最新履歴開始とする。一致がなくてもhistoryが存在すれば先頭を開始位置にする。そこから末尾までのrawをCRで連結し、新欄だけへ下線なしの1行として追加する。過去履歴をすべて並べない。日付を計算・推測・補完しない。
履歴を加える前に行が存在する場合だけhistoryGap=True。本文変更がなくても最新履歴があればそれだけを掲載する。

【Word文書・表の作成】
非表示の新Documentを作る。Configの用紙、向き、四辺余白、フッター距離を設定する。mmはWordのMillimetersToPointsで変換する。先頭ページ別・偶数奇数別ヘッダーフッターは無効。
NormalスタイルにはConfigの本文字体、東アジア字体、サイズ、色、太さ、配置、段落前後余白を設定し、下線なしにする。通常の1行間隔を用い、行グリッド使用時はグリッドへ合わせる。本文のWidowControl、KeepWithNext、KeepTogetherはFalse。
第1段落はConfigの接頭・新文書名・接尾による表題。TitleスタイルへConfigの表題書式を適用し、前余白0、設定の後余白、次段落と一緒、段落罫線なし。第2段落は設定の凡例、書式、配置、次段落と一緒。
文書末尾へ掲載行数+1行、2列の表を作る。左が新、右が旧。AutoFitは使わず、列幅・合計幅・セル余白・表配置・ページまたぎはConfigから設定。行高は自動。全セルの縦位置は上揃え。基本の本文書式と段落余白を適用する。
表の全罫線を一旦解除し、RC_SetBorderで外枠4辺、中央縦線、本文横線を個別に設定する。列見出し行の直下も個別設定する。enabled=Falseなら罫線なし、TrueならConfigの線種・幅・色。
1行目に設定の新旧ラベルを入れ、列見出し字体・太さ・色・配置・塗りを適用する。見出しの反復はConfigによる。列見出し行はページ分割しない。
各掲載行の左右をRC_FillCellへ渡す。bodyRowsが0なら架空の変更条文を足さず、文書Commentsへ本文・見出し・番号の変更がなく最新履歴のみ掲載する状態を記録する。
フッターは新規程名へConfigの前後文字を付ける。表示不要なら空にする。第1Sectionの通常・先頭・偶数の3種へ同じ内容と設定書式を入れ、非表示フォント・下線を解除する。ページ番号を新たに加えない。
Documentをアクティブにする。行グリッド使用時はページ設定ダイアログAPIをUpdateし、LinePitchへグリッドptの20倍をLongにしたtwipsを指定してExecuteする。ダイアログ画面を表示しない。LinesPageや固定段落行間で代用しない。
出力ウィンドウの表グリッド線、すべての編集記号、段落記号を非表示にし、Repaginateする。未保存のDocumentを返し、保存・閉じる責任はMainに委ねる。

【RC_FillCell：部分書式と位置】
セル終端記号を本文へ取り込まないRangeを使いTextを挿入し、挿入文字数だけのRangeへ本文書式と下線なしを設定する。
Heading文字数が正なら先頭の見出しへ設定字体・サイズ・太さ・色、見出し前余白、次段落と一緒を適用し、本文開始offsetをHeading文字数+CR1文字にする。HeadingBlank=Trueなら最初の空段落へ同じ見出し字体・サイズ・前余白・次段落と一緒を設定し、offsetはCR1文字。
Article=TrueかつPrefixが正なら、offsetからPrefix文字分へ条番号の見出し書式を適用する。通常の項や号番号へ条番号の書式を一律適用しない。
Noteがある場合は本文開始以後のインデントを0にしてタブ設定を消す。番号がある場合は段落を左揃えにし、セル幅から左右セル余白を引いた内幅にRC_NOTE_TAB_POSITIONを掛けた位置へ、設定のタブ揃え・空白リーダーを作る。番号は左に残す。番号のない注記はRC_NOTE_ALIGNで横位置を指定し、縦は上揃えを維持する。
historyGap=Trueならセル最初の段落前余白にRC_HISTORY_GAP_PTを入れる。
Spansを順に適用する。startが正で、区間末尾がText文字数以下のものだけを、セル開始+start-1からlength文字分のRangeへ変換し、RC_DIFF_UNDERLINEを付ける。注記には区間を作らない。

【確認例】
変更なし条は非掲載。番号変更のみは本文省略と番号差分の下線。下位変更なら柱書全文。新設削除は全文下線で反対欄注記1回、見出し付きなら反対欄の1行目空白。すべての本文セルは縦上揃え。最新履歴は新欄末尾へ原文コピーし、前に設定の1行分を空ける。会社書式はConfigだけで指定し、会社値をOutputへ直接埋め込まない。
