2012-12-03 14 views
6

Sto provando a scrivere un parser per il calcolo proposizionale usando Parsec. Il parser utilizza la funzione buildExpressionParser da Text.Parsec.Expr. Ecco il codice in cui definisco gli operatori logici.Perché viene analizzato solo il primo operatore infisso definito quando si utilizza buildExpressionParser di Parsec?

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" 

ho omesso i parser per le variabili, i termini e le espressioni parentesi, ma se si pensa che possono essere rilevanti per il problema è possibile leggere le full source for the parser.

Il parser ha esito positivo per le espressioni che utilizzano solo la negazione e la congiunzione, ovvero l'unico operatore di prefisso e il primo operatore di infisso.

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

Espressioni che utilizzano altri operatori non riescono sul primo carattere dell'operatore, con un errore simile al seguente:

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

Se commento fuori la linea che definisce il parser per congiunzioni, allora il parser perché la disgiunzione funzionerà (ma il resto continuerà a fallire). Inserirli tutti in un'unica lista (vale a dire della stessa precedenza) non funziona neanche: lo stesso problema si manifesta ancora.

Qualcuno può indicare cosa sto facendo male? Grazie molto.


Grazie a Daniel Fischer per una risposta così rapida e utile.

Per completare il corretto funzionamento di questo parser, ho anche dovuto gestire le ripetute applicazioni del simbolo di negazione, in modo tale che ad es. ~~p potrebbe analizzare correttamente. This SO answer mi ha mostrato come farlo, e la modifica apportata al parser può essere trovata here.

risposta

8

tuo problema è che

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

il primo provato operatore infisso consuma uno spazio prima non riesce, quindi le possibilità successive non sono provati. (Parsec favorisce consumando parser, e <|> cerca solo per eseguire la seconda parser se il primo non è riuscito senza consumare alcun input.)

Per avere gli altri operatori infissi provato se la prima fallisce, si potrebbe o avvolgere le binary parser in un try

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

modo che quando un tale parser fallisce, non consuma alcun input, o, meglio, e la soluzione convenzionale a questo problema, rimuovere l'iniziale spaces da esso,

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

e hanno tutti i parser consumare spazi dopo il token hanno analizzato

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

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

Naturalmente, se si dispone di parser in grado di analizzare gli operatori con un prefisso comune, si sarebbe ancora bisogno di avvolgere quelle di un try in modo che, se ad esempio l'analisi di >= fallisce, è ancora possibile provare >>=.

Mocking un tipo di dati per le proposizioni e modificando il comportamento di consumo dello spazio, come indicato sopra,

*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')))) 

ancora un'espressione più complessa viene analizzata.

+0

Grazie per l'ottima risposta, Daniel. Risolve il problema e lo spiega anche. Sono molto grato. –

Problemi correlati