Probabilmente la cosa principale che lo butta via è che \s
corrisponde allo spazio orizzontale e verticale. Per abbinare spazio solo orizzontale, l'uso \h
, e per abbinare lo spazio solo in verticale, \v
.
Una piccola raccomandazione che vorrei fare è di evitare di includere le nuove righe nel token. Potresti anche voler utilizzare gli operatori di alternanza %
o %%
, poiché sono progettati per gestire questo tipo di lavoro:
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* }
}
Il risultato di Parser.parse($dat)
questo è il seguente:
「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 => 「」
che ci mostra che la grammatica ha analizzato con successo tutto. Tuttavia, concentriamoci sulla seconda parte della tua domanda, che vuoi che sia disponibile in una variabile per te. Per fare ciò, dovrai fornire una classe di azioni molto semplice per questo progetto. Devi solo creare una classe i cui metodi corrispondono ai metodi della tua grammatica (anche se quelli molto semplici, come value
/ header
che non richiedono un'elaborazione speciale oltre alla stringa, possono essere ignorati). Ci sono alcuni modi più creativi / compatti per gestire la tua elaborazione, ma seguirò un approccio abbastanza rudimentale per l'illustrazione. Ecco la nostra classe:
class ParserActions {
method headerRow ($/) { ... }
method valueRow ($/) { ... }
method TOP ($/) { ... }
}
Ogni metodo ha la firma ($/)
che è la variabile di corrispondenza regex. Quindi ora chiediamo quali informazioni vogliamo da ogni token. Nella riga di intestazione, vogliamo ciascuno dei valori di intestazione, in una riga. Così:
method headerRow ($/) {
my @headers = $<header>.map: *.Str
make @headers;
}
Ogni token con un quantificatore su di esso sarà trattata come una Positional
, così abbiamo potuto anche accedere l'intestazione di ogni incontro individuale con $<header>[0]
, $<header>[1]
ecc Ma quelli sono oggetti partita, quindi abbiamo rapidamente stringa i. Il make
comando consente ad altri token di accedere a questi dati speciali che abbiamo creato.
La nostra riga di valore avrà un aspetto identico, perché i $<value>
token sono ciò che ci interessa.
method valueRow ($/) {
my @values = $<value>.map: *.Str
make @values;
}
Quando arriveremo all'ultimo metodo, vorremmo creare l'array con gli hash.
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;
}
Qui puoi vedere come accediamo alle cose in cui abbiamo elaborato headerRow()
e valueRow()
: usi il .made
metodo. Perché ci sono più valueRows, per ottenere ciascuno dei loro made
valori, dobbiamo fare una mappa (questa è una situazione in cui tendo a scrivere la mia grammatica per avere semplicemente <header><data>
nella grammatica, e definire i dati come più righe, ma questo è abbastanza semplice non è poi così male).
Ora che abbiamo le intestazioni e le righe in due array, è semplicemente una questione di renderli una serie di hash, cosa che facciamo nel for
loop. Il flat @x Z @y
giusto intercollega gli elementi e l'assegnazione hash fa ciò che intendiamo, ma ci sono altri modi per ottenere l'array nell'hash desiderato.
Una volta terminato, basta make
e quindi sarà disponibile nel made
parse:
say Parser.parse($dat, :actions(ParserActions)).made
-> [{Email => test@email.com, ID => 1, Name => test} {Email => stan@nowhere.net, ID => 321, Name => stan} {}]
È abbastanza comune avvolgerli in un metodo, come
sub parse-tsv($tsv) {
return Parser.parse($tsv, :actions(ParserActions)).made
}
In questo modo puoi solo dire
my @entries = parse-tsv($dat);
say @entries[0]<Name>; # test
say @entries[1]<Email>; # stan@nowhere.net
Nil
. È piuttosto sterile per quanto riguarda il feedback, giusto? Per il debug, scarica virgola se non lo hai già fatto e / o vedi Come è possibile migliorare la segnalazione degli errori nelle grammatiche? . Hai avutoNil
il motivo per cui il tuo modello ha assunto la semantica di backtracking. Vedi la mia risposta a riguardo. Ti consiglio di evitare il backtracking. Vedi la risposta di @ user0721090601 al riguardo. Per pura praticità e velocità, vedi la risposta di JJ. Inoltre, risposta generale introduttiva a "Voglio analizzare X con Raku. Qualcuno può aiutare?" .