
Michael Mossey schrieb:
Here's what I eventually came up with. I decided to make a function parseArg that can parse either "v" args or "c" args, and use
sepBy parseArg space
You may want to reject duplicate v or c args rather than to discard them silently. You can do so by checking the list before returning "Play singleV singleC" and use the "fail" or "unexpected" parser for non-proper lists. C.
to parse all the args on the command line. I created the algebraic type Arg to express both kinds of args, which I call Verts and Chans, so that parseArg can have the signature
parseArg :: Parser Arg
and parsing the whole command line:
parseArgs :: Parser [Arg]
Then I grab the first Verts and first Chans from the resulting list, and ignore the other ones (if any others are present). I use fromMaybe to supply a default value.
data Command = Forward Int | Backward Int | Jump Int | Play Int [Int] | Quit deriving (Show)
data Arg = Verts Int | Chans [Int] deriving (Show)
integer :: Parser Int integer = do ds <- many1 digit return (read ds)
digitList :: Parser [Int] digitList = do d <- digit remainder <- digitList return $ read [d] : remainder <|> return []
-- Parse an argument for the p command. -- Note that so-called "v" args are now just bare integers. -- "c" args are still prefaced with a "c" parseArg :: Parser Arg parseArg = do fmap Verts integer <|> do char 'c' fmap Chans digitList
parseArgs :: Parser [Arg] parseArgs = sepBy parseArg space
parseP :: Parser Command parseP = (do many space -- Zero or more arguments may be present. -- We will consider at most one "v" argument -- and at most one "c" argument. There are -- default values for the "V" and "C" arguments -- when none are present. args <- parseArgs let vArgs = [i | Verts i <- args] cArgs = [c | Chans c <- args] singleV = fromMaybe 1 (listToMaybe vArgs) singleC = fromMaybe [] (listToMaybe cArgs) return $ Play singleV singleC) <|> (return $ Play 1 [])
parseCommand :: Parser Command parseCommand = do char 'j' fmap Jump integer <|> do char 'f' fmap Forward (option 1 integer) <|> do char 'b' fmap Backward (option 1 integer) <|> do char 'p' parseP <|> do char 'q' return Quit
runParse :: String -> Either String Command runParse s = case parse parseCommand "" s of Left err -> Left $ show err Right x -> Right x