Ещё одна ошибка в Perl regexp?
78 сообщений
#14 лет назад
Приглядимся к regexp:'ab' =~ /((\w+)(?{print defined $2 ? "\$2=$2\n" : "\$2 not defined\n"})){2}/;
Оно выводит:
$2=ab
$2 not defined
$2=b
Почему $2 not defined? Должно печататься $2=a. Мы ведь только что захватили 2-ми скобками хотя бы 1 букву. Я думаю, что это очередной баг.
Сергей
1322 сообщения
#14 лет назад
Вряд ли баг. Слишком тут все для re гуру просто чтобы незамеченным остаться.Тут непонятно, что вы сделать хотите. если eval убрать, то
/((\w+)){2}/
остается.
кроме того я думаю, что этот eval выполняется независимо от будет ли соответствие шаблону или нет. Надо уточнить..
'ab' =~ /((\w+)(?{print defined $2 ? pos()."\$1=$1 \$2=$2 all=$& \n" : pos() . "\$1=$1 \$2=$2 all=$& not defined\n"})){2}/;
на строке где not defined нет совпадения.
78 сообщений
#14 лет назад
> Слишком тут все для re гуру просто чтобы незамеченным остаться.Я в 2005 г. написал такую программку из 19 байтов:
map{while(1){}}@a;
Она завешивала Perl под виндовс, Юникс и, очевидно, всеми остальными системами на этапе трансляции!
perl -c a.pl
Висим! А куда уже проще? Тогда у меня был Perl 5.8.0, я не делал багрепорт, было исправлено только в 5.8.7. Здесь смысл в том, что while(1) ставится внутрь map, остальное произвольно. Я как это увидел, то мне показалось, что я первый, кто стал писать на Perl.
В 2009 г. я нашёл такую ошибку:
print "Match" if 'ab' =~ /^a?(?=b)b/;
не находит, а должен бы. ^ можно заменить на \A, а ? можно заменить на * с тем же результатом.
Тоже вроде всё просто... Я засабмитил багрепорт на bugs{a]perl.org, через день пришёл ответ, что да, это баг, причём, в ядре Perl, появился где-то в perl 5.0.004. В след. версии исправили, причём, пришлось существенно менять исходники, т.к. ошибка была в алгоритме работы search engine.
> этот eval выполняется независимо от будет ли соответствие шаблону или нет.
Это понятно, код перла является условием, которое всегда выполняется. Но ведь оно не выполняется, когда идёт возврат влево в связи с откатом? И оно выполняется сразу после того, как закроется 2-я пара скобок, которая обязана захватить хотя бы 1 букву, иначе выхода за скобку не будет...
Ещё были ошибки в re, которых в ранних версиях не было, потом они появлялись, потом опять исчезали...
Я об этом написал статейку ссылка
Я написал сейчас письмо с этим примером Джеффри Фридлу, но уверенности в ответе что-то не чувствуется: с примерно таким же успехом можно написать и Ларри Уоллу на larrywall.org. :-)
Интересно, можно ли на других языках прогнать такой пример, на Pyton, PHP, .NET, ...?
1322 сообщения
#14 лет назад
Да может. Я далеко не perl hacker, чтобы выводы о багах делать.. так ковыряюсь для общего развития. Пока понял только то, что не все так просто

