2016-08-20 3 views
0

Ich habe diesen Parsec Parser:Warum läuft Parsec nicht zurück, wenn ein Teil des Parsers erfolgreich ist und der Rest fehlschlägt?

a = optionMaybe $ do {try $ spaceornull *> string "hello";string "No"}       

Wo spaceornull ist ((:[]) <$> try space) <|> string ""

Als ich mit Eingang testen "" Ich bekomme:

Left (Zeile 1, Spalte 2):
unerwartetes Ende des Eingangs
erwartet "Hallo"

ich das nicht verstehen, sollte spaceornull *> string "hello" scheitern, weil es keine „Hallo“, dann mit try Parsec ziehen zurück und jetzt gibt es keinen verbrauchten Eingang aber try nicht ohnehin so die zu optionMaybe weitergegeben Parsern (die innerhalb do) ausfallen insgesamt sollte es nicht versuchen, weitere Eingaben zu konsumieren, so dass wir mit einem fehlerhaften Parser enden, ohne irgendeine Eingabe zu verbrauchen, so dass ich Right Nothing bekommen sollte.

Aber die Fehlermeldung sagt es, der Raum ist verbraucht, also try nicht wirklich zurückverfolgen, tut try nicht zurückverfolgen, wenn Teil des Parsers erfolgreich ist? und wie man es mit dem oben genannten zurückbringt?

Antwort

4

try hat nichts damit zu tun, ob ein Fehler erlaubt ist oder nicht. Es macht es nur möglich Backtrack im Fehlerfall , aber um das Backtracking zu beginnen, müssen Sie einen alternativen Parser bereitstellen, um an diesem Punkt zu starten. Der üblicher Weg, das zu tun, ist mit dem <|> operator:

a = optionMaybe $ (try $ spaceornull *> string "hello") <|> string "No" 

OTOH, Ihr Code entspricht

a = optionMaybe $ (try $ spaceornull *> string "hello") >> string "No" 

wo die monadische Verkettungs operator >> (gleich wie *>) wird im Fall von Parsec überprüfen, ob die LHS ist erfolgreich, dann gehen Sie weiter und führen Sie auch den RHS-Parser. So muss es sein, weil Sie auch schreiben können:

a = optionMaybe $ do 
     s <- try $ spaceornull *> string "hello" 
     string $ "No"++s 

Hier habe ich das Ergebnis des ersten Parsers verwendet (die man einfach wegwarf, indem sie nicht <- es auf jede Variable Zusammenpassender) in entscheiden, was der zweite sollte suchen. Dies ist eindeutig nur möglich von dem ersten tatsächlich gelungen!


Grundsätzlich <|> funktioniert nur, wenn entweder die LHS sofort direkt beim ersten Zeichen fehlschlägt, oder wenn Sie einen Rückzieher Punkt mit try gesetzt. Der Grund, warum dies erforderlich ist, ist, dass es sehr ineffizient wäre, wenn Parsec vor jeder einzelnen zu prüfenden Alternative einen Rückverfolgungspunkt zurücklassen müsste.

+0

gute Antwort, aber wenn ich ersetzen 'string„Nein“' 'mit" return ‚Nein‘ '(etwas, das immer gelingt) es Rückzieher nicht einmal mit' '<|> wenn' spaceornull' gelingt – niceman

+0

Nun, ja. ..'<|>' berücksichtigt den RHS-Parser nur dann, wenn der LHS-Vorgang fehlschlägt, ohne Eingabe zu verbrauchen. Damit? – leftaroundabout

+0

was ist mit 'versuchen'? Ich meine '(try $ spaceornull *> String" Hallo ") <|> (zurück" Nein ")' sollte nicht mit der Eingabe "" "!!! – niceman

Verwandte Themen