Вероятно, главное, что его отбрасывает, это то, что \s
соответствует горизонтальному и вертикальному пространству. Чтобы соответствовать только горизонтальному пространству, используйте \h
, а чтобы соответствовать только вертикальному пространству, \v
.
Одна небольшая рекомендация, которую я хотел бы сделать, состоит в том, чтобы избегать включения новых строк в токене. Возможно, вы также захотите использовать операторы чередования %
или %%
, так как они предназначены для работы с этим типом работы:
grammar Parser {
token TOP {
<headerRow> \n
<valueRow>+ %% \n
}
token headerRow { <.ws>* %% <header> }
token valueRow { <.ws>* %% <value> }
token header { \S+ }
token value { \S+ }
token ws { \h* }
}
Результат Parser.parse($dat)
для этого следующий:
「ID Name Email
1 test test@email.com
321 stan stan@nowhere.net
」
headerRow => 「ID Name Email」
header => 「ID」
header => 「Name」
header => 「Email」
valueRow => 「 1 test test@email.com」
value => 「1」
value => 「test」
value => 「test@email.com」
valueRow => 「 321 stan stan@nowhere.net」
value => 「321」
value => 「stan」
value => 「stan@nowhere.net」
valueRow => 「」
, который показывает нам, что грамматика успешно все проанализировала. Однако давайте сосредоточимся на второй части вашего вопроса, которая заключается в том, что вы хотите, чтобы он был доступен в переменной для вас. Для этого вам нужно предоставить класс действий, который очень прост для этого проекта. Вы просто создаете класс, методы которого соответствуют методам вашей грамматики (хотя очень простые, такие как value
/ header
, которые не требуют специальной обработки помимо строкового преобразования, могут игнорироваться). Есть несколько более креативных / компактных способов обработки ваших, но я приведу go с довольно элементарным подходом для иллюстрации. Вот наш класс:
class ParserActions {
method headerRow ($/) { ... }
method valueRow ($/) { ... }
method TOP ($/) { ... }
}
Каждый метод имеет сигнатуру ($/)
, которая является переменной соответствия регулярного выражения. Итак, теперь давайте спросим, какую информацию мы хотим получить от каждого токена. В строке заголовка мы хотим, чтобы каждое из значений заголовка было в строке. Итак:
method headerRow ($/) {
my @headers = $<header>.map: *.Str
make @headers;
}
Любой токен с квантификатором будет рассматриваться как Positional
, поэтому мы также можем получить доступ к каждому отдельному совпадению заголовка с $<header>[0]
, $<header>[1]
и т. Д. c. Но это совпадающие объекты, поэтому мы просто быстро их упорядочиваем. Команда make
позволяет другим токенам получать доступ к созданным нами специальным данным.
Наша строка значений будет выглядеть одинаково, потому что токены $<value>
- это то, что нас волнует.
method valueRow ($/) {
my @values = $<value>.map: *.Str
make @values;
}
Когда мы перейдем к последнему методу, мы захотим создать массив с хешами.
method TOP ($/) {
my @entries;
my @headers = $<headerRow>.made;
my @rows = $<valueRow>.map: *.made;
for @rows -> @values {
my %entry = flat @headers Z @values;
@entries.push: %entry;
}
make @entries;
}
Здесь вы можете увидеть, как мы получаем доступ к материалам, которые мы обработали в headerRow()
и valueRow()
: вы используете метод .made
. Поскольку существует несколько значений ValueRows, чтобы получить каждое из их значений made
, нам нужно составить карту (в этой ситуации я склонен писать свою грамматику так, чтобы в грамматике было просто <header><data>
, и определять данные как несколько строк, но это достаточно просто, это не так уж плохо).
Теперь, когда у нас есть заголовки и строки в двух массивах, просто нужно сделать их массивом хэшей, что мы делаем в for
л oop. flat @x Z @y
просто объединяет элементы, и присвоение ha sh делает то, что мы имеем в виду, но есть другие способы получить массив в ха sh, который вы хотите.
Как только вы закончите, Вы просто make
это, и тогда он будет доступен в made
анализа:
say Parser.parse($dat, :actions(ParserActions)).made
-> [{Email => test@email.com, ID => 1, Name => test} {Email => stan@nowhere.net, ID => 321, Name => stan} {}]
Это довольно распространено, чтобы обернуть их в метод, как
sub parse-tsv($tsv) {
return Parser.parse($tsv, :actions(ParserActions)).made
}
Таким образом, вы можете просто сказать
my @entries = parse-tsv($dat);
say @entries[0]<Name>; # test
say @entries[1]<Email>; # stan@nowhere.net