2012-12-03 2 views
6

Ich versuche, einen Parser für den aussagenlogischen Kalkül mit Parsec zu schreiben. Der Parser verwendet die -Funktion von Text.Parsec.Expr. Hier ist der Code, in dem ich die logischen Operatoren definiere.Warum analysiert nur der erste definierte Infix-Operator, wenn Parsec buildExpressionParser verwendet wird?

operators = [ [Prefix (string "~" >> return Negation)] 
      , [binary "&" Conjunction] 
      , [binary "|" Disjunction] 
      , [binary "->" Conditional] 
      , [binary "<->" Biconditional] 
      ] 

binary n c = Infix (spaces >> string n >> spaces >> return c) AssocRight 

expr = buildExpressionParser operators term 
    <?> "compound expression" 

Ich habe die Parser für Variablen, Begriffe und Ausdrücke parenthesised weggelassen, aber wenn Sie denken, dass sie für das Problem relevant sein können Sie die full source for the parser lesen kann.

Der Parser ist erfolgreich für Ausdrücke, die nur Negation und Konjunktion verwenden, d. H. Der einzige Präfixoperator und der erste Infixoperator.

*Data.Logic.Propositional.Parser2> runPT expr() "" "p & ~q" 
Right (p ∧ ¬q) 

Ausdrücke alle anderen Operatoren nicht auf dem ersten Zeichen des Betreibers, mit einem Fehler wie folgt aus:

*Data.Logic.Propositional.Parser2> runPT expr() "" "p | q" 
Left (line 1, column 3): 
unexpected "|" 
expecting space or "&" 

Wenn ich die Linie definieren den Parser für Konjunktionen kommentieren, dann den Parser für disjunction wird funktionieren (aber der Rest wird immer noch scheitern). Sie alle in eine einzige Liste (d. H. Mit der gleichen Priorität) zu setzen, funktioniert auch nicht: das gleiche Problem manifestiert sich immer noch.

Kann jemand darauf hinweisen, was ich falsch mache? Danke vielmals.


Danke an Daniel Fischer für eine so schnelle und hilfreiche Antwort.

Um zu beenden, diesen Parser korrekt zu arbeiten, musste ich auch wiederholte Anwendungen des Negationssymbols behandeln, so dass z. ~~p würde korrekt analysieren. This SO answer zeigte mir, wie es geht, und die Änderung, die ich an den Parser gemacht habe, kann here gefunden werden.

Antwort

8

Ihr Problem ist, dass

binary n c = Infix (spaces >> string n >> spaces >> return c) AssocRight 

die erste versuchte Infixoperator einen Raum verbraucht, bevor es versagt, so die späteren Möglichkeiten nicht versucht. (Parsec begünstigt Parser raubend und <|> versucht, nur den zweiten Parser ausgeführt werden, wenn die ersten ohne konnte keine Eingabe raubend.)

die anderen Infixoperatoren versucht zu haben, wenn die ersten fehlschlägt, können Sie entweder wickeln die binary Parser in einem try

binary n c = Infix (try $ ...) AssocRight 

so dass, wenn ein solcher Parser fehlschlägt, macht es keinen Eingang verbrauchen, oder, besser, und die herkömmliche Lösung für dieses Problem, entfernen sie die anfängliche spaces von ihm,

binary n c = Infix (string n >> spaces >> return c) AssocRight 

und haben alle Ihre Parser verbrauchen Räume nach das Token sie

variable = do c <- letter 
       spaces 
       return $ Variable (Var c) 
     <?> "variable" 

parens p = do char '(' 
       spaces 
       x <- p 
       char ')' 
       spaces 
       return x 
     <?> "parens" 

Natürlich analysiert, wenn Sie Parser haben, dass die Betreiber mit einem gemeinsamen Präfix analysieren kann, würden Sie noch diejenigen in eine wickeln müssen try, so dass, wenn z. B. Parsing >= fehlschlägt, >>= noch ausprobiert werden kann.

Mocking einen Datentyp für die Sätze und die Änderung der platzraubende Verhalten wie oben angegeben,

*PropositionalParser Text.Parsec> head $ runPT expr() "" "p | q -> r & s" 
Right (Conditional (Disjunction (Variable (Var 'p')) (Variable (Var 'q'))) (Conjunction (Variable (Var 'r')) (Variable (Var 's')))) 

auch ein komplizierter Ausdruck wird analysiert.

+0

Danke für die tolle Antwort, Daniel. Löst das Problem und erklärt es auch. Ich bin sehr dankbar. –

Verwandte Themen