スキップしてメイン コンテンツに移動

Perl 5 to 6 - 正規表現(またの名をルール)

これはMoritz Lenz氏のWebサイトPerlgeek.deで公開されているブログ記事"Perl 5 to 6" Lesson 07 - Regexes (also called "rules")の日本語訳です。

原文は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 07 - 正規表現(またの名をルール)

SYNOPSIS

grammar URL {
    token TOP {
        <schema> '://' 
        [<ip> | <hostname> ]
        [ ':' <port>]?
        '/' <path>?
    }
    token byte {
        (\d**{1..3}) <?{ $0 < 256 }>
    }
    token ip {
        <byte> [\. <byte> ] ** 3
    }
    token schema {
        \w+
    }
    token hostname {
        (\w+) ( \. \w+ )*
    }
    token port {
        \d+
    }
    token path {
        <[ a..z A..Z 0..9 \-_.!~*'():@&=+$,/ ]>+
    }
}

my $match = URL.parse('http://perl6.org/documentation/');
say $match<hostname>;       # perl6.org

DESCRIPTION

正規表現(Regex)はPerl6で一番改良のあった領域です。Perl5でそうであったほどに正規ではないので、もはやRegular expressionとは呼ばれません。

訳注: タイトルにある通り「ルール」とも呼ばれるようになりました。この記事でもルールと書いてある部分があります。

大きく3つの変更点と改良点があります。

整理された構文
書き易さを向上させる多くの細かい変更がなされました。例えば.はすべての文字にマッチするようになり、今までの意味論(改行以外全部)は\Nで提供されるようになりました。 修飾子は正規表現の頭に付くようになり、キャプチャしないグループは(?:...)より書き易い[...]になりました。
入れ子のキャプチャとマッチオブジェクト
Perl5では(a(b))(c)のような正規表現はマッチ成功時にはab$1b$2c$3にセットしていました。これは変更され、$0(列挙は0から始まります)はab$0[0]$/[0][0]b$1cを保持するようになりました。 マッチ変数はすべて$/経由でもアクセスできます。これはマッチオブジェクトとも呼ばれ、完全なマッチの木を格納しています。
名前付き正規表現とグラマー
サブルーチンやメソッドのように、正規表現に名前を付けて宣言できます。ルール中で他のルールを<name>のように参照できます。 複数の正規表現をグラマーの中に置くことができます。グラマーはクラスのように継承や合成をサポートしています。

これらの変更がルールをPerl5より書き易く、メンテナンスし易いものにしています。

変更点は極めて多岐に渡るので、ここではその上っ面を擦る程度しか紹介できません。

整理された構文

レター文字(アンダースコア、数字とすべてのUnicode letter)はそれ自身にマッチし、バックスラッシュでエスケープされた時は特別の(メタ構文的)意味を持ちます。 それ以外の文字の場合は逆になります——これらはエスケープされないときにメタ構文的な役割を持ちます。

字句通り         メタ構文的
a  b  1  2      \a \b \1 \2
\* \: \. \?     *  :  .  ? 

メタ構文的トークンすべてに意味があるわけではありません(今のところは)。未定義の意味を使うのは不正です。

文字列を正規表現中でエスケープする方法がもう1つあります: クォートすることです。

m/'a literal text: $#@!!'/

.の意味論が変更されたことと、[...]がキャプチャしないグループになったことは既に述べました。 文字クラスは<[...]>、否定形の文字クラスは<-[...]>です。^$はいつでも文字列の先頭と末尾にマッチします。行の先頭や末尾にマッチさせるには^^$$を使って下さい。

これは修飾子/s/mがなくなったということです。修飾子は正規表現の頭に付くようになり、ペアとして書かれます。

if "abc" ~~ m:i/B/ {
    say "Match";
}

修飾子は短い形式と長い形式があります。昔の/x修飾子はデフォルトになりました。つまり、空白は無視されます。

短い形式 長い形式         意味
--------------------------------------------------------------
:i      :ignorecase     大文字小文字の違いを無視する(かつての/i)
:m      :ignoremark     記号を無視する(アクセント記号、分音記号など)
:g      :global         可能な限り繰り返しマッチする(/g)
:s      :sigspace       正規表現中の空白が(省略可能な)空白にマッチする
:P5     :Perl5          Perl5互換の構文に戻す
:4x     :x(4)           4回マッチする(他の数字でも同様)
:3rd    :nth(3)         3番目のマッチ
:ov     :overlap        :gと似ているが、範囲がオーバーラップしたマッチも考慮する
:ex     :exhaustive     マッチ可能性をすべて尽くす
        :ratchet        バックトラックしない

:sigspaceにはもう少し説明が必要です。これはパターン中のすべての空白を<.ws>(ルールwsを呼び出し、結果を保存しません)に置換します。このルールはオーバーライドできます。デフォルトではワード文字列で囲まれている場合は1個以上の空白にマッチし、それ以外の位置では0個以上の空白にマッチします。

(他にも新しい修飾子はありますが、ここに挙げたものよりは重要ではないでしょう)

マッチオブジェクト

すべてのマッチはマッチオブジェクトと呼ばれるものを生成し、特殊変数$/に格納します。 これにはいろいろな使い方ができます。真理値コンテキストではマッチ成功時にはBool::Trueを返します。文字列コンテキストではマッチした文字列を返し、リストとして使われればキャプチャのリストを返します。ハッシュとして使われると名前付きキャプチャを返します。 .fromメソッドと.toメソッドはマッチした先頭と末尾の位置を返します。

if 'abcdefg' ~~ m/(.(.)) (e | bla ) $<foo> = (.) / {
    say $/[0][0];           # d
    say $/[0];              # cd
    say $/[1];              # e
    say $/<foo>             # f
}

$0$1などは$/[0]$/[1]などの単なる別名です。同様に$/<x>$/{'x'}$<x>という別名を持ちます。

$/[...]$/{...}でアクセスできるものもまた、マッチオブジェクト(あるいはそのリスト)であることに留意して下さい。 これによってルールの完全な解析木を作ることができます。

名前付き正規表現とグラマー

ルールは旧来のm/.../で使ったり、サブルーチンやメソッドのように宣言することができます。

regex a { ... }
token b { ... }
rule  c { ... }

これらの違いは、token:ratchet修飾子が有効になり(バックトラックしなくなる。Perl5で正規表現の各部を(?>...)で囲むようなもの)、rule:ratchet:sigspaceが有効になることです。 このようなルール(どのキーワードで宣言したかに関係なくルールと呼びます)を呼び出すには、その名前を角カッコで囲みます: <a>。これはサブルールを文字列の現在からマッチさせ、結果を$/<a>に格納します。つまりこれは名前付きキャプチャです。 結果をキャプチャすることなくルールを呼び出すには、名前の先頭にドットを付けます: <.a>

グラマーはルールの寄せ集めで、クラスに似ています(例えばSYNOPSISを見て下さい)。グラマーは継承したり、ルールをオーバーライドしたりできます。

grammar URL::HTTP is URL {
    token schema { 'http' }
}

MOTIVATION

Perl5の正規表現は解読不能になることがよくありますが、グラマーは巨大な正規表現を小さな読み易い断片に分割することを促進します。 名前付きキャプチャはルールを自己文書化し、多くのものが以前より一貫性ある形になりました。

最後に、グラマーはPerl6を含むほとんどすべてのプログラミング言語を構文解析できるくらい強力です。 このことがPerl6の構文をPerl5よりメンテナンスし易く、変更し易いものにしています(訳注: Perl6の構文はグラマーを使って定義されている)。Perl5では構文解析器はCで書かれており、構文解析時に変更できませんでした。

SEE ALSO

http://perlcabal.org/syn/S05.html

コメント

このブログの人気の投稿

多分週刊チラシの裏 (Oct 19, 2020 - Feb 26, 2021)

週刊とは言ったが毎週刊とは言ってないという言い訳。 C++ のコンパイルを高速化する小技 ビルドシステムやツールを変更せずともコーディングだけで改善できるコンパイル時間短縮テクニック。 #include を減らす インライン化を明示的に避ける 関数オーバーロードの可視性を制限する 公開シンボルを減らす の 4 本。 歯医者で歯を治したら記憶能力を失った話 歯医者で簡単な治療を受けた日から後、記憶が 90 分しか保持できなくなった英国の軍人の話。まるで「博士の愛した数式」だが実話である。 DRPK で売られていた Sim City っぽいゲームのリバースエンジニアリング 平壌市内のアプリストア (物理) で売られていた Sim City 風ゲームがインストールに失敗してライセンス認証で止まってしまったのでなんとか動かせないものかとリバースエンジニアリングしてみた話。 日本にあっては DPRK のデジタル事情というと 3G セルラーが現役とか国内 Web サイトのリストがポスター一枚に収まるとか何故かコンピュータ将棋の古豪とかの断片的な情報が伝え聞かれる程度だが、近頃は Android タブレットでゲームなどもできるらしい。 国内のインフラ及びエコシステム事情に合わせて元々フリーミアム + アプリ内課金モデルだったものが買い切り 5,000 KPW (< 1 USD) になっているなど、我々が失った自由が我々よりも不自由な (はずだと我々が信じている) 国に残存しているのは皮肉だろうか。 typosquatting は単なる typo じゃ済まない typo を狙って人気のあるドメインやソフトウェアに類似した名前をつける手法 (typosquatting) は人を辟易させるのみならずセキュリティの脅威である。 IQT が 2017 年から 2020 年にかけて Python ライブラリの中央リポジトリである PyPI において行った調査で、メジャーなライブラリに名前を似せたマルウェアが 40 個確認されたとのこと。 その内 16 個が単純なスペルミス狙い (e.g., “urlib3” vs. “urllib3”) で、26 個は正当なパッケージと混同するような名前 (e.g., “nmap-python” vs. “pytho...

多分週刊チラシの裏 (Sep 28 - Oct 04, 2020)

Chrome Web Store が有料 Chrome 拡張の取扱を終了 Chrome Web Store で提供されている有料 Chrome 拡張及びアプリ内課金 API の両方が 2021 年 1 月いっぱいで廃止される。 開発者はそれまでに代替となるサードパーティの課金 API に移行し、購入済ライセンスの移行手段も用意する必要がある。 この決定の発表時点で新規の有料ないしアプリ内課金のある Chrome 拡張の新規登録は終了している。実際のところ 2020 年 3 月時点で既に「一時的に」停止されており、その措置が恒久化されただけとの由。 シェルスクリプティングには長いオプションを使え 「短いオプション (e.g., -x ) はコマンドライン上での略記である。スクリプトにおいては自分や将来の同僚のためにも長いオプション (e.g., ---do-something ) を与える方が理解が容易だろう」という主張。 異論の余地なく正論である。 CobWeb - COBOL to WebAssembly Compiler COBOL から WebAssembly へのコンパイラ。いやマジで。 Cloudflare が何を思ったか同社のサーバレス環境である Workers に COBOL 対応を追加した際 の成果物である。 COBOL から C へのトランスレータである GNU COBOL と C コードをコンパイルして WebAssembly を出力する Emscripten から成っており、他の言語に比べて軽量なバイナリを生成するとのこと。 「ウチではそんな風にはやらないんだ (“We don’t do that here”)」 昨今ソフトウェア開発のコミュニティでも Code of Conduct を用意するところが増えてきたが、コミュニティの文化を明文化するのは難しい。 長大な「べからず集」は息苦しいし、肯定的なガイドラインは時に抽象的で実効的に使えない。問題となるようなふるまいの動機が善意であった場合は特にそうだ。 仮に優れたガイドラインがあっても、それに基いて人を実際に咎めるのは骨が折れることである。初中やればコミュニティ内でも疎まれる。 話の分かる相手ならそれでもまだ説得する意義もあるが、Web 上の対話で当事者双方が納得し合っ...

Perl 5 to 6 - コンテキスト

2011-02-27: コメント欄で既に改訂された仕様の指摘がありました ので一部補足しました。 id:uasi に感謝します。 これはMoritz Lenz氏のWebサイト Perlgeek.de で公開されているブログ記事 "Perl 5 to 6" Lesson 06 - Contexts の日本語訳です。 原文は 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 06 - コンテキスト SYNOPSIS my @a = <a b c> my $x = @a; say $x[2]; # c say (~2).WHAT # Str() say +@a; # 3 if @a < 10 { say "short array"; } DESCRIPTION 次のように書いたとき、 $x = @a Perl5では $x は @a より少ない情報—— @a の要素数だけ——しか持ちません。 すべての情報を保存しておくためには明示的にリファレンスを取る必要があります: $x = \@a Perl6ではこれらは反対になります: デフォルトでは何も失うことなく、スカラ変数は配列を単に格納します。 これは一般要素コンテキスト(Perl5で scalar と呼ばれていたもの)及びより特化された数値、整数、文字列コンテキストの導入によって可能となりました。無効コンテキストとリストコンテキストは変更されていません。 特別な構文でコンテキストを強制できます。 構文 コンテキスト ~stuff 文字列 ?stuff 真理値 +stuff ...

Mac から iPhone のカメラを起動して写真を直接取り込める

Via: The Verge ID セルフィーや (物理) 書籍のページスキャンなど携帯電話のカメラを使って写真を取り込むことは日常的な所作になっているが、写真の使い途が何かの申し込み用 Web フォームなどで iPhone より Mac の方が操作し易いときなどは億劫だ。Mac 組込の FaceTime カメラは 720p とか 1080p しかなくて非力すぎ、かといって iPhone で一旦撮影したものを Photos から探して AirDrop するのも面倒である。 実は macOS Mojave / iOS 12 以降には Continuity Camera という機能がある。これを使うと Apple 製の Mac アプリケーションから iPhone / iPad のカメラを起動して、余計な中間コピーを残すことなく写真を Mac に転送できる。 使い方は簡単で、対応している Mac アプリケーションのコンテキストメニューに “Import (or Insert) from iPhone (or iPad)” という項目がある。“Take Photo” だと一枚、“Scan Documents” だと複数の写真を (歪み補正しつつ) 連続で撮影して転送できる。 対応 Mac アプリケーションは Finder のほか iWork (Keynote, Numbers, Pages), Mail, Messages, Notes, TextEdit となっている、のだが実は Preview でも使える。同様にコンテキストメニューあるいは “File” メニューから起動できる。

開発環境の構築に asdf が便利なので anyenv から移行した

プロジェクト毎に異なるバージョンの言語処理系やツールを管理するために、pyenv や nodenv など *env の利用はほとんど必須となっている。 これらはほとんど一貫したコマンド体系を提供しており、同じ要領で様々な環境構築ができる非常に便利なソフトウェアだが、それを使うことで別の問題が出てくる: *env 自身の管理である。 無数の *env をインストールし、シェルを設定し、場合によりプラグインを導入し、アップデートに追従するのは非常に面倒な作業だ。 幸いなことにこれをワンストップで解決してくれるソリューションとして anyenv がある。これは各種 *env のパッケージマネージャというべきもので、一度 anyenv をインストールすれば複数の *env を簡単にインストールして利用できる。さらに anyenv-update プラグインを導入すればアップデートまでコマンド一発で完了する。素晴らしい。 そういうわけでもう長いこと anyenv を使ってきた。それで十分だった。 ——のだが、 ここにもう一つ、対抗馬となるツールがある。 asdf である。anyenv に対する asdf の優位性は大きく2つある: 一貫性と多様性だ。 一貫性 “Manage multiple runtime versions with a single CLI tool” という触れ込み通り、asdf は様々な言語やツールの管理について一貫したインタフェースを提供している。対して anyenv は *env をインストールするのみで、各 *env はそれぞれ個別のインタフェースを持っている。 基本的なコマンド体系は元祖である rbenv から大きく外れないにしても、例えば jenv のように単体で処理系を導入する機能を持たないものもある。それらの差異はユーザが把握し対応する必要がある。 多様性 asdf はプラグインシステムを持っている。というより asdf 本体はインタフェースを規定するだけで、環境構築の実務はすべてプラグイン任せである。 そのプラグインの数は本稿を書いている時点でおよそ 300 を数える。これは言語処理系ばかりでなく jq などのユーティリティや MySQL のようなミドルウェアも含むが、いずれにしても膨大なツールが asdf を使えば...