Хочу все понять как работает use re 'debug' чтобы именно вывод отладчика понимать.
На php 1 в 1 этот не запускается. Не понимает он eval блоков
78 сообщений
#14 лет назад
Интересно, где-то есть объяснения вывода этого псевдокода в отладке...Фридл молчит, как партизан, а товарищ с perl.org опять ожил и прислал очередную отписку. Я изложил ему мои рассуждения (по-англ., насколько смог, так):
Представим это re
'ab' =~ /((\w+)(?{print defined $2 ? "\$2=$2\n" : "\$2 not defined\n"})){2}/;
в виде
((\w+)(?{print...}))((\w+)(?{print...}))
\w{2} эквивалентно \w\w, верно? Но мы здесь считаем, что вторая копия
re создаёт те же самые $1 и $2 (не $3 и $4). Текущая позиция маркируется символом |.
1. 1-я копия (\w+) захватывает весь текст:
((\w+) | (?{print...}))((\w+)(?{print...}))
$2 получает значение 'ab', eval выводит $2=ab.
2. Дальше входим во 2-ю копию (\w+):
((\w+)(?{print...}))(( | \w+)(?{print...}))
$2 (а также $+, $^N, \2) получает значение undefined.
3. Видим, что \w не совпадает. Делаем бэктрекинг:
((\w+ | )(?{print...}))((\w+)(?{print...}))
Мы входим в 1-ю копию (\w+) справа, и $2 опять получает значение undefined.
4. (\w+) захватывает символ a:
((\w+) | (?{print...}))((\w+)(?{print...}))
$2 должна получить значение a, но почему-то становится undefined... Почему?
Возможно, два значения undefined запоминаются в $2 как в стеке ($2 локализуется внутри re?),
потом последнее значение undefined выталкивается из стека и $2 опять равно undefined?
Здесь eval должно вывести $2=a.
5. 2-я копия (\w+) захватывает b:
((\w+)(?{print...}))((\w+) | (?{print...}))
Eval выводит $2=b. Match successfull.
Видите вы какую-либо ошибку в этих рассуждениях?
===
Я ещё поумал и вижу, что я ошибался: когда \w+ отдаёт символ b, то мы
не заходим левее открывающей скобки в выражении (\w+), поэтому $2 остаётся неопределённым, что и печатается.
===
Опа, я через неск. часов после этого опять раза 2 подумал и пришёл к выводу, что $2=undefined интуитивно должно быть ошибочно, правильно всё-таки выводить $2=a, как я раньше писал.
Проверил почту - вот это да, пришёл ответ от гуру рег. выражений Дж. Фридла, который живёт на regex.info. (В сети есть 3-е издание его книги "Регулярные выражения"

Hi Serge,
I've been thinking about this for a while, and as far as I can tell it does seem
to be a bug. By definition, $2 must be defined before the (?{...}) can run.
It's probably a problem with how it backtracks. I'd suggest filing a bug report..
Поначалу я раздвоил это выражение ввиду квантификатора {2}, получилось такое:
((\w+)(?{print...}))((\w+)(?{print...}))
(Здесь имеем в виду, что вторая копия также производит $1 и $2). Но так рассуждать неправильно, реально ничего не раздваивается.
Вот почему выводится $2=undefined, по моему мнению:
вначале (\w+) захватывает всё и выводится, что $2=ab.
Далее выходим на модификатор {2}. Текущая позиция отмечена через |:
(\w+)) | {2}
Видим, что повтор \w не совпадает. Делаем бэктрекинг и по пути входим справа за закрывающую скобку:
(\w+ | )
Видимо, в этом случае движок делает $2 undefined, а почему? Интуитивно кажется, что это надо делать только, когда мы выходим левее соответствующей открывающей скобки.
Но я сомневаюсь, что авторы движка станут это исправлять: это вопрос идеологии работы движка, и ведь тогда теоретически некоторые старые программы могут начать не так работать...
78 сообщений
#14 лет назад
Народ, который занимается разработкой Перла, окончательно признал, что "This *is* a bug. An optimization bug probably. In the PLUS regop." И вот интересный пример такого человека, который написал вышеприведённые слова: заменим \w+ на (?:\w|z)+ и потом на (?:\w|zz)+, получим разный вывод:'ab' =~ /(((?:\w|z)+)(?{print defined $2 ? "\$2=$2\n" : "\$2 not defined\n"})){2}/;
$2=ab
$2 not defined
$2=b
'ab' =~ /(((?:\w|zz)+)(?{print defined $2 ? "\$2=$2\n" : "\$2 not defined\n"})){2}/;
$2=ab
$2=a
$2=b
Кстати, я на днях засабмитил ещё один багрепорт, связанный с неправильной работой операторов \L, \l, \U и \u. Эти операторы, в отличие от функций lc, lcfirst, uc и ucfirst, выполняются слева направо:
print "\L\udD\n"; # Dd верно
print "\LdD\udD\n"; # dddd уже неверно!
А если рядом поставить \U и \L (4 комбинации), то вообще будет кошмар: ошибка синтаксиса:
print "\U\La\n";
Сергей
1322 сообщения
#14 лет назад
Serg31416, браво.Вот так вот разобраться, связаться и т.д. дорогово стоит.
78 сообщений
#14 лет назад
Спасибо, да, стоит дорогого, но за это никто не платит, хотя времени тратится много. Я однажды написал курс лекций по рег. выражениям для intuit.ru, потом они заказали такую же книжку, а не так давно я сделал страницу на ссылка, где думал публиковать свои статейки и часть глав из той книги, но тема очень узкая, толку вообще никакого нет, только время потратил...Сергей
1322 сообщения
#14 лет назад
Емае.. Я же эту книгу покупал года полтора два назад 
Пишите, пожалуйста еще

Цитата:
но за это никто не платит, хотя времени тратится много
Да, но это и есть опенсорс, никто не заставляет, никто не платит, но и за пользование никто денег не просит.
Читал тут на эту тему ссылка
78 сообщений
#14 лет назад
Писать, конечно, можно, но проблема в том, что я не получаю ни зарплат, ни пенсий, ни пособий... Думал, что там хоть с адсенса что-то закапает, но получается 0. Что взять с 2-х показов страниц в день? Хотя в ya.ru по запросу Perl регулярные выражения я стою на 10-м месте, в dmoz.org прописался... Если сравнить это с сайтом Дж. Фридла regex.info, то разница в числе посетитеей большая.На Западе есть специальные конторки, которые зарабатывают на написании рег. выражений, но мне что-то таких систематических заказов видеть не удавалось.
А с этой ошибкой с \L и \u вообще интересно: я её нашёл как раз когда писал эти лекции, т.е. года 4-5 назад, а багрепорт отправил на прошлой неделе. Такое впечатление, как будто Перлом и этими операторами не пользуются тысячи программистов, а я первый, кто написал \LDd\u.