2016-01-19 27 views
9

Chciałbym wdrożyć tę zasadę gramatyki używając Haskell za parsec Biblioteka:Haskell parsec: `many` syntezatora wewnątrz` optional` combinator

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

co jest regułą, że parser przyjmuje opcjonalny (czyli potencjalnie pusty) strunowy. Jeśli łańcuch jest acccepts nie jest pusty, to może być spożywany przez przepuszczenie przez zero lub więcej wystąpień ab lub c parserami, ale akceptowane ciąg przez zewnętrzną najbardziej ? opcjonalnym parsera musi być spożywane zarówno przez parser a lub b, ale nie c. Oto przykład:

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 

Pierwsza próba może wyglądać następująco:

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

Ale many tylko konsumuje wszystko 'A' 'b' i 'znaków C' w każdym przypadku testowego, pozostawiając a <|> b żadnych postaci do konsumpcji.

Pytanie:

Korzystanie parsec kombinatorów, jaka jest prawidłowa realizacja ((a | b | c)* (a | b))? zdefiniować myParser?

+0

Może parse (a | b | c) + i odrzucić go później, jeśli kończy się na c? –

Odpowiedz

4

Możemy również stwierdzić to nieco inaczej: c w parser może dokonać jedynie to następuje dowolny znak, co można zrobić z jednym lookAhead:

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

dzięki, to działa. W połowie spodziewałem się, że nie zadziała z powodu "anyToken", który zakładałem, że ma na myśli właśnie to, czy to jest "d", "\ n", czy cokolwiek innego. Myślałem coś w stylu 'myParser = many (a <|> b <|> (c <* (lookAhead (spróbuj <|> try b) " non C token "))) <* eof'. Jednak twoja definicja 'myParser' * działa *. Na przykład. Parsowanie "ca" zwraca "ca", podczas gdy próba parsowania "cd" kończy się niepowodzeniem z "nieoczekiwanym" d. Oczekuje "a", "b", "c" lub końca wejścia ". Dlaczego kombinator "anyToken" akceptuje tylko "a", "b" lub "c"? –

+1

@RobStewart: Ze względu na '<* eof'. 'myParser' spodziewa się pochłonąć wszystkie dane wejściowe. – Zeta

+0

dzięki! Oznaczona jako zaakceptowana odpowiedź. –