Perlの正規表現の限界を突破する

Perlerなら誰しも一度はお世話になったことのあるであろう、大崎氏のPerlメモというサイトがあります。

ここで紹介されているHTMLタグの正規表現正規表現の叡智が詰め込まれたすばらしいものですが、Perlではこれである特定の文字列系に対してマッチを行うと、うまく動きません。具体的には、Perl 5.8系ではSEGVし、5.10以上だと警告が出た上で、本来成功するはずのマッチに失敗します。もっとも、これはこの正規表現に問題があるのではなく、Perl正規表現エンジンの限界です。これは、バックトラックCレベルの関数の再帰で実装しているためです。
例:

#!perl -w
# complex_re.pl
use strict;
my $tag_regex_ = q{[^"'<>]*(?:"[^"]*"[^"'<>]*|'[^']*'[^"'<>]*)*(?:>|(?=<)|$(?!\n))}; #'}}}}
my $comment_tag_regex =
    '<!(?:--[^-]*-(?:[^-]+-)*?-(?:[^>-]*(?:-[^>-]+)*?)??)*(?:>|$(?!\n)|--.*$)';
my $tag_regex = qq{$comment_tag_regex|<$tag_regex_};

for my $n(1 .. 64) {
    my $m = 2 ** $n;
    print $m, "\n";
    my $html = sprintf '<meta %s/>', "foo='bar' " x $m;
    $html =~ /$tag_regex/ or die "not ok\n";
}

結果:

$ perl complex_re.pl
2
4
8
16
32
64
128
256
512
1024
2048
4096
8192
16384
32768
Complex regular subexpression recursion limit (32766) exceeded at complex_re.pl line 13.
not ok

Ruby 1.9 (正規表現エンジンは鬼車 / oniguruma)ではこの問題は起こらないようです。

#!ruby -w
# Ruby 1.9.2 is OK.
# 注意!!!実行するとメモリを食いつぶすので適当に^Cで殺すこと!!!

tag_regex_ = %q{[^"'<>]*(?:"[^"]*"[^"'<>]*|'[^']*'[^"'<>]*)*(?:>|(?=<)|$(?!\n))}; #'}}}}
comment_tag_regex =
    '<!(?:--[^-]*-(?:[^-]+-)*?-(?:[^>-]*(?:-[^>-]+)*?)??)*(?:>|$(?!\n)|--.*$)';
tag_regex = /#{comment_tag_regex}|<#{tag_regex_}/;

(1 .. 64).each { |n|
    m = 2 ** n;
    puts m;
    html = sprintf '<meta %s/>', "foo='bar' " * m;
    html =~ tag_regex or raise "not ok\n";
}

HTMLタグのケースでは偶然この限界に当たることはまずないでしょうが、Xslateではこれまでで二度もこの限界に当たってしまったのでした。