これはMoritz Lenz氏のWebサイトPerlgeek.deで公開されているブログ記事"Perl 5 to 6" Lesson 19 - Regexes strike backの日本語訳です。
原文はCreative Commons Attribution 3.0 Germanyに基づいて公開されています。
本エントリにはCreative Commons Attribution 3.0 Unportedを適用します。
Original text: Copyright© 2008-2010 Moritz Lenz
Japanese translation: Copyright© 2011 SATOH Koichi
NAME
"Perl 5 to 6" Lesson 19 - 正規表現の逆襲
SYNOPSIS
# 通常のマッチング:
if 'abc' ~~ m/../ {
say $/; # ab
}
# 暗黙的な:sigspace修飾子を使ったマッチング
if 'ab cd ef' ~~ mm/ (..) ** 2 / {
say $1; # cd
}
# :sigspace修飾子を使った文字列置換
my $x = "abc defg";
$x ~~ ss/c d/x y/;
say $x; # abx yefg
DESCRIPTION
正規表現の基本はLesson 07で既に述べたので、役に立つ(が体系立っていない)内容をいくらか補足します。
マッチング
正規表現マッチングのためにグラマーを書かないといけない訳ではなく、昔ながらのm/.../
はまだ動きます。
新しい兄弟分としてmm/.../
形式があり、:sigspace
修飾子を暗黙的に有効にします。これは正規表現中の空白を<.ws>
ルールで置換するものだったことを思い出して下さい。
このルールのデフォルトは、2つの単語文字で囲まれている(つまりそれぞれが\w
にマッチする)場合は\s+
にマッチし、さもなくば\s*
にマッチします。
文字列置換において:samespace
修飾子は<ws>
ルールにマッチした空白を保存します。
同様に:samecase
及び短縮形の:ii
(:i
の親戚だからです)修飾子は大文字小文字の違いを保存します。
my $x = 'Abcd';
$x ~~ s:ii/^../foo/;
say $x; # Foocd
$x = 'ABC'
$x ~~ s:ii/^../foo/;
say $x # FOO
これはモジュールFoo
をすべてBar
に改名したいけれど、例えば環境変数がすべて大文字で書かれているときに非常に便利です。:ii
修飾子が大文字小文字の違いを自動で保存してくれます。
これは大文字か小文字かという情報を文字毎にコピーしますが、より頭の良いバージョンもあります; :sigspace
(短縮形は:s
)修飾子と一緒に使うと、大文字小文字のパターンをソース文字列から探します。
.lc
、.uc
、.lc.ucfirst
、uc.lcfirst
、lc.capitalize
(Str.capitalize
は各単語の最初の文字を大文字にするメソッド)が認識されます。このようなパターンを見つけると置換後の文字列にも適用します。
my $x = 'The Quick Brown Fox';
$x ~~ s :s :ii /brown.*/perl 6 developer/;
# $xは'The Quick Perl 6 Developer'
選択肢
選択肢は相変わらず|
で表現されますが、Perl5とは違った意味になります。
選択肢を順番にマッチングしていって最初にマッチしたものを取るのではなく、全選択肢を並列にマッチングして最長一致のものを取ります。
'aaaa' ~~ m/ a | aaa | aa /;
say $/ # aaa
つまらない変更に見えるかも知れませんがこれは広範囲に影響し、また拡張性のあるグラマーにとって極めて重要です。
Perl6はグラマーを使って解析されるので、++$a
の++
が2つのprefix:<+>
トークンではなく1つのトークンとして解析されるのはこのためです。
順番にマッチングする古い方式は||
で利用できます:
grammar Math::Expression {
token value {
| <number>
| '('
<expression>
[ ')' || { fail("Parenthesis not closed") } ]
}
...
}
{ ... }
はクロージャを実行し、その中でのfail
の呼び出しは式を失敗させます。
この枝は前のマッチング(ここでは')')が失敗したときだけ呼び出されることが保証されているので、解析中に役立つエラーメッセージを出すのに使えます。
選択肢を書く方法は他にもあり、例えば配列を「展開」すると要素の選択肢としてマッチさせることができます:
$_ = '12 oranges';
my @fruits = <apple organge banana kiwi>;
if m:i:s/ (\d+) (@fruits)s? / {
say "You've got $0 $1, I've got { $0 + 2 } of them. You lost.";
}
自動的に最長一致にマッチする選択肢の構築方法がもう1つあります: 多重正規表現です。
これはmulti token name
のように書かれるか、proto
を使って次のように書かれます:
grammar Perl {
...
proto token sigil { ... }
token sigil:sym<$> { <sym> }
token sigil:sym<@> { <sym> }
token sigil:sym<%> { <sym> }
...
token variable { <sigil> <twigil>? <identifier> }
}
この例はsigil
という多重トークンを表しており、これはsym
でパラメータ化されています。
短い名前、つまりsigil
が使われた場合は全トークンを選択肢としてマッチします。
選択肢を書くのにこれは非常に面倒くさいと思うかも知れませんが、これには'$'|'@'|'%'
と書くのに比べてとても大きな利点があります: 多重正規表現は容易に拡張可能です。
grammar AddASigil is Perl {
token sigil:sym<!> { <sym> }
}
# わあ、新しいシジルを持ったPerl6グラマーができたぞ!
さらに、既存の選択肢を上書きすることもできます:
grammar WeirdSigil is Perl {
token sigil:sym<$> { '°' }
}
このグラマーではスカラーのシジルは°
なので、このグラマーはシジルを探すときは毎回$
の代わりに°
を探索しますが、コンパイラにはそれにマッチした正規表現がsigil:sym<$>
であることが分かります。
次のレッスンでは実際の、Rakudoで動作するグラマーをお見せしましょう。
コメント
コメントを投稿