th-printf-0.8: src/Language/Haskell/Printf/Printers.hs
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE NoMonomorphismRestriction #-}
module Language.Haskell.Printf.Printers where
import Data.Char
import Data.Maybe (fromMaybe)
import Data.String (IsString, fromString)
import qualified Data.Text as S
import qualified Data.Text.Lazy as L
import Foreign.Ptr
import GHC.Float (FFFormat (..))
import Language.Haskell.Printf.Geometry
import Language.Haskell.PrintfArg
import Math.NumberTheory.Logarithms
import Buf
import NumUtils
import qualified Parser.Types as P
type Printer n buf = PrintfArg n -> Value buf
printfString :: (Buf buf) => Printer String buf
printfString spec =
Value
{ valArg = case prec spec of
Nothing -> str <$> spec
Just c -> str . take c <$> spec
, valPrefix = Nothing
, valSign = Nothing
}
printfStrictText :: (Buf buf) => Printer S.Text buf
printfStrictText spec =
Value
{ valArg = case prec spec of
Nothing -> sText <$> spec
Just c -> sText . S.take c <$> spec
, valPrefix = Nothing
, valSign = Nothing
}
printfLazyText :: (Buf buf) => Printer L.Text buf
printfLazyText spec =
Value
{ valArg = case prec spec of
Nothing -> lText <$> spec
Just c -> lText . L.take (fromIntegral c) <$> spec
, valPrefix = Nothing
, valSign = Nothing
}
printfShow :: (Buf buf, Show a) => Printer a buf
printfShow spec = printfString (fromString . show <$> spec)
printfChar :: (Buf buf) => Printer Char buf
printfChar spec =
Value
{ valArg = singleton <$> spec
, valPrefix = Nothing
, valSign = Nothing
}
{-# ANN printfPtr ("HLint: ignore Use showHex" :: String) #-}
printfPtr :: (Buf buf) => Printer (Ptr a) buf
printfPtr spec =
Value
{ valArg =
PrintfArg
{ width = width spec
, prec = Nothing
, flagSet = P.emptyFlagSet{P.prefixed = True}
, lengthSpec = Nothing
, fieldSpec = 'p'
, value = showIntAtBase 16 intToDigit (ptrToWordPtr $ value spec)
}
, valPrefix = Just (str "0x")
, valSign = Nothing
}
printfDecimal :: (Buf buf, Show n, Integral n) => PrintfArg n -> Value buf
printfDecimal spec =
Value
{ valArg = padDecimal spec . showIntAtBase 10 intToDigit . abs <$> spec
, valPrefix = Nothing
, valSign = sign' spec
}
fmtUnsigned ::
(Bounded a, Integral a, Buf buf) =>
(Integer -> buf) ->
(PrintfArg a -> Maybe buf) ->
Printer a buf
fmtUnsigned shower p spec =
Value
{ valArg = padDecimal spec . shower . clampUnsigned <$> spec
, valPrefix = p spec
, valSign = Nothing
}
printfHex :: (Bounded a, Integral a, Buf buf, IsString buf) => Bool -> Printer a buf
printfHex b = fmtUnsigned showHex (prefix (if b then "0X" else "0x"))
where
showHex = showIntAtBase 16 ((if b then toUpper else id) . intToDigit)
printfUnsigned :: (Bounded a, Integral a, Buf buf) => Printer a buf
printfUnsigned = fmtUnsigned (showIntAtBase 10 intToDigit) (const Nothing)
-- printing octal is really annoying. consider
--
-- printf "%#-8.5x" 1234
--
-- "0x004d2 "
-- ^~~~~~~^ width (8)
-- ^~~~^ precision (5)
-- ^^ prefix (2)
-- ^^ padding (2)
--
-- printf "%#-8.5o" 1234
--
-- "02322 "
-- ^~~~~~~^ width (8)
-- ^~~~^ precision (5)
-- ^ prefix (1)
-- ^ padding (1, same character)
--
-- in octal, when combining prefix and padding, the prefix
-- must eat the first padding char
{-# ANN printfOctal ("HLint: ignore Use showOct" :: String) #-}
printfOctal :: (Buf buf, IsString buf, Bounded n, Integral n) => PrintfArg n -> Value buf
printfOctal spec =
fmtUnsigned
(showIntAtBase 8 intToDigit)
(\y -> if shouldUnpad then Nothing else prefix "0" y)
spec
where
expectedWidth = integerLogBase 8 (max 1 $ clampUnsigned $ value spec) + 1
shouldUnpad = prefixed spec && fromMaybe 0 (prec spec) > expectedWidth
printfFloating :: (Buf buf, RealFloat n) => Bool -> PrintfArg n -> Value buf
printfFloating upperFlag spec =
Value
{ valArg = showFloat . abs <$> spec
, valPrefix = Nothing
, valSign = sign' spec
}
where
precision = case prec spec of
Just n -> Just (fromIntegral n)
Nothing | Just P.ZeroPadded <- adjustment spec -> Just 6
_ -> Nothing
showFloat = formatRealFloatAlt FFFixed precision (prefixed spec) upperFlag
printfScientific :: (Buf buf, RealFloat n) => Bool -> PrintfArg n -> Value buf
printfScientific upperFlag spec =
Value
{ valArg = showSci . abs <$> spec
, valPrefix = Nothing
, valSign = sign' spec
}
where
showSci =
formatRealFloatAlt
FFExponent
(fromIntegral <$> prec spec)
(prefixed spec)
upperFlag
printfGeneric :: (Buf buf, RealFloat n) => Bool -> PrintfArg n -> Value buf
printfGeneric upperFlag spec =
Value
{ valArg = showSci . abs <$> spec
, valPrefix = Nothing
, valSign = sign' spec
}
where
showSci =
formatRealFloatAlt
FFGeneric
(fromIntegral <$> prec spec)
(prefixed spec)
upperFlag
printfFloatHex :: (Buf buf, RealFloat n, IsString buf) => Bool -> PrintfArg n -> Value buf
printfFloatHex upperFlag spec =
Value
{ valArg = showHexFloat . abs <$> spec
, valPrefix = Just (if upperFlag then "0X" else "0x")
, valSign = sign' spec
}
where
showHexFloat =
formatHexFloat (fromIntegral <$> prec spec) (prefixed spec) upperFlag
clampUnsigned :: (Bounded a, Integral a) => a -> Integer
clampUnsigned x
| x < 0 = toInteger x + (-2 * toInteger (minBound `asTypeOf` x))
| otherwise = toInteger x