Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to parse integer with base prefix using parsec in haskell?

I'm trying to parse an input integer string in haskell using parsec. The string might either be in decimal, octal or hexadecimal. The base is specified by a prefix of #d, #o or #x for decimal, octal and hexadecimal respectively, which is then followed by the integer. If no prefix is specified, the base is assumed to be 10. Here's what I've done so far:

parseNumber = do x <- noPrefix <|> withPrefix
             return x
          where noPrefix = many1 digit
                withPrefix = do char '#'
                                prefix <- oneOf "dox"
                                return $ case prefix of
                                    'd' -> many1 digit
                                    'o' -> fmap (show . fst . head . readOct) (many1 octDigit)
                                    'x' -> fmap (show . fst . head . readHex) (many1 hexDigit)

However, this isn't compiling and is failing with type errors. I don't quite really understand the type error and would just like help in general with this problem. Any alternative ways to solve it will also be appreciated.

Thank you for your time and help. :)

EDIT: Here's the error I've been getting.

like image 719
sayantankhan Avatar asked Apr 29 '26 21:04

sayantankhan


1 Answers

In Megaparsec—a modern fork of Parsec, this problem is non-existent (from documentation of hexadecimal):

Parse an integer in hexadecimal representation. Representation of hexadecimal number is expected to be according to Haskell report except for the fact that this parser doesn't parse “0x” or “0X” prefix. It is responsibility of the programmer to parse correct prefix before parsing the number itself.

For example you can make it conform to Haskell report like this:

hexadecimal = char '0' >> char' 'x' >> L.hexadecimal

So in your case you can just define (note how it's more readable):

import Data.Void
import Text.Megaparsec
import Text.Megaparsec.Char
import qualified Text.Megaparsec.Char.Lexer as L

type Parser = Parsec Void String

parseNumber :: Parser Integer
parseNumber = choice
  [ L.decimal
  , (string  "o#" *> L.octal)       <?> "octal integer"
  , (string  "d#" *> L.decimal)     <?> "decimal integer"
  , (string  "h#" *> L.hexadecimal) <?> "hexadecimal integer" ]

Let's try the parser (note quality of error messages):

λ> parseTest' (parseNumber <* eof) ""
1:1:
  |
1 | <empty line>
  | ^
unexpected end of input
expecting decimal integer, hexadecimal integer, integer, or octal integer
λ> parseTest' (parseNumber <* eof) "d#3"
3
λ> parseTest' (parseNumber <* eof) "h#ff"
255
λ> parseTest' (parseNumber <* eof) "o#8"
1:3:
  |
1 | o#8
  |   ^
unexpected '8'
expecting octal integer
λ> parseTest' (parseNumber <* eof) "o#77"
63
λ> parseTest' (parseNumber <* eof) "190"
190

Full-disclosure: I'm the author/maintainer of Megaparsec.

like image 111
Mark Karpov Avatar answered May 01 '26 11:05

Mark Karpov



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!