Search code examples
haskellparsec

Parsec if a match it found then throw error


I am trying to distinguish between Ints and floats in a parser. I have 2 parsers one for each int and float. However, I am having trouble getting into to fail on a '.'. I looked for negating and look ahead and didn't seem to get and fruits.

I hope I am not duplicating any questions.

I had it working with looking at the next character that is not a '.' but that is an ugly solution.

EDIT: Added more code.

--Int--------------------------------------------------------------------
findInt :: Parser String
findInt = plus <|> minus <|> number

number :: Parser String
number = many1 digit

plus :: Parser String
plus = char '+' *> number

minus :: Parser String
minus = char '-' <:> number

makeInt :: Parser Int
makeInt = prepareResult (findInt  <* many (noneOf ".")  <* endOfLine)
    where readInt = read :: String -> Int
          prepareResult = liftA readInt
makeInt2 :: Parser Int
makeInt2 = do
  numberFound <- (findInt  <* many (noneOf ".")  <* endOfLine)
  match <- char '.'
  return  (prepareResult numberFound)
  where readInt = read :: String -> Int
        prepareResult = readInt
--End Int----------------------------------------------------------------

Solution

  • I think you are best off actually combining the two parsers into one. Try something like this:

    import Text.Parsec.String (Parser)
    import Control.Applicative ((<|>))
    import Text.Parsec.Char (char,digit)
    import Text.Parsec.Combinator (many1,optionMaybe)
    
    makeIntOrFloat :: Parser (Either Int Float)
    makeIntOrFloat = do
        sign <- optionMaybe (char '-' <|> char '+')
        n <- many1 digit
        m <- optionMaybe (char '.' *> many1 digit)
        return $ case (m,sign) of
            (Nothing, Just '-') -> Left (negate (read n))
            (Nothing, _)        -> Left (read n)
            (Just m, Just '-')  -> Right (negate (read n + read m / 10.0^(length m)))
            (Just m, _)         -> Right (read n + read m / 10.0^(length m))
    

    ErikR has a correct solution, but the use of try means that parsec has to keep track of the possibility of backtracking (which is a bit inefficient) when in fact that is unnecessary in this case.

    Here, the key difference is that we can actually tell right away if we have a float or not - if we don't have a float, the char '.' *> many1 digit parser in optionMaybe will fail immediately (without consuming input), so there is no need to consider backtracking.

    At GHCi

    ghci> import Text.Parsec.Prim
    ghci> parseTest makeIntOrFloat "1234.012"
    Right 1234.012
    ghci> parseTest makeIntOrFloat "1234"
    Left 1234