I am learning to use Parsec by parsing lines in a text file. What I have is the following:
import Text.Parsec (ParseError, parse)
import Text.Parsec.String (Parser)
import Text.Parsec.Char (anyChar, digit, char, oneOf)
import Control.Monad (void)
import qualified Text.Parsec.Combinator as C
data Action =
ActionA Int Int
| ActionB Int Int Int Int Int
| ActionC Int Int Int
deriving (Show)
parseWithEof :: Parser a -> String -> Either ParseError a
parseWithEof p = parse (p <* C.eof) ""
parseActionA :: Parser Action
parseActionA = do
char 'A'
void $ oneOf " "
a <- C.many1 digit
void $ oneOf " "
b <- C.many1 digit
return $ ActionA (read a) (read b)
parseActionB :: Parser Action
parseActionB = do
char 'B'
void $ oneOf " "
a <- C.many1 digit
void $ oneOf " "
b <- C.many1 digit
void $ oneOf " "
c <- C.many1 digit
void $ oneOf " "
d <- C.many1 digit
void $ oneOf " "
e <- C.many1 digit
return $ ActionB (read a) (read b) (read c) (read d) (read e)
parseActionC :: Parser Action
parseActionC = do
char 'C'
void $ oneOf " "
a <- C.many1 digit
void $ oneOf " "
b <- C.many1 digit
void $ oneOf " "
c <- C.many1 digit
return $ ActionC (read a) (read b) (read c)
I would like to be able to generalize these parsing functions since I feel that they are repetitive. I don't know if that is possible, or how it is possible.
I would also like to know if it is possible to have a function like this:
parseAction :: String -> Either ParseError Action
parseAction input =
parseWithEof parseActionA input
<some operator|combinator> parseWithEof parseActionB input
<some operator|combinator> parseWithEof parseActionC input
So when parseAction
receives a string as parameter it will try to parse it with the different parsers. I expect it to return (Left ParseError) if no parser could parse the input and (Right Action) if a parser succeeded in parsing the input.
Is it possible?
Using Applicative combinators you can write:
num = do oneOf " "; fmap read (C.many1 digit)
parseActionA = ActionA <$> (char 'A' >> num) <*> num
parseActionB = ActionB <$> (char 'B' >> num) <*> num <*> num <*> num <*> num
For your second question, just use <|>
with try
parseAction = try parseActionA <|> try parseActionB <|> try parseActionC
Note - try
shouldn't be needed on the last parser, but it doesn't hurt to have it. Also, if you know enough about how your parsers work you may be able to do away with some of the try
s.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.