正規表現(regular expression)は、 多くの現代的プログラミング言語で見られる、 文字列のパターン・マッチング・アルゴリズムです。
あなたは require regexp.fs
を使用して正規表現機能を Gforth に追加できます。
このパターン・マッチングの古典的な実装はバックトラッキング・アルゴリズムです。 これは、 後方参照などの機能を使用する場合にも必要です。 Gforth
は、 パターン・マッチングのためのバックトラッキング・プログラムを定義する言語(language)を提供することで正規表現を実装します。
基本要素は制御構造 FORK
… JOIN
です。 これはワード内での前方呼び出しであるため、
軽量の試行錯誤制御構造(try and fail control structure)をコーディングできます。
FORK
( compilation – orig ; run-time f – ) gforth-0.7 “FORK”
AHEAD のような制御構造: JOIN の後にコードを呼び出します。
JOIN
( orig – ) gforth-0.7 “JOIN”
FORK のための、 THEN のような制御構造
あなたは、 チェックが失敗した場合に、 フラグと ?LEAVE
を計算することで、 任意の種類のチェックを自分でプログラムできます。
正規表現コードは ((
と ))
で囲まれています。
((
( addr u – ) regexp-pattern “((”
正規表現ブロックの開始
))
( – flag ) regexp-pattern “))”
正規表現ブロックの終了
正規表現のパターン・マッチングには要素として文字セットがあるため、 多くの機能を使用して文字クラス(charclass
と呼ばれる)を作成・変更できます。 ここでの文字はすべてバイトであるため、 ユニコード文字用には拡張されません。
charclass
( – ) regexp-cg “charclass”
文字クラスを作成します
+char
( char – ) regexp-cg “+char”
現在の文字クラスに char を追加します
-char
( char – ) regexp-cg “-char”
現在の文字クラスから char を削除します
..char
( start end – ) regexp-cg “..char”
char の範囲(start 〜 end)を現在の文字クラスに追加します
+chars
( addr u – ) regexp-cg “+chars”
charの文字列(string)を現在の文字クラスに追加します
+class
( class – ) regexp-cg “+class”
文字クラス class と現在の文字クラスの結合(union)
-class
( class – ) regexp-cg “-class”
現在の文字クラスから、 文字クラス class を抜き取ります(subtract)。
事前定義された文字クラス(charclasses)とそれらのテストと、 一般的なチェックがあります。 チェックが失敗した場合は、 次に考えられる正規表現の代替が試行されるか、 または、 ループが終了(leave)します(訳注: 基本的には、 チェックが成功した場合は addr を 1文字(char)分進めた addr’ を返し、 失敗した場合はスタックに何も積まずに leave する)。
c?
( addr class – addr’ ) regexp-pattern “c?”
文字クラス class にマッチ(addr の位置の char が文字クラス class に含まれているなら
addr の 1 char 次のアドレスを addr‘ に返し、 含まれていないなら leave
する)
-c?
( addr class – addr’ ) regexp-pattern “-c?”
文字クラス class 以外にマッチ(addr の位置の文字が文字クラス class に含まれていなければ
addr の1文字後ろのアドレスを addr’ に返し、含まれていたら leave
する)
\d
( addr – addr’ ) regexp-pattern “\d”
数字([0-9])にマッチ(指定の addr の char が digit かどうか([0-9])しらべ、 そうなら addr
の char 分次のアドレスを addr’ に返す。 そうでなければ leave
する)
\s
( addr – addr’ ) regexp-pattern “\s”
非表示文字(\x00〜\x20)にマッチ(指定の addr の char が blanks かどうか([\x00-\x20])しらべ、
そうなら addr の char 分次のアドレスを addr’ に返す。 そうでなければ leave
する)
.?
( addr – addr’ ) regexp-pattern “.?”
任意の1文字(char)にマッチ(任意の1文字(char)であれば次の文字のアドレスを addr’ に返します。 そうでなければ leave します)
-\d
( addr – addr’ ) regexp-pattern “-\d”
addr の文字が数字で無ければ([^0-9])次の文字のアドレスを addr’ に返します。 そうでなければ leave します。
-\s
( addr – addr’ ) regexp-pattern “-\s”
指定の addr の char が blanks で無いかどうか([^\x00-\x20])しらべ、 そうなら addr の
char 分次のアドレスを addr’ に返す。 そうでなければ leave
する
`
( addr "char" – addr’ ) regexp-pattern “‘”
指定の文字をチェックします。 ` c
の形で使用し、 addr の文字が 文字 c ならば、 次の文字へのアドレスを
addr’ に返し、そうでなければ leave します。
`?
( addr "char" – addr | addr’ ) regexp-pattern “‘?” \ 訳注:`? c
addr の文字が c ならば addr をそのまま返す。 c でなければ次の文字へのアドレスを addr’ に返す。
-`
( addr "char" – addr’ ) regexp-pattern “-‘” \ 訳注:-` c
addr の文字が c でなければ次の文字へのアドレスを addr’ に返す。 そうでなければ leave する。
指定の文字をチェックします。 ` c
の形で使用し、 addr の文字が 文字 c ならば、 次の文字へのアドレスを
addr’ に返し、そうでなければ leave します。
明示的に文字列の開始と終了、および文字列定数全体をチェックすることもできます。
\^
( addr – addr ) regexp-pattern “\^”
文字列の開始にマッチ
\$
( addr – addr ) regexp-pattern “\$”
文字列の終了にマッチ
str=?
( addr1 addr u – addr2 ) regexp-pattern “str=?”
指定の文字列 addr u にマッチ(等しい)するかどうかチェックします(マッチすれば文字列の次のアドレスを addr2 に返します。 マッチしない場合は leave します)
doc-=” (原文未記述)
繰り返される文字セットをチェックするループは、 貪欲(greedy)または非貪欲(non-greedy)にすることができます。
{**
( addr – addr addr ) regexp-pattern “begin-greedy-star”
貪欲な 0 個以上のパターンの開始
**}
( sys – ) regexp-pattern “end-greedy-star”
貪欲な 0 個以上のパターンの終わり
{++
( addr – addr addr ) regexp-pattern “begin-greedy-plus”
貪欲な 1 つ以上のパターンの開始
++}
( sys – ) regexp-pattern “end-greedy-plus”
貪欲な 1 つ以上のパターンの終わり
{*
( addr – addr addr ) regexp-pattern “begin-non-greedy-star”
非貪欲な 0 個以上のパターンの開始
*}
( addr addr’ – addr’ ) regexp-pattern “end-non-greedy-star”
非貪欲な 0 個以上のパターンの終わり
{+
( addr – addr addr ) regexp-pattern “begin-non-greedy-plus”
非貪欲な 1 つ以上のパターンの開始
+}
( addr addr’ – addr’ ) regexp-pattern “end-non-greedy-plus”
非貪欲な 1 つ以上のパターンの終わり
例: 部分文字列の検索は、 実際にはその前にあるものとの非貪欲な一致です。
//
( – ) regexp-pattern “//”
文字列内を検索(後続のパターンを文字列先頭ではなく文字列中でのマッチにする)(訳注:
: ([0-9]) ( addr u – ) (( \(
\d \) )) IF ." [" \1 type ." ]" ELSE ." fail" THEN ;
"0123" ([0-9]) [0]
ok
"A123" ([0-9]) fail ok
: (//[0-9]) ( addr u – ) (( // \( \d \) ))
IF ." [" \1 type ." ]" ELSE ." fail" THEN ;.
"0123" (//[0-9]) [0]
ok
"A123" (//[0-9]) [1] ok
"ABC3" (//[0-9]) [3] ok)
選択肢(alternative)は以下のように書きます。
{{
( addr – addr addr ) regexp-pattern “begin-alternatives”
選択肢(alternatives)の開始
||
( addr addr – addr addr ) regexp-pattern “next-alternative”
選択肢の区切り文字
}}
( addr addr – addr ) regexp-pattern “end-alternatives”
選択肢の終了
\1
~ \9
という名前の変数を最大 9 つ使用して、 一致した部分文字列を参照できます
\(
( addr – addr ) regexp-pattern “\(”
マッチング変数の開始;変数は \\1〜9 として参照されます
\)
( addr – addr ) regexp-pattern “\)”
マッチング変数の終わり
\0
( – addr u ) regexp-pattern “\0”
文字列全体
もちろん、 あなたは、 見つけたパターンを置換するコードを作成することもできます。
s>>
( addr – addr ) regexp-replace “s>>”
検索/置換 の置換パターン領域の開始
>>
( addr – addr ) regexp-replace “>>”
任意の置換コードを開始し、 コードはスタック上の文字列を評価し、 それを <<
に渡します。
<<
( run-addr addr u – run-addr ) regexp-replace “<<”
置換パターン領域の先頭からを、 指定の文字列 addr u に置換します
<<"
( "string<">" – ) regexp-replace “<<"”
置換パターン領域の先頭から文字列を string に置換します(訳注: 使い方: <<" string
)
s//
( addr u – ptr ) regexp-replace “s//”
検索/置換 ループの開始
//s
( ptr – ) regexp-replace “//s”
検索の終わり
//o
( ptr addr u – addr’ u’ ) regexp-replace “//o”
検索/置換 の単一ループの終了
//g
( ptr addr u – addr’ u’ ) regexp-replace “//g”
検索/置換 の全てのループの終了
これらの使用例は test/regexp-test.fs
にあります。