Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In MegaParsec is there a way of anding 2 parsers?

If I have a parser like:

notZeroOrOne :: Parser Char
notZeroOrOne = noneOf ['0' , '1'] 

Is there a way I can combine the rules of another parser like digitChar so I can get a parser that will only pass if both parsers would pass?

Something like

biggerThanOne :: Parser Char
biggerThanOne  = digitChar && notZeroOrOne 
like image 877
John Walker Avatar asked Jan 06 '23 13:01

John Walker


2 Answers

As user2407038 suggests in the comments, it's achievable using the lookAhead function.

biggerThanOne :: Parser Char
biggerThanOne =
  lookAhead digitChar *> notZeroOrOne

However, the parsers are sequential by nature, so it's both more efficient and comprehendable to apply the sequential logic. E.g., using the Monad instance of Parser:

biggerThanOne :: Parser Char
biggerThanOne =
  do
    c <- digitChar
    if c /= '0' && c /= '1'
      then return c
      else unexpected "Not bigger than one"

Or MonadPlus:

biggerThanOne :: Parser Char
biggerThanOne =
  mfilter (\c -> c /= '0' && c /= '1') digitChar

which can be refactored to use the Ord instance of Char and pretty much clearly express your intent:

biggerThanOne :: Parser Char
biggerThanOne =
  mfilter (> '1') digitChar
like image 88
Nikita Volkov Avatar answered Jan 15 '23 16:01

Nikita Volkov


In the uu-parsinglib there is an amb combinator, which gives you all possible parses. You can use this to see whether you get both parses. If you get only a single parse you can use a monad to fail

p pAnd q = amb (p <|> q) >>= \ r -> if length r == 2 then return ... else pFail

like image 32
Doaitse Swierstra Avatar answered Jan 15 '23 16:01

Doaitse Swierstra