<[abcdef...]>
в регулярном выражении P6 является "классом символов" в смысле совпадения - one -character. 1
Идиоматический способ получить то, чтовы хотите использовать квантификатор **
:
my $a = "39 3A 3B ";
grammar Hex {
token TOP { <hex_array>+ }
token hex_array { <[0..9 A..F]>**1..2 " " }
};
Hex.parse($a);
Остальная часть этого ответа - «бонусный» материал о том, почему и как использовать rule
с.
Вы, конечно, совершенно свободны для сопоставления пробельных ситуаций, включая шаблоны пробелов в произвольные отдельные токены, как вы это сделали с " "
в своем токене hex_array
.
Однако это хорошая практикаиспользовать rule
s вместо этого, когда это необходимо - что происходит большую часть времени.
Во-первых, используйте ws
вместо "", \s*
и т. д.
Давайте уберем пробел во втором token
и переместим его вместо первого:
token TOP { [ <hex_array> " " ]+ }
token hex_array { <[0..9 A..F]>**1..2 }
Мы добавили квадратные скобки ([...]
), которые объединяют hex_array
и пробели затем применил квантификатор +
к этому объединенному атому.Это простое изменение, и грамматика продолжает работать так же, как и раньше, соответствуя пробелу, как и раньше, за исключением того, что теперь пробел не будет захвачен токеном hex_array
.
Далее, давайте перейдем к использованиюв ws
token
:
token TOP { [ <hex_array> <.ws> ]+ }
Значение по умолчанию <ws>
является более полезным в желаемых способах, чем \s*
. 2 Иесли по умолчанию ws
не делает то, что вам нужно, вы можете указать свой собственный ws
токен.
Мы использовали <.ws>
вместо <ws>
, потому что, как и \s*
, использование<.ws>
позволяет избежать дополнительного захвата пробелов, который, вероятно, просто захламляет дерево разбора и тратит память.
Часто требуется что-то вроде <.ws>
после почти каждого токена в правилах синтаксического анализа более высокого уровня, которые объединяют токены вместе.Но если бы это было просто написано в явном виде, это было бы очень повторяющимся и отвлекающим образцом <.ws>
и [ ... <.ws> ]
.Чтобы избежать этого, есть встроенный ярлык для неявно , выражающий предположение по умолчанию о вставке шаблона для вас.Этот ярлык является декларатором rule
, который в свою очередь использует :sigspace
.
Используя rule
(который использует :sigspace
)
A rule
равен точно то же самое, что и token
, за исключением того, что он включает :sigspace
в начале шаблона:
rule { <hex_array>+ }
token { :sigspace <hex_array>+ } # exactly the same thing
без :sigspace
(поэтому в token
с и regex
с по умолчанию) все буквенные пробелы в шаблоне (если вы их не заключили в кавычки) игнорируются .Как правило, это желательно для читаемых шаблонов отдельных token
с , поскольку они обычно определяют буквальные элементы для сопоставления.
Но как только действует :sigspace
, пробелы после атомы становятся «значимыми» - потому что они неявно преобразуются в <.ws>
или [ ... <.ws> ]
вызовы.Это желательно для читаемых шаблонов, задающих последовательности токенов или подправил, потому что это естественный способ избежать беспорядка всех этих дополнительных вызовов.
Первый шаблон ниже будет соответствовать одному или нескольким токенам hex_array
без пробелов.подбирается между ними или в конце.Последние два будут соответствовать одному или нескольким hex_array
с без пробелов, а затем с или без пробелов в самом конце:
token TOP { <hex_array>+ }
# ^ ignored ^ ^ ignored
token TOP { :sigspace <hex_array>+ }
# ^ ignored ^ ^ significant
rule TOP { <hex_array>+ }
# ^ ignored ^ ^ significant
NB. Наречия (как :sigspace
) не являются атомами.Пробелы сразу до первого атома (в вышеприведенных пробелах до <hex_array>
) никогда значимы (независимо от того, * :sigspace
действует или не действует).Но после этого, если действует :sigspace
, все не заключенные в кавычки интервалы в шаблоне являются «значительными», то есть они преобразуются в <.ws>
или [ ... <.ws> ]
.
В приведенном выше кодевторой токен и правило будут соответствовать single hex_array
с пробелами после него, потому что пробел сразу после +
и перед }
означает, что шаблон переписан в:
token TOP { <hex_array>+ <.ws> }
Но этот переписанный токен не будет совпадать, если ваш ввод содержит несколько hex_array
токенов с одним или несколькими пробелами между ими.Вместо этого вы хотели бы написать:
rule TOP { <hex_array> + }
# ignored ^ ^ ^ both these spaces are significant
который переписан на:
token TOP { [ <hex_array> <.ws> ]+ <.ws> }
Это будет соответствовать вашему вводу.
Заключение
Итак, после всей этой кажущейся сложности, которая на самом деле является исключительно точной, я предлагаю вам написать свой оригинальный код как:
my $a = "39 3A 3B ";
grammar Hex {
rule TOP { <hex_array> + }
token hex_array { <[0..9 A..F]>**1..2 }
};
Hex.parse($a);
и это будет соответствовать более гибко, чем ваш оригинал (я предполагаю, что это было бы хорошо, хотя, конечно, это может быть не так для некоторых случаев использования) и, возможно, было бы легче читать для большинства P6ers.
Наконец, чтобы подчеркнуть, как избежать двух из трех уловок rule
с, см. Также Какой лучший способ быть слабым на пустом месте в грамматике perl6? . (Третье замечание - нужно ли вам ставить пробел между атомом и квантификатором, как и пробел между <hex_array>
и +
в приведенном выше.)
Сноска
1 Если вы хотите сопоставить несколько символов, добавьте подходящий квантификатор в класс символов. Это разумный способ для вещей, и предполагаемое поведение «класса персонажей» согласно Википедии . К сожалению, документ P6 в настоящее время запутывает проблему, например, объединяя как подлинные классы символов, так и другие правила, которые соответствуют нескольким символам под заголовком Предопределенные классы символов .
2 Правило ws
по умолчанию разработано для соответствия между словами , где «слово» представляет собой непрерывную последовательность букв (Unicode-категория L), цифры (Nd) или подчеркивает. В коде это указано как:
regex ws { <!ww> \s* }
ww
- это тест "внутри слова". Таким образом, <!ww>
означает , а не в «слове». <ws>
всегда будет успешным, где \s*
- за исключением того, что, в отличие от \s*
, не будет успешным в середине слова. (Как и любой другой атом, квантифицированный с помощью *
, простой \s*
всегда будет совпадать, потому что он соответствует любому количеству пробелов, включая ни одного .)