Извините, почему-то исправляется не последнее, а предпоследнее моё сообщение, а потом форум не даёт его редактировать, поэтому оно раздвоилось.
========================================
Я над этим серьёзно ещё не думал, а сейчас подумал раза 2 и считаю, что это всё-таки ошибка. Поспешил я со 2-м письмом в perl.org, ладно, посмотрим, что завтра-послезавтра оттуда ответят.
Вот мой разбор работы этого re, как я понимаю этот механизм.
Итак, имеем минимальный пример с подобной ошибкой:
\'ab\' =~ /((\\w+)(?{print defined $2 ? "\\$2=$2\\n" : "\\$2 not defined\\n"})){2}/;
Оно выводит:
$2=ab
$2 not defined
$2=b
Проблема: почему $2 not defined? Ведь мы визуально только что перед этим выводом закрыли 2-ю скобку и должны получить $2 (а также $+, $^N и \\2 определёнными, т.к. 2-я пара скобок только что участвовала в совпадении. А они все неопределённые. Что-то не то...
Я подозреваю, что возможная ошибка возникла от того, что нумерованные переменные (и вообще спец. переменные re) локализуются не только при входе в каждый блок программы (т.е. имеют структуру стека)), но и внутри re, а это неправильно.
При об-ке этого re оно должно как бы раскручиваться: аналогично тому, как \\w{2} эквивалентно \\w\\w, так и всё re эквивалентно
((\\w+)({print ...})((\\w+)({print ...})
Но при этом во 2-м экз. скобки нумеруются тоже 1 и 2 и порождают $1 и $2.
Вначале 1-е \\w+ захватит всё ab и на печать выйдет $2=ab.
Затем входим во 2-й экз. re и когда входим во 2-1 экз. 2-й скобки, $2 получает значение undefined. Видим, что \\w не совпадает. Не беда, у нас есть сохр. состояние, возвращаемся к 1-му экз. 2-й скобки. Но при возврате мы входим справа в 1-й экз. 2-й пары скобок, поэтому $2 опять получает значение undefined, которое наслаивается на предыдущее, а это неправильно. Квантификатор + отдаёт 1 букву, мы захватываем 1-м экз. 2-й пары скобок символ a, затем выходим за скобку. Тут надо сформировать $2 и т.д., но, видимо, здесь механизм об-ки re видит, что $2 уже имеет значение, поэтому вместо этого отменяется предыдущее значение undefined для $2, в результате $2 опять получает значение undefined, что и выводится, а надо вывести a.
Вот так мне кажется...
===
Я ещё поумал и вижу, что я ошибался: когда \\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, а почему? Интуитивно кажется, что это надо делать только, когда мы выходим левее соответствующей открывающей скобки.
Но я сомневаюсь, что авторы движка станут это исправлять: это вопрос идеологии работы движка, и ведь тогда теоретически некоторые старые программы могут начать не так работать...