「現代」正規表現の認識力
-
27-10-2019 - |
質問
言語のどのクラスの本当の近代的な正規表現が実際に認識していますか?
バック参照して無限長捕捉基が存在するときはいつでも(例えば(.*)_\1
)正規表現は、現在、非正規言語と一致しています。しかし、これは、自分自身で、S ::= '(' S ')' | ε
のようなものと一致するだけでは十分ではない - 。括弧のマッチングペアの文脈自由言語を
(私に新しいですが、私はPerlとPCREで保証が存在しています)再帰的正規表現は、少なくとも最もCFLのを認識するように見える。
誰もが行われたり、この領域内の任意の研究を読んでいますか?これらの「現代」正規表現の限界は何ですか?彼らはLLまたはLR文法の、厳密により厳密以下のCFGよりも認識していますか?または正規表現によって認識することができますが、両方の言語が存在しないCFG との反対?
関連論文へのリンクがはるかに高く評価されるだろう。
解決
パターン再帰
再帰的なパターンでは、のマッチングの再帰下降の形を持っています。
これは、さまざまな問題のための罰金ですが、あなたが実際に再帰下降をしたいたら、の構文解析の、あなたはここにあるキャプチャグループを挿入する必要があり、完全な解析構造を回復するために厄介ですこの方法では。ダミアン・コンウェイの正規表現::文法のモジュールのためにPerlは自動的に解析された構造の方がはるかに簡単検索のために作り、再帰的なデータ構造に取り込むという名前のすべてのことを行い同等のものに単純なパターンを変換します。私は、この投稿の最後にこの2つの方法を比較したサンプルを持っています。
再帰の制限を
問題は、再帰的なパターンが一致することができ文法の種類をということでした。まあ、彼らは確かに再帰下降する型のmatcherです。頭に浮かぶ唯一のことは、を再帰的なパターンは、左再帰を扱うことができないということです。あなたがそれらを適用することができ文法の種類に強い>このプット制約を。時には、あなたは左再帰を除去するために、自分の作品を並べ替えることができます。
ところで、PCREとPerlはあなたが再帰句を許可している方法に若干異なります。 「再帰的パターン」とのpcrepattern のマニュアルページの「Perlのからの再帰差」のセクションを参照してください。例えば:PCREはなく^(.|(.)(?1)\2)$
を必要とする場合、Perlは^((.)(?1)\2|.)$
を扱うことができます。
再帰デモ
再帰的なパターンの必要性は驚くほど頻繁に発生します。 1つのよく訪れた例あなたは、このようなバランスの取れた括弧、引用符、あるいはHTML / XMLタグとして缶巣、という何かを一致させる必要がある場合です。ここでbalenced括弧の一致だ。
\((?:[^()]*+|(?0))*\)
私は、そのコンパクトな性質のトリッキーが読みことがわかります。これは空白もはや重要作る/x
モードで容易に硬化可能である。
\( (?: [^()] *+ | (?0) )* \)
我々は再帰を括弧を使用しているので、その後、再び、より明確な例では、ネストされた単一引用符に一致することになるます:
‘ (?: [^‘’] *+ | (?0) )* ’
あなたが一致することを望むかもしれないもう一つの再帰的に定義された事が回文になります。この単純なパターンは、Perlで動作します:
^((.)(?1)\2|.?)$
あなたはこのようなものを使用してほとんどのシステム上でテストすることができます。
$ perl -nle 'print if /^((.)(?1)\2|.?)$/i' /usr/share/dict/words
再帰のPCREの実装は、より多くの
手の込んだ必要とすることに注意^(?:((.)(?1)\2|)|((.)(?3)\4|.))
このためにはどのようにPCREの再帰作品の制限である。
適切な解析
私には、上記の例では、実際に、ほとんどのおもちゃの一致ではなく、すべてののことの興味深いものです。それが面白いとなった場合、あなたが解析しようとしている本当の文法を持っている場合です。例えば、RFC 5322はむしろ精巧メールアドレスを定義します。ここでは、それに合わせて、「文法」のパターンがあります:
$rfc5322 = qr{
(?(DEFINE)
(?<address> (?&mailbox) | (?&group))
(?<mailbox> (?&name_addr) | (?&addr_spec))
(?<name_addr> (?&display_name)? (?&angle_addr))
(?<angle_addr> (?&CFWS)? < (?&addr_spec) > (?&CFWS)?)
(?<group> (?&display_name) : (?:(?&mailbox_list) | (?&CFWS))? ; (?&CFWS)?)
(?<display_name> (?&phrase))
(?<mailbox_list> (?&mailbox) (?: , (?&mailbox))*)
(?<addr_spec> (?&local_part) \@ (?&domain))
(?<local_part> (?&dot_atom) | (?"ed_string))
(?<domain> (?&dot_atom) | (?&domain_literal))
(?<domain_literal> (?&CFWS)? \[ (?: (?&FWS)? (?&dcontent))* (?&FWS)?
\] (?&CFWS)?)
(?<dcontent> (?&dtext) | (?"ed_pair))
(?<dtext> (?&NO_WS_CTL) | [\x21-\x5a\x5e-\x7e])
(?<atext> (?&ALPHA) | (?&DIGIT) | [!#\$%&'*+-/=?^_`{|}~])
(?<atom> (?&CFWS)? (?&atext)+ (?&CFWS)?)
(?<dot_atom> (?&CFWS)? (?&dot_atom_text) (?&CFWS)?)
(?<dot_atom_text> (?&atext)+ (?: \. (?&atext)+)*)
(?<text> [\x01-\x09\x0b\x0c\x0e-\x7f])
(?<quoted_pair> \\ (?&text))
(?<qtext> (?&NO_WS_CTL) | [\x21\x23-\x5b\x5d-\x7e])
(?<qcontent> (?&qtext) | (?"ed_pair))
(?<quoted_string> (?&CFWS)? (?&DQUOTE) (?:(?&FWS)? (?&qcontent))*
(?&FWS)? (?&DQUOTE) (?&CFWS)?)
(?<word> (?&atom) | (?"ed_string))
(?<phrase> (?&word)+)
# Folding white space
(?<FWS> (?: (?&WSP)* (?&CRLF))? (?&WSP)+)
(?<ctext> (?&NO_WS_CTL) | [\x21-\x27\x2a-\x5b\x5d-\x7e])
(?<ccontent> (?&ctext) | (?"ed_pair) | (?&comment))
(?<comment> \( (?: (?&FWS)? (?&ccontent))* (?&FWS)? \) )
(?<CFWS> (?: (?&FWS)? (?&comment))*
(?: (?:(?&FWS)? (?&comment)) | (?&FWS)))
# No whitespace control
(?<NO_WS_CTL> [\x01-\x08\x0b\x0c\x0e-\x1f\x7f])
(?<ALPHA> [A-Za-z])
(?<DIGIT> [0-9])
(?<CRLF> \x0d \x0a)
(?<DQUOTE> ")
(?<WSP> [\x20\x09])
)
(?&address)
}x;
ご覧の通り、非常にBNFのようです。問題はそれだけで一致し、ないのキャプチャです。そして、あなたは本当にそれがどの部分生産がマッチしているあなたを教えてくれないので、ちょうどキャプチャ括弧で全体を囲むようにしたくありません。前述の正規表現を使用して::文法モジュールは、我々はできます。
#!/usr/bin/env perl
use strict;
use warnings;
use 5.010;
use Data::Dumper "Dumper";
my $rfc5322 = do {
use Regexp::Grammars; # ...the magic is lexically scoped
qr{
# Keep the big stick handy, just in case...
# <debug:on>
# Match this...
<address>
# As defined by these...
<token: address> <mailbox> | <group>
<token: mailbox> <name_addr> | <addr_spec>
<token: name_addr> <display_name>? <angle_addr>
<token: angle_addr> <CFWS>? \< <addr_spec> \> <CFWS>?
<token: group> <display_name> : (?:<mailbox_list> | <CFWS>)? ; <CFWS>?
<token: display_name> <phrase>
<token: mailbox_list> <[mailbox]> ** (,)
<token: addr_spec> <local_part> \@ <domain>
<token: local_part> <dot_atom> | <quoted_string>
<token: domain> <dot_atom> | <domain_literal>
<token: domain_literal> <CFWS>? \[ (?: <FWS>? <[dcontent]>)* <FWS>?
<token: dcontent> <dtext> | <quoted_pair>
<token: dtext> <.NO_WS_CTL> | [\x21-\x5a\x5e-\x7e]
<token: atext> <.ALPHA> | <.DIGIT> | [!#\$%&'*+-/=?^_`{|}~]
<token: atom> <.CFWS>? <.atext>+ <.CFWS>?
<token: dot_atom> <.CFWS>? <.dot_atom_text> <.CFWS>?
<token: dot_atom_text> <.atext>+ (?: \. <.atext>+)*
<token: text> [\x01-\x09\x0b\x0c\x0e-\x7f]
<token: quoted_pair> \\ <.text>
<token: qtext> <.NO_WS_CTL> | [\x21\x23-\x5b\x5d-\x7e]
<token: qcontent> <.qtext> | <.quoted_pair>
<token: quoted_string> <.CFWS>? <.DQUOTE> (?:<.FWS>? <.qcontent>)*
<.FWS>? <.DQUOTE> <.CFWS>?
<token: word> <.atom> | <.quoted_string>
<token: phrase> <.word>+
# Folding white space
<token: FWS> (?: <.WSP>* <.CRLF>)? <.WSP>+
<token: ctext> <.NO_WS_CTL> | [\x21-\x27\x2a-\x5b\x5d-\x7e]
<token: ccontent> <.ctext> | <.quoted_pair> | <.comment>
<token: comment> \( (?: <.FWS>? <.ccontent>)* <.FWS>? \)
<token: CFWS> (?: <.FWS>? <.comment>)*
(?: (?:<.FWS>? <.comment>) | <.FWS>)
# No whitespace control
<token: NO_WS_CTL> [\x01-\x08\x0b\x0c\x0e-\x1f\x7f]
<token: ALPHA> [A-Za-z]
<token: DIGIT> [0-9]
<token: CRLF> \x0d \x0a
<token: DQUOTE> "
<token: WSP> [\x20\x09]
}x;
};
while (my $input = <>) {
if ($input =~ $rfc5322) {
say Dumper \%/; # ...the parse tree of any successful match
# appears in this punctuation variable
}
}
ご覧のとおり、パターンに非常にわずかに異なる表記を使用することにより、あなたは今、きちんとラベルされたすべてのもので、%/
変数であなたのために離れて全体の構文解析ツリーを保存する何かを得ます。あなたは=~
オペレータが見ることができるように変換の結果は、まだパターンです。それはちょうどビットの魔法です。