问题
Consider a simple language that's a list of space-delimited commands. Each command takes a single letter as the command name, and a series of space-delimited numbers as its arguments; e.g. a 1 2 3 b 4 5 6 d 7 e f
would represent the following commands:
- a 1 2 3
- b 4 5 6
- d 7
- e
- f
The problem is that it keeps grabbing items through sepBy
, and reaches for another number but fails when it gets to "b".
However, this yields the following error when passed through the code below:
Left "error" (line 1, column 5):
unexpected "b"
expecting space
I realize now that this is likely coming from an error in the way I'm looking at the problem, and would appreciate someone pointing out how to parse something like this.
module Main where
import Control.Applicative hiding (many, optional, (<|>))
import Text.ParserCombinators.Parsec
import Numeric (readSigned, readFloat)
commands = command `sepBy` spaces
command = do
l <- letter
ns <- number `sepBy` spaces
return (l, ns)
main :: IO ()
main = print $ show $ parse commands "error" "a 1 2 3 b 4 5 6 d 7 e f"
number :: CharParser () Double
number = do s <- getInput
case readSigned readFloat s of
[(n, s')] -> n <$ setInput s'
_ -> empty
回答1:
Try consuming the spaces after each non-space token. E.g.
commands = many (command <* spaces)
command = do
l <- letter
spaces
ns <- many (number <* spaces)
return (l, ns)
Or
command = (,) <$> (letter <* spaces) <*> many (number <* spaces)
The main idea is: after you parse something, the next character should be a non-space starting the next item to be parsed.
回答2:
I would write the parser as @chi has suggested.
The deal with sepBy
is that if the separator is matched then sepBy
expects to parse another item.
来源:https://stackoverflow.com/questions/33881260/parsec-parsing-a-list-of-lists-both-with-the-same-delimiter