2017-05-10 2 views
2

Für die folgende Parslet ParserParslet Parsen Nicht ganze Reihe

require 'parslet' 
require 'parslet/convenience' 

class Lines < Parslet::Parser 
     rule(:open_tag) {str('[')} 
    rule(:close_tag) {str(']')} 
    rule(:data) {str('name') | str('name_id') } 
    rule(:text) { open_tag >> data >> close_tag } 
    root :text 
end 

begin 
    p Lines.new.parse("[name_id]") <---- It throws error 
rescue Parslet::ParseFailed => failure 
    Lines.new.parse_with_debug("[name_id]") 
end 

gibt es folgende Fehler

Failed to match sequence (OPEN_TAG NAME CLOSE_TAG) at line 1 char 6. 
`- Expected "]", but got "_" at line 1 char 6. 

Wenn ich data rule von

rule(:data) {str('name') | str('name_id') } 

zu

rule(:data) {str('name_id') | str('name') } 
012 ändern

dann funktioniert es wie erwartet.

Aber ich erzeuge Regeln dynamisch basierend auf Benutzereingaben. Also diese Lösung wird nicht für mich arbeiten.

Vielen Dank im Voraus.

+0

Ich denke, ich brauche mehr Informationen darüber, was Ihr Gesamtziel ist. Ich kann viele Probleme mit dem Generieren eines Parsers basierend auf Benutzereingaben vorhersehen. –

Antwort

2

Regel :data wird gebaut und dann in der Reihenfolge überprüft, in der die Artikel bereitgestellt wurden. Zur Durchsetzung länger Matcher vor den kürzeren auftreten, könnte man einfach sortieren:

data = %w|name name_id| 

data = data.sort { |a, b| b <=> a } 

rule(:data) { data.map(&method(:str)).reduce(:|) } 
+0

Ich habe diese Alternative verwendet, aber ich bin daran interessiert, dieses Problem mit der Parslet-API zu lösen, um das ganze Wort zu finden. –

+0

Parsolet verbraucht Eingaben aus einem Stream. Wenn ein Token vollständig übereinstimmt, wird dieser Teil des Streams verbraucht. Wenn "name" vollständig übereinstimmt, versucht die Grammatik, den Rest der Eingabe mit dem Rest der Grammatik abzugleichen. Also ... Sie müssen einen Weg finden zu sagen, dass "Name" nicht übereinstimmen sollte. Eine Möglichkeit besteht darin, name_id an erster Stelle zu setzen ... so dass es bevorzugt übereinstimmt. Der andere ist zu wissen, welcher Token dem Namen folgt und diesen explizit abgleicht. –

1

Wie mudasobwa sagt ... Name wird übereinstimmen, so geht es nicht eine Chance zu versuchen name_id. Sie müssen entweder die Reihenfolge ändern, sodass name_id zuerst versucht wird, oder Sie müssen den Namen nicht übereinstimmen lassen. Wie Sie das tun, hängt von Ihrer Grammatik ab.

Ich denke, ich würde stattdessen den Parser den Text für mich brechen lassen, dann überprüfen Sie die Struktur danach .. z.

require 'parslet' 
require 'parslet/convenience' 

class Lines < Parslet::Parser 
    rule(:open_tag) {str('[')} 
    rule(:close_tag) {str(']')} 
    rule(:data) { (close_tag.absnt? >> any).repeat(1).as(:data) } 
    rule(:text) { open_tag >> data >> close_tag } 
    root :text 
end 

begin 
    p Lines.new.parse("[name_id]") # => {:data=>"name_id"@1} 
rescue Parslet::ParseFailed => failure 
    Lines.new.parse_with_debug("[name_id]") 
end 

Parslet soll in zwei Phasen arbeiten .. Die erste konvertiert Ihr Dokument in einen Baum. Die zweite konvertiert Ihren Baum in eine gewünschte Datendarstellung.

In diesem Fall zieht die erste Analyse die Struktur heraus. Der Sekundenpass könnte prüfen, ob "name_id" gültig ist. usw.

Verwandte Themen