2016-01-19 18 views
9

mi piacerebbe implementare questa regola grammaticale utilizzando la libreria parsec di Haskell:Haskell parsec: `many` combinatore all'interno di un combinatore` optional`

((a | b | c)* (a | b))? 

che è una regola parser che accetta un optional (cioè potenzialmente vuoto) stringa. Se la stringa che acccepts non è vuota, allora può essere consumato passando attraverso zero o più occorrenze dei ab o c parser, ma la stringa accettata dalla più esterna ? parser opzionale deve essere consumato mediante parser a o b, ma non c. Ecco un esempio:

module Main where 

import Text.Parsec 
import Text.Parsec.Text 

a,b,c :: GenParser() Char 
a = char 'a' 
b = char 'b' 
c = char 'c' 

-- ((a | b | c)* (a | b))? 
myParser = undefined 

shouldParse1,shouldParse2,shouldParse3, 
     shouldParse4,shouldFail :: Either ParseError String 
-- these should succeed 
shouldParse1 = runParser myParser() "" "" -- because ? optional 
shouldParse2 = runParser myParser() "" "b" 
shouldParse3 = runParser myParser() "" "ccccccb" 
shouldParse4 = runParser myParser() "" "aabccab" 

-- this should fail because it ends with a 'c' 
shouldFail = runParser myParser() "" "aabccac" 

main = do 
    print shouldParse1 
    print shouldParse2 
    print shouldParse3 
    print shouldParse4 
    print shouldFail 

Un primo tentativo potrebbe essere simile a questo:

myParser = option "" $ do 
    str <- many (a <|> b <|> c) 
    ch <- a <|> b 
    return (str ++ [ch]) 

Ma la many appena consuma tutta 'a' 'b' e '' caratteri C in ogni caso di test, lasciando a <|> b nessun personaggio da consumare.

La domanda:

Utilizzando combinatori parsec, qual è la corretta attuazione del ((a | b | c)* (a | b))? per definire myParser?

+0

Forse parse (A | B | c) + e rifiutalo più tardi se termina con c? –

risposta

4

Possiamo anche affermare questo leggermente diversa: c nel parser può riuscire solo se è seguita da alcuna ragione, che può essere fatto con un unico lookAhead:

myParser = many (a <|> b <|> (c <* (lookAhead anyToken <?> "non C token"))) <* eof 
+1

grazie, funziona. Mi aspettavo quasi che non funzionasse a causa di "anyToken", avevo supposto che intendesse solo questo, sia che fosse una "d", "\ n", o qualsiasi altra cosa. Stavo pensando qualcosa come 'myParser = many (a <|> b <|> (c <* (lookAhead (prova a <|> prova b) " token non C "))) <* eof'. Tuttavia, la tua definizione di 'myParser' * funziona *. Per esempio. L'analisi "ca" restituisce "ca", mentre il tentativo di analizzare "cd" fallisce con "imprevisto" d "aspettandosi" a "," b "," c "o fine dell'input". Perché il combinatore 'anyToken' è disposto ad accettare 'a', 'b' o 'c'? –

+1

@RobStewart: a causa di '<* eof'. 'myParser' si aspetta di consumare tutti gli input. – Zeta

+0

grazie! Contrassegnato come risposta accettata. –

Problemi correlati