user668074
user668074

Reputation: 1141

Parsing escaped separation character in Parsec

I just started using parsec and I am trying to do something simple.

I want to separate key-value strings, as shown in this parsec tutorial.

For example, the string FirstN=Tom&LastN=Brady should give [["FirstN","Tom"],["LastN","Brady"]].

This is easy, but I would also like to allow the '=' character in the string to be escaped. For example, the string Equation=1+1\\=2 should give [["Equation", "1+1\\=2"]] (or [["Equation","1+1=2"]] but I haven't decided which is best yet).

For the simple example the parsing code is the following:

kvParser :: String -> Either ParseError [[String]]
kvParser input = parse kvString "Error text?" input

kvString = sepBy kvVal (char '&')
kvVal = sepBy (many (noneOf "=&")) (char '=')

To allow an escaped = I think I need to modify the (char '=') value but I'm not sure how. Does anyone have any suggestions?

Thanks

Edit: The final working parser is

kvParser :: String -> Either ParseError [[String]]
kvParser input = parse kvString "Error text?" input

kvString = sepBy kvVal (char '&')
kvVal = sepBy (many kvChar) (char '=')
kvChar = noneOf "\\&=" <|> (char '\\' >> anyChar)

I also got the following to work using a try combinator.

kvParser :: String -> Either ParseError [[String]]
kvParser input = parse kvString "Error text?" input

kvString = sepBy kvVal (char '&')
kvVal = sepBy (many kvChar) (char '=')
kvChar = try (string "\\=" >> return '=') <|> noneOf "&="

Upvotes: 0

Views: 486

Answers (1)

MathematicalOrchid
MathematicalOrchid

Reputation: 62848

The separator is fine; what you want is to accept \= as part of a key or value. Instead of

noneOf "=&"

you might try

(noneOf "\\&" <|> (char '\\' >> anyChar))

That is, noneOf will accept anything that isn't a backslash, otherwise the parser on the right will accept (and skip) a backslash and keep the character following it. That should prevent it being detected as a separator.

Upvotes: 3

Related Questions