"несовместимый" результат совпадения при использовании блока кода в регулярном выражении [Raku] - PullRequest
7 голосов
/ 08 марта 2020

При проверке и тестировании различных аспектов регулярных выражений я наткнулся на странное и "незаметное" поведение. Я пытался использовать некоторый код в регулярном выражении, но то же самое поведение применяется также с использованием блока пустого кода. Больше всего меня поразила разница в результате матча, когда я поменял местами модификаторы: g и the: x.

Следующие фрагменты кода изображают «несовместимое» поведение.

Сначала без блока кода:

use v6.d;

if "test1 test2 test3 test4" ~~ m:g/ (\w+) / {
    say ~$_ for $/.list;
}

Результат:

test1
test2
test3
test4

, затем с модификатором: g и блоком кода:

use v6.d;

if "test1 test2 test3 test4" ~~ m:g/ (\w+) {} / {
    say ~$_ for $/.list;
}

Результат:

test4

и, наконец, с модификатором: x и блоком кода

use v6.d;

if "test1 test2 test3 test4" ~~ m:x(4)/ (\w+) {} / {
    say ~$_ for $/.list;
}

Результат:

test1
test2
test3
test4

Я ожидал, что три результата быть таким же, но я был отрицательно удивлен.

Есть ли какое-либо объяснение этому поведению?

1 Ответ

5 голосов
/ 08 марта 2020

(Переписано после дополнительного тестирования и написания кода).

Это выглядит для меня (и, вероятно, для вас) как ошибка. $/ как-то становится кибошедом при использовании :g и встроенного блока.

Этот ответ охватывает:

  • Обнаружение проблемы

  • Просмотр исходного кода компилятора

  • Поиск очередей и / или регистрация новой проблемы

Обнуление задачи

my &debug = {;} # start off doing no debugging
$_ = 'aa';

say       m      / {debug 1} 'a' {debug 2} /; debug 3; # 「a」
say $/ if m      / {debug 1} 'a' {debug 2} /; debug 3; # 「a」

say       m:x(2) / {debug 1} 'a' {debug 2} /; debug 3; # (「a」 「a」)
say $/ if m:x(2) / {debug 1} 'a' {debug 2} /; debug 3; # (「a」 「a」)

say       m:g    / {debug 1} 'a' {debug 2} /; debug 3; # (「a」 「a」)
say $/ if m:g    / {debug 1} 'a' {debug 2} /; debug 3; # 「a」 <--- Uhoh

Теперь заставьте debug сказать что-нибудь полезное и запустите первую пару (без наречия регулярного выражения):

&debug = { say $_, $/.WHICH } # Say location of object bound to `$/`

say       m      / {debug 1} 'a' {debug 2} /; debug 3; # 「a」
# 1Match|66118928
# 2Match|66118928
# 「a」
# 3Match|66118928

say $/ if m      / {debug 1} 'a' {debug 2} /; debug 3; # 「a」
# 1Match|66119072
# 2Match|66119072
# 「a」
# 3Match|66119072

Один и тот же простой результат в обоих случаях. Процесс сопоставления создает объект Match и придерживается того же объекта.

Теперь два варианта с наречием :x(2):

say       m:x(2) / {debug 1} 'a' {debug 2} /; debug 3; # (「a」 「a」)
# 1Match|66119936
# 2Match|66119936
# 1Match|66120080
# 2Match|66120080
# 1Match|66120224
# (「a」 「a」)
# 3List|67612624

say $/ if m:x(2) / {debug 1} 'a' {debug 2} /; debug 3; # (「a」 「a」)
# 1Match|66120368
# 2Match|66120368
# 1Match|66120512
# 2Match|66120512
# 1Match|66120656
# (「a」 「a」)
# 3List|67612672

На этот раз процесс сопоставления создает Match объект и придерживается его в течение одного прохода, затем второй объект сопоставления для второго прохода и, наконец, третий объект сопоставления для третьего прохода, прежде чем он не будет соответствовать третьему 'a' (и, следовательно, соответствующий debug 2 не не звонят). В конце вызова m.../.../ он создал объект List и связал с до $/.

Далее запустим первый из двух случаев :g:

say       m:g    / {debug 1} 'a' {debug 2} /; debug 3; # (「a」 「a」)
# 1Match|66119216
# 2Match|66119216
# 1Match|66119360
# 2Match|66119360
# 1Match|66119504
# (「a」 「a」)
# 3Match|66119504

Как и в случае x:(2), мы пытаемся в третий раз и терпим неудачу. Но процесс сопоставления не возвращает List, а вместо этого Match объект. И это тот, который был создан на третьем проходе. (Что меня удивляет.)

Наконец, есть случай "Uhoh":

say $/ if m:g    / {debug 1} 'a' {debug 2} /; debug 3; # 「a」 <--- Uhoh
# 1Match|66119648
# 2Match|66119648
# 1Match|66119792
# 2Match|66119792
# 「a」
# 3Match|66119792

Примечательно, что ожидаемый третий проход не начинается.

Глядя на исходный код компилятора

Вероятно, что изучение соответствующего исходного кода будет полезным. Я напишу об этом здесь, на случай, если это заинтересует вас или других читателей, и если это ошибка, и то, что я пишу, будет интересно тому, кто ее исправит.

Указание блока кода в отведениях регулярных выражений для генерируемого узла AST здесь , который вставляет подузел перед операторами в блоке, который выполняет операцию связывания:

                    :op('bind'),

                    QAST::Var.new( :name('$/'), :scope('lexical') ),

                    QAST::Op.new(
                        QAST::Var.new( :name('$¢'), :scope('lexical') ),
                        :name('MATCH'),
                        :op('callmethod')
                    )

Мое прочтение вышеизложенного состоит в том, что он вставляет код, который связывает лексический символ $/ с результатом вызова метода .MATCH для объекта, привязанного к лексическому символу , непосредственно перед запуском кода в блоке.

do c имеет раздел на ; Я процитирую предложение:

Основное различие между $/ и заключается в объеме: последний имеет значение только внутри [a] регулярного выражения

Мне остается удивляться, почему существует и какие существуют другие различия.

Идем дальше ...

Я вижу, что уровень раку .MATCH. Но это почти ничего не делает. Поэтому я предполагаю, что соответствующий код здесь .

На этом этапе я сделаю паузу. Я мог бы продолжить дальше в более поздней редакции.

Поиск очередей проблем и / или регистрация новой проблемы

Если кто-то придет с ответом в ближайшие несколько дней, демонстрируя, что вы показали не является ошибкой, или уже была подана как ошибка, то достаточно справедливо.

В противном случае, пожалуйста, подумайте о том, чтобы выполнить собственный поиск в очередях проблем и / или начать новую проблему в любой очереди проблем, которую вы считаете наиболее подходящий (по умолчанию /rakudo/rakudo/issues).

Я уже искал четыре очереди проблем github.com, которые я посчитал правдоподобными при написании этого ответа:

Я искал два ключевых слова, которые, как я надеялся, могут раскрыть существующий выпуск («глобальный» и «опубликованный sh»). Нет подходящих проблем. Возможно, вы также можете найти другие ключевые слова, которые, по вашему мнению, может использовать файлер.

Если вы подаете проблему, рассмотрите возможность добавления ваших тестов или моего или другого варианта, преобразованного в стандартные тестовые наборы, если вы знаете, как это сделать.

...