module NumericQQ
(
  bin,
  oct,
  hex
)
where

import NumericQQ.Prelude
import Numeric
import Data.Char
import Language.Haskell.TH
import Language.Haskell.TH.Quote


type Base = Int

-- |
-- A binary number quasi-quoter. 
-- 
-- >>> [bin|011|]
-- 3
-- 
-- >>> [bin|1000001001|]
-- 521
-- 
bin :: QuasiQuoter
bin = qq 2

-- |
-- An octal number quasi-quoter.
-- 
-- >>> [oct|7634|]
-- 3996
-- 
oct :: QuasiQuoter
oct = qq 8

-- |
-- A hexadecimal number quasi-quoter.
-- 
-- >>> [hex|a23f|]
-- 41535
-- 
hex :: QuasiQuoter
hex = qq 16

qq :: Base -> QuasiQuoter
qq base = QuasiQuoter {quoteExp = exp} where
  exp s = 
    case parse base s of
      Nothing -> fail $ "A string \"" <> s <> 
                        "\" cannot be parsed as a base-" <> 
                        show base <> " number"
      Just i -> return $ LitE (IntegerL (fromIntegral i))

parse :: Base -> String -> Maybe Int
parse base string = 
  case readInt base ((< base) . digitToInt) digitToInt string of
    [] -> Nothing
    [(r, "")] -> Just r
    [(_, _)] -> Nothing
    r -> $bug $ "Unexpected parsing result: " <> show r