Обсуждение на p5p: флаги регулярных выражений

Темы:

На прошлой неделе в рассылке perl5-porters была затронута тема флагов регулярных выражений. В Perl 5.14 появился флаг /a, который ограничивал поиск совпадений для \d, \s и \w символами ASCII, а удвоенный флаг /aa усиливал эффект и исключал поиск совпадений между ASCII и не-ASCII символами (например, k перестаёт совпадать с \N{KELVIN SIGN}).

regexp

Карл Уильямсон предложил признать устаревшим возможность указывать такие флаги в перемешку с другими флагами, например, вместо /aa писать /ama. Также предложено запретить возможность множественного использования флага /i, чтобы в будущем появилась возможность задавать /ii как вариант для задания более простого варианта поиска без учёта регистра (без учёта фолдинга). Это должно улучшить читаемость регулярных выражений. Но...

Но тут же отметили, что это противоречит примеру использованию /ee, который допускает смешивание. Кроме того, флаги, которые не имеют особых действий, могут указываться любое число раз (например, /egg, что даёт возможность строить забавные слова из флагов, но не несёт практического смысла). Правила должны быть либо строгими для всех, либо допускать свободное использование.

Было высказано предложение создать либо новое предупреждение, либо опцию к движку регулярных выражений, которая бы предупреждала о бесполезных флагах в регулярных выражениях. Но возможно стоит пойти по пути создания какого-то объектного/функционального интерфейса к регулярным выражениям.

Yves Orton показал такой пример:

match( /PATTERN/, case_insensitive  => 1, extended_mode => 1,
    dot_matches_newline => 1, multiline => 1);

Где опции /ixs описываются человеко-понятными терминами. Вариант для поиска с заменой мог бы выглядеть так:

search_and_replace(/PATTERN/, "REPLACEMENTS", global => 1, eval => 1,
    extended_eval =>1);

В данном случае составной флаг /ee описывается двумя разными ключами.

Brad Gilbert продолжил конкурс забавных интерфейсов. По аналогии с интерфейсом Perl 6:

s:global:ignorecase/PATTERN/{ EVAL "REPLACEMENTS" }/;
s:g(True):ignorecase(True)/PATTERN/{ EVAL "REPLACEMENTS" }/;

Но сделать для Perl5 наоборот:

s/PATTERN/"REPLACEMENTS"/ :eval(2) :extended :ignorecase ;
s/PATTERN/"REPLACEMENTS"/ :e(2) :x :i ;
s/PATTERN/"REPLACEMENTS"/ :eval :eval :x :i(1) ;

Правда в этом случае возникает сложность с парсингом тернарного оператора, например:

$bool ? s/PATTERN/"REPLACEMENTS"/ :eval(2);

Рикардо Сигнес развил эту идею дальше, включив флаги в фигурные скобки:

$x =~ s{foo}{bar}{ :eval(2) :extended :ignorecase };
$z =~ m/baz/{ :nocapture };

Father Chrysostomos показал вариант объектного решения:

qr/foo/->match(...)
qr/bar/->subst(...)
qr/baz/->(...)  # тоже самое, что и match

Рикардо Сигнес добавил к нему вариант с заданием флагов:

state  $regex = qr/baz/->flags(qw(i nocapture));
return $regex->( $input );

Правда вариант с вызовом метода очень не понравился Yves Orton. Он указал на то, что qr// создаёт готовый скомпилированный объект класса Regexp, а вызов метода flags изменяет опции конструктора объекта. Кроме того вызов $regex->($input) противоречит логике, т.к. $regex это не ссылка на код, а объект (что впрочем легко решается через use overload '&{}'). Взамен он предлагает ещё один вариант указания флагов, внутри регулярного выражения:

qr/(+case-sensitive, no-capture)baz/
qr/(+no-case-sensitive, no-capture)baz/

Весь тред можно посмотреть здесь. А какой вариант понравился вам?