As an exercise in learning to use Parsec, I'm writing a parser that validates balanced brackets. I'm only worried about the pairs ()
, []
, and {}
, but can't get my parser to handle multiple bracket groups inside the first.
I'm testing against a number of test cases pulled from the bracket-push exercise from exercism.io, but notable edge cases are:
isBalanced "" = True
isBalanced "(some[nonsense]with)brackets" = True
isBalanced "}{" = False -- reversed brackets no good.
isBalanced "{}}" = False -- extra ending brackets no good either.
and my parser looks like:
import Text.Parsec
import Text.Parsec.Char
hasMatchingBrackets = go >> eof
where
go = skipMany (noneOf "[{()}]")
>> optional (
between (char '(') (char ')') go
<|> between (char '{') (char '}') go
<|> between (char '[') (char ']') go )
>> skipMany (noneOf "[{()}]")
isBalanced :: String -> Bool
isBalanced xs = case parse hasMatchingBrackets "isBalanced" xs of
Right _ -> True
Left _ -> False
My parser as-is works for all the above cases, but the following test case that should pass does not
isBalanced "([{}({}[])])" = True -- Fails with the parser above!
I've identified the problem as my alternation of between
s only allowing one occurrence of go
between a pair of brackets, and the open paren in [{}(...
makes two, but I'm not sure how to fix it. I tried slapping skipMany1
in front of go
, to read
between (char '(') (char ')') (skipMany1 go)
<|> ...etc
but I get the error:
*** Exception: Text.ParserCombinators.Parsec.Prim.many: combinator 'many' is applied to a parser that accepts an empty string.