printf
Lennart Augustsson
lennart at augustsson.net
Tue Nov 23 16:37:42 EST 2004
John Goerzen wrote:
> On Tue, Nov 23, 2004 at 09:59:35PM +0100, Lennart Augustsson wrote:
>
>>John Goerzen wrote:
>>
>>>On 2004-11-23, Lennart Augustsson <lennart at augustsson.net> wrote:
>>>
>>>
>>>>Being an old C programmer I'd like to suggest a library, Text.Printf,
>>>>for C printf() style formatting.
>>>
>>>
>>>Please take a look at MissingH.Printf:
>>>
>>>http://gopher.quux.org:70/devel/missingh/html/MissingH.Printf.html
>>>
>>
>>My printf is a little different in that it doesn't require a union
>>type for the arguments, nor do they have to be in a list. But
>>those are details.
>
>
> Where can I find yours?
>
>
>> -- Lennart
>>
>>
>
>
-------------- next part --------------
-----------------------------------------------------------------------------
-- |
-- Module : Text.Printf
-- Copyright : (c) Lennart Augustsson, 2004
-- License : No license, do whatever you like
--
-- Maintainer : lennart at augustsson.net
-- Stability : provisional
-- Portability : portable
--
-- $Id$
--
--
-- A C printf like formatter.
-- Conversion specs:
-- - left adjust
-- num field width
-- * as num, but taken from argument list
-- . separates width from precision
-- Formatting characters:
-- c Char, Int, Integer
-- d Char, Int, Integer
-- o Char, Int, Integer
-- x Char, Int, Integer
-- u Char, Int, Integer
-- f Float, Double
-- g Float, Double
-- e Float, Double
-- s String
--
-- The printf function takes a formatting string followed by a variable
-- number of arguments. It returns a String or an IO a.
--
-----------------------------------------------------------------------------
module Printf(printf) where
import Array
import Char
import Numeric(showEFloat, showFFloat, showGFloat)
-------------------
-- | Format a variable number of arguments with the C style formatting string.
-- The return value is a String or (IO a).
printf :: (PrintfType r) => String -> r
printf fmt = spr fmt []
class PrintfType t where
spr :: String -> [UPrintf] -> t
{- not allowed in Haskell 98
instance PrintfType String where
spr fmt args = uprintf fmt (reverse args)
-}
instance (IsChar c) => PrintfType [c] where
spr fmt args = map fromChar (uprintf fmt (reverse args))
instance PrintfType (IO a) where
spr fmt args = do
putStr (uprintf fmt (reverse args))
return undefined
instance (PrintfArg a, PrintfType r) => PrintfType (a -> r) where
spr fmt args = \ a -> spr fmt (toUPrintf a : args)
class PrintfArg a where
toUPrintf :: a -> UPrintf
instance PrintfArg Char where
toUPrintf c = UChar c
{- not allowed in Haskell 98
instance PrintfArg String where
toUPrintf s = UString s
-}
instance (IsChar c) => PrintfArg [c] where
toUPrintf s = UString (map toChar s)
instance PrintfArg Int where
toUPrintf i = UInt i
instance PrintfArg Integer where
toUPrintf i = UInteger i
instance PrintfArg Float where
toUPrintf f = UFloat f
instance PrintfArg Double where
toUPrintf d = UDouble d
class IsChar c where
toChar :: c -> Char
fromChar :: Char -> c
instance IsChar Char where
toChar c = c
fromChar c = c
-------------------
data UPrintf = UChar Char | UString String | UInt Int | UInteger Integer | UFloat Float | UDouble Double
uprintf :: String -> [UPrintf] -> String
uprintf "" [] = ""
uprintf "" (_:_) = fmterr
uprintf ('%':'%':cs) us = '%':uprintf cs us
uprintf ('%':_) [] = argerr
uprintf ('%':cs) us@(_:_) = fmt cs us
uprintf (c:cs) us = c:uprintf cs us
fmt :: String -> [UPrintf] -> String
fmt cs us =
let (width, prec, ladj, zero, cs', us') = getSpecs False False cs us
adjust (pre, str) =
let lstr = length str
lpre = length pre
fill = if lstr+lpre < width then take (width-(lstr+lpre)) (repeat (if zero then '0' else ' ')) else ""
in if ladj then pre ++ str ++ fill else if zero then pre ++ fill ++ str else fill ++ pre ++ str
in
case cs' of
[] -> fmterr
c:cs'' ->
case us' of
[] -> argerr
u:us'' ->
(case c of
'c' -> adjust ("", [toEnum (toint u)])
'd' -> adjust (fmti u)
'x' -> adjust ("", fmtu 16 u)
'o' -> adjust ("", fmtu 8 u)
'u' -> adjust ("", fmtu 10 u)
'e' -> adjust (dfmt' c prec u)
'f' -> adjust (dfmt' c prec u)
'g' -> adjust (dfmt' c prec u)
's' -> adjust ("", tostr u)
c -> perror ("bad formatting char " ++ [c])
) ++ uprintf cs'' us''
fmti (UInt i) = if i < 0 then
if i == -i then fmti (UInteger (toInteger i)) else ("-", itos (-i))
else
("", itos i)
fmti (UInteger i) = if i < 0 then ("-", itos (-i)) else ("", itos i)
fmti (UChar c) = fmti (UInt (fromEnum c))
fmti u = baderr
fmtu b (UInt i) = if i < 0 then
if i == -i then itosb b (maxi - toInteger (i+1) - 1) else itosb b (maxi - toInteger (-i))
else
itosb b (toInteger i)
fmtu b (UInteger i) = itosb b i
fmtu b (UChar c) = itosb b (toInteger (fromEnum c))
fmtu b u = baderr
maxi :: Integer
maxi = (toInteger (maxBound::Int) + 1) * 2
toint (UInt i) = i
toint (UInteger i) = toInt i
toint (UChar c) = fromEnum c
toint u = baderr
tostr (UString s) = s
tostr u = baderr
itos n =
if n < 10 then
[toEnum (fromEnum '0' + toInt n)]
else
let (q, r) = quotRem n 10 in
itos q ++ [toEnum (fromEnum '0' + toInt r)]
chars = array (0,15) (zipWith (,) [0..] "0123456789abcdef")
itosb :: Integer -> Integer -> String
itosb b n =
if n < b then
[chars!n]
else
let (q, r) = quotRem n b in
itosb b q ++ [chars!r]
stoi :: Int -> String -> (Int, String)
stoi a (c:cs) | isDigit c = stoi (a*10 + fromEnum c - fromEnum '0') cs
stoi a cs = (a, cs)
getSpecs :: Bool -> Bool -> String -> [UPrintf] -> (Int, Int, Bool, Bool, String, [UPrintf])
getSpecs l z ('-':cs) us = getSpecs True z cs us
getSpecs l z ('0':cs) us = getSpecs l True cs us
getSpecs l z ('*':cs) us =
case us of
[] -> argerr
nu : us' ->
let n = toint nu
(p, cs'', us'') =
case cs of
'.':'*':r -> case us' of { [] -> argerr; pu:us'' -> (toint pu, r, us'') }
'.':r -> let (n, cs') = stoi 0 r in (n, cs', us')
_ -> (-1, cs, us')
in (n, p, l, z, cs'', us'')
getSpecs l z ('.':cs) us =
let (p, cs') = stoi 0 cs
in (0, p, l, z, cs', us)
getSpecs l z cs@(c:_) us | isDigit c =
let (n, cs') = stoi 0 cs
(p, cs'') = case cs' of
'.':r -> stoi 0 r
_ -> (-1, cs')
in (n, p, l, z, cs'', us)
getSpecs l z cs us = (0, -1, l, z, cs, us)
dfmt' c p (UDouble d) = dfmt c p d
dfmt' c p (UFloat f) = dfmt c p f
dfmt' c p u = baderr
dfmt c p d =
case (case c of 'e' -> showEFloat; 'f' -> showFFloat; 'g' -> showGFloat)
(if p < 0 then Nothing else Just p) d "" of
'-':cs -> ("-", cs)
cs -> ("" , cs)
perror s = error ("Printf.printf: "++s)
fmterr = perror "formatting string ended prematurely"
argerr = perror "argument list ended prematurely"
baderr = perror "bad argument"
toInt :: (Integral a) => a -> Int
toInt x = fromInteger (toInteger x)
More information about the Libraries
mailing list