I am using the Parsec
module in Haskell to parse files. One of the components of these files are colors. I have created a type for colors like this:
data Color = Yellow | Red | Blue | Green deriving (Show)
My initial attempt at parsing colors looks like this:
symbol :: String -> Parsec String () String
symbol s = spaces >> string s
colorP :: Parsec String () Color
colorP =
liftM mkColor $ symbol "Yellow" <|> symbol "Red" <|> symbol "Blue" <|> symbol "Green"
where
mkColor :: String -> Color
mkColor "Yellow" = Yellow
mkColor "Red" = Red
mkColor "Blue" = Blue
mkColor "Green" = Green
I created the symbol
parser which basically eats as much whitespace as possible and then eats the given string s
. However, this does not seem to work. I test this code with the following call:
parse colorP "" " Red"
This gave me the following error:
unexpected "R"
expecting space or "Yellow"
However, I went looking on Hoogle for the documentation that goes with the <|>
operator and there I found the following:
This combinator implements choice. The parser p <|> q first applies p. If it succeeds, the value of p is returned. If p fails without consuming any input, parser q is tried.
So, I think the problem with the above example is that parser p
(in this case symbol "Yellow"
) already did consume some input, namely the blanks! So, I refactored my colorP
like this:
colorP =
liftM mkColor $ spaces >> (string "Yellow" <|> string "Red" <|> string "Blue" <|> string "Green")
where
-- mkColor same as before
which gives the result I want.
Now, I am wondering if there is no parser like the symbol
parser that I wrote, but that puts back the input in case of a failure. Or is the second implementation of colorP
the one that is most Haskell-ish?
You could use try