In genere definisco una regola per end_of_line. Questo è basato sul trucco in http://kschiess.github.io/parslet/tricks.html per la corrispondenza end_of_file.
class MyParser < Parslet::Parser
rule(:cr) { str("\n") }
rule(:eol?) { any.absent? | cr }
rule(:line_body) { (eol?.absent? >> any).repeat(1) }
rule(:line) { cr | line_body >> eol? }
rule(:lines?) { line.repeat (0)}
root(:lines?)
end
puts MyParser.new.parse(""" this is a line
so is this
that was too
This ends""").inspect
Ovviamente se si vuole fare di più con il parser di quanto si possa ottenere con String :: split ("\ n") si sostituire il line_body
con qualcosa di utile :)
I ha fatto un rapido tentativo di rispondere a questa domanda e l'ha fatto saltare in aria. Io solo vorrei spiegare l'errore che ho fatto e mostrarti come evitare errori di questo tipo.
Ecco la mia prima risposta.
rule(:eol) { str('\n') | any.absent? }
rule(:line) { (eol.absent? >> any).repeat >> eol }
rule(:lines) { line.as(:line).repeat }
non ho seguito le mie solite regole:
- Assicurarsi sempre numero di ripetizioni esplicito
- Qualsiasi regola che può corrispondere a zero stringhe di lunghezza, dovrebbe avere nome che termina con un '?'
Quindi, consente di applicare questi ...
rule(:eol?) { str('\n') | any.absent? }
# as the second option consumes nothing
rule(:line?) { (eol.absent? >> any).repeat(0) >> eol? }
# repeat(0) can consume nothing
rule(:lines?) { line.as(:line?).repeat(0) }
# We have a problem! We have a rule that can consume nothing inside a `repeat`!
Qui vedono perché otteniamo un ciclo infinito. Man mano che l'input viene consumato, si ottiene solo lo end of file
, che corrisponde allo eol?
e quindi allo line?
(poiché il corpo della linea può essere vuoto). Essendo all'interno di lines
'repeat
, mantiene la corrispondenza senza consumare nulla e loop per sempre.
Abbiamo bisogno di cambiare la regola della linea in modo che consuma sempre qualcosa.
rule(:cr) { str('\n') }
rule(:eol?) { cr | any.absent? }
rule(:line_body) { (eol.absent? >> any).repeat(1) }
rule(:line) { cr | line_body >> eol? }
rule(:lines?) { line.as(:line).repeat(0) }
Ora line
deve corrispondere qualcosa, o un cr
(per le linee vuote), o almeno un carattere seguito dal opzionale eol?
. Tutti gli repeat
hanno corpi che consumano qualcosa. Siamo ora d'oro.
Questa sembra una buona soluzione. La mia soluzione era di lavorare anche con '\ n' e di aggiungere una nuova riga alla stringa in entrata per evitare un errore di corrispondenza alla fine. Questo sembra più pulito, però. Grazie! – Danyel