[Haskell-beginners] Parsec and Validation

Stephen Tetley stephen.tetley at gmail.com
Sat Jul 31 17:47:31 EDT 2010


Hello Vladimir

In general it is better to avoid the try combinator and left factor
the grammar instead.

Because parsers are functions you can abuse left factoring a bit,
parse the prefix in a top level combinator and supply the prefix to
the sub-parsers:

leftFactoredTime :: Parser TimeOfDay
leftFactoredTime = do
  hh   <- width2Num
  sep  <- optionMaybe (oneOf ";,.")
  case sep of
    Nothing -> tTimeHourMin hh
    Just _  -> t24hrClock hh

tTimeHourMin :: Int -> Parser TimeOfDay
tTimeHourMin hh = do
  mm <- width2Num
  return (TimeOfDay hh mm 0)


t24hrClock :: Int -> Parser TimeOfDay
t24hrClock hh = do
  mm <- width2Num
  return (TimeOfDay hh mm 0)


However in this case, the 24 hour clock and TimeHourMin are identical
functions, the separator is a McGuffin [*] so:

betterTime :: Parser TimeOfDay
betterTime = do
  hh    <- rangeP 0 23 width2Num
  _sep  <- optionMaybe (oneOf ";,.")
  mm    <- rangeP 0 59 width2Num
  return (TimeOfDay hh mm 0)

To parse ranges I would make a new combinator that takes a range plus
a number parser and returns a new number parser:


rangeP :: Int -> Int -> Parser Int -> Parser Int
rangeP hi lo p = do
  a <- p
  if (lo <= a && a <= hi) then return a else fail "out-of-range"

Finally avoiding using read is good when using Parsec. Parsec has
token parsers to read numbers, but here read can be avoided with this
one:

width2Num :: Parser Int
width2Num = do
   a <- digit
   b <- digit
   return $ (10*digitToInt a) + digitToInt b


digitToInt is in the Data.Char module.

[*] A plot device used by Alfred Hitchcock films to throw the viewer
off the scent.


More information about the Beginners mailing list