{-# LANGUAGE TemplateHaskell, NoMonomorphismRestriction #-}
-- The rest is Haskell98

-- | Type-safe printf and scanf with C-like formatting string
-- We also permit a generic format specifier %a to format or parse
-- any showable and readable value.
-- Our format descriptors are first-class and can be built incrementally.
-- We can use the same format descriptor to format to a string,
-- to the standard output or any other data sink. Furthermore,
-- we can use the same format descriptor to parse from a string
-- or any data source.
-- What we print we can parse, using the same descriptor.

module Text.TFTest where

import Text.TotalPrintF
import Prelude hiding ((^))

-- The following import is needed only for the extensibility test
-- at the end of the file
import Text.PrintScanF (FormattingSpec(..), PrinterParser(..)) 

t1 = sprintf $(spec "Hello, there!")
-- "Hello, there!"

t1s = sscanf "Hello, there!" $(spec "Hello, there!") ()
-- Just ()

t2 = sprintf $(spec "Hello, %s!") "there"
-- "Hello, there!"

t3 = sprintf $(spec "The value of %s is %d") "x" 3
-- "The value of x is 3"

-- What we print we can parse, using the same descriptor
t31 = let printed = sprintf desc "x" 3
          parsed  = sscanf printed desc (\s i -> (s,i))
      in (printed, parsed)
  where desc = $(spec "The value of %s is %d")
-- ("The value of x is 3",Just ("x",3))

-- Mismatch between the formatting string and the printf arguments
-- is a type error.

-- t32 = sprintf $(spec "The value of %s is %d") "x" True
--     Couldn't match expected type `Bool' against inferred type `Int'

{-
t33 = sprintf $(spec "The value of %s is %d") "x" 3 10
    Couldn't match expected type `t1 -> t'
           against inferred type `String'
    Probable cause: `printf' is applied to too many arguments
-}

t4 = let x = [9,16,25] 
	 i = 2 
     in sprintf $(spec "The element number %d of %a is %a") i x (x !! i)
-- "The element number 2 of [9,16,25] is 25"

t4s = sscanf "The element number 2 of [9,16,25] is 25"
        $(spec "The element number %d of %a is %a")
        (\i a e -> (i,a::[Int],e::Int))
-- Just (2,[9,16,25],25)



-- Demonstrating that printf is first-class
t5 = map (uncurry printer) [("x",3), ("y",5), ("z",7)]
 where
 printer = sprintf $(spec "The value of %s is %d")

-- ["The value of x is 3","The value of y is 5","The value of z is 7"]

{- An inconsistent format descriptor generates a type error

t6 = map (uncurry printer) [("x",3), ("y",5), ("z",7)]
 where
 printer = sprintf $(spec "The value of %d is %d")
    Couldn't match expected type `Int' against inferred type `[Char]'
    In the expression: "x"
    In the expression: ("x", 3)
    In the second argument of `map', namely
        `[("x", 3), ("y", 5), ("z", 7)]'

-}

-- Demonstrating that format descriptors are first-class and can be built 
-- incrementally

spec71 = $(spec "The value of %s")
spec72 = $(spec " is %d")
t7 = sprintf (spec71 ^ spec72) "x" 3
-- "The value of x is 3"


-- Extensibility test

-- Furthermore, the user can always extend printf, for example, to
-- send the formatted data to the standard output, without first building
-- the formatted output as a string.


newtype FPrIO a b = FPrIO ((IO () -> a) -> b)

instance FormattingSpec FPrIO where
    lit str = FPrIO $ \k -> k $ putStr str
    int     = FPrIO $ \k -> \x -> k $ putStr (show x)
    char    = FPrIO $ \k -> \x -> k $ putStr [x]
    fpp (PrinterParser pr _) = FPrIO $ \k -> \x -> k $ putStr (pr x)
    (FPrIO a) ^ (FPrIO b)  = FPrIO $ \k -> a (\sa -> b (\sb -> k (sa >> sb)))

printf (FPrIO fmt) = fmt (\m -> m >> putStrLn "\n-end-of-output-")

t3io = printf $(spec "The value of %s is %d") "x" 3
{- printed on the standard output:

The value of x is 3
-end-of-output-
-}