• R/O
  • HTTP
  • SSH
  • HTTPS

gikonavi: Commit


Commit MetaInfo

Revision7edfa0f09b28c926f42ddf9402cddcf6a8f2501d (tree)
Time2015-01-07 20:52:19
Authorzako <zako@user...>
Commiterzako

Log Message

NGワードで特定の正規表現を使用するとフリーズする不具合を修正

Change Summary

Incremental Difference

--- a/bmRegExp/Readme.txt
+++ b/bmRegExp/Readme.txt
@@ -1,11 +1,8 @@
11 正規表現文字列操作ライブラリについて
22
3-このフォルダのソースコードは添付していません。
4-以下のところからダウンロードする必要があります。
3+ギコナビでは下記ページで公開されている REXP017.LZH を修正して使用しています。
54
65 http://homepage1.nifty.com/bmonkey/delphi/delphi.html
76
87
9-ギコナビでは REXP017.LZH を使用しています。
10-
118 非常に便利なライブラリを公開していらっしゃる峯島雄治さんに感謝感激!
--- /dev/null
+++ b/bmRegExp/bmregexp.pas
@@ -0,0 +1,4142 @@
1+unit bmRegExp;
2+{******************************************************************************
3+タイトル :正規表現を使った文字列探索/操作コンポーネント集ver0.17
4+ユニット名 :bmRegExp.pas
5+バージョン :version 0.17
6+日付 :2001/09/15
7+動作確認環境 : Windows 98 + Borland Delphi6 Japanese Personal edition
8+作者 : 峯島 bmonkey 雄治 ggb01164@nifty.ne.jp
9+変更履歴 : 0.17 バグ修正 2001/09/15
10+ : ・MP3の魔術師さんに教えて頂いたメモリリークの修正を適用。
11+ : ・おかぽんさんに教えて頂いたメモリリークの修正を適用。
12+ : 詳細は同梱のchangelog.htmlを参照。
13+ : 0.16 第二次 一般公開 1998/03/07
14+ : version 0.15 -> version 0.16
15+ : ・TGrepで英大文字/小文字が正しく検索できなかったバグを修正。
16+ : ・漢字のキャラクタクラス指定([亜-熙]など)のバグを修正。
17+ : ・Delphi3, C++Builder1に対応
18+ : ・ユニットファイル名をRegExp.pasからbmRegExp.pasに変更
19+ : ・コンパイラの厳しくなった型チェックに対応
20+ : ・MBUtils.pasを使わないように変更。
21+ : 0.15B バグ修正、Delphi3、C++Builder1対応
22+ : 0.15 一般公開
23+主要クラス : TGrep, TAWKStr
24+継承関係 : TObject
25+
26+*******************************************************************************
27+使用方法 :ヘルプファイルとサンプルプロジェクトを参照のこと
28+補足説明 :
29+
30+定義型 :
31+
32+******************************************************************************}
33+
34+interface
35+
36+uses
37+ SysUtils, Classes, Windows, Forms
38+{$IFDEF DEBUG}
39+ ,OutLine
40+{$ENDIF}
41+ ;
42+
43+type
44+{ -========================== 例外クラス =====================================-}
45+{TREParser が出す例外。
46+ ErrorPos によって、正規表現文字列の何文字目で例外が発生したかを示す。}
47+ ERegExpParser = class(Exception)
48+ public
49+ ErrorPos: Integer;
50+ constructor Create(const Msg: string; ErrorPosition: Word);
51+ end;
52+{-============================= ====================-}
53+{ 2バイト文字型}
54+ WChar_t = Word;
55+
56+{ 2バイト文字型へのポインタ型}
57+ PWChar_t = ^WChar_t;
58+
59+{ WChar_t型 2つぶんの型}
60+ DoubleWChar_t = Integer;
61+
62+{ -====================== 文字列操作関数 =====================================-}
63+ {説明 : 16進数を表す文字を受け取り、整数にして返す。
64+ 引数 : WCh: WChar_t; 16進数を表す1バイト文字 [0-9a-fA-F]
65+
66+ 返り値: 正常時: 0 <= result <= 15
67+ 異常時: -1}
68+ function HexWCharToInt(WCh: WChar_t): Integer;
69+
70+ {説明 : 8進数を表す文字を受け取り、整数にして返す。
71+ 引数 : WCh: WChar_t; 8進数を表す1バイト文字 [0-7]
72+
73+ 返り値: 正常時: 0 <= result <= 7
74+ 異常時: -1}
75+ function OctWCharToInt(WCh: WChar_t): Integer;
76+
77+ {説明 : 16進数表記の文字列をWord型の数値に変換する。
78+ 引数 : Str: String 変換元の文字列
79+ Index: Integer 引数StrのIndex番目のバイト位置から変換を始める。
80+ 副作用: 処理したバイト数だけIndexがインクリメントされる。
81+ 返り値: 文字列が表すWord型の値}
82+ function HexStrToInt(const Str: String; var Index: Integer): Word;
83+
84+ {説明 : 8進数表記の文字列をWord型の数値に変換する。
85+ 引数 : Str: String 変換元の文字列
86+ Index: Integer 引数StrのIndex番目のバイト位置から変換を始める。
87+ 副作用: 処理したバイト数だけIndexがインクリメントされる。
88+ 返り値: 文字列が表すWord型の値}
89+ function OctStrToInt(const Str: String; var Index: Integer): Word;
90+
91+ {説明 : 引数Strから1文字読み出す。
92+ 動作 : 引数Str の中の引数Index番目のバイト位置から1文字得て、Indexを増やす。
93+ 引数 : Str: String; 2バイト文字コードを含んだString
94+ Index: Integer; 文字を読み出す位置の先頭からのバイト数
95+ 返り値: 読み出した WChar_t型の値
96+ 副作用:
97+ 注意 : Indexが文字列の長さよりすでに長い場合は常に WChType.Nullを返し、Indexを増やさない。
98+ つまり、Indexは最大でも Length(Str)+1 である。}
99+ function GetWChar(const Str: String;var Index: Integer): WChar_t;
100+
101+ {機能: GetWCharメソッドによって進んだ Indexを1文字分戻す(1〜2バイト)
102+ 注意: ヌル・キャラクタ(GetWCharの返り値 WChType.Null)を戻すことはできない。}
103+ procedure UnGetWChar(const Str: String; var Index: Integer);
104+
105+
106+ {機能: GetWCharのPChar型バージョン}
107+ function PCharGetWChar(var pText: PChar): WChar_t;
108+
109+ {機能: WChar_t型の値をString型へ変換する。}
110+ function WCharToStr(WCh: WChar_t): String;
111+
112+ {機能: '\' で 引用されたキャラクタを得る。 \n, \t \\ ...
113+ 注意: Indexは'\'の次の文字を指しているとする。}
114+ function GetQuotedWChar(const Str: String; var Index: Integer): WChar_t;
115+
116+
117+ {説明 : FS:WChar_tを区切り文字として、バイト位置Indexから始まるトークンを1つ返す。
118+ 引数 : Str: String
119+ Index: Integer 引数StrのIndex番目のバイト位置から変換を始める。
120+ 返り値: FSで区切られた、バイト位置Indexから始まるトークン}
121+ function WCharGetToken(const Str: String; var Index: Integer; FS: WChar_t): String;
122+
123+
124+ {説明 : 引数Str中のメタキャラクタに'\'をつける。
125+ 引数 : Str: String
126+ 返り値: メタキャラクタの前に'\'がついたStr}
127+ function QuoteMetaWChar(Str: String): String;
128+
129+const
130+ CONST_DOLLAR = $24; // '$'
131+ CONST_LPAR = $28; // '('
132+ CONST_RPAR = $29; // ')'
133+ CONST_STAR = $2A; // '*'
134+ CONST_PLUS = $2B; // '+'
135+ CONST_DOT = $2E; // '.'
136+ CONST_QMARK = $3F; // '?'
137+ CONST_VL = $7C; // '|'
138+
139+ CONST_LBRA = $5B; // '['
140+ CONST_RBRA = $5D; // ']'
141+ CONST_CARET = $5E; // '^'
142+ CONST_YEN = $5C; // '\'
143+ CONST_MINUS = $2D; // '-'
144+
145+ CONST_b = $62; // 'b'
146+ CONST_r = $72; // 'r'
147+ CONST_n = $6E; // 'n'
148+ CONST_t = $74; // 't'
149+ CONST_x = $78; // 'x'
150+
151+ CONST_BS = $08; // BackSpace
152+ CONST_CR = $0D; // Carriage Return
153+ CONST_LF = $0A; // Line Feed
154+ CONST_TAB = $09; // TAB
155+
156+ CONST_ANP = $26; // '&'
157+
158+ CONST_NULL = $0000;
159+
160+ METACHARS: Array[0..11] of WChar_t = (CONST_CARET,
161+ CONST_LPAR,
162+ CONST_VL,
163+ CONST_RPAR,
164+ CONST_PLUS,
165+ CONST_STAR,
166+ CONST_QMARK,
167+ CONST_DOT,
168+ CONST_LBRA,
169+ CONST_RBRA,
170+ CONST_DOLLAR,
171+ CONST_YEN);
172+
173+ CONST_EMPTY = $FFFF; {TNFA, TDFA状態表で「文字がない」ことを表すコードとして使う}
174+ CONST_LINEHEAD = $FFFD; {文頭メタキャラクタ'^'を表す文字コードとして使う。}
175+ CONST_LINETAIL = $FFFE; {文尾メタキャラクタ'$'を表す文字コードとして使う。}
176+
177+ REFuzzyWChars: array [0..144] of String =
178+ ('A,a,A,a',
179+ 'B,b,B,b',
180+ 'C,c,C,c',
181+ 'D,d,D,d',
182+ 'E,e,E,e',
183+ 'F,f,F,f',
184+ 'G,g,G,g',
185+ 'H,h,H,h',
186+ 'I,i,I,i',
187+ 'J,j,J,j',
188+ 'K,k,K,k',
189+ 'L,l,L,l',
190+ 'M,m,M,m',
191+ 'N,n,N,n',
192+ 'O,o,O,o',
193+ 'P,p,P,p',
194+ 'Q,q,Q,q',
195+ 'R,r,R,r',
196+ 'S,s,S,s',
197+ 'T,t,T,t',
198+ 'U,u,U,u',
199+ 'V,v,V,v',
200+ 'W,w,W,w',
201+ 'X,x,X,x',
202+ 'Y,y,Y,y',
203+ 'Z,z,Z,z',
204+ '0,0,零',
205+ '1,1,一,?,?,壱',
206+ '2,2,二,?,?,弐',
207+ '3,3,三,?,?,参',
208+ '4,4,四,?,?',
209+ '5,5,五,?,?,伍',
210+ '6,6,六,?,?',
211+ '7,7,七,?,?',
212+ '8,8,八,?,?',
213+ '9,9,九,?,?',
214+ '" "," "',
215+ '!,!',
216+ '"""",”',
217+ '#,#',
218+ '$,$',
219+ '%,%',
220+ '&,&',
221+ ''',’',
222+ '(,(',
223+ '),)',
224+ '*,*',
225+ '+,+',
226+ 'ー,〜,ー,', { 長音記号は、''ヌルとも一致させる}
227+ '-,ー,−,〜,ー',
228+ '・,・',
229+ '/,/',
230+ ':,:',
231+ ';,;',
232+ '<,<',
233+ '=,=',
234+ '>,>',
235+ '?,?',
236+ '@,@',
237+ '[,[,〔',
238+ '\,¥',
239+ '],],〕',
240+ '^,^',
241+ '_,_',
242+ '{,{',
243+ '|,|',
244+ '},}',
245+ '~, ̄',
246+ '",",、,、,,',
247+ '。,.,。,.',
248+ '「,『,「',
249+ '」,』,」',
250+ 'ん,ン,ン',
251+ 'が,ガ,ガ,か゛,カ゛',
252+ 'ぎ,ギ,ギ,き゛,キ゛',
253+ 'ぐ,グ,グ,く゛,ク゛',
254+ 'げ,ゲ,ゲ,け゛,ケ゛',
255+ 'ご,ゴ,ゴ,こ゛,コ゛',
256+ 'ざ,ザ,ザ,さ゛,サ゛',
257+ 'じ,ジ,ジ,し゛,シ゛,ぢ,ヂ,ヂ,ち゛,チ゛',
258+ 'ず,ズ,ズ,ス゛,ス゛,づ,ヅ,ヅ,つ゛,ツ゛',
259+ 'ぜ,ゼ,ゼ,せ゛,セ゛',
260+ 'ぞ,ゾ,ゾ,そ゛,ソ゛',
261+ 'だ,ダ,ダ,た゛,タ゛',
262+ 'で,デ,デ,て゛,テ゛',
263+ 'ど,ド,ド,と゛,ト゛',
264+ 'ば,バ,バ,は゛,ハ゛,ヴァ,う゛ぁ,ウ゛ァ,ヴァ',
265+ 'び,ビ,ビ,ひ゛,ヒ゛,ヴィ,う゛ぃ,ウ゛ィ,ヴィ',
266+ 'ぶ,ブ,ブ,ふ゛,フ゛,ヴ,ウ゛,う゛,ヴ',
267+ 'べ,ベ,ベ,へ゛,ヘ゛,ヴェ,う゛ぇ,ウ゛ェ,ヴェ',
268+ 'ぼ,ボ,ボ,ほ゛,ホ゛,ヴォ,う゛ぉ,ウ゛ォ,ヴォ',
269+ 'ぱ,パ,パ,は゜,ハ゜',
270+ 'ぴ,ピ,ピ,ひ゜,ヒ゜',
271+ 'ぷ,プ,プ,ふ゜,フ゜',
272+ 'ぺ,ペ,ペ,へ゜,ヘ゜',
273+ 'ぽ,ポ,ポ,ほ゜,ホ゜',
274+ 'あ,ア,ア,ぁ,ァ,ァ',
275+ 'い,イ,イ,ぃ,ィ,ィ',
276+ 'う,ウ,ウ,ぅ,ゥ,ゥ',
277+ 'え,エ,エ,ぇ,ェ,ェ',
278+ 'お,オ,オ,ぉ,ォ,ォ',
279+ 'か,カ,カ',
280+ 'き,キ,キ',
281+ 'く,ク,ク',
282+ 'け,ケ,ケ',
283+ 'こ,コ,コ',
284+ 'さ,サ,サ',
285+ 'し,シ,シ',
286+ 'す,ス,ス',
287+ 'せ,セ,セ',
288+ 'そ,ソ,ソ',
289+ 'た,タ,タ',
290+ 'ち,チ,チ',
291+ 'つ,ツ,ツ,っ,ッ,ッ',
292+ 'て,テ,テ',
293+ 'と,ト,ト',
294+ 'な,ナ,ナ',
295+ 'に,ニ,ニ',
296+ 'ぬ,ヌ,ヌ',
297+ 'ね,ネ,ネ',
298+ 'の,ノ,ノ',
299+ 'は,ハ,ハ',
300+ 'ひ,ヒ,ヒ',
301+ 'ふ,フ,フ',
302+ 'へ,ヘ,ヘ',
303+ 'ほ,ホ,ホ',
304+ 'ま,マ,マ',
305+ 'み,ミ,ミ',
306+ 'む,ム,ム',
307+ 'め,メ,メ',
308+ 'も,モ,モ',
309+ 'や,ヤ,ヤ,ゃ,ャ,ャ',
310+ 'ゆ,ユ,ユ,ゅ,ュ,ュ',
311+ 'よ,ヨ,ヨ,ょ,ョ,ョ',
312+ 'ら,ラ,ラ',
313+ 'り,リ,リ',
314+ 'る,ル,ル',
315+ 'れ,レ,レ',
316+ 'ろ,ロ,ロ',
317+ 'わ,ワ,ワ,うぁ,ウァ,ウァ',
318+ 'ヰ,ゐ,うぃ,ウィ,ウィ',
319+ 'ヱ,ゑ,うぇ,ウェ,ウェ',
320+ 'を,ヲ,ヲ,うぉ,ウォ,ウォ',
321+ '゙,゛',
322+ '゚,゜'); {濁点、半濁点はこの位置にないと ”が”→”ガ”に変換されない。}
323+
324+type
325+{ -============================= TREScanner Class ==================================-}
326+ { 文字の範囲を表す型。}
327+ RECharClass_t = record
328+ case Char of
329+ #0: (StartChar: WChar_t; EndChar: WChar_t);
330+ #1: (Chars: DoubleWChar_t);
331+ end;
332+
333+const
334+ CONST_EMPTYCharClass: RECharClass_t = ( StartChar: CONST_EMPTY;
335+ EndChar: CONST_EMPTY);
336+
337+type
338+
339+ { RECharClass_tへのポインタ型}
340+ REpCharClass_t = ^RECharClass_t;
341+
342+ {トークンの種類を表す型 }
343+ REToken_t = ( retk_Char, {通常の文字 }
344+ retk_CharClass, {'[]'で囲まれたキャラクタクラス正規表現の中で
345+ '-'を使って範囲指定された物 }
346+ retk_Union, { '|'}
347+ retk_LPar, { '('}
348+ retk_RPar, { ')'}
349+ retk_Star, { '*'}
350+ retk_Plus, { '+'}
351+ retk_QMark, { '?'}
352+ retk_LBra, { '['}
353+ retk_LBraNeg, { '[^'}
354+ retk_RBra, { ']'}
355+ retk_Dot, { '.'}
356+ retk_LHead, { '^'}
357+ retk_LTail, { '$'}
358+ retk_End); { 文字列の終わり }
359+
360+ { REToken_tの集合集合型}
361+ RETokenSet_t = set of REToken_t;
362+
363+ RESymbol_t = record
364+ case REToken_t of
365+ retk_CharClass: (CharClass: RECharClass_t);
366+ retk_Char: (WChar: WChar_t);
367+ end;
368+
369+{● 文字列からトークンを切り出すクラス}
370+ TREScanner = class
371+ private
372+ FRegExpStr: String;
373+ FIndex: Integer;
374+ FToken: REToken_t;
375+ FSymbol: RESymbol_t;
376+ FInCharClass: Boolean;
377+ protected
378+ procedure SetRegExpStr(RegExpStr: String);
379+
380+ {次のトークンを得る。}
381+ function GetTokenStd: REToken_t; virtual;
382+ {キャラクタクラス正規表現 "[ ]" の中のトークンを得る。}
383+ function GetTokenCC: REToken_t; virtual;
384+ public
385+ constructor Create(Str: String);
386+
387+ function GetToken: REToken_t;
388+
389+ {現在のトークン}
390+ property Token: REToken_t read FToken;
391+
392+ { Tokenに対応する文字[列](Lexeme)
393+ Token <> retk_CharClass のとき 現在のトークンの文字値 WChar_t型
394+ Token = retk_CharClass のときはRECharClass_tレコード型
395+ ※FToken = retk_LBraNegの時はブラケット'['1文字分しかない。}
396+ property Symbol: RESymbol_t read FSymbol;
397+
398+ {処理対象の文字列}
399+ property RegExpStr: String read FRegExpStr write SetRegExpStr;
400+
401+ {インデックス
402+ InputStr文字列中で次のGetWCharメソッドで処理する文字のインデックス
403+ ※ Symbolの次の文字を指していることに注意}
404+ property Index: Integer read FIndex;
405+ end;
406+
407+{-============================= ====================-}
408+ {トークンの情報をひとまとめにしたもの}
409+ RETokenInfo_t = record
410+ Token: REToken_t;
411+ Symbol: RESymbol_t;
412+ FromIndex: Integer;
413+ ToIndex: Integer;
414+ end;
415+
416+ REpTokenInfo_t = ^RETokenInfo_t;
417+
418+ {TREPreProcessorクラス内部で使用}
419+ TREPreProcessorFindFunc = function(FromTokenIndex, ToTokenIndex: Integer): Integer of object;
420+
421+ TREPreProcessor = class
422+ private
423+ FScanner: TREScanner;
424+ FProcessedRegExpStr: String;
425+ FListOfSynonymDic: TList;
426+ FListOfFuzzyCharDic: TList;
427+ FTokenList: TList;
428+ FSynonymStr: String;
429+
430+ FUseFuzzyCharDic: Boolean;
431+ FUseSynonymDic: Boolean;
432+ protected
433+ procedure MakeTokenList;
434+ procedure DestroyTokenListItems;
435+
436+ function ReferToOneList(FromTokenIndex, ToTokenIndex: Integer; SynonymDic: TList): Integer;
437+ function FindSynonym(FromTokenIndex, ToTokenIndex: Integer): Integer;
438+ function FindFuzzyWChar(FromTokenIndex, ToTokenIndex: Integer): Integer;
439+
440+ procedure Process(FindFunc: TREPreProcessorFindFunc);
441+
442+ function GetTargetRegExpStr: String;
443+ procedure SetTargetRegExpStr(Str: String);
444+ public
445+ constructor Create(Str: String);
446+ destructor Destroy; override;
447+ procedure Run;
448+
449+ property TargetRegExpStr: String read GetTargetRegExpStr write SetTargetRegExpStr;
450+ property ProcessedRegExpStr: String read FProcessedRegExpStr;
451+
452+ property UseSynonymDic: Boolean read FUseSynonymDic write FUseSynonymDic;
453+ property ListOfSynonymDic: TList read FListOfSynonymDic;
454+ property UseFuzzyCharDic: Boolean read FUseFuzzyCharDic write FUseFuzzyCharDic;
455+ property ListOfFuzzyCharDic: TList read FListOfFuzzyCharDic;
456+ end;
457+
458+{-=========================== TREParseTree Class ===============================-}
459+{**************************************************************************
460+● 構文木を管理するクラス TREParseTree
461+
462+特徴: 中間節(Internal node)と葉(Leaf)を作るときは、それぞれMakeInternalNode
463+ メソッドとMakeLeafメソッドを使う。
464+ また、構文木とは別に、FNodeListとFLeafListから中間節と葉へリンクして
465+ おくことにより、途中でエラーが発生しても必ずメモリを開放する。
466+**************************************************************************}
467+ { TREParseTreeの節の種類を表す型}
468+ REOperation_t = (reop_Char, { 文字そのもの }
469+ reop_LHead, { 文頭 }
470+ reop_LTail, { 文尾 }
471+ reop_Concat, { XY }
472+ reop_Union, { X|Y}
473+ reop_Closure, { X* }
474+ reop_Empty); { 空 }
475+
476+ { RENode_tへのポインタ型}
477+ REpNode_t = ^RENode_t;
478+
479+ { TREParseTreeの子節へのポインタ型}
480+ REChildren_t = record
481+ pLeft: REpNode_t;
482+ pRight: REpNode_t;
483+ end;
484+
485+ { TREParseTreeの節}
486+ RENode_t = record
487+ Op: REOperation_t;
488+ case Char of
489+ #0: (CharClass: RECharClass_t);
490+ #1: (Children: REChildren_t);
491+ end;
492+
493+{● 構文木を管理するクラス}
494+ TREParseTree = class
495+ private
496+ FpHeadNode: REpNode_t;{構文木の頂点にある節}
497+ FNodeList: TList; {中間節のリスト。}
498+ FLeafList: TList; {葉のリスト。}
499+ public
500+ constructor Create;
501+ destructor Destroy; override;
502+
503+ {構文木の内部節を作成。
504+ op はノードが表す演算、leftは左の子、rightは右の子 }
505+ function MakeInternalNode(TheOp: REOperation_t; pLeft, pRight: REpNode_t): REpNode_t;
506+
507+ {構文木の葉を作成。
508+ aStartChar, aEndChar でキャラクタクラスを表す}
509+ function MakeLeaf(aStartChar, aEndChar: WChar_t): REpNode_t;
510+
511+ {任意の一文字を表す'.'メタキャラクタに対応する部分木を作る。
512+ ※CR LFを除く全てのキャラクタを表す葉をreop_Union操作を表す中間節で結んだもの}
513+ function MakeAnyCharsNode: REpNode_t; virtual;
514+
515+ {文頭メタキャラクタを表す葉を作成
516+ ※ 葉を返すが、MakeInternalNodeを使う。}
517+ function MakeLHeadNode(WChar: WChar_t): REpNode_t;
518+
519+ {文尾メタキャラクタを表す葉を作成
520+ ※ 葉を返すが、MakeInternalNodeを使う。}
521+ function MakeLTailNode(WChar: WChar_t): REpNode_t;
522+
523+ {引数が aStartChar <= aEndChar の関係を満たしているときに、MakeLeafを呼ぶ
524+ それ以外は、nil を返す。}
525+ function Check_and_MakeLeaf(aStartChar, aEndChar: WChar_t):REpNode_t;
526+
527+ {葉を内部節に変える。}
528+ procedure ChangeLeaftoNode(pLeaf, pLeft, pRight: REpNode_t);
529+
530+ {全ての葉が持つキャラクタクラスの範囲がそれぞれ重複しないように分割する。}
531+ procedure ForceCharClassUnique;
532+
533+ {すべての節(内部節、葉)を削除。}
534+ procedure DisposeTree;
535+
536+ {構文木の頂点にある節}
537+ property pHeadNode: REpNode_t read FpHeadNode write FpHeadNode;
538+
539+ {内部節のリスト}
540+ property NodeList: TList read FNodeList;
541+ {葉のリスト}
542+ property LeafList: TList read FLeafList;
543+ end;
544+
545+{-=========================== TREParser Class ===============================-}
546+{● 正規表現文字列を解析して構文木にするパーサー }
547+ TREParser = class
548+ private
549+ FParseTree: TREParseTree; {ユニットParseTre.pas で定義されている構文木クラス}
550+ FScanner: TREScanner; {トークン管理クラス}
551+
552+ protected
553+ { <regexp>をパースして、得られた構文木を返す。
554+ 選択 X|Y を解析する}
555+ function Regexp: REpNode_t;
556+
557+ { <term>をパースして、得られた構文木を返す。
558+ 連結XYを解析する}
559+ function term: REpNode_t;
560+
561+ { <factor>をパースして、得られた構文木を返す。
562+ 繰り返しX*, X+を解析する}
563+ function factor: REpNode_t;
564+
565+ { <primary>をパースして、得られた構文木を返す。
566+ 文字そのものと、括弧で括られた正規表現 (X) を解析する}
567+ function primary: REpNode_t;
568+
569+ { <charclass> をパースして、得られた構文木を返す。
570+ [ abcd] で括られた正規表現を解析する}
571+ function CharacterClass(aParseTree: TREParseTree): REpNode_t;
572+
573+ { <negative charclass>をパースして、得られた構文木を返す。
574+ [^abcd] で括られた正規表現を解析する}
575+ function NegativeCharacterClass: REpNode_t;
576+
577+ public
578+ constructor Create(RegExpStr: String);
579+ destructor Destroy; override;
580+
581+ {正規表現をパースする。
582+ regexp, term, factor, primary, charclass の各メソッドを使い再帰下降法
583+ によって解析する。}
584+ procedure Run;
585+
586+ {構文木を管理するオブジェクト}
587+ property ParseTree: TREParseTree read FParseTree;
588+
589+ {入力文字列からトークンを切り出すオブジェクト}
590+ property Scanner: TREScanner read FScanner;
591+
592+{$IFDEF DEBUG}
593+ {アウトライン・コントロールに構文木の図を書き出すメソッド}
594+ procedure WriteParseTreeToOutLine(anOutLine: TOutLine);
595+{$ENDIF}
596+ end;
597+
598+{$IFDEF DEBUG}
599+ function DebugWCharToStr(WChar: WChar_t): String;
600+{$ENDIF}
601+
602+{ -============================== TRE_NFA Class ==================================-}
603+type
604+ RE_pNFANode_t = ^RE_NFANode_t;
605+
606+ { NFA状態表の節
607+ RE_NFANode_t は 1つのNFA状態が、キャラクタクラス(CharClass)内の文字によっ
608+ て遷移するNFA状態の状態番号(TransitTo)を格納する。
609+ 1つのNFA状態へ入力されるキャラクタクラス毎にリンク・リストを形成する}
610+ RE_NFANode_t = record
611+ CharClass: RECharClass_t;{ 入力 : CharClass.StartChar 〜 CharClass.EndChar}
612+ TransitTo: integer; { 遷移先: FStateListのインデックス}
613+
614+ Next: RE_pNFANode_t; { リンクリストの次節}
615+ end;
616+
617+{● 構文木を解析してNFA状態表を作るクラス}
618+ TRE_NFA = class
619+ private
620+ FStateList: TList;
621+ FEntryState: Integer;
622+ FExitState: Integer;
623+ FParser: TREParser;
624+ FRegExpHasLHead: Boolean;
625+ FRegExpHasLTail: Boolean;
626+ FLHeadWChar: WChar_t;
627+ FLTailWChar: WChar_t;
628+ protected
629+ { ノードに番号を割り当てる}
630+ function NumberNode: Integer;
631+
632+ { NFA状態節 を1つ作成}
633+ function MakeNFANode: RE_pNFANode_t;
634+
635+ { FStateListに状態遷移を追加する。
636+ 状態 TransFrom に対して、ChrClassのときに状態 TransTo への遷移を追加する。}
637+ procedure AddTransition(TransFrom, TransTo: Integer; aCharClass: RECharClass_t);
638+
639+ { 構文木 pTree に対する StateListを生成する
640+ NFAの入り口をentry, 出口をway_outとする }
641+ procedure GenerateStateList(pTree: REpNode_t; entry, way_out: Integer);
642+
643+ { NFA状態表を破棄する}
644+ procedure DisposeStateList;
645+
646+ public
647+ constructor Create(Parser: TREParser; LHeadWChar, LTailWChar: WChar_t);
648+ destructor Destroy;override;
649+
650+ { 構文木 Treeに対応するNFAを生成する}
651+ procedure Run;
652+
653+ {NFA 状態のリスト}
654+ property StateList: TList read FStateList;
655+
656+ {NFAの初期状態のFStateListのインデックス}
657+ property EntryState: Integer read FEntryState;
658+ {NFAの終了状態のFStateListのインデックス}
659+ property ExitState: Integer read FExitState;
660+
661+ {正規表現が、文頭メタキャラクタを含むか}
662+ property RegExpHasLHead: Boolean read FRegExpHasLHead;
663+ {正規表現が、文尾メタキャラクタを含むか}
664+ property RegExpHasLTail: Boolean read FRegExpHasLTail;
665+
666+ {文頭を表すメタキャラクタ '^'に与えるユニークなキャラクタコード}
667+ property LHeadWChar: WChar_t read FLHeadWChar write FLHeadWChar;
668+ {文尾を表すメタキャラクタ '$'に与えるユニークなキャラクタコード}
669+ property LTailWChar: WChar_t read FLTailWChar write FLTailWChar;
670+
671+{$IFDEF DEBUG}
672+ {TStringsオブジェクトに、NFA の内容を書き込む}
673+ procedure WriteNFAtoStrings(Strings: TStrings);
674+{$ENDIF}
675+ end;
676+
677+{ -========================== TRE_NFAStateSet Class =============================-}
678+{● NFAの状態集合を表すオブジェクト
679+ 内部ではビットベクタで状態集合を実現している。}
680+ TRE_NFAStateSet = class
681+ private
682+ FpArray: PByteArray;
683+ FCapacity: Integer;
684+ public
685+ {コンストラクタには、最大状態数を指定する。}
686+ constructor Create(StateMax: Integer);
687+ destructor Destroy; override;
688+
689+ {オブジェクトの集合が、StateIndexを含むか?}
690+ function Has(StateIndex: Integer): Boolean;
691+ {オブジェクトの集合が、AStateSetと同じ集合状態か?}
692+ function Equals(AStateSet: TRE_NFAStateSet): Boolean;
693+ {オブジェクトの集合にStateIndexを含める。}
694+ procedure Include(StateIndex: Integer);
695+ {オブジェクトが持つバイト配列へのポインタ}
696+ property pArray: PByteArray read FpArray;
697+ {オブジェクトが持つバイト配列の要素数}
698+ property Capacity: Integer read FCapacity;
699+ end;
700+
701+{ -============================= TRE_DFA Class ==================================-}
702+{● TRE_DFA NFA状態表からDFA状態表を作るクラス
703+ コンストラクタ Create に、正規表現を表すNFA(非決定性有限オートマトン
704+ Non-deterministic Finite Automaton)の状態表を持つTRE_NFAを受け取り、
705+ 対応するDFA(決定性有限オートマトンDeterministic Finite Automaton)
706+ の状態リストオブジェクトを構築するTRE_DFAクラス。}
707+
708+ RE_pDFATransNode_t = ^RE_DFATransNode_t;
709+
710+ {TRE_DFAのメソッドCompute_Reachable_N_state(DState: PD_state_t): RE_pDFATransNode_t;
711+ がこの型の値を返す。
712+ キャラクタクラス(CharClass)で遷移可能なNFA状態集合(ToNFAStateSet)}
713+ RE_DFATransNode_t = record
714+ CharClass: RECharClass_t;{Char;}
715+ ToNFAStateSet: TRE_NFAStateSet;
716+
717+ next: RE_pDFATransNode_t;{リンクリストを形成}
718+ end;
719+
720+ RE_pDFAStateSub_t = ^RE_DFAStateSub_t;
721+ RE_pDFAState_t = ^RE_DFAState_t;
722+
723+ { RE_DFAState_tによって使用される
724+ キャラクタクラス(CharClass)によってDFA状態(TransitTo) へ遷移する。}
725+ RE_DFAStateSub_t = record
726+ CharClass: RECharClass_t;
727+ TransitTo: RE_pDFAState_t; {CharClass範囲内の文字で DFA 状態 TransitToへ}
728+
729+ next: RE_pDFAStateSub_t; {リンクリストの次のデータ}
730+ end;
731+
732+ { RE_DFAState_tはDFA状態を表す型}
733+ RE_DFAState_t = record
734+ StateSet: TRE_NFAStateSet; {このDFA状態を表すNFA状態集合}
735+ Visited: wordbool; { 処理済みなら1}
736+ Accepted: wordbool;{ StateSetフィールドがNFAの終了状態を含むなら1}
737+ Next: RE_pDFAStateSub_t; { キャラクタクラス毎の遷移先のリンクリスト}
738+ end;
739+
740+{ ● NFA状態表からDFA状態表を作るクラス}
741+ TRE_DFA = class
742+ private
743+ FStateList: TList;
744+ FpInitialState: RE_pDFAState_t;
745+ FNFA: TRE_NFA;
746+
747+ FRegExpIsSimple: Boolean;
748+ FSimpleRegExpStr: String;
749+ FRegExpHasLHead: Boolean;
750+ FRegExpHasLTail: Boolean;
751+ protected
752+ { NFA状態集合 StateSet に対して ε-closure操作を実行する。
753+ ε遷移で遷移可能な全てのNFA状態を追加する}
754+ procedure Collect_Empty_Transition(StateSet: TRE_NFAStateSet);
755+
756+ { NFA状態集合 aStateSet をDFAに登録して、DFA状態へのポインタを返す。
757+ aStateSetが終了状態を含んでいれば、acceptedフラグをセットする。
758+ すでにaStateSetがDFAに登録されていたら何もしない}
759+ function Register_DFA_State(var aStateSet: TRE_NFAStateSet): RE_pDFAState_t;
760+
761+ { 処理済みの印がついていないDFA状態を探す。
762+ 見つからなければnilを返す。}
763+ function Fetch_Unvisited_D_state: RE_pDFAState_t;
764+
765+ { DFA状態pDFAStateから遷移可能なNFA状態を探して、リストにして返す}
766+ function Compute_Reachable_N_state(pDFAState: RE_pDFAState_t): RE_pDFATransNode_t;
767+
768+ { Compute_Reachable_N_stateメソッドか作る RE_DFATransNode_t型のリンクリストを
769+ 廃棄する}
770+ procedure Destroy_DFA_TransList(pDFA_TransNode: RE_pDFATransNode_t);
771+
772+ { NFAを等価なDFAへと変換する}
773+ procedure Convert_NFA_to_DFA;
774+
775+ { StateListの各リンクリストをソートする}
776+ procedure StateListSort;
777+
778+ procedure CheckIfRegExpIsSimple;
779+ procedure DestroyStateList;
780+ public
781+ constructor Create(NFA: TRE_NFA);
782+ destructor Destroy; override;
783+
784+ procedure Run;
785+
786+ property StateList: TList read FStateList;
787+
788+ property pInitialState: RE_pDFAState_t read FpInitialState;
789+
790+ {正規表現が単純な文字列か?}
791+ property RegExpIsSimple: Boolean read FRegExpIsSimple;
792+ {正規表現と等価な単純な文字列}
793+ property SimpleRegExpStr: String read FSimpleRegExpStr;
794+
795+ {正規表現が、文頭メタキャラクタを含むか}
796+ property RegExpHasLHead: Boolean read FRegExpHasLHead;
797+ {正規表現が、文尾メタキャラクタを含むか}
798+ property RegExpHasLTail: Boolean read FRegExpHasLTail;
799+ {$IFDEF DEBUG}
800+ {TStringsオブジェクトに、DFA の内容を書き込む}
801+ procedure WriteDFAtoStrings(Strings: TStrings);
802+{$ENDIF}
803+ end;
804+
805+{ -=================== TRegularExpression Class ==============================-}
806+ {TStringList に格納できる項目数の範囲型}
807+ RE_IndexRange_t = 1..Classes.MaxListSize;
808+
809+{● 正規表現文字列からDFA状態表を作るクラス}
810+ TRegularExpression = class(TComponent)
811+ private
812+ protected
813+ FLineHeadWChar: WChar_t;
814+ FLineTailWChar: WChar_t;
815+ {プリプロセッサを通る前の正規表現}
816+ FRegExp: String;
817+ {正規表現の文字列リスト。ObjectsプロパティにTDFAオブジェクトを持つ}
818+ FRegExpList: TStringList;
819+ {FRegExpListに格納する項目数の最大値。 デフォルト 30}
820+ FRegExpListMax: RE_IndexRange_t;
821+ {現在指定されている正規表現 RegExpの正規表現文字列リストRegExpList中での
822+ インデックス
823+ ※ FRegExpList[FCurrentIndex] = RegExp}
824+ FCurrentIndex: Integer;
825+ {同意語処理プリプロセッサ}
826+ FPreProcessor: TREPreProcessor;
827+
828+ { 内部使用のための手続き・関数}
829+ {***** 正規表現文字列→構文木構造→NFA→DFA の変換を行う *****}
830+ procedure Translate(RegExpStr: String); virtual;
831+
832+ {正規表現リスト(RegExpList: TStringList)とObjectsプロパティに結び付けられた
833+ TRE_DFAオブジェクトを破棄}
834+ procedure DisposeRegExpList;
835+
836+ {プロパティ・アクセス・メソッド}
837+ procedure SetRegExp(Str: String); virtual;
838+ function GetProcessedRegExp: String;
839+ function GetListOfFuzzyCharDic: TList;
840+ function GetListOfSynonymDic: TList;
841+ function GetRegExpIsSimple: Boolean;
842+ function GetSimpleRegExp: String;
843+ function GetHasLHead: Boolean;
844+ function GetHasLTail: Boolean;
845+ function GetUseFuzzyCharDic: Boolean;
846+ procedure SetUseFuzzyCharDic(Val: Boolean);
847+ function GetUseSynonymDic: Boolean;
848+ procedure SetUseSynonymDic(Val: Boolean);
849+ function GetLineHeadWChar: WChar_t; virtual;
850+ function GetLineTailWChar: WChar_t; virtual;
851+ {DFAオブジェクト関連メソッド}
852+ {現在指定されている正規表現に対応するDFA状態表の初期状態へのポインタを返す}
853+ function GetpInitialDFAState: RE_pDFAState_t;
854+ {現在指定されている正規表現に対応するTRE_DFAオブジェクトを返す}
855+ function GetCurrentDFA: TRE_DFA;
856+ {状態 DFAstateから文字cによって遷移して、遷移後の状態を返す。
857+ 文字cによって遷移出来なければnilを返す}
858+ function NextDFAState(DFAState: RE_pDFAState_t; c: WChar_t): RE_pDFAState_t;
859+ {DFA状態表の中で文頭メタキャラクタを表すキャラクタコード}
860+ property LineHeadWChar: WChar_t read GetLineHeadWChar;
861+ {DFA状態表の中で文尾メタキャラクタを表すキャラクタコード}
862+ property LineTailWChar: WChar_t read GetLineTailWChar;
863+
864+ {正規表現関連プロパティ}
865+ {現在指定されている正規表現}
866+ property RegExp: String read FRegExp write SetRegExp;
867+
868+ {現在指定されている正規表現に同意語処理を施したもの}
869+ property ProcessedRegExp: String read GetProcessedRegExp;
870+
871+ {正規表現が単純な文字列か?}
872+ property RegExpIsSimple: Boolean read GetRegExpIsSimple;
873+ {正規表現と等価な単純な文字列(※RegExpIsSimple=Falseの時はヌル文字列)}
874+ property SimpleRegExp: String read GetSimpleRegExp;
875+
876+ {正規表現が、文頭メタキャラクタを含むか}
877+ property HasLHead: Boolean read GetHasLHead;
878+ {正規表現が、文尾メタキャラクタを含むか}
879+ property HasLTail: Boolean read GetHasLTail;
880+
881+ {辞書関連プロパティ}
882+ {文字同一視辞書を使う/使わない指定}
883+ property UseFuzzyCharDic: Boolean read GetUseFuzzyCharDic write SetUseFuzzyCharDic;
884+ {文字の同一視辞書のリスト}
885+ property ListOfFuzzyCharDic: TList read GetListOfFuzzyCharDic;
886+
887+ {同意語辞書を使う/使わない指定}
888+ property UseSynonymDic: Boolean read GetUseSynonymDic write SetUseSynonymDic;
889+ {同意語辞書のリスト}
890+ property ListOfSynonymDic: TList read GetListOfSynonymDic;
891+ public
892+ constructor Create(AOwner: TComponent); override;
893+ destructor Destroy; override;
894+ end;
895+
896+{ -========================== TAWKStr Class ==================================-}
897+ TMatchCORE_LineSeparator = (mcls_CRLF, mcls_LF);
898+
899+ TMatchCORE = class(TRegularExpression)
900+ private
901+ FLineSeparator: TMatchCORE_LineSeparator;
902+ protected
903+ function IsLineEnd(WChar: WChar_t): Boolean;
904+ property LineSeparator: TMatchCORE_LineSeparator
905+ read FLineSeparator write FLineSeparator;
906+ protected
907+
908+ {説明 : マッチ
909+ (正規表現が行頭/行末メタキャラクタを含まないとき用)
910+ 動作 :
911+ 引数 : pText: PChar ヌルキャラクタで終わる検索対象文字列へのポインタ
912+ 副作用: pStart:PChar マッチした部分の先頭文字へのポインタ
913+ pEnd :PChar マッチした部分の次の文字へのポインタ
914+ 注意 : マッチした部分のバイト数は、pEnd - pStartで得られる。}
915+ procedure MatchStd(pText: PChar; var pStart, pEnd: PChar);
916+
917+
918+ {説明 : マッチ(正規表現が行頭/行末メタキャラクタを含むとき用)
919+ 動作 :
920+ 引数 : pText: PChar ヌルキャラクタで終わる検索対象文字列へのポインタ
921+ 副作用: pStart:PChar マッチした部分の先頭文字へのポインタ
922+ pEnd :PChar マッチした部分の次の文字へのポインタ
923+ 注意 : マッチした部分のバイト数は、pEnd - pStartで得られる。}
924+ procedure MatchEX(pText: PChar; var pStart, pEnd: PChar);
925+
926+ {説明 : マッチ(内部処理用。正規表現が行頭/行末メタキャラクタを含むとき用)
927+ 動作 : MatchEx_Headメソッドとの違いは、引数pTextが行の途中をポイントして
928+ いるものとして、行頭メタキャラクタにマッチしないこと。
929+ 引数 : pText: PChar ヌルキャラクタで終わる検索対象文字列へのポインタ
930+ (行の中を指しているものとして扱う。)
931+ 副作用: pStart:PChar マッチした部分の先頭文字へのポインタ
932+ pEnd :PChar マッチした部分の次の文字へのポインタ
933+ 注意 : マッチした部分のバイト数は、pEnd - pStartで得られる。}
934+ procedure MatchEX_Inside(pText: PChar; var pStart, pEnd: PChar);
935+
936+{----------------マッチ 下請け -------------}
937+{MatchHead, MatchInsideは、引数 pTextが指す文字を先頭としてマッチするかを検査する}
938+
939+ {説明 : pTextは、ある文字列の行頭をポイントしているものと見なす。
940+ したがって、pTextが指す文字は行頭メタキャラクタにマッチする。
941+ 行末メタキャラクタを考慮する。
942+ 引数 : pText: PChar 検索対象文字列(行の最初の文字を指す)
943+ pDFAState 初期値として使うDFA状態表の1状態
944+ 返り値: マッチした部分文字列の次の文字。
945+ マッチした部分文字列のバイト長は、result - pText
946+ 注意 : }
947+ function MatchHead(pText: PChar; pDFAState: RE_pDFAState_t): PChar;
948+
949+ {説明 : pTextは、ある文字列の中(行頭ではない)をポイントしているものと見なす。
950+ したがって、pTextが指す文字は行頭メタキャラクタにマッチしない。
951+ 行末メタキャラクタを考慮する。
952+ 引数 : pText: PChar 検索対象文字列(行中の文字を指す)
953+ pDFAState 初期値として使うDFA状態表の1状態
954+ 返り値: マッチした部分文字列の次の文字。
955+ マッチした部分文字列のバイト長は、result - pText
956+ 注意 : }
957+ function MatchInside(pText: PChar; pDFAState: RE_pDFAState_t): PChar;
958+ public
959+ constructor Create(AOwner: TComponent); override;
960+ end;
961+
962+{ -========================== TAWKStr Class ==================================-}
963+ TAWKStrMatchProc = procedure(pText: PChar; var pStart, pEnd: PChar) of object;
964+
965+{● AWK言語の文字列操作関数群をDelphiで実現するクラス TAWKStr}
966+ TAWKStr = class(TMatchCORE)
967+ private
968+ FMatchProc: TAWKStrMatchProc;
969+ protected
970+ procedure SetRegExp(Str: String); override;
971+ {Sub, GSubメソッドで使用。 '&'をマッチした文字列に置換える}
972+ function Substitute_MatchStr_For_ANDChar(Text: String; MatchStr: String): String;
973+ public
974+ constructor Create(AOwner: TComponent); override;
975+ function ProcessEscSeq(Text: String): String;
976+
977+ {文字の同一視辞書のリスト}
978+ property ListOfFuzzyCharDic;
979+ {同意語辞書のリスト}
980+ property ListOfSynonymDic;
981+
982+ {正規表現が、文頭メタキャラクタを含むか}
983+ property HasLHead;
984+ {正規表現が、文尾メタキャラクタを含むか}
985+ property HasLTail;
986+
987+ property ProcessedRegExp;
988+
989+ {DFA状態表の中で文頭メタキャラクタを表すキャラクタコード}
990+ property LineHeadWChar;
991+ {DFA状態表の中で文尾メタキャラクタを表すキャラクタコード}
992+ property LineTailWChar;
993+
994+ function Match(Text: String; var RStart, RLength: Integer): Integer;
995+
996+ function Sub(SubText: String; var Text: String): Boolean;
997+
998+ function GSub(SubText: String; var Text: String): Integer;
999+
1000+ function Split(Text: String; StrList: TStrings): Integer;
1001+ published
1002+ property RegExp;
1003+ {行の区切り文字指定}
1004+ property LineSeparator;
1005+
1006+ {文字の同一視辞書を使うか}
1007+ property UseFuzzyCharDic;
1008+ {同意語辞書を使うか}
1009+ property UseSynonymDic;
1010+
1011+ end;
1012+
1013+{ -========================== 例外クラス =====================================-}
1014+ EEndOfFile = class(EInOutError);
1015+
1016+ EFileNotFound = class(EInOutError);
1017+
1018+ EGrepCancel = class(Exception);
1019+
1020+{ -=========================== TTxtFile Class ================================-}
1021+ {TTextFileクラスのGetThisLineが返すファイル中の1行の情報を表す型}
1022+ RE_LineInfo_t = record
1023+ Line: String;
1024+ LineNo: Integer; {行番号}
1025+ end;
1026+
1027+{● TTxtFile テキストファイル・アクセス・クラス}
1028+ TTxtFile = Class
1029+ private
1030+ protected
1031+ public
1032+ FBuffSize: Integer; {バッファのサイズ}
1033+ FTailMargin: Integer;
1034+ FpBuff: PChar; {読み込みバッファへのポインタ}
1035+
1036+ FFileName: String; {処理対象ファイル名 (フルパス表記)}
1037+ FF: File; {FFileName に関連付けられる型なしファイル変数}
1038+ FFileOpened: Boolean;
1039+
1040+ {バッファ中の文字位置を表す重要なポインタ3種類}
1041+ FpBase: PChar; {文中で検索対象となる部分文字列の先頭を指す}
1042+ FpLineBegin: PChar; {FpBaseが指す文の先頭文字へのポインタ}
1043+ FpForward: PChar; {検索中の文字へのポインタ}
1044+
1045+ FLineNo: Integer; {現在の行番号}
1046+ FReadCount: Integer;{BlockRead で何バイト読み込んだか。}
1047+ FBrokenLine: String;{バッファの境界で分断された文の前半部分}
1048+
1049+ FpCancelRequest: ^Boolean;
1050+ {IncPBaseメソッドでFpBaseがヌル・キャラクタを指したときの処理}
1051+ procedure IncPBaseNullChar(Ch: Char);
1052+ {GetCharメソッドでFpForwardがヌル・キャラクタを指したときの処理}
1053+ procedure GetCharNullChar(Ch: Char);
1054+
1055+ constructor Create(aFileName: String; var CancelRequest: Boolean);
1056+ destructor Destroy; override;
1057+ procedure BuffRead(pBuff: PChar);
1058+ function IncPBase: Char; {FpBaseが次のバイトを指すようにする}
1059+ function AdvanceBase: WChar_t;
1060+ function GetChar: Char;
1061+ function GetWChar: WChar_t;
1062+ function GetThisLine: RE_LineInfo_t;{FpBaseが指している文字を含む文を得る}
1063+ end;
1064+
1065+{ -=========================== TGrep Class ==================================-}
1066+
1067+ TGrepOnMatch = procedure (Sender: TObject; LineInfo: RE_LineInfo_t) of Object;
1068+
1069+ TGrepGrepProc = procedure (FileName: String) of Object;
1070+
1071+{● ファイル正規表現検索クラス TGrep }
1072+ TGrep = class(TRegularExpression)
1073+ private
1074+ FOnMatch: TGrepOnMatch;
1075+// FDummyIgnoreCase: Boolean;
1076+ FCancel: Boolean;
1077+ FGrepProc: TGrepGrepProc;
1078+ protected
1079+ procedure SetRegExp(Str: String); override;
1080+ function GetLineHeadWChar: WChar_t; override;
1081+ function GetLineTailWChar: WChar_t; override;
1082+ public
1083+ constructor Create(AOwner: TComponent); override;
1084+
1085+ procedure GrepByRegExp(FileName: String);
1086+ procedure GrepByStr(FileName: String);
1087+
1088+ {機能 指定されたテキスト・ファイル中で正規表現(RegExpプロパティ)にマッチ
1089+ する行を探し、見つけるたびにOnMatch イベントハンドラを呼び出します。
1090+
1091+ (RegExpプロパティに設定されている正規表現を検査して、普通の文字列ならば
1092+ GrepByStrメソッド、メタキャラクタを含むときはGrepByRegExpメソッドを
1093+ 呼び出します。)
1094+ ※ OnMatch イベントハンドラが指定されていないときは、何もしません。
1095+
1096+ 引数 FileNmae 検索対象のテキストファイル名(フルパス指定)
1097+ CancelRequest 検索を途中で止めたいときにTrueにする。
1098+ ※ Grepメソッドは内部で、Application.ProcessMessagesを呼び出す
1099+ ので、そのときに、CancelRequestをTrueに設定することができます。}
1100+
1101+ {正規表現が単純な文字列か?}
1102+ property RegExpIsSimple;
1103+ {正規表現と等価な単純な文字列(※RegExpIsSimple=Falseの時はヌル文字列)}
1104+ property SimpleRegExp;
1105+
1106+ {正規表現が、文頭メタキャラクタを含むか}
1107+ property HasLHead;
1108+ {正規表現が、文尾メタキャラクタを含むか}
1109+ property HasLTail;
1110+
1111+ {RegExpプロパティの正規表現に同意語処理を施したもの}
1112+ property ProcessedRegExp;
1113+ {文字の同一視辞書のリスト}
1114+ property ListOfFuzzyCharDic;
1115+ {同意語辞書のリスト}
1116+ property ListOfSynonymDic;
1117+
1118+ property Grep: TGrepGrepProc read FGrepProc;
1119+ published
1120+ {正規表現文字列}
1121+ property RegExp;
1122+ {文字の同一視辞書を使うか}
1123+ property UseFuzzyCharDic;
1124+ {同意語辞書を使うか}
1125+ property UseSynonymDic;
1126+
1127+ property OnMatch: TGrepOnMatch read FOnMatch write FOnMatch;
1128+
1129+ property Cancel: Boolean read FCancel write FCancel;
1130+ end;
1131+
1132+
1133+
1134+var
1135+ RE_FuzzyCharDic: TList;
1136+
1137+procedure Register;
1138+
1139+implementation
1140+{************************ Implementation ************************************}
1141+constructor ERegExpParser.Create(const Msg: string; ErrorPosition: Word);
1142+begin
1143+ inherited Create(Msg);
1144+ ErrorPos := ErrorPosition;
1145+end;
1146+{ -====================== 文字列操作関数 =====================================-}
1147+{説明 : 16進数を表す文字を受け取り、整数にして返す。
1148+ 引数 : WCh: WChar_t; 16進数を表す1バイト文字 [0-9a-fA-F]
1149+
1150+ 返り値: 正常時: 0 <= result <= 15
1151+ 異常時: -1}
1152+function HexWCharToInt(WCh: WChar_t): Integer;
1153+begin
1154+ case WCh of
1155+ Ord('0')..Ord('9'): result := WCh - Ord('0');
1156+ Ord('A')..Ord('F'): result := WCh - Ord('A')+10;
1157+ Ord('a')..Ord('f'): result := WCh - Ord('a')+10;
1158+ else result := -1;
1159+ end;
1160+end;
1161+
1162+{説明 : 8進数を表す文字を受け取り、整数にして返す。
1163+ 引数 : WCh: WChar_t; 8進数を表す1バイト文字 [0-7]
1164+
1165+ 返り値: 正常時: 0 <= result <= 7
1166+ 異常時: -1}
1167+function OctWCharToInt(WCh: WChar_t): Integer;
1168+begin
1169+ case WCh of
1170+ Ord('0')..Ord('7'): result := WCh - Ord('0');
1171+ else result := -1;
1172+ end;
1173+end;
1174+
1175+{機能: Str から 1文字 得る
1176+ 解説: Str中でIndexが指す位置から1文字(2バイト文字含む) 得てから、Indexが
1177+ 次の文字を指すように進める
1178+ 注意: Indexが文字列の長さよりすでに長い場合は常に 0を返し、Indexを増やさない。
1179+ つまり、Indexは最大でも Length(Str)+1 である。}
1180+function GetWChar(const Str: String; var Index: Integer): WChar_t;
1181+begin
1182+ if (Index >= 1) and (Index <= Length(Str)) then begin
1183+ if IsDBCSLeadByte(Byte(Str[Index])) then begin
1184+ {Strの最後の文字が2バイト文字コードの1バイトのときは例外生成}
1185+ if Index = Length(Str) then
1186+ raise ERegExpParser.Create('不正な2バイト文字コードです。', Index);
1187+ WordRec(result).Hi := Byte(Str[Index]);
1188+ WordRec(result).Lo := Byte(Str[Index+1]);
1189+ Inc(Index, 2);
1190+ end else begin
1191+ result := Byte(Str[Index]);
1192+ Inc(Index);
1193+ end;
1194+ end else begin
1195+ result := CONST_NULL;
1196+ end;
1197+end;
1198+
1199+//1997/09/25 FIX: MBUtils.pasがなくても動作するように変更
1200+function IsTrailByteInStr(pText: PAnsiChar;
1201+ ptr: PAnsiChar
1202+ ): Boolean;
1203+var
1204+ p: PAnsiChar;
1205+begin
1206+ Result := false;
1207+ if pText = ptr then Exit;
1208+ p := ptr - 1;
1209+ while (p <> pText) do
1210+ begin
1211+ if not IsDBCSLeadByte(Ord(p^)) then Break;
1212+ Dec(p);
1213+ end;
1214+ if ((ptr - p) mod 2) = 0 then Result := true;
1215+end;
1216+
1217+procedure UnGetWChar(const Str: String; var Index: Integer);
1218+begin
1219+ if Index <= 1 then
1220+ Exit
1221+ else if (Index > 2) and IsTrailByteInStr(PAnsiChar(Str), PAnsiChar(Str)+Index-2) then
1222+ Dec(Index, 2)
1223+ else
1224+ Dec(Index);
1225+end;
1226+
1227+function PCharGetWChar(var pText: PChar): WChar_t;
1228+begin
1229+ if Byte(pText^) <> CONST_NULL then begin
1230+ if IsDBCSLeadByte(Byte(pText^)) then begin
1231+ WordRec(result).Hi := Byte(pText^);
1232+ WordRec(result).Lo := Byte((pText+1)^);
1233+ Inc(pText, 2);
1234+ end else begin
1235+ result := Byte(pText^);
1236+ Inc(pText);
1237+ end;
1238+ end else begin
1239+ result := CONST_NULL;
1240+ end;
1241+end;
1242+
1243+{機能: WChar_t型の値をString型へ変換する。}
1244+function WCharToStr(WCh: WChar_t): String;
1245+begin
1246+ if IsDBCSLeadByte(Hi(WCh)) then
1247+ result := Chr(Hi(WCh))+Chr(Lo(WCh))
1248+ else
1249+ result := Chr(Lo(WCh));
1250+end;
1251+
1252+{機能: '\' で 引用されたキャラクタを得る。 \n, \t \\ ...
1253+ 注意: Indexは'\'の次の文字を指しているとする。}
1254+function GetQuotedWChar(const Str: String; var Index: Integer): WChar_t;
1255+var
1256+ WCh: WChar_t;
1257+begin
1258+ WCh := GetWChar(Str, Index);
1259+ if WCh = 0 then
1260+ raise ERegExpParser.Create('"\"の次には文字が必要です。', Index);
1261+
1262+ if WCh = CONST_b then {'b'}
1263+ result := CONST_BS {back space}
1264+ else if WCh = CONST_r then {'r'}
1265+ result := CONST_CR {Carriage Return}
1266+ else if WCh = CONST_n then {'n'}
1267+ result := CONST_LF {Line Feed}
1268+ else if WCh = CONST_t then {'t'}
1269+ result := CONST_TAB {tab}
1270+ else if WCh = CONST_x then {'x'}
1271+ result := HexStrToInt(Str, Index)
1272+ else if OctWCharToInt(WCh) >= 0 then begin
1273+ UnGetWChar(Str, Index); {WChを戻す}
1274+ result := OctStrToInt(Str, Index);
1275+ end else
1276+ result := WCh;
1277+end;
1278+
1279+{説明 : 16進数表記の文字列をWord型の数値に変換する。
1280+引数 : Str: String 変換元の文字列
1281+ Index: Integer 引数StrのIndex番目のバイト位置から変換を始める。
1282+返り値: 文字列が表すWord型の値}
1283+function HexStrToInt(const Str: String; var Index: Integer): Word;
1284+var
1285+ Val, i: Integer;
1286+ WCh: WChar_t;
1287+begin
1288+ result := 0;
1289+ i := 1;
1290+ WCh := GetWChar(Str, Index);
1291+ Val := HexWCharToInt(WCh);
1292+ while (WCh <> CONST_NULL) and (Val >= 0) and (i < 5) do begin
1293+ result := result * 16 + Val;
1294+ WCh := GetWChar(Str, Index);
1295+ Val := HexWCharToInt(WCh);
1296+ Inc(i);
1297+ end;
1298+ if i = 1 then
1299+ raise ERegExpParser.Create('不正な16進数コード表記です。', Index);
1300+ if WCh <> CONST_NULL then
1301+ UnGetWChar(Str, Index);
1302+end;
1303+
1304+{説明 : 8進数表記の文字列をWord型の数値に変換する。
1305+引数 : Str: String 変換元の文字列
1306+ Index: Integer 引数StrのIndex番目のバイト位置から変換を始める。
1307+返り値: 文字列が表すWord型の値}
1308+function OctStrToInt(const Str: String; var Index: Integer): Word;
1309+var
1310+ Val, i: Integer;
1311+ WCh: WChar_t;
1312+begin
1313+ result := 0;
1314+ i := 1;
1315+ WCh := GetWChar(Str, Index);
1316+ Val := OctWCharToInt(WCh);
1317+ while (WCh <> CONST_NULL) and (Val >= 0) and (i < 7) do begin
1318+ if (result * 8 + Val) > $FFFF then
1319+ raise ERegExpParser.Create('不正な8進数コード表記です。', Index);
1320+ result := result * 8 + Val;
1321+ WCh := GetWChar(Str, Index);
1322+ Val := OctWCharToInt(WCh);
1323+ Inc(i);
1324+ end;
1325+ if i = 1 then
1326+ raise ERegExpParser.Create('不正な8進数コード表記です。', Index);
1327+ if WCh <> CONST_NULL then
1328+ UnGetWChar(Str, Index);
1329+end;
1330+
1331+{説明 : FS:WChar_tを区切り文字として、バイト位置Indexから始まるトークンを1つ返す。
1332+引数 : Str: String
1333+ Index: Integer 引数StrのIndex番目のバイト位置から変換を始める。
1334+返り値: FSで区切られた、バイト位置Indexから始まるトークン}
1335+function WCharGetToken(const Str: String; var Index: Integer; FS: WChar_t): String;
1336+var
1337+ WCh: WChar_t;
1338+begin
1339+ result := '';
1340+ WCh := GetWChar(Str, Index);
1341+ while WCh <> 0 do begin
1342+ if WCh = FS then
1343+ break
1344+ else begin
1345+ result := result + WCharToStr(WCh);
1346+ WCh := GetWChar(Str, Index);
1347+ end;
1348+ end;
1349+end;
1350+
1351+{説明 : 引数Str中のメタキャラクタに'\'をつける。
1352+引数 : Str: String
1353+返り値: メタキャラクタの前に'\'がついたStr}
1354+function QuoteMetaWChar(Str: String): String;
1355+var
1356+ i, j: Integer;
1357+ WChar: WChar_t;
1358+begin
1359+ result := '';
1360+ i := 1;
1361+ WChar := GetWChar(Str, i);
1362+ while WChar <> 0 do begin
1363+ j := 0;
1364+ while j <= High(METACHARS) do begin
1365+ if METACHARS[j] = WChar then
1366+ break
1367+ else
1368+ Inc(j);
1369+ end;
1370+ if j <= High(METACHARS) then
1371+ result := result + '\' + WCharToStr(WChar)
1372+ else
1373+ result := result + WCharToStr(WChar);
1374+ WChar := GetWChar(Str, i);
1375+ end;
1376+
1377+end;
1378+
1379+{ -============================ TREScanner Class =================================-}
1380+constructor TREScanner.Create(Str: String);
1381+begin
1382+ inherited Create;
1383+ Self.SetRegExpStr(Str);
1384+end;
1385+
1386+procedure TREScanner.SetRegExpStr(RegExpStr: String);
1387+begin
1388+ FRegExpStr := RegExpStr;
1389+ FIndex := 1;
1390+end;
1391+
1392+{機能: トークンを得る
1393+ 解説: GetWCharおよびUnGetWCharメソッドを使ってトークンを得る。
1394+ 注意: 返り値は、列挙型 REToken_tのうちretk_CharClass以外のどれか}
1395+function TREScanner.GetTokenStd: REToken_t;
1396+var
1397+ WChar: WChar_t;
1398+begin
1399+ WChar := GetWChar(FRegExpStr, FIndex);
1400+ FSymbol.WChar := WChar;
1401+
1402+ { 文字(列)をトークンに変換する }
1403+ if WChar = CONST_NULL then
1404+ FToken := retk_End
1405+ else if WChar = CONST_DOLLAR then
1406+ FToken := retk_LTail
1407+ else if WChar = CONST_LPAR then
1408+ FToken := retk_LPar
1409+ else if WChar = CONST_RPAR then
1410+ FToken := retk_RPar
1411+ else if WChar = CONST_STAR then
1412+ FToken := retk_Star
1413+ else if WChar = CONST_PLUS then
1414+ FToken := retk_Plus
1415+ else if WChar = CONST_DOT then
1416+ FToken := retk_Dot
1417+ else if WChar = CONST_QMARK then
1418+ FToken := retk_QMark
1419+ else if WChar = CONST_VL then
1420+ FToken := retk_Union
1421+ else if WChar = CONST_RBRA then
1422+ FToken := retk_RBra
1423+ else if WChar = CONST_LBRA then begin
1424+ WChar := GetWChar(FRegExpStr, FIndex);
1425+ if WChar = CONST_NULL then
1426+ raise ERegExpParser.Create('右ブラケット"]"が必要です', FIndex);
1427+ if WChar = CONST_CARET then
1428+ FToken := retk_LBraNeg {補キャラクタクラス}
1429+ else begin
1430+ UnGetWChar(FRegExpStr, FIndex);
1431+ FToken := retk_LBra;
1432+ end;
1433+ end
1434+ else if WChar = CONST_YEN then begin
1435+ FToken := retk_Char;
1436+ FSymbol.WChar := GetQuotedWChar(FRegExpStr, FIndex);
1437+ end
1438+ else if WChar = CONST_CARET then begin
1439+ FToken := retk_LHead;
1440+ end else
1441+ FToken := retk_Char;
1442+
1443+ result := FToken;
1444+end;
1445+
1446+{機能: '[]'で囲まれたキャラクタクラス正規表現の中のトークンを得る。
1447+ 解説: GetWCharおよびUnGetWCharメソッドを使ってトークンを得る。
1448+ 注意: 返り値は、列挙型 REToken_tのうち
1449+ retk_Char, retk_CharClass, retk_RBraのどれか。
1450+ ヌル・キャラクタを見つけたときは例外を生成する。}
1451+function TREScanner.GetTokenCC: REToken_t;
1452+var
1453+ WChar, WChar2, WChar3: WChar_t;
1454+begin
1455+ WChar := GetWChar(FRegExpStr, FIndex);
1456+ FSymbol.WChar := WChar;
1457+
1458+ { 文字(列)をトークンに変換する }
1459+ if WChar = CONST_NULL then
1460+ raise ERegExpParser.Create('右ブラケット"]"が必要です', FIndex);
1461+ if WChar = CONST_RBRA then
1462+ FToken := retk_RBra
1463+ else begin
1464+ if WChar = CONST_YEN then
1465+ {エスケープシーケンスを処理}
1466+ WChar := GetQuotedWChar(FRegExpStr, FIndex);
1467+
1468+ {キャラクタ範囲を表す'-'に関する処理をする}
1469+ FToken := retk_Char;
1470+ WChar2 := GetWChar(FRegExpStr, FIndex);
1471+ if WChar2 = CONST_MINUS then begin
1472+ {2番目の文字が'-'だったとき}
1473+ WChar3 := GetWChar(FRegExpStr, FIndex);
1474+ if WChar3 = CONST_NULL then
1475+ {3番目の文字がヌルキャラクタのとき}
1476+ raise ERegExpParser.Create('右ブラケット"]"が必要です', FIndex);
1477+
1478+ if WChar3 = CONST_RBRA then begin
1479+ {3番目の文字が ']'のとき}
1480+ UnGetWChar(FRegExpStr, FIndex); { WChar3を戻す }
1481+ UnGetWChar(FRegExpStr, FIndex); { WChar2を戻す }
1482+ FSymbol.WChar := WChar;
1483+ end else begin
1484+ if WChar3 = CONST_YEN then
1485+ WChar3 := GetQuotedWChar(FRegExpStr, FIndex);
1486+ FToken := retk_CharClass;
1487+ if WChar > WChar3 then
1488+ raise ERegExpParser.Create('不正なキャラクタ範囲です', FIndex);
1489+ FSymbol.CharClass.StartChar := WChar;
1490+ FSymbol.CharClass.EndChar := WChar3;
1491+ end
1492+ end else begin
1493+ {2番目の文字が'-'ではないとき}
1494+ if WChar2 = CONST_NULL then
1495+ {2番目の文字がヌルキャラクタのとき}
1496+ raise ERegExpParser.Create('右ブラケット"]"が必要です', FIndex);
1497+ UnGetWChar(FRegExpStr, FIndex);{WChar2を戻す}
1498+ FSymbol.WChar := WChar;
1499+ end;
1500+ end;
1501+ result := FToken;
1502+end;
1503+
1504+function TREScanner.GetToken: REToken_t;
1505+begin
1506+ if FInCharClass then begin
1507+ result := GetTokenCC;
1508+ if result = retk_RBra then
1509+ FInCharClass := False;
1510+ end else begin
1511+ result := GetTokenStd;
1512+ if (result = retk_LBra) or (result = retk_LBraNeg) then
1513+ FInCharClass := True;
1514+ end;
1515+end;
1516+
1517+constructor TREPreProcessor.Create(Str: String);
1518+begin
1519+ inherited Create;
1520+ FScanner := TREScanner.Create(Str);
1521+ FTokenList := TList.Create;
1522+ FListOfSynonymDic := TList.Create;
1523+ FListOfFuzzyCharDic := TList.Create;
1524+end;
1525+
1526+destructor TREPreProcessor.Destroy;
1527+begin
1528+ FScanner.Free;
1529+ DestroyTokenListItems;
1530+ FTokenList.Free;
1531+ FListOfSynonymDic.Free;
1532+ FListOfFuzzyCharDic.Free;
1533+ inherited Destroy;
1534+end;
1535+
1536+{説明 : FTokenList: TList を、アイテムデータ (RETokenInfo_t型レコード)と共に廃棄する。
1537+注意 : MakeTokenListと対で使用する。}
1538+procedure TREPreProcessor.DestroyTokenListItems;
1539+var
1540+ i: Integer;
1541+begin
1542+ if FTokenList = nil then
1543+ exit;
1544+
1545+ i := 0;
1546+ while i < FTokenList.Count do begin
1547+ Dispose(REpTokenInfo_t(FTokenList.Items[i]));
1548+ FTokenList.Items[i] := nil;
1549+ Inc(i);
1550+ end;
1551+ FTokenList.Clear;
1552+end;
1553+
1554+{説明 : FTokenList: TListに RETokenInfo_t型のレコードを構築する。
1555+動作 : 最後尾のRETokenInfo_t型レコードは、常にToken = retk_Endである。
1556+注意 : DestroyTokenListメソッドと対で使用する。}
1557+procedure TREPreProcessor.MakeTokenList;
1558+var
1559+ pTokenInfo: REpTokenInfo_t;
1560+ prevIndex: Integer;
1561+begin
1562+ prevIndex := FScanner.Index;
1563+ DestroyTokenListItems;
1564+ while FScanner.GetToken <> retk_End do begin
1565+ New(pTokenInfo);
1566+ try
1567+ FTokenList.Add(pTokenInfo);
1568+ except
1569+ on Exception do begin
1570+ Dispose(pTokenInfo);
1571+ raise;
1572+ end;
1573+ end;
1574+ with pTokenInfo^ do begin
1575+ Token := FScanner.Token;
1576+ Symbol := FScanner.Symbol;
1577+ FromIndex := prevIndex;
1578+ ToIndex := FScanner.Index;
1579+ end;
1580+ prevIndex := FScanner.Index;
1581+ end;
1582+
1583+ {最後尾 retk_End}
1584+ New(pTokenInfo);
1585+ try
1586+ FTokenList.Add(pTokenInfo);
1587+ except
1588+ on Exception do begin
1589+ Dispose(pTokenInfo);
1590+ raise;
1591+ end;
1592+ end;
1593+ with pTokenInfo^ do begin
1594+ Token := retk_End;
1595+ Symbol.WChar := CONST_NULL;
1596+ FromIndex := 0;
1597+ ToIndex := 0;
1598+ end;
1599+end;
1600+
1601+function TREPreProcessor.GetTargetRegExpStr: String;
1602+begin
1603+ result := FScanner.RegExpStr;
1604+end;
1605+
1606+procedure TREPreProcessor.SetTargetRegExpStr(Str: String);
1607+begin
1608+ FScanner.RegExpStr := Str;
1609+end;
1610+
1611+{説明 : 正規表現文字列に同意語を組み込む。}
1612+procedure TREPreProcessor.Run;
1613+begin
1614+ FProcessedRegExpStr := FScanner.RegExpStr;
1615+ if FUseSynonymDic then begin
1616+ Self.Process(FindSynonym);
1617+ FScanner.RegExpStr := FProcessedRegExpStr;
1618+ end;
1619+
1620+ if FUseFuzzyCharDic then
1621+ Self.Process(FindFuzzyWChar);
1622+end;
1623+
1624+{説明 : 同意語埋め込み処理 Runメソッドの下請け}
1625+procedure TREPreProcessor.Process(FindFunc: TREPreProcessorFindFunc);
1626+var
1627+ j, k: Integer;
1628+ TkIndex: Integer;
1629+ Info: RETokenInfo_t;
1630+ InCC: Boolean;
1631+begin
1632+ FProcessedRegExpStr := '';
1633+ MakeTokenList;
1634+ InCC := False;
1635+ TkIndex := 0;
1636+ {すべてのトークンを検査する}
1637+ while TkIndex < FTokenList.Count do begin
1638+ Info := REpTokenInfo_t(FTokenList[TkIndex])^;
1639+ {キャラクタクラス ('[]'でくくられた部分)に入る}
1640+ if Info.Token = retk_LBra then
1641+ InCC := True;
1642+
1643+ {キャラクタクラスから出た}
1644+ if Info.Token = retk_RBra then
1645+ InCC := False;
1646+
1647+ {トークンがキャラクタ以外か、キャラクタクラス '[ ]'の中の場合}
1648+ if (Info.Token <> retk_Char) or InCC then begin
1649+ FProcessedRegExpStr := FProcessedRegExpStr +
1650+ Copy(FScanner.RegExpStr, Info.FromIndex, Info.ToIndex-Info.FromIndex);
1651+ Inc(TkIndex); {何もせずにFProcessedRegExpStrへ追加}
1652+ {トークンがキャラクタの場合}
1653+ end else begin
1654+ j := TkIndex;
1655+ {jがキャラクタ以外を指すまでインクリメント}
1656+ while REpTokenInfo_t(FTokenList[j])^.Token = retk_Char do
1657+ Inc(j);
1658+
1659+ {キャラクタの連続を1つづつ検査}
1660+ while TkIndex < j do begin
1661+ k := FindFunc(TkIndex, j);
1662+ if k <> -1 then begin
1663+ {マッチした部分を追加}
1664+ FProcessedRegExpStr := FProcessedRegExpStr + FSynonymStr;
1665+ TkIndex := k; {次のトークンからマッチする部分を引き続きさがす。}
1666+ end else begin
1667+ {マッチしなければ、一文字分追加して、インデックスを進める}
1668+ Info := REpTokenInfo_t(FTokenList[TkIndex])^;
1669+ FProcessedRegExpStr := FProcessedRegExpStr +
1670+ Copy(FScanner.RegExpStr, Info.FromIndex, Info.ToIndex-Info.FromIndex);;
1671+ Inc(TkIndex);
1672+ end;
1673+ end;
1674+ TkIndex := j;
1675+ end;
1676+ end;
1677+end;
1678+
1679+{説明 : 同意語辞書 SynonymDic: TListを使って、同意語を探す。
1680+返り値: トークンリスト内の同意語の次のインデックス
1681+ 見つからなければ -1}
1682+function TREPreProcessor.ReferToOneList(FromTokenIndex, ToTokenIndex: Integer; SynonymDic: TList): Integer;
1683+var
1684+ StrList: TStrings;
1685+ i, j, k, m: Integer;
1686+
1687+ {StrとFTokenListを比較}
1688+ function Match(Str: String): Integer;
1689+ var
1690+ StrIndex, TkIndex: Integer;
1691+ WChar: WChar_t;
1692+ begin
1693+ if Str = '' then begin
1694+ result := -1;
1695+ exit;
1696+ end;
1697+
1698+ TkIndex := FromTokenIndex;
1699+ StrIndex := 1;
1700+ WChar := GetWChar(Str, StrIndex);
1701+ while (WChar <> CONST_NULL) and (TkIndex < ToTokenIndex) do begin
1702+ if WChar <> REpTokenInfo_t(FTokenList[TkIndex])^.Symbol.WChar then begin
1703+ result := -1;
1704+ exit;
1705+ end else begin
1706+ Inc(TkIndex);
1707+ WChar := GetWChar(Str, StrIndex);
1708+ end;
1709+ end;
1710+ if WChar = CONST_NULL then
1711+ result := TkIndex
1712+ else
1713+ result := -1;
1714+ end;
1715+begin
1716+ result := -1;
1717+ i := 0;
1718+ while i < SynonymDic.Count do begin
1719+ StrList := TStrings(SynonymDic[i]);
1720+ j := 0;
1721+ while j < StrList.Count do begin
1722+ k := Match(StrList[j]);
1723+ if k <> -1 then begin
1724+ {マッチした}
1725+ FSynonymStr := '(' + QuoteMetaWChar(StrList[0]);
1726+ m := 1;
1727+ while m < StrList.Count do begin
1728+ FSynonymStr := FSynonymStr + '|' + QuoteMetaWChar(StrList[m]);
1729+ Inc(m);
1730+ end;
1731+ FSynonymStr := FSynonymStr + ')';
1732+ result := k;
1733+ exit;
1734+ end;
1735+ Inc(j);
1736+ end;
1737+ Inc(i);
1738+ end;
1739+end;
1740+
1741+{説明 :
1742+返り値: トークンリスト内の同意語の次のインデックス
1743+ 見つからなければ -1
1744+注意 : RunメソッドがメソッドポインタをProcessメソッドに渡し、
1745+ Processメソッドが呼び出す。}
1746+function TREPreProcessor.FindSynonym(FromTokenIndex, ToTokenIndex: Integer): Integer;
1747+var
1748+ i: Integer;
1749+begin
1750+ result := -1;
1751+ i := 0;
1752+ while i < FListOfSynonymDic.Count do begin
1753+ result := ReferToOneList(FromTokenIndex, ToTokenIndex, FListOfSynonymDic[i]);
1754+ if result <> -1 then
1755+ exit;
1756+ Inc(i);
1757+ end;
1758+end;
1759+
1760+{説明 :
1761+返り値: トークンリスト内の同意語の次のインデックス
1762+ 見つからなければ -1
1763+注意 : RunメソッドがメソッドポインタをProcessメソッドに渡し、
1764+ Processメソッドが呼び出す。}
1765+function TREPreProcessor.FindFuzzyWChar(FromTokenIndex, ToTokenIndex: Integer): Integer;
1766+var
1767+ i: Integer;
1768+begin
1769+ result := -1;
1770+ i := 0;
1771+ while i < FListOfFuzzyCharDic.Count do begin
1772+ result := ReferToOneList(FromTokenIndex, ToTokenIndex, FListOfFuzzyCharDic[i]);
1773+ if result <> -1 then
1774+ exit;
1775+ Inc(i);
1776+ end;
1777+end;
1778+
1779+constructor TREParseTree.Create;
1780+begin
1781+ inherited Create;
1782+ FNodeList := TList.Create;
1783+ FLeafList := TList.Create;
1784+end;
1785+
1786+destructor TREParseTree.Destroy;
1787+begin
1788+ DisposeTree;
1789+ FNodeList.Free;
1790+ FLeafList.Free;
1791+ inherited Destroy;
1792+end;
1793+
1794+{構文木のノードを作成する。
1795+ op はノードが表す演算、leftは左の子、rightは右の子 }
1796+function TREParseTree.MakeInternalNode(TheOp: REOperation_t; pLeft,
1797+ pRight: REpNode_t): REpNode_t;
1798+begin
1799+ New(result);
1800+ with result^ do begin
1801+ op := TheOp;
1802+ Children.pLeft := pLeft;
1803+ Children.pRight := pRight;
1804+ end;
1805+ try
1806+ FNodeList.Add(result);
1807+ except
1808+ {TListでメモリ不足の時は,新しい構文木の節も開放してしまう}
1809+ on EOutOfMemory do begin
1810+ Dispose(result);
1811+ raise;
1812+ end;
1813+ end;
1814+end;
1815+
1816+{構文木の葉を作る
1817+ TheC はこの葉が表す文字}
1818+function TREParseTree.MakeLeaf(aStartChar, aEndChar: WChar_t): REpNode_t; {char}
1819+var
1820+ i: Integer;
1821+begin
1822+ {既に同じキャラクタクラスを持つ葉が存在すれば、それを返す。}
1823+ for i := 0 to FLeafList.Count-1 do begin
1824+ if (REpNode_t(FLeafList[i])^.CharClass.StartChar = aStartChar) and
1825+ (REpNode_t(FLeafList[i])^.CharClass.EndChar = aEndChar) then begin
1826+ result := FLeafList[i];
1827+ exit;
1828+ end;
1829+ end;
1830+
1831+ New(result);
1832+ with result^ do begin
1833+ op := reop_char;
1834+ CharClass.StartChar := aStartChar;
1835+ CharClass.EndChar := aEndChar;
1836+ end;
1837+ try
1838+ FLeafList.Add(result);
1839+ except
1840+ {TListでメモリ不足の時は,新しい構文木の節も開放してしまう}
1841+ on EOutOfMemory do begin
1842+ Dispose(result);
1843+ raise;
1844+ end;
1845+ end;
1846+end;
1847+
1848+{文頭メタキャラクタを表す節。 ※子を持たないが、MakeInternalNodeを使う}
1849+function TREParseTree.MakeLHeadNode(WChar: WChar_t): REpNode_t;
1850+begin
1851+ result := MakeInternalNode(reop_LHead, nil, nil);
1852+ with result^ do begin
1853+ CharClass.StartChar := WChar;
1854+ CharClass.EndChar := WChar;
1855+ end;
1856+end;
1857+
1858+{文尾メタキャラクタを表す節。 ※子を持たないが、MakeInternalNodeを使う}
1859+function TREParseTree.MakeLTailNode(WChar: WChar_t): REpNode_t;
1860+begin
1861+ result := MakeInternalNode(reop_LTail, nil, nil);
1862+ with result^ do begin
1863+ CharClass.StartChar := WChar;
1864+ CharClass.EndChar := WChar;
1865+ end;
1866+end;
1867+
1868+{任意の一文字を表す'.'メタキャラクタに対応する部分木を作る。
1869+ ※CR LFを除く全てのキャラクタを表す葉をreop_Union操作を表す中間節で結んだもの}
1870+function TREParseTree.MakeAnyCharsNode: REpNode_t;
1871+begin
1872+ result := MakeInternalNode(reop_Union, MakeLeaf($1, $09), MakeLeaf($0B, $0C));
1873+ result := MakeInternalNode(reop_Union, result, MakeLeaf($0E, $FCFC));
1874+end;
1875+
1876+{引数が aStartChar <= aEndChar の関係を満たしているときに、MakeLeafを呼ぶ
1877+ それ以外は、nil を返す。}
1878+function TREParseTree.Check_and_MakeLeaf(aStartChar, aEndChar: WChar_t):REpNode_t;
1879+begin
1880+ if aStartChar <= aEndChar then begin
1881+ result := MakeLeaf(aStartChar, aEndChar);
1882+ end else
1883+ result := nil;
1884+end;
1885+
1886+{葉を内部節に変える。}
1887+procedure TREParseTree.ChangeLeaftoNode(pLeaf, pLeft, pRight: REpNode_t);
1888+begin
1889+ if (pLeft = nil) or (pRight = nil) then
1890+ raise Exception.Create('TREParseTree : 致命的エラー');{ debug }
1891+ with pLeaf^ do begin
1892+ op := reop_Union;
1893+ Children.pLeft := pLeft;
1894+ Children.pRight := pRight;
1895+ end;
1896+ FLeafList.Remove(pLeaf);
1897+ try
1898+ FNodeList.Add(pLeaf);
1899+ except
1900+ on EOutOfMemory do begin
1901+ FreeMem(pLeaf, SizeOf(RENode_t));
1902+ raise;
1903+ end;
1904+ end;
1905+end;
1906+
1907+{機能: 個々の葉が持つキャラクタ範囲が1つも重複しないようにする。
1908+ 解説: 葉は、CharClassフィールドを持ち、CharClassフィールドはStartCharとEndChar
1909+ をフィールドに持つレコードである。
1910+ 個々の葉が持つキャラクタの範囲が重複しないか調べて、重複する場合には、
1911+ その葉を分割し、reop_Unionを持つ内部節で等価な部分木に直す。}
1912+procedure TREParseTree.ForceCharClassUnique;
1913+var
1914+ i, j: Integer;
1915+ Changed: Boolean;
1916+
1917+ {機能: 重複するキャラクタ範囲をもつ葉の分割
1918+ 解説: 2つの葉pCCLeaf1とpCCLeaf2のキャラクタ範囲を調べて、重複するときは
1919+ 分割するして等価な部分木に変換する。}
1920+ function SplitCharClass(pCCLeaf1, pCCLeaf2: REpNode_t): Boolean;
1921+ var
1922+ pNode1, pNode2, pNode3: REpNode_t;
1923+ S1, S2, SmallE, BigE: WChar_t;
1924+ begin
1925+ result := False;
1926+ {前処理: pCCLeaf1 のStartChar <= pCCLeaf2 のStartChar を保証する}
1927+ if pCCLeaf1^.CharClass.StartChar > pCCLeaf2^.CharClass.StartChar then begin
1928+ pNode1 := pCCLeaf1;
1929+ pCCLeaf1 := pCCLeaf2;
1930+ pCCLeaf2 := pNode1;
1931+ end;
1932+
1933+ {キャラクタクラスの範囲が重複しない 又は 同一ならば Exit
1934+ ※ MakeLeafメソッドの構造からいって最初は重複する事はないが、分割を繰り返す
1935+ と重複する可能性がある。}
1936+ if (pCCLeaf1^.CharClass.EndChar < pCCLeaf2^.CharClass.StartChar) or
1937+ (pCCLeaf1^.CharClass.Chars = pCCLeaf2^.CharClass.Chars) then
1938+ exit;
1939+
1940+ {(pCCLeaf1 のStartChar) S1 <= S2 (pCCLeaf2 のStartChar)}
1941+ S1 := pCCLeaf1^.CharClass.StartChar;
1942+ S2 := pCCLeaf2^.CharClass.StartChar;
1943+
1944+ {SmallE は、pCCLeaf1, pCCLeaf2 の EndChar の小さい方
1945+ SmallE <= E2}
1946+ if pCCLeaf1^.CharClass.EndChar > pCCLeaf2^.CharClass.EndChar then begin
1947+ SmallE := pCCLeaf2^.CharClass.EndChar;
1948+ BigE := pCCLeaf1^.CharClass.EndChar;
1949+ end else begin
1950+ SmallE := pCCLeaf1^.CharClass.EndChar;
1951+ BigE := pCCLeaf2^.CharClass.EndChar;
1952+ end;
1953+
1954+ pNode1 := Check_and_MakeLeaf(S1, S2-1);
1955+ pNode2 := Check_and_MakeLeaf(S2, SmallE);
1956+ pNode3 := Check_and_MakeLeaf(SmallE+1, BigE);
1957+ {if (pNode1 = nil) and (pNode2 = nil) and (pNode3 = nil) then
1958+ raise ERegExpParser.Create('致命的なエラー', 0); }
1959+ if pNode1 = nil then begin {S1 = S2 のとき}
1960+ if pCCLeaf1^.CharClass.EndChar = BigE then
1961+ ChangeLeaftoNode(pCCLeaf1, pNode2, pNode3)
1962+ else
1963+ ChangeLeaftoNode(pCCLeaf2, pNode2, pNode3);
1964+ end else if pNode3 = nil then begin {SmallE = BigE の時}
1965+ ChangeLeaftoNode(pCCLeaf1, pNode1, pNode2);
1966+ end else begin
1967+ if pCCLeaf1^.CharClass.EndChar = BigE then begin{pCCLeaf1にpCCLeaf2が含まれる}
1968+ ChangeLeaftoNode(pCCLeaf1, MakeInternalNode(reop_Union, pNode1, pNode2),
1969+ pNode3)
1970+ end else begin {pCCLeaf1 と pCCLeaf2 の1部分が重なっている}
1971+ ChangeLeaftoNode(pCCLeaf1, pNode1, pNode2);
1972+ ChangeLeaftoNode(pCCLeaf2, pNode2, pNode3);
1973+ end;
1974+ end;
1975+ result := True;
1976+ end;
1977+begin {procedure TREParser.ForceCharClassUnique}
1978+ i := 0;
1979+ while i < LeafList.Count do begin
1980+ j := i + 1;
1981+ Changed := False;
1982+ while j < LeafList.Count do begin
1983+ Changed := SplitCharClass(LeafList[j], LeafList[i]);
1984+ if not Changed then
1985+ Inc(j)
1986+ else
1987+ break;
1988+ end;
1989+ if not Changed then
1990+ Inc(i);
1991+ end;
1992+end; {procedure TREParser.ForceCharClassUnique}
1993+
1994+procedure TREParseTree.DisposeTree;
1995+var
1996+ i: Integer;
1997+begin
1998+ if FNodeList <> nil then begin
1999+ for i := 0 to FNodeList.Count - 1 do begin
2000+ if FNodeList[i] <> nil then
2001+ Dispose(REpNode_t(FNodeList.Items[i]));
2002+ end;
2003+ FNodeList.Clear;
2004+ end;
2005+
2006+ if FLeafList <> nil then begin
2007+ for i := 0 to FLeafList.Count -1 do begin
2008+ if FLeafList[i] <> nil then
2009+ Dispose(REpNode_t(FLeafList[i]));
2010+ end;
2011+ FLeafList.Clear;
2012+ end;
2013+ FpHeadNode := nil;
2014+end;
2015+
2016+{-=========================== TREParser Class ===============================-}
2017+constructor TREParser.Create(RegExpStr: String);
2018+begin
2019+ inherited Create;
2020+ FScanner := TREScanner.Create(RegExpStr);
2021+ FParseTree := TREParseTree.Create;
2022+ {準備完了。 Runメソッドを呼べば構文解析をする。}
2023+end;
2024+
2025+destructor TREParser.Destroy;
2026+begin
2027+ FScanner.Free;
2028+ FParseTree.Free;
2029+ inherited Destroy;
2030+end;
2031+
2032+{**************************************************************************
2033+ 正規表現をパースするメソッド群
2034+ **************************************************************************}
2035+procedure TREParser.Run;
2036+begin
2037+ FParseTree.DisposeTree; {すでにある構文木を廃棄して初期化}
2038+
2039+ FScanner.GetToken; {最初のトークンを読み込む}
2040+
2041+ {正規表現をパースする}
2042+ FParseTree.pHeadNode := regexp;
2043+
2044+ {次のトークンがretk_End でなければエラー}
2045+ if FScanner.Token <> retk_End then begin
2046+ raise ERegExpParser.Create('正規表現に余分な文字があります',
2047+ FScanner.Index);
2048+ end;
2049+
2050+ FParseTree.ForceCharClassUnique;{キャラクタクラスを分割してユニークにする}
2051+end;
2052+
2053+{ <regexp>をパースして、得られた構文木を返す。
2054+ 選択 X|Y を解析する }
2055+function TREParser.regexp: REpNode_t;
2056+begin
2057+ result := term;
2058+ while FScanner.Token = retk_Union do begin
2059+ FScanner.GetToken;
2060+ result := FParseTree.MakeInternalNode(reop_union, result, term);
2061+ end;
2062+end;
2063+
2064+{ <term>をパースして、得られた構文木を返す
2065+ 連結XYを解析する}
2066+function TREParser.Term: REpNode_t;
2067+begin
2068+ if (FScanner.Token = retk_Union) or
2069+ (FScanner.Token = retk_RPar) or
2070+ (FScanner.Token = retk_End) then
2071+ result := FParseTree.MakeInternalNode(reop_Empty, nil, nil)
2072+ else begin
2073+ result := factor;
2074+ while (FScanner.Token <> retk_Union) and
2075+ (FScanner.Token <> retk_RPar) and
2076+ (FScanner.Token <> retk_End) do begin
2077+ result := FParseTree.MakeInternalNode(reop_concat, result, factor);
2078+ end;
2079+ end;
2080+end;
2081+
2082+{ <factor>をパースして、得られた構文木を返す
2083+ 繰り返しX*, X+, X?を解析する}
2084+function TREParser.Factor: REpNode_t;
2085+begin
2086+ result := primary;
2087+ if FScanner.Token = retk_Star then begin
2088+ result := FParseTree.MakeInternalNode(reop_closure, result, nil);
2089+ FScanner.GetToken;
2090+ end else if FScanner.Token = retk_Plus then begin
2091+ result := FParseTree.MakeInternalNode(reop_concat, result,
2092+ FParseTree.MakeInternalNode(reop_closure, result, nil));
2093+ FScanner.GetToken;
2094+ end else if FScanner.Token = retk_QMark then begin
2095+ result := FParseTree.MakeInternalNode(reop_Union, result,
2096+ FParseTree.MakeInternalNode(reop_Empty, nil, nil));
2097+ FScanner.GetToken;
2098+ end;
2099+end;
2100+
2101+{ <primary>をパースして、得られた構文木を返す。
2102+ 文字そのもの、(X)を解析する}
2103+function TREParser.Primary: REpNode_t;
2104+begin
2105+ case FScanner.Token of
2106+ retk_Char: begin
2107+ result := FParseTree.MakeLeaf(FScanner.Symbol.WChar, FScanner.Symbol.WChar);
2108+ FScanner.GetToken;
2109+ end;
2110+ retk_LHead: begin
2111+ result := FParseTree.MakeLHeadNode(FScanner.Symbol.WChar);
2112+ FScanner.GetToken;
2113+ end;
2114+ retk_LTail: begin
2115+ result := FParseTree.MakeLTailNode(FScanner.Symbol.WChar);
2116+ FScanner.GetToken;
2117+ end;
2118+ retk_Dot: begin
2119+ result := FParseTree.MakeAnyCharsNode;
2120+ FScanner.GetToken;
2121+ end;
2122+ retk_LPar: begin
2123+ FScanner.GetToken;
2124+ result := regexp;
2125+ if FScanner.Token <> retk_RPar then
2126+ raise ERegExpParser.Create('右(閉じ)括弧が必要です', FScanner.Index);
2127+ FScanner.GetToken;
2128+ end;
2129+ retk_LBra, retk_LBraNeg: begin
2130+ if FScanner.Token = retk_LBra then
2131+ result := CharacterClass(FParseTree)
2132+ else
2133+ result := NegativeCharacterClass;
2134+ if FScanner.Token <> retk_RBra then
2135+ raise ERegExpParser.Create('右ブラケット"]"が必要です', FScanner.Index);
2136+ FScanner.GetToken;
2137+ end;
2138+ else
2139+ raise ERegExpParser.Create('普通の文字、または左括弧"("が必要です', FScanner.Index);
2140+ end;
2141+end;
2142+
2143+{ <charclass> をパースして、得られた構文木を返す。
2144+ [] で括られた正規表現を解析する}
2145+function TREParser.CharacterClass(aParseTree: TREParseTree): REpNode_t;
2146+ {Tokenに対応した葉を作る}
2147+ function WCharToLeaf: REpNode_t;
2148+ begin
2149+ result := nil;
2150+ case FScanner.Token of
2151+ retk_Char:
2152+ result := aParseTree.MakeLeaf(FScanner.Symbol.WChar, FScanner.Symbol.WChar);
2153+
2154+ retk_CharClass:
2155+ result := aParseTree.MakeLeaf(FScanner.Symbol.CharClass.StartChar,
2156+ FScanner.Symbol.CharClass.EndChar);
2157+ end;
2158+ end;
2159+begin {function TREParser.CharacterClass}
2160+ FScanner.GetToken; {GetScannerCCは、retk_RBra, retk_Char, retk_CharClassしか返さない}
2161+ if FScanner.Token = retk_RBra then
2162+ raise ERegExpParser.Create('不正なキャラクタクラス指定です。', FScanner.Index);
2163+
2164+ result := WCharToLeaf;
2165+ FScanner.GetToken;
2166+ while FScanner.Token <> retk_RBra do begin
2167+ result := aParseTree.MakeInternalNode(reop_Union, result, WCharToLeaf);
2168+ FScanner.GetToken;
2169+ end;
2170+
2171+end;{function TREParser.CharacterClass}
2172+
2173+
2174+{ <negative charclass>をパースして、得られた構文木を返す。
2175+ [^ ] で括られた正規表現を解析する}
2176+function TREParser.NegativeCharacterClass: REpNode_t;
2177+var
2178+ aParseTree, aNeg_ParseTree: TREParseTree;
2179+ i: Integer;
2180+ aCharClass: RECharClass_t;
2181+ procedure RemoveCC(pLeaf: REpNode_t);
2182+ var
2183+ i: Integer;
2184+ pANode, pNode1, pNode2: REpNode_t;
2185+ begin
2186+ i := 0;
2187+ while i < aNeg_ParseTree.LeafList.Count do begin
2188+ pANode := aNeg_ParseTree.LeafList[i];
2189+ if (pLeaf^.CharClass.EndChar < pANode^.CharClass.StartChar) or
2190+ (pLeaf^.CharClass.StartChar > pANode^.CharClass.EndChar) then
2191+ Inc(i)
2192+ else begin
2193+ pNode1 := aNeg_ParseTree.Check_and_MakeLeaf(pANode^.CharClass.StartChar,
2194+ pLeaf^.CharClass.StartChar-1);
2195+ pNode2 := aNeg_ParseTree.Check_and_MakeLeaf(pLeaf^.CharClass.EndChar+1,
2196+ pANode^.CharClass.EndChar);
2197+ if (pNode1 <> nil) or (pNode2 <> nil) then begin
2198+ Dispose(REpNode_t(aNeg_ParseTree.LeafList[i]));
2199+ aNeg_ParseTree.LeafList.Delete(i);
2200+//======== 2015/01/07 mod zako ========
2201+ end else begin
2202+ Inc(i);
2203+//======== 2015/01/07 mod zako ========
2204+ end;
2205+ end;
2206+ end;
2207+ end;
2208+begin
2209+{ [^abc] = . - [abc] という動作をする。}
2210+
2211+ aParseTree := TREParseTree.Create;
2212+ try
2213+ aNeg_ParseTree := TREParseTree.Create;
2214+ try
2215+ {aParseTreeに'[]'で囲まれたキャラクタクラス正規表現の中に対応する節を作る。}
2216+ aParseTree.pHeadNode := CharacterClass(aParseTree);
2217+ {aParseTreeの葉が持つキャラクタクラスの範囲が重複しないように整形}
2218+ aParseTree.ForceCharClassUnique;
2219+
2220+ {任意の一文字を表す木をaNeg_ParseTreeに作成}
2221+ aNeg_ParseTree.MakeAnyCharsNode;
2222+
2223+ for i := 0 to aParseTree.LeafList.Count-1 do begin
2224+ {aNeg_ParseTreeの葉からaParseTreeの葉と同じ物を削除}
2225+ RemoveCC(aParseTree.LeafList[i]);
2226+ end;
2227+
2228+ {aNeg_ParseTreeの葉をFParseTreeにコピー}
2229+ result := nil;
2230+ if aNeg_ParseTree.LeafList.Count > 0 then begin
2231+ aCharClass := REpNode_t(aNeg_ParseTree.LeafList[0])^.CharClass;
2232+ result := FParseTree.MakeLeaf(aCharClass.StartChar, aCharClass.EndChar);
2233+ for i := 1 to aNeg_ParseTree.LeafList.Count-1 do begin
2234+ aCharClass := REpNode_t(aNeg_ParseTree.LeafList[i])^.CharClass;
2235+ result := FParseTree.MakeInternalNode(reop_Union, result,
2236+ FParseTree.MakeLeaf(aCharClass.StartChar, aCharClass.EndChar));
2237+ end;
2238+ end;
2239+ finally
2240+ aNeg_ParseTree.Free;
2241+ end;
2242+ finally
2243+ aParseTree.Free;
2244+ end;
2245+end;
2246+
2247+{$IFDEF DEBUG}
2248+function DebugWCharToStr(WChar: WChar_t): String;
2249+begin
2250+ if WChar > $FF then
2251+ result := ' ' + Chr(Hi(WChar))+Chr(Lo(WChar))+'($' + IntToHex(WChar, 4) + ')'
2252+ else
2253+ result := ' ' + Chr(Lo(WChar))+' ($00' + IntToHex(WChar, 2) + ')';
2254+
2255+end;
2256+
2257+{ デバッグ用メッソッド。構文木をVCL のTOutLineコンポーネントに書き込む}
2258+{ 構文木が大きすぎると、TOutLineコンポーネントが”死ぬ”ので注意}
2259+procedure TREParser.WriteParseTreeToOutLine(anOutLine: TOutLine);
2260+ procedure SetOutLineRecursive(pTree: REpNode_t; ParentIndex: Integer);
2261+ var
2262+ aStr: String;
2263+ NextParentIndex: Integer;
2264+ begin
2265+ if pTree = nil then
2266+ exit;
2267+
2268+ case pTree^.op of
2269+ reop_Char: begin{ 文字そのもの }
2270+ if pTree^.CharClass.StartChar <> pTree^.CharClass.EndChar then
2271+ aStr := DebugWCharToStr(pTree^.CharClass.StartChar)
2272+ + ' 〜 '+ DebugWCharToStr(pTree^.CharClass.EndChar)
2273+ else
2274+ aStr := DebugWCharToStr(pTree^.CharClass.StartChar);
2275+ end;
2276+ reop_LHead:
2277+ aStr := '文頭 '+DebugWCharToStr(pTree^.CharClass.StartChar);
2278+ reop_LTail:
2279+ aStr := '文尾 '+DebugWCharToStr(pTree^.CharClass.StartChar);
2280+ reop_Concat:{ XY }
2281+ aStr := '連結 ';
2282+ reop_Union:{ X|Y}
2283+ aStr := '選択 "|"';
2284+ reop_Closure:{ X* }
2285+ aStr := '閉包 "*"';
2286+ reop_Empty:{ 空 }
2287+ aStr := '空';
2288+ end;
2289+
2290+ NextParentIndex := anOutLine.AddChild(ParentIndex, aStr);
2291+
2292+ if pTree^.op in [reop_Concat, reop_Union, reop_Closure] then begin
2293+ SetOutLineRecursive(pTree^.Children.pLeft, NextParentIndex);
2294+ SetOutLineRecursive(pTree^.Children.pRight, NextParentIndex);
2295+ end;
2296+ end;
2297+begin
2298+ anOutLine.Clear;
2299+ SetOutLineRecursive(FParseTree.pHeadNode, 0);
2300+end;
2301+
2302+{$ENDIF}
2303+
2304+{ -============================== TRE_NFA Class ==================================-}
2305+constructor TRE_NFA.Create(Parser: TREParser; LHeadWChar, LTailWChar: WChar_t);
2306+begin
2307+ inherited Create;
2308+ FStateList := TList.Create;
2309+ FParser := Parser;
2310+ FLHeadWChar := LHeadWChar;
2311+ FLTailWChar := LTailWChar;
2312+end;
2313+
2314+destructor TRE_NFA.Destroy;
2315+begin
2316+ DisposeStateList;
2317+ inherited Destroy;
2318+end;
2319+
2320+{ NFA状態表を破棄する}
2321+procedure TRE_NFA.DisposeStateList;
2322+var
2323+ i: Integer;
2324+ pNFANode, pNext: RE_pNFANode_t;
2325+begin
2326+ if FStateList <> nil then begin
2327+ for i := 0 to FStateList.Count-1 do begin
2328+ pNFANode := FStateList.Items[i];
2329+ while pNFANode <> nil do begin
2330+ pNext := pNFANode^.Next;
2331+ Dispose(pNFANode);
2332+ pNFANode := pNext;
2333+ end;
2334+ end;
2335+ FStateList.Free;
2336+ FStateList := nil;
2337+ end;
2338+end;
2339+
2340+{ 構文木 Treeに対応するNFAを生成する}
2341+procedure TRE_NFA.Run;
2342+begin
2343+ { NFA の初期状態のノードを割り当てる。}
2344+ FEntryState := NumberNode;
2345+
2346+ { NFA の終了状態のノードを割り当てる }
2347+ FExitState := NumberNode;
2348+
2349+ { NFA を生成する }
2350+ GenerateStateList(FParser.ParseTree.pHeadNode, FEntryState, FExitState);
2351+end;
2352+
2353+{ ノードに番号を割り当てる}
2354+function TRE_NFA.NumberNode: Integer;
2355+begin
2356+ with FStateList do begin
2357+ result := Add(nil);
2358+ end;
2359+end;
2360+
2361+{ NFA状態節 を1つ作成}
2362+function TRE_NFA.MakeNFANode: RE_pNFANode_t;
2363+begin
2364+ New(result);
2365+end;
2366+
2367+{ FStateListに状態遷移を追加する。
2368+ 状態 TransFrom に対して aCharClass内の文字で状態 TransTo への遷移を追加する。}
2369+procedure TRE_NFA.AddTransition(TransFrom, TransTo: Integer;
2370+ aCharClass: RECharClass_t); {Char}
2371+var
2372+ pNFANode: RE_pNFANode_t;
2373+begin
2374+ pNFANode := MakeNFANode;
2375+
2376+ with pNFANode^ do begin
2377+ CharClass := aCharClass;
2378+ TransitTo := TransTo;
2379+ Next := RE_pNFANode_t(FStateList.Items[TransFrom]);
2380+ end;
2381+ FStateList.Items[TransFrom] := pNFANode;
2382+end;
2383+
2384+{ 構文木 pTree に対する StateListを生成する
2385+ NFAの入り口をentry, 出口をway_outとする }
2386+procedure TRE_NFA.GenerateStateList(pTree: REpNode_t; entry, way_out: Integer);
2387+var
2388+ aState1, aState2: Integer;
2389+ aCharClass: RECharClass_t;
2390+begin
2391+ case pTree^.op of
2392+ reop_Char:
2393+ AddTransition(entry, way_out, pTree^.CharClass);
2394+ reop_LHead: begin {'^'}
2395+ {文頭メタキャラクタ'^' は TransFrom = FEntryStateのとき以外は、
2396+ 通常のキャラクタとして扱う。}
2397+ if Entry <> FEntryState then begin
2398+ AddTransition(entry, way_out, pTree^.CharClass);
2399+ end else begin
2400+ FRegExpHasLHead := True;
2401+ with aCharClass do begin
2402+ StartChar := FLHeadWChar;
2403+ EndChar := FLHeadWChar;
2404+ end;
2405+ AddTransition(entry, way_out, aCharClass);
2406+ end;
2407+ end;
2408+ reop_LTail: begin
2409+ {行末メタキャラクタ '$'は、TransTo = FExitStateのとき以外は、
2410+ 通常のキャラクタとして扱う。}
2411+ if way_out <> FExitState then begin
2412+ AddTransition(entry, way_out, pTree^.CharClass);
2413+ end else begin
2414+ FRegExpHasLTail := True;
2415+ with aCharClass do begin
2416+ StartChar := FLTailWChar;
2417+ EndChar := FLTailWChar;
2418+ end;
2419+ AddTransition(entry, way_out, aCharClass);
2420+ end;
2421+ end;
2422+ reop_Union: begin {'|'}
2423+ GenerateStateList(pTree^.Children.pLeft, entry, way_out);
2424+ GenerateStateList(pTree^.Children.pRight, entry, way_out);
2425+ end;
2426+ reop_Closure: begin {'*'}
2427+ aState1 := NumberNode;
2428+ aState2 := NumberNode;
2429+ { 状態 entry → ε遷移 → 状態 aState1}
2430+ AddTransition(entry, aState1, CONST_EMPTYCharClass);
2431+ { 状態 aState1 → (pTree^.Children.pLeft)以下の遷移 → 状態 aState2}
2432+ GenerateStateList(pTree^.Children.pLeft, aState1, aState2);
2433+ { 状態 aState2 → ε遷移 → 状態 aState1}
2434+ AddTransition(aState2, aState1, CONST_EMPTYCharClass);
2435+ { 状態 aState1 → ε遷移 → 状態 way_out}
2436+ AddTransition(aState1, way_out, CONST_EMPTYCharClass);
2437+ end;
2438+ reop_Concat: begin {'AB'}
2439+ aState1 := NumberNode;
2440+ { 状態 entry → (pTree^.Children.pLeft)遷移 → 状態 aState1}
2441+ GenerateStateList(pTree^.Children.pLeft, entry, aState1);
2442+ { 状態 aState1 → (pTree^.Children.pRight)遷移 → 状態 way_out}
2443+ GenerateStateList(pTree^.Children.pRight, aState1, way_out);
2444+ end;
2445+ reop_Empty:
2446+ AddTransition(entry, way_out, CONST_EMPTYCharClass);
2447+ else begin
2448+ raise Exception.Create('This cannot happen in TRE_NFA.GenerateStateList');
2449+ end;
2450+ end;
2451+end;
2452+
2453+{$IFDEF DEBUG}
2454+{TStringsオブジェクトに、NFA の内容を書き込む}
2455+procedure TRE_NFA.WriteNFAtoStrings(Strings: TStrings);
2456+var
2457+ i: Integer;
2458+ pNFANode: RE_pNFANode_t;
2459+ Str: String;
2460+begin
2461+ Strings.clear;
2462+ Strings.BeginUpDate;
2463+ for i := 0 to FStateList.Count-1 do begin
2464+ pNFANode := FStateList.items[i];
2465+ if i = EntryState then
2466+ Str := Format('開始 %2d : ', [i])
2467+ else if i = ExitState then
2468+ Str := Format('終了 %2d : ', [i])
2469+ else
2470+ Str := Format('状態 %2d : ', [i]);
2471+ while pNFANode <> nil do begin
2472+ if pNFANode^.CharClass.StartChar = CONST_EMPTY then
2473+ Str := Str + Format('ε遷移で 状態 %2d へ :',[pNFANode^.TransitTo])
2474+ else if pNFANode^.CharClass.StartChar <> pNFANode^.CharClass.EndChar then
2475+ Str := Str + Format('文字%s から%s で 状態 %2d へ :',
2476+ [DebugWCharToStr(pNFANode^.CharClass.StartChar),
2477+ DebugWCharToStr(pNFANode^.CharClass.EndChar), pNFANode^.TransitTo])
2478+ else if pNFANode^.CharClass.StartChar = FLHeadWChar then begin
2479+ Str := Str + Format('文頭コード%s で 状態 %2d へ :',
2480+ [DebugWCharToStr(pNFANode^.CharClass.StartChar), pNFANode^.TransitTo]);
2481+ end else if pNFANode^.CharClass.StartChar = FLTailWChar then begin
2482+ Str := Str + Format('文尾コード%s で 状態 %2d へ :',
2483+ [DebugWCharToStr(pNFANode^.CharClass.StartChar), pNFANode^.TransitTo]);
2484+ end else
2485+ Str := Str + Format('文字%s で 状態 %2d へ :',
2486+ [DebugWCharToStr(pNFANode^.CharClass.StartChar), pNFANode^.TransitTo]);
2487+
2488+ pNFANode := pNFANode^.Next;
2489+ end;
2490+ Strings.Add(Str);
2491+ end;
2492+ Strings.EndUpDate;
2493+end;
2494+{$ENDIF}
2495+
2496+{ -========================== TRE_NFAStateSet Class =============================-}
2497+constructor TRE_NFAStateSet.Create(StateMax: Integer);
2498+var
2499+ i: Integer;
2500+begin
2501+ inherited Create;
2502+ FCapacity := StateMax div 8 + 1;
2503+ GetMem(FpArray, FCapacity);
2504+ for i := 0 to FCapacity-1 do
2505+ FpArray^[i] := 0;
2506+end;
2507+
2508+destructor TRE_NFAStateSet.Destroy;
2509+begin
2510+ FreeMem(FpArray, FCapacity);
2511+ inherited Destroy;
2512+end;
2513+
2514+function TRE_NFAStateSet.Has(StateIndex: Integer): Boolean;
2515+begin
2516+ result := (FpArray^[StateIndex div 8] and (1 shl (StateIndex mod 8))) <> 0;
2517+end;
2518+
2519+procedure TRE_NFAStateSet.Include(StateIndex: Integer);
2520+begin
2521+ FpArray^[StateIndex div 8] := FpArray^[StateIndex div 8] or
2522+ (1 shl (StateIndex mod 8));
2523+end;
2524+
2525+function TRE_NFAStateSet.Equals(AStateSet: TRE_NFAStateSet): Boolean;
2526+var
2527+ i: Integer;
2528+begin
2529+ result := False;
2530+ for i := 0 to FCapacity - 1 do begin
2531+ if FpArray^[i] <> AStateSet.pArray^[i] then
2532+ exit;
2533+ end;
2534+ result := True;
2535+end;
2536+
2537+{ -============================= TRE_DFA Class ==================================-}
2538+constructor TRE_DFA.Create(NFA: TRE_NFA);
2539+begin
2540+ inherited Create;
2541+ FNFA := NFA;
2542+ FStateList := TList.Create;
2543+end;
2544+
2545+destructor TRE_DFA.Destroy;
2546+begin
2547+ DestroyStateList;
2548+
2549+ inherited Destroy;
2550+end;
2551+
2552+{DFA状態のリストを破棄}
2553+procedure TRE_DFA.DestroyStateList;
2554+var
2555+ i: Integer;
2556+ pDFA_State: RE_pDFAState_t;
2557+ pDFA_StateSub, pNextSub: RE_pDFAStateSub_t;
2558+begin
2559+ if FStateList <> nil then begin
2560+ for i := 0 to FStateList.Count-1 do begin
2561+ pDFA_State := FStateList.Items[i];
2562+ if pDFA_State <> nil then begin
2563+ pDFA_StateSub := pDFA_State^.next;
2564+ while pDFA_StateSub <> nil do begin
2565+ pNextSub := pDFA_StateSub^.next;
2566+ Dispose(pDFA_StateSub);
2567+ pDFA_StateSub := pNextSub;
2568+ end;
2569+ pDFA_State^.StateSet.Free;
2570+ Dispose(pDFA_State);
2571+ end;
2572+ end;
2573+ FStateList.Free;
2574+ FStateList := nil;
2575+ end;
2576+end;
2577+
2578+procedure TRE_DFA.Run;
2579+begin
2580+ FRegExpHasLHead := FNFA.RegExpHasLHead;
2581+ FRegExpHasLTail := FNFA.RegExpHasLTail;
2582+ Convert_NFA_to_DFA; {NFA状態表からDFA状態表を作る}
2583+ StateListSort; {DFA状態表の節を入力キー順に整列する。※検索の高速化のため}
2584+ CheckIfRegExpIsSimple;{正規表現が単純な文字列かチェック}
2585+end;
2586+
2587+{ NFAを等価なDFAへと変換する}
2588+procedure TRE_DFA.Convert_NFA_to_DFA;
2589+var
2590+ Initial_StateSet: TRE_NFAStateSet;
2591+ t: RE_pDFAState_t;
2592+ pDFA_TransNode, pTransNodeHead: RE_pDFATransNode_t;
2593+ pDFA_StateSub: RE_pDFAStateSub_t;
2594+begin
2595+{DFAの初期状態を登録する}
2596+ Initial_StateSet := TRE_NFAStateSet.Create(FNFA.StateList.Count);
2597+ Initial_StateSet.Include(FNFA.EntryState);
2598+ {NFA初期状態の集合を求める(ε遷移も含む)}
2599+ Collect_Empty_Transition(Initial_StateSet);
2600+ FpInitialState := Register_DFA_State(Initial_StateSet);
2601+
2602+ {未処理のDFA状態があれば、それを取り出して処理する
2603+ 注目しているDFA状態をtとする}
2604+ t := Fetch_Unvisited_D_state;
2605+ while t <> nil do begin
2606+
2607+ {処理済みの印を付ける}
2608+ t^.visited := True;
2609+
2610+ {状態tから遷移可能なDFA状態をすべてDFAに登録する。}
2611+ pTransNodeHead := Compute_Reachable_N_state(t);
2612+ try
2613+ pDFA_TransNode := pTransNodeHead;
2614+ while pDFA_TransNode <> nil do begin
2615+ { NFA状態集合のε-closureを求める}
2616+ Collect_Empty_Transition(pDFA_TransNode^.ToNFAStateSet);
2617+
2618+ { 遷移情報をDFA状態に加える}
2619+ New(pDFA_StateSub);
2620+ with pDFA_StateSub^ do begin
2621+ next := nil;
2622+ CharClass := pDFA_TransNode^.CharClass;
2623+ next := t^.next;
2624+ end;
2625+ t^.next := pDFA_StateSub;
2626+
2627+ {現在のDFA状態からの遷移先の新しいDFA状態を登録}
2628+ pDFA_StateSub^.TransitTo :=
2629+ Register_DFA_State(pDFA_TransNode^.ToNFAStateSet);
2630+ {Register_DFA_StateメソッドによりToNFAStateSetオブジェクトはDFA_Stateに所有される}
2631+ {pDFA_TransNode^.ToNFAStateSet := nil;}
2632+
2633+ pDFA_TransNode := pDFA_TransNode^.next;
2634+ end;
2635+ t := Fetch_Unvisited_D_state;
2636+ finally
2637+ Destroy_DFA_TransList(pTransNodeHead);
2638+ end;
2639+ end;
2640+end;
2641+
2642+{ NFA状態集合 StateSet に対して ε-closure操作を実行する。
2643+ ε遷移で遷移可能な全てのNFA状態を追加する}
2644+procedure TRE_DFA.Collect_Empty_Transition(StateSet: TRE_NFAStateSet);
2645+var
2646+ i: Integer;
2647+ { NFA状態集合 StateSetにNFA状態 sを追加する。
2648+ 同時にNFA状態sからε遷移で移動できるNFA状態も追加する}
2649+ procedure Mark_Empty_Transition(StateSet: TRE_NFAStateSet; s: Integer);
2650+ var
2651+ pNFANode: RE_pNFANode_t;
2652+ begin
2653+ StateSet.Include(s);
2654+ pNFANode := FNFA.StateList[s];
2655+ while pNFANode <> nil do begin
2656+ if (pNFANode^.CharClass.StartChar = CONST_EMPTY) and
2657+ (not StateSet.Has(pNFANode^.TransitTo)) then
2658+ Mark_Empty_Transition(StateSet, pNFANode^.TransitTo);
2659+ pNFANode := pNFANode^.next;
2660+ end;
2661+ end;
2662+begin
2663+ for i := 0 to FNFA.StateList.Count-1 do begin
2664+ if StateSet.Has(i) then
2665+ Mark_Empty_Transition(StateSet, i);
2666+ end;
2667+end;
2668+
2669+{ NFA状態集合 aStateSet をDFAに登録して、DFA状態へのポインタを返す。
2670+ aStateSetが終了状態を含んでいれば、acceptedフラグをセットする。
2671+ すでにaStateSetがDFAに登録されていたら何もしない}
2672+function TRE_DFA.Register_DFA_State(var aStateSet: TRE_NFAStateSet): RE_pDFAState_t;
2673+var
2674+ i: Integer;
2675+begin
2676+ { NFA状態 aStateSet がすでにDFAに登録されていたら、何もしないでリターンする}
2677+ for i := 0 to FStateList.Count-1 do begin
2678+ if RE_pDFAState_t(FStateList[i])^.StateSet.Equals(aStateSet) then begin
2679+ result := RE_pDFAState_t(FStateList[i]);
2680+ exit;
2681+ end;
2682+ end;
2683+
2684+ {DFAに必要な情報をセットする}
2685+ New(result);
2686+ with result^ do begin
2687+ StateSet := aStateSet;
2688+ visited := False;
2689+ if aStateSet.Has(FNFA.ExitState) then
2690+ accepted := True
2691+ else
2692+ accepted := False;
2693+ next := nil;
2694+ end;
2695+ aStateSet := nil;
2696+ FStateList.add(result);
2697+end;
2698+
2699+{ 処理済みの印がついていないDFA状態を探す。
2700+ 見つからなければnilを返す。}
2701+function TRE_DFA.Fetch_Unvisited_D_state: RE_pDFAState_t;
2702+var
2703+ i: Integer;
2704+begin
2705+
2706+ for i := 0 to FStateList.Count-1 do begin
2707+ if not RE_pDFAState_t(FStateList[i])^.visited then begin
2708+ result := FStateList[i];
2709+ exit;
2710+ end;
2711+ end;
2712+ result := nil;
2713+end;
2714+
2715+{Compute_Reachable_N_state が作る RE_DFATransNode_t型のリンクリストを破棄する}
2716+procedure TRE_DFA.Destroy_DFA_TransList(pDFA_TransNode: RE_pDFATransNode_t);
2717+var
2718+ pNext: RE_pDFATransNode_t;
2719+begin
2720+ if pDFA_TransNode <> nil then begin
2721+ while pDFA_TransNode <> nil do begin
2722+ pNext := pDFA_TransNode^.next;
2723+ if pDFA_TransNode^.ToNFAStateSet <> nil then
2724+ pDFA_TransNode^.ToNFAStateSet.Free;
2725+ Dispose(pDFA_TransNode);
2726+
2727+ pDFA_TransNode := pNext;
2728+ end;
2729+ end;
2730+end;
2731+
2732+{ DFA状態pDFAStateから遷移可能なNFA状態を探して、リンクリストにして返す}
2733+function TRE_DFA.Compute_Reachable_N_state(pDFAState: RE_pDFAState_t): RE_pDFATransNode_t;
2734+var
2735+ i: Integer;
2736+ pNFANode: RE_pNFANode_t;
2737+ a, b: RE_pDFATransNode_t;
2738+label
2739+ added;
2740+begin
2741+ result := nil;
2742+try
2743+ {すべてのNFA状態を順に調べる}
2744+ for i := 0 to FNFA.StateList.Count-1 do begin
2745+
2746+ { NFA状態iがDFA状態 pDFAStateに含まれていれば、以下の処理を行う}
2747+ if pDFAState^.StateSet.Has(i) then begin
2748+
2749+ { NFA状態 i から遷移可能なNFA状態をすべて調べてリストにする}
2750+ pNFANode := RE_pNFANode_t(FNFA.StateList[i]);
2751+ while pNFANode <> nil do begin
2752+ if pNFANode^.CharClass.StartChar <> CONST_EMPTY then begin {ε遷移は無視}
2753+ a := result;
2754+ while a <> nil do begin
2755+ if a^.CharClass.Chars = pNFANode^.CharClass.Chars then begin
2756+ a^.ToNFAStateSet.Include(pNFANode^.TransitTo);
2757+ goto added;
2758+ end;
2759+ a := a^.next;
2760+ end;
2761+ {キャラクタ pNFANode^.CharClass.cによる遷移が登録されていなければ追加}
2762+ New(b);
2763+ with b^ do begin
2764+ CharClass := pNFANode^.CharClass;
2765+ ToNFAStateSet := TRE_NFAStateSet.Create(FNFA.StateList.Count);
2766+ ToNFAStateSet.Include(pNFANode^.TransitTo);
2767+ next := result;
2768+ end;
2769+ result := b;
2770+ added:
2771+ ;
2772+ end;
2773+ pNFANode := pNFANode^.next;
2774+ end;
2775+ end;
2776+ end;
2777+except
2778+ on EOutOfMemory do begin
2779+ Destroy_DFA_TransList(result); {構築中のリスト廃棄}
2780+ raise;
2781+ end;
2782+end;
2783+end;
2784+
2785+{状態リストのリンクリストを整列する(マージ・ソートを使用)}
2786+procedure TRE_DFA.StateListSort;
2787+var
2788+ i: Integer;
2789+ {マージ・ソート処理を再帰的に行う}
2790+ function DoSort(pCell: RE_pDFAStateSub_t): RE_pDFAStateSub_t;
2791+ var
2792+ pMidCell, pACell: RE_pDFAStateSub_t;
2793+
2794+ {2つのリストをソートしながら併合する}
2795+ function MergeList(pCell1, pCell2: RE_pDFAStateSub_t): RE_pDFAStateSub_t;
2796+ var
2797+ Dummy: RE_DFAStateSub_t;
2798+ begin
2799+ Result := @Dummy;
2800+ {どちらかのリストが、空になるまで反復}
2801+ while (pCell1 <> nil) and (pCell2 <> nil) do begin
2802+ {pCell1 と pCell2 を比較して小さい方をResultに追加していく}
2803+ if pCell1^.CharClass.StartChar > pCell2^.CharClass.StartChar then begin
2804+ {pCell2の方が小さい}
2805+ Result^.Next := pCell2;
2806+ Result := pCell2;
2807+ pCell2 := pCell2^.Next;
2808+ end else begin
2809+ {pCell1の方が小さい}
2810+ Result^.Next := pCell1;
2811+ Result := pCell1;
2812+ pCell1 := pCell1^.Next;
2813+ end;
2814+ end;
2815+ {余ったリストをそのままresult に追加}
2816+ if pCell1 = nil then
2817+ Result^.Next := pCell2
2818+ else
2819+ Result^.Next := pCell1;
2820+
2821+ result := Dummy.Next;
2822+ end;
2823+
2824+ {DoSort本体}
2825+ begin
2826+ if (pCell = nil) or (pCell^.Next = nil) then begin
2827+ result := pCell;
2828+ exit; {要素が1つ、または、無いときは、すぐに exit}
2829+ end;
2830+
2831+ {ACell が3番目のセルを指すようにする。無ければ、nil を持たせる}
2832+ {リストが2〜3個のセルを持つときにも、分割を行うようにする。}
2833+ pACell := pCell^.Next^.Next;
2834+ pMidCell := pCell;
2835+ {MidCell が、リストの真ん中あたりのセルを指すようにする。}
2836+ while pACell <> nil do begin
2837+ pMidCell := pMidCell^.Next;
2838+ pACell := pACell^.Next;
2839+ if pACell <> nil then
2840+ pACell := pACell^.Next;
2841+ end;
2842+
2843+ {MidCell の後ろでリストを2分割する}
2844+ pACell := pMidCell^.Next;
2845+ pMidCell^.Next := nil;
2846+
2847+ result := MergeList(DoSort(pCell), DoSort(pACell));
2848+ end;
2849+begin {Sort 本体}
2850+ for i := 0 to FStateList.Count-1 do begin
2851+ RE_pDFAState_t(FStateList[i])^.next :=
2852+ DoSort(RE_pDFAState_t(FStateList[i])^.next);
2853+ end;
2854+end;
2855+
2856+{機能: 現在の正規表現が、普通の文字列か?
2857+ 普通の文字列だったら、FRegExpIsSimple = True; FSimpleRegExpStrに文字列に設定
2858+ それ以外の場合は、 FRegExpIsSimple = False;FSimpleRegExpStr = ''}
2859+procedure TRE_DFA.CheckIfRegExpIsSimple;
2860+var
2861+ pDFAState: RE_pDFAState_t;
2862+ pSub: RE_pDFAStateSub_t;
2863+ WChar: WChar_t;
2864+begin
2865+ FRegExpIsSimple := False;
2866+ FSimpleRegExpStr := '';
2867+
2868+ pDFAState := FpInitialState;
2869+
2870+ while pDFAState <> nil do begin
2871+ pSub := pDFAState^.next;
2872+ if pSub = nil then
2873+ break;
2874+ if (pSub^.next <> nil) or
2875+ {複数のキャラクタを受け入れる}
2876+ (pSub^.CharClass.StartChar <> pSub^.CharClass.EndChar) or
2877+ {キャラクタ範囲を持つ}
2878+ (pDFAState^.Accepted and (pSub^.TransitTo <> nil))
2879+ {受理後もキャラクタを受け入れる}then begin
2880+
2881+ FSimpleRegExpStr := '';
2882+ exit;
2883+ end else begin
2884+ WChar := pSub^.CharClass.StartChar;
2885+ FSimpleRegExpStr := FSimpleRegExpStr + WCharToStr(WChar);
2886+ end;
2887+ pDFAState := pSub^.TransitTo;
2888+ end;
2889+ FRegExpIsSimple := True;
2890+end;
2891+
2892+
2893+{$IFDEF DEBUG}
2894+{TStringsオブジェクトに、DFA の内容を書き込む}
2895+procedure TRE_DFA.WriteDFAtoStrings(Strings: TStrings);
2896+var
2897+ i: Integer;
2898+ pDFA_State: RE_pDFAState_t;
2899+ pDFA_StateSub: RE_pDFAStateSub_t;
2900+ Str: String;
2901+begin
2902+ Strings.clear;
2903+ Strings.BeginUpDate;
2904+ for i := 0 to FStateList.Count-1 do begin
2905+ pDFA_State := FStateList.items[i];
2906+ if pDFA_State = FpInitialState then
2907+ Str := Format('開始 %2d : ', [i])
2908+ else if pDFA_State^.Accepted then
2909+ Str := Format('終了 %2d : ', [i])
2910+ else
2911+ Str := Format('状態 %2d : ', [i]);
2912+ pDFA_StateSub := pDFA_State^.next;
2913+ while pDFA_StateSub <> nil do begin
2914+ if pDFA_StateSub^.CharClass.StartChar <> pDFA_StateSub^.CharClass.EndChar then
2915+ Str := Str + Format('文字 %s から 文字%s で 状態 %2d へ :',
2916+ [DebugWCharToStr(pDFA_StateSub^.CharClass.StartChar),
2917+ DebugWCharToStr(pDFA_StateSub^.CharClass.EndChar),
2918+ FStateList.IndexOf(pDFA_StateSub^.TransitTo)])
2919+
2920+ else if pDFA_StateSub^.CharClass.StartChar = FNFA.LHeadWChar then begin
2921+ Str := Str + Format('文頭コード %s で 状態 %2d へ :',
2922+ [DebugWCharToStr(pDFA_StateSub^.CharClass.StartChar),
2923+ FStateList.IndexOf(pDFA_StateSub^.TransitTo)]);
2924+ end else if pDFA_StateSub^.CharClass.StartChar = FNFA.LTailWChar then begin
2925+ Str := Str + Format('文尾コード %s で 状態 %2d へ :',
2926+ [DebugWCharToStr(pDFA_StateSub^.CharClass.StartChar),
2927+ FStateList.IndexOf(pDFA_StateSub^.TransitTo)]);
2928+ end else
2929+ Str := Str + Format('文字 %s で 状態 %2d へ :',
2930+ [DebugWCharToStr(pDFA_StateSub^.CharClass.StartChar),
2931+ FStateList.IndexOf(pDFA_StateSub^.TransitTo)]);
2932+
2933+ pDFA_StateSub := pDFA_StateSub^.Next;
2934+ end;
2935+ Strings.Add(Str);
2936+ end;
2937+ Strings.EndUpDate;
2938+end;
2939+{$ENDIF}
2940+
2941+{ -=================== TRegularExpression Class ==============================-}
2942+constructor TRegularExpression.Create(AOwner: TComponent);
2943+begin
2944+ inherited Create(AOwner);
2945+ FRegExpList := TStringList.Create;
2946+ FRegExpListMax := 30; {RegExpListの項目数設定 30}
2947+ {FCurrentIndex = 0 はヌルの正規表現で常に使えるようにする。}
2948+ FCurrentIndex := FRegExpList.Add('');
2949+ FPreProcessor := TREPreProcessor.Create('');
2950+ Translate(FRegExpList[FCurrentIndex]);
2951+end;
2952+
2953+destructor TRegularExpression.Destroy;
2954+begin
2955+ FPreProcessor.Free;
2956+ DisposeRegExpList;
2957+ inherited Destroy;
2958+end;
2959+
2960+{正規表現リスト(FRegExpList: TStringList)とObjectsプロパティに結び付けられた
2961+ TRE_DFAオブジェクトを破棄}
2962+procedure TRegularExpression.DisposeRegExpList;
2963+var
2964+ i: Integer;
2965+begin
2966+ if FRegExpList <> nil then begin
2967+ with FRegExpList do begin
2968+ for i := 0 to Count-1 do begin
2969+ TRE_DFA(Objects[i]).Free;
2970+ end;
2971+ end;
2972+ FRegExpList.Free;
2973+ FRegExpList := nil;
2974+ end;
2975+end;
2976+
2977+{ ---------------------- プロパティ アクセス メソッド -----------------}
2978+{RegExpプロパティのwriteメソッド}
2979+procedure TRegularExpression.SetRegExp(Str: String);
2980+var
2981+ OrigRegExp: String;
2982+ function FindRegExpInList(RegExpStr: String): Integer;
2983+ var
2984+ i: Integer;
2985+ begin
2986+ result := -1;
2987+ i := 0;
2988+ while i < FRegExpList.Count do begin
2989+ if RegExpStr = FRegExpList[i] then begin
2990+ result := i;
2991+ exit;
2992+ end;
2993+ Inc(i);
2994+ end;
2995+ end;
2996+begin
2997+ OrigRegExp := Str;{プリプロセッサを通る前の正規表現を退避}
2998+ with FPreProcessor do begin
2999+ TargetRegExpStr := Str;
3000+ Run;
3001+ Str := ProcessedRegExpStr;
3002+ end;
3003+
3004+ try
3005+ FCurrentIndex := FindRegExpInList(Str);
3006+ {FRegExpList内にキャッシュされていないときは、コンパイル}
3007+ if FCurrentIndex = -1 then begin
3008+ if FRegExpList.Count = FRegExpListMax then begin
3009+ TRE_DFA(FRegExpList.Objects[FRegExpList.Count-1]).Free;
3010+ FRegExpList.Delete(FRegExpList.Count-1);
3011+ end;
3012+ FRegExpList.Insert(1, Str);
3013+ FCurrentIndex := 1;
3014+ Translate(FRegExpList[1]);
3015+ end;
3016+ FRegExp := OrigRegExp;
3017+ except
3018+ {例外が発生したときは、常にヌル正規表現を設定する。}
3019+ on Exception do begin
3020+ FCurrentIndex := 0;
3021+ FRegExp := '';
3022+ raise;
3023+ end;
3024+ end;
3025+end;
3026+
3027+{RegExpプロパティのreadメソッド}
3028+function TRegularExpression.GetProcessedRegExp: String;
3029+begin
3030+ result := FRegExpList[FCurrentIndex];
3031+end;
3032+
3033+{ListOfFuzzyCharDicプロパティ readメソッド}
3034+function TRegularExpression.GetListOfFuzzyCharDic: TList;
3035+begin
3036+ result := FPreProcessor.ListOfFuzzyCharDic;
3037+end;
3038+
3039+{GetListOfSynonymDicプロパティ readメソッド}
3040+function TRegularExpression.GetListOfSynonymDic: TList;
3041+begin
3042+ result := FPreProcessor.ListOfSynonymDic;
3043+end;
3044+
3045+{RegExpIsSimpleプロパティ readメソッド}
3046+function TRegularExpression.GetRegExpIsSimple: Boolean;
3047+begin
3048+ result := GetCurrentDFA.RegExpIsSimple;
3049+end;
3050+
3051+{SimpleRegExpプロパティ readメソッド}
3052+function TRegularExpression.GetSimpleRegExp: String;
3053+begin
3054+ result := GetCurrentDFA.SimpleRegExpStr;
3055+end;
3056+
3057+{HasLHeadプロパティ readメソッド}
3058+function TRegularExpression.GetHasLHead: Boolean;
3059+begin
3060+ result := GetCurrentDFA.RegExpHasLHead;
3061+end;
3062+
3063+{HasLTailプロパティ writeメソッド}
3064+function TRegularExpression.GetHasLTail: Boolean;
3065+begin
3066+ result := GetCurrentDFA.RegExpHasLTail;
3067+end;
3068+
3069+{現在の正規表現に対応するTRE_DFA型オブジェクトを得る}
3070+function TRegularExpression.GetCurrentDFA: TRE_DFA;
3071+begin
3072+ result := TRE_DFA(FRegExpList.Objects[FCurrentIndex]);
3073+end;
3074+
3075+{DFA状態表の初期状態を表すノードへのポインタを得ることができる。}
3076+function TRegularExpression.GetpInitialDFAState: RE_pDFAState_t;
3077+begin
3078+ result := TRE_DFA(FRegExpList.Objects[FCurrentIndex]).pInitialState;
3079+end;
3080+
3081+function TRegularExpression.GetUseFuzzyCharDic: Boolean;
3082+begin
3083+ result := FPreProcessor.UseFuzzyCharDic;
3084+end;
3085+
3086+procedure TRegularExpression.SetUseFuzzyCharDic(Val: Boolean);
3087+begin
3088+ FPreProcessor.UseFuzzyCharDic := Val;
3089+ Self.RegExp := FRegExp; {新しい設定で再コンパイル}
3090+end;
3091+
3092+function TRegularExpression.GetUseSynonymDic: Boolean;
3093+begin
3094+ result := FPreProcessor.UseSynonymDic;
3095+end;
3096+
3097+procedure TRegularExpression.SetUseSynonymDic(Val: Boolean);
3098+begin
3099+ FPreProcessor.UseSynonymDic := Val;
3100+ Self.RegExp := FRegExp; {新しい設定で再コンパイル}
3101+end;
3102+
3103+function TRegularExpression.GetLineHeadWChar: WChar_t;
3104+begin
3105+ result := CONST_LINEHEAD;
3106+end;
3107+
3108+function TRegularExpression.GetLineTailWChar: WChar_t;
3109+begin
3110+ result := CONST_LINETAIL;
3111+end;
3112+
3113+{***** 正規表現文字列→構文木構造→NFA→DFA の変換を行う *****}
3114+procedure TRegularExpression.Translate(RegExpStr: String);
3115+var
3116+ DFA: TRE_DFA;
3117+ Parser: TREParser;
3118+ NFA: TRE_NFA;
3119+begin
3120+ DFA := nil;
3121+ try
3122+ Parser := TREParser.Create(RegExpStr);
3123+ try
3124+ Parser.Run;
3125+ NFA := TRE_NFA.Create(Parser, GetLineHeadWChar, GetLineTailWChar);
3126+ try
3127+ Self.FLineHeadWChar := NFA.LHeadWChar;
3128+ Self.FLineTailWChar := NFA.LTailWChar;
3129+ NFA.Run;
3130+ DFA := TRE_DFA.Create(NFA);
3131+ FRegExpList.Objects[FCurrentIndex] := DFA;
3132+ TRE_DFA(FRegExpList.Objects[FCurrentIndex]).Run;
3133+ finally
3134+ NFA.Free;
3135+ end;
3136+ finally
3137+ Parser.Free;
3138+ end;
3139+ except
3140+ On Exception do begin
3141+ DFA.Free;
3142+ FRegExpList.Delete(FCurrentIndex);
3143+ FCurrentIndex := 0;
3144+ raise;
3145+ end;
3146+ end;
3147+end;
3148+
3149+{状態 DFAstateから文字cによって遷移して、遷移後の状態を返す。
3150+ 文字cによって遷移出来なければnilを返す}
3151+function TRegularExpression.NextDFAState(DFAState: RE_pDFAState_t; c: WChar_t): RE_pDFAState_t;
3152+var
3153+ pSub: RE_pDFAStateSub_t;
3154+begin
3155+ {1つのDFAStateが持つ pSubのリンクではキャラクタクラスが昇順にならんでいること
3156+ を前提としている。}
3157+ result := nil;
3158+ pSub := DFAState^.next;
3159+ while pSub <> nil do begin
3160+ if c < pSub^.CharClass.StartChar then
3161+ exit
3162+ else if c <= pSub^.CharClass.EndChar then begin
3163+ result := pSub^.TransitTo;
3164+ exit;
3165+ end;
3166+ pSub := pSub^.next;
3167+ end;
3168+end;
3169+
3170+constructor TMatchCORE.Create(AOwner: TComponent);
3171+begin
3172+ inherited Create(AOwner);
3173+ FLineSeparator := mcls_CRLF;
3174+end;
3175+
3176+procedure TMatchCORE.MatchStd(pText: PChar; var pStart, pEnd: PChar);
3177+var
3178+ pDFAState: RE_pDFAState_t;
3179+ pp: PChar;
3180+begin
3181+ pStart := nil;
3182+ pEnd := nil;
3183+
3184+ {pTextがヌル文字列で、正規表現がヌル文字列に一致するとき}
3185+ if (Byte(pText^) = CONST_NULL) and GetCurrentDFA.pInitialState.Accepted then begin
3186+ pStart := pText;
3187+ pEnd := pText;
3188+ exit;
3189+ end;
3190+
3191+ {注目点を1文字づつずらしながらマッチする最左部分を見つける}
3192+ while Byte(pText^) <> CONST_NULL do begin
3193+ {DFAの初期状態を設定}
3194+ pDFAState := Self.GetCurrentDFA.pInitialState;
3195+ pp := pText;
3196+ {DFA状態表に文字を入力していってマッチする最長部分を見つける}
3197+ repeat
3198+ if pDFAState^.accepted then begin
3199+ {終了状態であれば場所を記録しておく。
3200+ 結果としてマッチした最左最長部分が記録される}
3201+ pStart := pText;
3202+ pEnd := pp;
3203+ end;
3204+ {次の状態に遷移}
3205+ pDFAState := NextDFAState(pDFAState, PCharGetWChar(pp));
3206+ until pDFAState = nil;
3207+
3208+ {マッチしたときはExit}
3209+ if pStart <> nil then
3210+ exit;
3211+
3212+ {注目位置を1文字分進める。}
3213+ if IsDBCSLeadByte(Byte(pText^)) then
3214+ Inc(pText, 2)
3215+ else
3216+ Inc(pText);
3217+ end;
3218+ {マッチしない。}
3219+end;
3220+
3221+procedure TMatchCORE.MatchEX(pText: PChar; var pStart, pEnd: PChar);
3222+begin
3223+ pStart := pText;
3224+ pEnd := MatchHead(pText, GetCurrentDFA.pInitialState);
3225+ if pEnd = nil then
3226+ MatchEX_Inside(pText, pStart, pEnd);
3227+end;
3228+
3229+procedure TMatchCORE.MatchEX_Inside(pText: PChar; var pStart, pEnd: PChar);
3230+var
3231+ DFA: TRE_DFA;
3232+ pInitialDFAState: RE_pDFAState_t;
3233+begin
3234+ pStart := nil;
3235+ pEnd := nil;
3236+
3237+ DFA := GetCurrentDFA;
3238+ pInitialDFAState := DFA.pInitialState;
3239+ while Byte(pText^) <> CONST_NULL do begin
3240+ pEnd := MatchInSide(pText, pInitialDFAState);
3241+ if pEnd <> nil then begin
3242+ pStart := pText;
3243+ exit;
3244+ end else if (Byte(pText^) = CONST_LF) and
3245+ DFA.RegExpHasLHead then begin
3246+ pEnd := MatchHead(pText+1, pInitialDFAState);
3247+ if pEnd <> nil then begin
3248+ pStart := pText+1;
3249+ exit;
3250+ end;
3251+ end;
3252+ {注目位置を1文字分進める。}
3253+ if IsDBCSLeadByte(Byte(pText^)) then
3254+ Inc(pText, 2)
3255+ else
3256+ Inc(pText);
3257+ end;
3258+
3259+ if DFA.RegExpHasLTail and (NextDFAState(pInitialDFAState, LineTailWChar) <> nil) then begin
3260+ {正規表現が文尾メタキャラクタのみのとき(RegExp = '$')の特殊処理}
3261+ pStart := pText;
3262+ pEnd := pText;
3263+ end;
3264+ end;
3265+
3266+function TMatchCORE.MatchHead(pText: PChar; pDFAState: RE_pDFAState_t): PChar;
3267+var
3268+ pEnd: PChar;
3269+begin
3270+{正規表現が行頭メタキャラクタを含んでいる}
3271+ if GetCurrentDFA.RegExpHasLHead then begin
3272+ result := MatchInSide(pText, NextDFAState(pDFAState, LineHeadWChar));
3273+ if result <> nil then begin
3274+ {マッチした。この時点で、result <> nil 確定}
3275+ pEnd := result;
3276+ {さらに、RegExp = '(^Love|Love me tender)'で、Text = 'Love me tender. Love me sweet'
3277+ の場合に最左最長でマッチするのは、'Love me tender'でなければならないので、その為の
3278+ マッチ検査を行う。}
3279+ result := MatchInside(pText, pDFAState);
3280+ if (result = nil) or (pEnd > result) then
3281+ result := pEnd;
3282+ end;
3283+ end else begin
3284+{正規表現が行頭メタキャラクタを含んでいない}
3285+ result := MatchInside(pText, pDFAState);
3286+ end;
3287+end;
3288+
3289+function TMatchCORE.MatchInside(pText: PChar; pDFAState: RE_pDFAState_t): PChar;
3290+var
3291+ pEnd: PChar;
3292+ WChar: WChar_t;
3293+ pPrevDFAState: RE_pDFAState_t;
3294+begin
3295+ result := nil;
3296+ pEnd := pText;
3297+
3298+ if pDFAState = nil then
3299+ exit;
3300+ repeat
3301+ if pDFAState^.accepted then begin
3302+ {終了状態であれば場所を記録しておく。
3303+ 結果としてマッチした最左最長部分が記録される}
3304+ result := pEnd;
3305+ end;
3306+ pPrevDFAState := pDFAState;
3307+ {DFAを状態遷移させる}
3308+ WChar := PCharGetWChar(pEnd);
3309+ pDFAState := NextDFAState(pDFAState, WChar);
3310+ until pDFAState = nil;
3311+
3312+ if (IsLineEnd(WChar) or (WChar = CONST_NULL)) and
3313+ (NextDFAState(pPrevDFAState, LineTailWChar) <> nil) then begin
3314+ {行末メタキャラクタを入力して、nil以外が帰ってくるときは必ず、マッチする}
3315+ result := pEnd;
3316+ if WChar <> CONST_NULL then
3317+ Dec(result); {CR($0d)の分 Decrement}
3318+ end;
3319+end;
3320+
3321+function TMatchCORE.IsLineEnd(WChar: WChar_t): Boolean;
3322+begin
3323+ result := False;
3324+ case FLineSeparator of
3325+ mcls_CRLF: result := (WChar = CONST_CR);
3326+ mcls_LF: result := (WChar = CONST_LF);
3327+ end;
3328+end;
3329+
3330+{ -========================== TAWKStr Class ==================================- }
3331+constructor TAWKStr.Create(AOwner: TComponent);
3332+begin
3333+ inherited Create(AOwner);
3334+
3335+ ListOfFuzzyCharDic.Add(RE_FuzzyCharDic); {キャラクタ同一視辞書を追加}
3336+end;
3337+
3338+procedure TAWKStr.SetRegExp(Str: String);
3339+begin
3340+ inherited SetRegExp(Str);
3341+ if not (HasLHead or HasLTail) then begin
3342+ FMatchProc := MatchStd;
3343+ end else begin
3344+ FMatchProc := MatchEx;
3345+ end;
3346+end;
3347+
3348+{文字列中の'\' で 引用されたキャラクタを処理する。 \n, \t \\ ...}
3349+function TAWKStr.ProcessEscSeq(Text: String): String;
3350+var
3351+ WChar: WChar_t;
3352+ Index: Integer;
3353+begin
3354+ result := '';
3355+ Index := 1;
3356+ while Index <= Length(Text) do begin
3357+ WChar := GetWChar(Text, Index);
3358+ if WChar = Ord('\') then
3359+ result := result + WCharToStr(GetQuotedWChar(Text, Index))
3360+ else
3361+ result := result + WCharToStr(WChar);
3362+ end;
3363+end;
3364+
3365+{Sub, GSubメソッドで使用。 '&'をマッチした文字列に置換える}
3366+function TAWKStr.Substitute_MatchStr_For_ANDChar(Text: String; MatchStr: String): String;
3367+var
3368+ i: Integer;
3369+ aStr: String;
3370+ WCh, WCh2: WChar_t;
3371+begin
3372+ i := 1;
3373+ aStr := '';
3374+{'\&'を'\\&'にしてから}
3375+ while i <= Length(Text) do begin
3376+ WCh := GetWChar(Text, i);
3377+ if WCh = CONST_YEN then begin
3378+ aStr := aStr + WCharToStr(WCh);
3379+
3380+ WCh := GetWChar(Text, i);
3381+ if WCh = CONST_ANP then begin
3382+ aStr := Concat(aStr, WCharToStr(CONST_YEN));
3383+ end;
3384+ end;
3385+ aStr := aStr + WCharToStr(WCh);
3386+ end;
3387+
3388+{エスケープ・シーケンスを処理}
3389+ Text := ProcessEscSeq(aStr);
3390+
3391+{'&' を MatchStrで置換え、'\&'を'&'に置換え}
3392+ result := '';
3393+ i := 1;
3394+ while i <= Length(Text) do begin
3395+ WCh := GetWChar(Text, i);
3396+ if WCh = CONST_ANP then
3397+ result := Concat(result, MatchStr)
3398+ else if WCh = CONST_YEN then begin
3399+ WCh2 := GetWChar(Text, i);
3400+ if WCh2 = CONST_ANP then begin
3401+ result := result + WCharToStr(WCh2);
3402+ end else begin
3403+ if WCh2 <> CONST_NULL then
3404+ UnGetWChar(Text, i);
3405+ result := result + WCharToStr(WCh);
3406+ end;
3407+ end else begin
3408+ result := result + WCharToStr(WCh);
3409+ end;
3410+ end;
3411+end;
3412+
3413+function TAWKStr.Match(Text: String; var RStart, RLength: Integer): Integer;
3414+var
3415+ pStart, pEnd: PChar;
3416+begin
3417+ FMatchProc(PChar(Text), pStart, pEnd);
3418+ if pStart = nil then begin
3419+ RStart := 0;
3420+ RLength := 0;
3421+ result := 0;
3422+ end else begin
3423+ RStart := pStart - PChar(Text)+1; {RStartは1ベース}
3424+ RLength := pEnd - pStart;
3425+ result := RStart;
3426+ end;
3427+end;
3428+
3429+{AWK Like function Sub_Raw}
3430+function TAWKStr.Sub(SubText: String; var Text: String): Boolean;
3431+var
3432+ pStart, pEnd: PChar;
3433+ MatchStr: String;
3434+begin
3435+ FMatchProc(PChar(Text), pStart, pEnd);
3436+ if pStart <> nil then begin
3437+{マッチした}
3438+ MatchStr := Copy(Text, pStart-PChar(Text)+1, pEnd-pStart); {マッチした部分}
3439+ Delete(Text, pStart-PChar(Text)+1, pEnd-pStart);
3440+ {SubTextのなかの&キャラクタをマッチした部分(MatchStr)で置換える。}
3441+ SubText := Substitute_MatchStr_For_ANDChar(SubText, MatchStr);
3442+ Insert(SubText, Text, pStart-PChar(Text)+1);
3443+ result := True;
3444+ end else begin
3445+{マッチしない}
3446+ result := False;
3447+ end;
3448+end;
3449+
3450+{AWK Like GSubRaw }
3451+function TAWKStr.GSub(SubText: String; var Text: String): Integer;
3452+var
3453+ pStart, pEnd: PChar;
3454+ ResultText, aText: String;
3455+ MatchStr: String;
3456+ WChar: WChar_t;
3457+begin
3458+ ResultText := ''; {結果の文字列を入れる変数}
3459+ aText := Text; {検索対象として使う}
3460+ result := 0;
3461+ FMatchProc(PChar(aText), pStart, pEnd);
3462+ if pStart = nil then
3463+ exit {何もマッチしない}
3464+ else if aText = '' then begin
3465+ result := 1; {マッチしたが Text=''}
3466+ Text := Substitute_MatchStr_For_ANDChar(SubText, '');
3467+ exit;
3468+ end;
3469+
3470+ {マッチして Text <> ''}
3471+ while True do begin
3472+ ResultText := Concat(ResultText, Copy(aText, 1, pStart-PChar(aText)));{前半部分}
3473+ MatchStr := Copy(aText, pStart-PChar(aText)+1, pEnd-pStart); {マッチした部分文字列}
3474+ MatchStr := Substitute_MatchStr_For_ANDChar(SubText, MatchStr);
3475+ ResultText := Concat(ResultText, MatchStr);{+ 置換文字列}
3476+
3477+ if pStart = pEnd then begin {空文字列にマッチしたときの特殊処理}
3478+ if isDBCSLeadByte(Byte(pStart^)) or
3479+ ((LineSeparator = mcls_CRLF) and (Byte(pStart^) = CONST_CR)) then begin
3480+ ResultText := Concat(ResultText, Copy(aText, pStart-PChar(aText)+1, 2));
3481+ Inc(pEnd, 2);
3482+ end else begin
3483+ ResultText := Concat(ResultText, Copy(aText, pStart-PChar(aText)+1, 1));
3484+ if Byte(pEnd^) <> CONST_NULL then
3485+ Inc(pEnd, 1);
3486+ end;
3487+ end;
3488+ Inc(result);
3489+
3490+ WChar := Byte((pEnd-1)^);
3491+ {Chr($0a)を調べる為だけなので、2バイト文字の考慮不要。 aText = ''はありえない}
3492+ aText := String(pEnd);
3493+ {マッチした部分文字列の後の部分をaTextに設定}
3494+ if aText = '' then
3495+ break;
3496+ if WChar = CONST_LF then begin
3497+ FMatchProc(PChar(aText), pStart, pEnd);
3498+ if pStart = nil then
3499+ break;
3500+ end else begin
3501+ MatchEX_Inside(PChar(aText), pStart, pEnd);
3502+ if pStart = nil then
3503+ break;
3504+ end;
3505+ end;
3506+ Text := Concat(ResultText, aText);
3507+end;
3508+
3509+function TAWKStr.Split(Text: String; StrList: TStrings): Integer;
3510+var
3511+ pStart, pEnd: PChar;
3512+ Str: String;
3513+begin
3514+ StrList.Clear;{結果文字列リストの内容クリア}
3515+ Str := '';
3516+ while Text <> '' do begin
3517+ FMatchProc(PChar(Text), pStart, pEnd);
3518+ if pStart = nil then begin
3519+ {マッチしなかったとき}
3520+ StrList.Add(Concat(Str, Text));
3521+ Str := '';
3522+ break;
3523+ end else if (pStart = PChar(Text)) and (pStart = pEnd) then begin
3524+ {先頭のヌル文字列にマッチしたときの特殊処理}
3525+ if IsDBCSLeadByte(Byte(Text[1])) then begin
3526+ Str := Concat(Str, Copy(Text, 1, 2));
3527+ Text := Copy(Text, 3, Length(Text));
3528+ end else begin
3529+ Str := Concat(Str, Text[1]);
3530+ Text := Copy(Text, 2, Length(Text));
3531+ end;
3532+ end else begin;
3533+ {マッチした}
3534+ StrList.Add(Concat(Str, Copy(Text, 1, pStart-PChar(Text))));
3535+ Str := '';
3536+ Text := String(pEnd);
3537+ if Text = '' then begin
3538+ {最後尾にマッチしたときの特殊処理}
3539+ StrList.Add('');
3540+ break;
3541+ end;
3542+ end;
3543+ end;
3544+ if Str <> '' then
3545+ StrList.Add(Str);
3546+ result := StrList.Count;
3547+end;
3548+
3549+{ -=========================== TTxtFile Class ================================-}
3550+constructor TTxtFile.Create(aFileName: String; var CancelRequest: Boolean);
3551+begin
3552+ inherited Create;
3553+ FpCancelRequest := @CancelRequest; {CancelRequestがTrueで途中終了する}
3554+
3555+ FBuffSize := 1024*100; {バッファのサイズ}
3556+ FTailMargin := 100;
3557+
3558+ FFileName := aFileName;
3559+ System.FileMode := 0; {ファイルアクセスモード を読み出し専用に設定}
3560+ AssignFile(FF, FFileName);
3561+ try
3562+ Reset(FF, 1);
3563+ except
3564+ on E: EInOutError do begin
3565+ raise EFileNotFound.Create(E.Message);
3566+ end;
3567+ end;
3568+ FFileOpened := True; { ファイルオープンのフラグ。Destroyで使用する}
3569+ FpBuff := AllocMem(FBuffSize+FTailMargin);
3570+ FpBuff^ := Chr($0a); { ファイル先頭行の行頭にLF Chr($0a)を付加}
3571+ BuffRead(FpBuff+1);
3572+ Inc(FReadCount); { 先頭のLF($0a)のぶんを加算}
3573+ FpBase := FpBuff;
3574+ FpLineBegin := FpBuff;
3575+ FpForward := FpBuff;
3576+ FLineNo := 0;
3577+end;
3578+
3579+destructor TTxtFile.Destroy;
3580+begin
3581+ if FFileOpened then
3582+ CloseFile(FF);
3583+
3584+ if FpBuff <> nil then begin
3585+ FreeMem(FpBuff, FBuffSize+FTailMargin);
3586+ end;
3587+
3588+ inherited Destroy;
3589+end;
3590+
3591+procedure TTxtFile.BuffRead(pBuff: PChar);
3592+begin
3593+ BlockRead(FF, pBuff^, FBuffSize, FReadCount);
3594+ if FReadCount = 0 then begin
3595+ {FpLineBegin := FpBase;}
3596+ raise EEndOfFile.Create('End Of File');
3597+ end;
3598+
3599+ {読み込んだデータの最後にヌル・キャラクタを書き込む}
3600+ if not Eof(FF) then begin
3601+ (pBuff+FReadCount)^ := Chr(0);
3602+ end else begin
3603+ if (pBuff+FReadCount-1)^ <> Chr($0a) then begin
3604+ (pBuff+FReadCount)^ := Chr($0a);
3605+ (pBuff+FREadCount+1)^ := Chr(0);
3606+ (pBuff+FReadCount+2)^ := Chr(0);
3607+ Inc(FReadCount);
3608+ end else begin
3609+ (pBuff+FReadCount)^ := Chr(0);
3610+ (pBuff+FreadCount+1)^ := Char(0);
3611+ end;
3612+ end;
3613+
3614+ Application.ProcessMessages;
3615+ if FpCancelRequest^ then
3616+ raise EGrepCancel.Create('CancelRequest');
3617+end;
3618+
3619+procedure TTxtFile.IncPBaseNullChar(Ch: Char);
3620+var
3621+ Distance: Integer;
3622+begin
3623+ if FpBase = (PChar(FBrokenLine)+Length(FBrokenLine)) then begin
3624+ {FBrokenLine(String型) の中でChr(0)に達したとき。}
3625+ FpBase := FpBuff;
3626+ end else begin
3627+ {FpBuff(PChar) バッファの中でChr(0)に達したとき。}
3628+ if FpBase < FpBuff+FReadCount then begin
3629+ {ファイル中の不正なヌルキャラクタ Chr(0)は、Space($20)に補正}
3630+ FpBase^ := Chr($20);
3631+ end else begin
3632+ {バッファの終わりに来た}
3633+ if Eof(FF) then begin
3634+ {ファイルの終わりに来た}
3635+ if Ch = Chr(0) then
3636+ Dec(FpBase);
3637+ raise EEndOfFile.Create('End Of File');
3638+ end else begin
3639+ {ファイルをまだ読める}
3640+ if (FpLineBegin >= PChar(FBrokenLine)) and
3641+ (FpLineBegin < (PChar(FBrokenLine)+Length(FBrokenLine))) then begin
3642+ {FpLineBeginがFBrokenLineの中を指している。}
3643+ Distance := FpLineBegin-PChar(FBrokenLine);
3644+ FBrokenLine := Concat(FBrokenLine, String(FpBuff));
3645+ FpLineBegin := PChar(FBrokenLine)+Distance;
3646+ BuffRead(FpBuff);
3647+ FpBase := FpBuff;
3648+ end else begin
3649+ {FpLineBeginがバッファ中を指しているのでそこからFBrokenLineを取る}
3650+ FBrokenLine := String(FpLineBegin);
3651+ BuffRead(FpBuff);
3652+ FpBase := FpBuff;
3653+ FpLineBegin := PChar(FBrokenLine);
3654+ end;
3655+ end;
3656+ end;
3657+ end;
3658+end;
3659+
3660+{機能: FpBaseをインクリメントして、次の1バイトを指すようにする。}
3661+function TTxtFile.IncPBase: Char;
3662+var
3663+ ApBase: PChar;
3664+begin
3665+ result := FpBase^;
3666+ Inc(FpBase);
3667+ if FpBase^ = Chr(0) then
3668+ {ヌル・キャラクタの処理}
3669+ IncPBaseNullChar(result);
3670+ if result = Chr($0a) then begin
3671+ {改行処理}
3672+ if (FpBase < PChar(FBrokenLine)) or (FpBase > (PChar(FBrokenLine) +
3673+ Length(FBrokenLine))) then begin
3674+ {FpBaseがバッファを指しているとき}
3675+ FBrokenLine := '';
3676+ FpLineBegin := FpBase;
3677+ Inc(FLineNo);
3678+ end else begin
3679+ {FpBaseがFBrokenLine中を指しているとき}
3680+ FpLineBegin := FpBase;
3681+ Inc(FLineNo);
3682+ end;
3683+ end;
3684+ if FpBase^ = Chr($0d) then begin
3685+ ApBase := FpBase;
3686+ Inc(FpBase);
3687+ if FpBase^ = Chr(0) then
3688+ {ヌル・キャラクタの処理}
3689+ IncPBaseNullChar(result);
3690+ if FpBase^ <> Chr($0a) then begin
3691+ { CR($0d)の次がLF($0a)でないときは、$0dを$0aに置換する。}
3692+ if FpBase = FpBuff then
3693+ FpBase := PChar(FBrokenLine)+Length(FBrokenLine)-1
3694+ else
3695+ FpBase := ApBase;
3696+ FpBase^ := Chr($0a);
3697+ end
3698+ end;
3699+ FpForward := FpBase;
3700+end;
3701+
3702+function TTxtFile.AdvanceBase: WChar_t;
3703+var
3704+ ApBase: PChar;
3705+ Ch: Char;
3706+begin
3707+ {↓高速化のためIncPBase埋め込み}
3708+ Ch := FpBase^;
3709+ Inc(FpBase);
3710+ if FpBase^ = Chr(0) then
3711+ {ヌル・キャラクタの処理}
3712+ IncPBaseNullChar(Ch);
3713+ if Ch = Chr($0a) then begin
3714+ {改行処理}
3715+ if (FpBase < PChar(FBrokenLine)) or (FpBase > (PChar(FBrokenLine) +
3716+ Length(FBrokenLine))) then begin
3717+ {FpBaseがバッファを指しているとき}
3718+ FBrokenLine := '';
3719+ FpLineBegin := FpBase;
3720+ Inc(FLineNo);
3721+ end else begin
3722+ {FpBaseがFBrokenLine中を指しているとき}
3723+ FpLineBegin := FpBase;
3724+ Inc(FLineNo);
3725+ end;
3726+ end;
3727+ if FpBase^ = Chr($0d) then begin
3728+ ApBase := FpBase;
3729+ Inc(FpBase);
3730+ if FpBase^ = Chr(0) then
3731+ {ヌル・キャラクタの処理}
3732+ IncPBaseNullChar(ApBase^);
3733+ if FpBase^ <> Chr($0a) then begin
3734+ { CR($0d)の次がLF($0a)でないときは、$0dを$0aに置換する。}
3735+ if FpBase = FpBuff then
3736+ FpBase := PChar(FBrokenLine)+Length(FBrokenLine)-1
3737+ else
3738+ FpBase := ApBase;
3739+ FpBase^ := Chr($0a);
3740+ end
3741+ end;
3742+ {↑高速化のためIncPBase埋め込み}
3743+ result := Byte(Ch);
3744+ case result of
3745+ $81..$9F, $E0..$FC: begin
3746+ {↓高速化のためIncPBase埋め込み}
3747+ Ch := FpBase^;
3748+ Inc(FpBase);
3749+ if FpBase^ = Chr(0) then
3750+ {ヌル・キャラクタの処理}
3751+ IncPBaseNullChar(Ch);
3752+ if Ch = Chr($0a) then begin
3753+ {改行処理}
3754+ if (FpBase < PChar(FBrokenLine)) or (FpBase > (PChar(FBrokenLine) +
3755+ Length(FBrokenLine))) then begin
3756+ {FpBaseがバッファを指しているとき}
3757+ FBrokenLine := '';
3758+ FpLineBegin := FpBase;
3759+ Inc(FLineNo);
3760+ end else begin
3761+ {FpBaseがFBrokenLine中を指しているとき}
3762+ FpLineBegin := FpBase;
3763+ Inc(FLineNo);
3764+ end;
3765+ end;
3766+ if FpBase^ = Chr($0d) then begin
3767+ ApBase := FpBase;
3768+ Inc(FpBase);
3769+ if FpBase^ = Chr(0) then
3770+ {ヌル・キャラクタの処理}
3771+ IncPBaseNullChar(ApBase^);
3772+ if FpBase^ <> Chr($0a) then begin
3773+ { CR($0d)の次がLF($0a)でないときは、$0dを$0aに置換する。}
3774+ if FpBase = FpBuff then
3775+ FpBase := PChar(FBrokenLine)+Length(FBrokenLine)-1
3776+ else
3777+ FpBase := ApBase;
3778+ FpBase^ := Chr($0a);
3779+ end
3780+ end;
3781+ {↑高速化のためIncPBase埋め込み}
3782+ result := (result shl 8) or Byte(Ch);
3783+ end;
3784+ end;
3785+ FpForward := FpBase;
3786+end;
3787+
3788+procedure TTxtFile.GetCharNullChar(Ch: Char);
3789+var
3790+ Distance, Distance2: Integer;
3791+begin
3792+ if FpForward = (PChar(FBrokenLine)+Length(FBrokenLine)) then begin
3793+ {FBrokenLine(String型) の中でChr(0)に達したとき。}
3794+ FpForward := FpBuff;
3795+ end else begin
3796+ {FpBuff バッファの中でChr(0)に達したとき。}
3797+ if FpForward < FpBuff+FReadCount then begin
3798+ {ファイル中の不正なヌルキャラクタ Chr(0) は Space($20)にする。}
3799+ FpForward^ := Chr($20);
3800+ end else begin
3801+ {バッファの終わりに来た}
3802+ if Eof(FF) then begin
3803+ {すでにファイルの終わりに達しているとき}
3804+ if Ch = Chr(0) then
3805+ Dec(FpForward); {ずnっとresut = Chr(0)を返すようにする}
3806+ exit;
3807+ end else begin
3808+ {まだファイルを読めるとき}
3809+ if (FpLineBegin >= PChar(FBrokenLine)) and
3810+ (FpLineBegin < PChar(FBrokenLine)+Length(FBrokenLine)) then begin
3811+ {FpLineBeginがFBrokenLine中を指しているとき}
3812+ Distance := FpLineBegin-PChar(FBrokenLine);
3813+ if (FpBase >= PChar(FBrokenLine)) and
3814+ (FpBase < PChar(FBrokenLine)+Length(FBrokenLine)) then
3815+ {FpBaseもFBrokenLine中を指しているとき}
3816+ Distance2 := FpBase-PChar(FBrokenLine)
3817+ else
3818+ {FpBaseはバッファ中を指しているとき}
3819+ Distance2 := Length(FBrokenLine)+FpBase-FpBuff;
3820+ FBrokenLine := Concat(FBrokenLine, String(FpBuff));
3821+ FpLineBegin := PChar(FBrokenLine)+Distance;
3822+ FpBase := PChar(FBrokenLine)+Distance2;
3823+ BuffRead(FpBuff);
3824+ FpForward := FpBuff;
3825+ end else begin
3826+ {FpLineBeginがバッファ中を指しているとき}
3827+ FBrokenLine := String(FpLineBegin);
3828+ FpBase := PChar(FBrokenLine)+(FpBase-FpLineBegin);
3829+ FpLineBegin := PChar(FBrokenLine);
3830+ BuffRead(FpBuff);
3831+ FpForward := FpBuff;
3832+ end;
3833+ end;
3834+ end;
3835+ end;
3836+end;
3837+
3838+function TTxtFile.GetChar: Char;
3839+var
3840+ ApForward: PChar;
3841+begin
3842+ ApForward := FpForward;
3843+ result := FpForward^;
3844+ Inc(FpForward);
3845+ {ヌル・キャラクタの処理}
3846+ if FpForward^ = Chr(0) then
3847+ GetCharNullChar(result);
3848+
3849+ if result = Chr($0d) then begin
3850+ if FpForward^ <> Chr($0a) then begin
3851+ {CR($0d)の次がLF($0a)でないときは、$0dを$0aに置換する。}
3852+ if FpForward = FpBuff then
3853+ FpForward := PChar(FBrokenLine)+Length(FBrokenLine)-1
3854+ else
3855+ FpForward := ApForward;
3856+ FpForward^ := Chr($0a);
3857+ result := Chr($0a);
3858+ end else begin
3859+ result := FpForward^;
3860+ Inc(FpForward);
3861+ {ヌル・キャラクタの処理}
3862+ if FpForward^ = Chr(0) then
3863+ GetCharNullChar(result);
3864+ end;
3865+ end;
3866+end;
3867+
3868+function TTxtFile.GetWChar: WChar_t;
3869+var
3870+ ApForward: PChar;
3871+ Ch: Char;
3872+begin
3873+ ApForward := FpForward;
3874+ Ch := FpForward^;
3875+ Inc(FpForward);
3876+ {ヌル・キャラクタの処理}
3877+ if FpForward^ = Chr(0) then
3878+ GetCharNullChar(Ch);
3879+
3880+ if Ch = Chr($0d) then begin
3881+ if FpForward^ <> Chr($0a) then begin
3882+ {CR($0d)の次がLF($0a)でないときは、$0dを$0aに置換する。}
3883+ if FpForward = FpBuff then
3884+ FpForward := PChar(FBrokenLine)+Length(FBrokenLine)-1
3885+ else
3886+ FpForward := ApForward;
3887+ FpForward^ := Chr($0a);
3888+ Ch := Chr($0a);
3889+ end else begin
3890+ Ch := FpForward^;
3891+ Inc(FpForward);
3892+ {ヌル・キャラクタの処理}
3893+ if FpForward^ = Chr(0) then
3894+ GetCharNullChar(Ch);
3895+ end;
3896+ end;
3897+ result := Byte(Ch);
3898+ case result of
3899+ $81..$9F, $E0..$FC: begin
3900+ Ch := FpForward^;
3901+ Inc(FpForward);
3902+ {ヌル・キャラクタの処理}
3903+ if FpForward^ = Chr(0) then
3904+ GetCharNullChar(Ch);
3905+ result := (result shl 8) or Byte(Ch);
3906+ end;
3907+ end;
3908+end;
3909+
3910+function TTxtFile.GetThisLine: RE_LineInfo_t;
3911+var
3912+ i: Integer;
3913+begin
3914+ Application.ProcessMessages;
3915+ if FpCancelRequest^ then
3916+ raise EGrepCancel.Create('CancelRequest');
3917+
3918+ {行末を見つける。}
3919+ while FpBase^ <> Chr($0a) do begin
3920+ IncPBase;
3921+ end;
3922+
3923+ if (FpLineBegin >= PChar(FBrokenLine)) and
3924+ (FpLineBegin < PChar(FBrokenLine)+Length(FBrokenLine)) then begin
3925+ {FpLineBeginがFBrokenLine中を指しているとき}
3926+ if (FpBase >= PChar(FBrokenLine)) and
3927+ (FpBase < PChar(FBrokenLine)+Length(FBrokenLine)) then begin
3928+ {FpBaseもFBrokenLine中を指しているとき}
3929+ result.Line := Copy(FBrokenLine, FpLineBegin-PChar(FBrokenLine)+1,
3930+ FpBase-FpLineBegin);
3931+ end else begin
3932+ {FpBaseはバッファ中を指しているとき}
3933+ SetString(result.Line, FpBuff, FpBase-FpBuff);
3934+ result.Line := Concat(Copy(FBrokenLine, FpLineBegin-PChar(FBrokenLine)+1,
3935+ Length(FBrokenLine)), result.Line);
3936+ end;
3937+ end else begin
3938+ SetString(result.Line, FpLineBegin, FpBase-FpLineBegin);
3939+ end;
3940+
3941+ {TrimRight}
3942+ i := Length(result.Line);
3943+ while (i > 0) and (result.Line[i] in [Chr($0d), Chr($0a)]) do Dec(I);
3944+ result.Line := Copy(result.Line, 1, i);
3945+
3946+ result.LineNo := FLineNo;
3947+end;
3948+
3949+function StringToWordArray(Str: String; pWCharArray: PWordArray): Integer;
3950+var
3951+ i, j: Integer;
3952+ WChar: WChar_t;
3953+begin
3954+ i := 1;
3955+ j := 0;
3956+ WChar := GetWChar(Str, i);
3957+ while WChar <> 0 do begin
3958+ pWCharArray^[j] := WChar;
3959+ Inc(j);
3960+ WChar := GetWChar(Str, i);
3961+ end;
3962+ pWCharArray^[j] := 0;
3963+ result := j;
3964+end;
3965+
3966+constructor TGrep.Create(AOwner: TComponent);
3967+begin
3968+ inherited Create(AOwner);
3969+
3970+ ListOfFuzzyCharDic.Add(RE_FuzzyCharDic); {キャラクタ同一視辞書を追加}
3971+end;
3972+
3973+procedure TGrep.SetRegExp(Str: String);
3974+begin
3975+ inherited SetRegExp(Str);
3976+ if Self.RegExpIsSimple then
3977+ FGrepProc := GrepByStr
3978+ else
3979+ FGrepProc := GrepByRegExp;
3980+end;
3981+
3982+function TGrep.GetLineHeadWChar: WChar_t;
3983+begin
3984+ result := CONST_LF;
3985+end;
3986+
3987+function TGrep.GetLineTailWChar: WChar_t;
3988+begin
3989+ result := CONST_LF;
3990+end;
3991+
3992+procedure TGrep.GrepByRegExp(FileName: String);
3993+var
3994+ TxtFile: TTxtFile;
3995+ pDFAState,pInitialDFAState: RE_pDFAState_t;
3996+ LineInfo: RE_LineInfo_t;
3997+ DFA: TRE_DFA;
3998+ WChar: WChar_t;
3999+ pSub: RE_pDFAStateSub_t;
4000+begin
4001+ {OnMatchイベントハンドラが設定されていないときは、何もしない}
4002+ if not Assigned(FOnMatch) then
4003+ exit;
4004+
4005+ FCancel := False;
4006+ DFA := GetCurrentDFA;
4007+ pInitialDFAState := DFA.pInitialState;
4008+ try
4009+ TxtFile := TTxtFile.Create(FileName, Self.FCancel);
4010+ except on EEndOfFile do exit; {ファイルサイズ0のときはexit} end;
4011+
4012+ try
4013+ try
4014+ {検索}
4015+ while True do begin
4016+ repeat
4017+ WChar := TxtFile.AdvanceBase;
4018+ {↓NextDFAStateメソッド埋め込み}
4019+ pDFAState := nil;
4020+ pSub := pInitialDFAState^.next;
4021+ while pSub <> nil do begin
4022+ if WChar < pSub^.CharClass.StartChar then
4023+ break
4024+ else if WChar <= pSub^.CharClass.EndChar then begin
4025+ pDFAState := pSub^.TransitTo;
4026+ break;
4027+ end;
4028+ pSub := pSub^.next;
4029+ end;
4030+ {↑NextDFAStateメソッド埋め込み}
4031+ until pDFAState <> nil;
4032+
4033+ while True do begin
4034+ if pDFAState^.accepted then begin
4035+ {マッチした}
4036+ LineInfo := TxtFile.GetThisLine;
4037+ FOnMatch(Self, LineInfo);
4038+ break;
4039+ end;
4040+
4041+ {DFAを状態遷移させる}
4042+ pDFAState := NextDFAState(pDFAState, TxtFile.GetWChar);
4043+ if pDFAState = nil then begin
4044+ break;
4045+ end;
4046+ end;
4047+ end;
4048+ finally TxtFile.Free; end;
4049+ except on EEndOfFile do ; end; {Catch EEndOfFile}
4050+end;
4051+
4052+procedure TGrep.GrepByStr(FileName: String);
4053+var
4054+ TxtFile: TTxtFile;
4055+ Pattern: String;
4056+ pPat: PWordArray;
4057+ PatLen: Integer;
4058+ i: Integer;
4059+ LineInfo: RE_LineInfo_t;
4060+begin
4061+ FCancel := False;
4062+ Pattern := Self.SimpleRegExp;
4063+ {OnMatchイベントハンドラが設定されていないときは、何もしない}
4064+ if not Assigned(FOnMatch) then
4065+ exit;
4066+
4067+ try
4068+ TxtFile := TTxtFile.Create(FileName, Self.FCancel);
4069+ except on EEndOfFile do exit; {ファイルサイズ0のときはexit} end;
4070+
4071+ try
4072+ pPat := AllocMem(Length(Pattern)*2+2);
4073+ try
4074+ PatLen := StringToWordArray(Pattern, pPat);
4075+ try
4076+ while True do begin
4077+ while (TxtFile.AdvanceBase <> Word(pPat^[0])) do
4078+ ;
4079+ i := 1;
4080+ while True do begin
4081+ if i = PatLen then begin
4082+ LineInfo := TxtFile.GetThisLine;
4083+ FOnMatch(Self, LineInfo);
4084+ break;
4085+ end;
4086+ if TxtFile.GetWChar = Word(pPat^[i]) then
4087+ Inc(i)
4088+ else
4089+ break;
4090+ end;
4091+ end;
4092+ except on EEndOfFile do ;{Catch EEndOfFile} end;
4093+ finally FreeMem(pPat, Length(Pattern)*2+2); end;
4094+ finally TxtFile.Free; end;
4095+end;
4096+
4097+procedure MakeFuzzyCharDic;
4098+var
4099+ StrList: TStrings;
4100+ i: Integer;
4101+begin
4102+ RE_FuzzyCharDic := nil;
4103+ RE_FuzzyCharDic := TList.Create;
4104+
4105+ i := 0;
4106+ repeat
4107+ StrList := TStringList.Create;
4108+ try
4109+ RE_FuzzyCharDic.Add(StrList);
4110+ except
4111+ on Exception do begin
4112+ StrList.Free;
4113+ raise;
4114+ end;
4115+ end;
4116+
4117+ StrList.CommaText := REFuzzyWChars[i];
4118+ Inc(i);
4119+ until i > High(REFuzzyWChars);
4120+end;
4121+
4122+procedure DestroyFuzzyCharDic;
4123+var
4124+ i: Integer;
4125+begin
4126+ for i := 0 to RE_FuzzyCharDic.Count-1 do
4127+ TStringList(RE_FuzzyCharDic[i]).Free;
4128+ RE_FuzzyCharDic.Free;
4129+end;
4130+
4131+procedure Register;
4132+begin
4133+ RegisterComponents('RegExp', [TGrep, TAWKStr]);
4134+end;
4135+
4136+initialization
4137+ MakeFuzzyCharDic;
4138+
4139+finalization
4140+ DestroyFuzzyCharDic;
4141+
4142+end.
Binary files a/gikoNavi.res and b/gikoNavi.res differ
Show on old repository browser