Сергей М.
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.