Search code examples
haskellparsec

Parsec - Roman Numerals - doesn't parse when I change the order, even with try


I'm writing a roman number parser for the daily programmer redit challenge. I've written a parsec parser to implement the challenge part of the challenge. This is to support brackets that multiply their contents by 1000.

(II)II = 2002

Now I've got the parser working however something isn't quite clear about using try.

This works:

romAdv :: GenParser Char st RomAdv
romAdv = complex <|> simple

But this does not:

romAdv :: GenParser Char st RomAdv
romAdv = try simple <|> complex

Can anyone please explain? I thought they would be somewhat equivalent.

Code:

data Numeral = I | V | X | L | C | D | M deriving (Read, Show)
type RomNum = [Numeral] deriving (Read, Show)
data RomAdv = Bracketed RomAdv RomNum | Simple RomNum deriving (Read, Show)

romNumbers :: GenParser Char st [RomAdv]
romNumbers = do
  numbers <- sepBy (romAdv) (char '\n')
  eof
  return numbers

romAdv :: GenParser Char st RomAdv
romAdv = complex <|> simple

complex :: GenParser Char st RomAdv
complex =
  do multed <- between (char '(') (char ')') romAdv
     remainder <- romNum
     return (Bracketed multed remainder)

simple :: GenParser Char st RomAdv
simple = do
  number <- romNum
  return (Simple number)

romNum :: GenParser Char st [Numeral]
romNum = many numeral

numeral :: GenParser Char st Numeral
numeral = do
  c <- char 'I' <|> char 'V' <|> char 'X' <|> char 'L' <|> char 'C' <|> char 'D' <|> char 'M'
  return $ read [c]

Solution

  • Figured it out.

    As it is my grammar would parse "" as (Simple []). Changing the grammar so that Simple requires at least one roman numeral gave me the desired behavior. try was working fine after all, behaving as I thought it would, I just didn't recognise it.

    data Numeral = I | V | X | L | C | D | M deriving (Read, Show)
    type RomNum = [Numeral]
    data RomAdv = Bracketed RomAdv RomNum | Simple Numeral RomNum deriving (Show)
    
    romAdv :: GenParser Char st RomAdv
    romAdv = try simple <|> complex
    
    simple :: GenParser Char st RomAdv
    simple = do
      number1 <- numeral
      number <- romNum
      return (Simple number1 number)