Provavelmente a principal coisa que está jogando fora é que \s
coincide com o espaço horizontal e vertical. Para corresponder apenas ao espaço horizontal, use \h
e para corresponder apenas ao espaço vertical \v
,.
Uma pequena recomendação que eu faria é evitar incluir as novas linhas no token. Você também pode usar os operadores de alternância %
ou %%
, como eles foram projetados para lidar com este tipo de trabalho:
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* }
}
O resultado Parser.parse($dat)
disso é o seguinte:
「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 => 「」
o que mostra que a gramática analisou tudo com êxito. No entanto, vamos nos concentrar na segunda parte da sua pergunta, que você deseja que ela esteja disponível em uma variável para você. Para fazer isso, você precisará fornecer uma classe de ações que seja muito simples para este projeto. Você acabou de criar uma classe cujos métodos correspondem aos métodos da sua gramática (embora os muito simples, como value
/ header
que não exijam processamento especial além da stringificação, possam ser ignorados). Existem algumas maneiras mais criativas / compactas de lidar com o processamento, mas seguirei com uma abordagem bastante rudimentar para ilustração. Aqui está a nossa turma:
class ParserActions {
method headerRow ($/) { ... }
method valueRow ($/) { ... }
method TOP ($/) { ... }
}
Cada método possui a assinatura ($/)
que é a variável de correspondência de regex. Então agora, vamos perguntar quais informações queremos de cada token. Na linha do cabeçalho, queremos cada um dos valores do cabeçalho, em uma linha. Assim:
method headerRow ($/) {
my @headers = $<header>.map: *.Str
make @headers;
}
Qualquer token com um quantificador sobre ele será tratado como um Positional
, por isso também pode acessar cada partida cabeçalho indivíduo com $<header>[0]
, $<header>[1]
etc. Mas esses são objetos jogo, então nós rapidamente stringify eles. O make
comando permite que outros tokens acessem esses dados especiais que criamos.
Nossa linha de valor será idêntica, porque os $<value>
tokens são o que nos interessa.
method valueRow ($/) {
my @values = $<value>.map: *.Str
make @values;
}
Quando chegarmos ao último método, queremos criar a matriz com hashes.
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;
}
Aqui você pode ver como acessamos as coisas em que processamos headerRow()
e valueRow()
: Você usa o .made
método Como existem várias valueRows, para obter cada um de seus made
valores, precisamos fazer um mapa (essa é uma situação em que costumo escrever minha gramática simplesmente <header><data>
na gramática e defino os dados como sendo várias linhas, mas isso é simples o suficiente, não é tão ruim).
Agora que temos os cabeçalhos e as linhas em duas matrizes, é simplesmente uma questão de torná-las uma matriz de hashes, o que fazemos no for
loop. O flat @x Z @y
just intercolates os elementos, e a atribuição de hash faz o que queremos dizer, mas existem outras maneiras de obter a matriz no hash desejado.
Quando terminar, você apenas make
o fará e estará disponível na made
análise:
say Parser.parse($dat, :actions(ParserActions)).made
-> [{Email => test@email.com, ID => 1, Name => test} {Email => stan@nowhere.net, ID => 321, Name => stan} {}]
É bastante comum agrupá-los em um método, como
sub parse-tsv($tsv) {
return Parser.parse($tsv, :actions(ParserActions)).made
}
Dessa forma, você pode apenas dizer
my @entries = parse-tsv($dat);
say @entries[0]<Name>; # test
say @entries[1]<Email>; # stan@nowhere.net
Nil
. É muito estéril no que diz respeito ao feedback, certo? Para depuração, faça o download commaide se você ainda não o fez e / ou consulte Como é possível melhorar o relatório de erros nas gramáticas? . Você assumiuNil
porque seu padrão assumiu a semântica de retrocesso. Veja minha resposta sobre isso. Eu recomendo que você evite voltar atrás. Veja a resposta de @ user0721090601 sobre isso. Para pura praticidade e velocidade, consulte a resposta de JJ. Além disso, a resposta geral introdutória a "Quero analisar X com Raku. Alguém pode ajudar?" .