packages feed

jacinda-3.3.0.0: src/Jacinda/Backend/Printf.hs

module Jacinda.Backend.Printf ( sprintf ) where

import           A
import qualified Data.ByteString                   as BS
import           Data.ByteString.Builder           (toLazyByteString)
import           Data.ByteString.Builder.RealFloat (doubleDec)
import qualified Data.ByteString.Lazy              as BSL
import           Data.Char                         (isDigit)
import qualified Data.Text                         as T
import           Data.Text.Encoding                (decodeUtf8, encodeUtf8)

sprintf :: BS.ByteString -- ^ Format string
        -> E a
        -> BS.ByteString
sprintf fmt e = encodeUtf8 (sprintf' (decodeUtf8 fmt) e)

pf :: Double -> T.Text
pf = decodeUtf8 . BSL.toStrict . toLazyByteString . doubleDec

pd n x = let (l,r) = T.break (=='.') (pf x) in l <> T.take (n+1) r

data PF = D !(Maybe Int) | I | B | S

{-# SCC next #-}
next :: T.Text -> (T.Text, Maybe (PF, T.Text))
next inp = case T.break (=='%') inp of
    (p, mfmt) | "%i" `T.isPrefixOf` mfmt -> (p, Just (I, T.drop 2 mfmt))
              | "%s" `T.isPrefixOf` mfmt -> (p, Just (S, T.drop 2 mfmt))
              | "%b" `T.isPrefixOf` mfmt -> (p, Just (B, T.drop 2 mfmt))
              | "%f." `T.isPrefixOf` mfmt -> let s=T.drop 3 mfmt in
                    case precision s of
                        Nothing    -> error "Missing digit in format specifier."
                        Just (n,f) -> (p, Just (D (Just n), f))
              | "%f" `T.isPrefixOf` mfmt -> (p, Just (D Nothing, T.drop 2 mfmt))
              | otherwise -> (p, Nothing)

precision :: T.Text -> Maybe (Int, T.Text)
precision t =
    let (n,f) = T.span isDigit t  in
    if T.null n then Nothing else Just (read (T.unpack n), f)

{-# SCC sp #-}
sp :: T.Text -> [E a] -> T.Text
sp t [] = t
sp t (e:es) =
    case (next t, e) of
        ((_, Nothing), _)                             -> error "Argument to sprintf has more values than expected by format string."
        ((p, Just (D Nothing, fmt)), Lit _ (FLit f))  -> p <> pf f <> sp fmt es
        ((p, Just (D (Just n), fmt)), Lit _ (FLit f)) -> p <> pd n f <> sp fmt es
        ((p, Just (B, fmt)), Lit _ (BLit b))          -> p <> showBool b <> sp fmt es
        ((p, Just (I, fmt)), Lit _ (ILit i))          -> p <> T.pack (show i) <> sp fmt es
        ((p, Just (S, fmt)), Lit _ (StrLit bs))       -> p <> decodeUtf8 bs <> sp fmt es
        ((_, Just{}), _)                              -> error "Argument type does not match sprintf format string."

  where
    showBool True = "#t"; showBool False = "#f"

sprintf' :: T.Text -> E a -> T.Text
sprintf' fmt e@Lit{}    = sp fmt [e]
sprintf' fmt (Tup _ es) = sp fmt es