2016-01-19 7 views
9
Ich mag

würden diese Grammatikregel mit Haskells Parsec Bibliothek implementieren:Haskell Parsec: `many` combinator innerhalb einer` optional` combinator

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

dem ein Parser-Regel ist, die eine optionale (dh potentiell leer akzeptiert) Zeichenfolge. Wenn die Zeichenfolge es acccepts nicht leer ist, dann kann es, indem sie durch null oder mehr Vorkommen der ab oder c Parser verbraucht werden, aber die akzeptierte Zeichenfolge, die von den äußeren meist ? optional Parser muss verbraucht wird entweder durch Parser a oder b, aber nicht c. Hier ein Beispiel:

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 

Ein erster Versuch könnte wie folgt aussehen:

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

Aber die many verbraucht nur all 'a' 'b' und 'c' Zeichen in jedem Testfall, a <|> b verlassen keine zu konsumierenden Zeichen

Die Frage:

Parsec combinators benutzen, was ist die korrekte Umsetzung der ((a | b | c)* (a | b))?-myParser zu definieren?

+0

Vielleicht analysieren (a | b | c) + und lehne es später ab, wenn es mit c endet? –

Antwort

4

Wir haben auch diese etwas anders angeben können: nur dann erfolgreich sein kann c in Ihrem Parser, wenn sie von jedem Token gefolgt wird, die mit einem einzigen lookAhead getan werden kann:

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

danke, das funktioniert. Ich hatte halb damit gerechnet, dass es nicht funktionieren würde, weil 'anyToken' genau das bedeutet hätte, sei es ein 'd', '\ n' oder was auch immer. Ich dachte etwas wie 'myParser = viele (a <|> b <|> (c <* (lookAhead (versuchen Sie eine <|> Versuch b) " non C-Token "))) <* eof'. Ihre Definition von 'myParser' * funktioniert jedoch *. Z.B. Parsing "ca" gibt "ca" zurück, während "cd" zu parsen versucht mit "unerwartete 'd'. Erwartet" a "," b "," c "oder Ende der Eingabe". Warum akzeptiert der 'anyToken'-Kombinator nur 'a', 'b' oder 'c'? –

+1

@RobStewart: Wegen '<* eof'. 'myParser' erwartet alle Eingaben zu verbrauchen. – Zeta

+0

danke! Als akzeptierte Antwort markiert. –