{-# LANGUAGE CPP #-}
{-# OPTIONS -Wall #-}
module ZMidi.Core.Internal.SimpleFormat
(
ColumnSpecs(..)
, ColumnSpec(..)
, Table
, runTable
, execTable
, tellFree
, tellRow
, tellBlank
, tellBreak
, localColumns
, nextTrack
, arbTrack
, incrDelta
, WString
, width
, outputDoc
, (<+>)
, sep
, char
, text
, repeatChar
, padl
, padr
, hex2
, hex4
, int
, integral
) where
#ifndef MIN_VERSION_GLASGOW_HASKELL
import Control.Applicative
#endif
#ifndef MIN_VERSION_GLASGOW_HASKELL
import Data.Monoid
#endif
import Data.Word
import Numeric
type H a = [a] -> [a]
type HString = H Char
type HLines = H String
spaceH :: Int -> HString
spaceH n = showString $ replicate n ' '
fromH :: H a -> [a]
fromH = ($ [])
emptyH :: H a
emptyH = id
snocH :: H a -> a -> H a
snocH f a = f . (a:)
data ColumnSpecs = ColumnSpecs { col_sep :: Char, col_fmts :: [ColumnSpec] }
deriving (Eq,Show)
data ColumnSpec = PadL Int | PadR Int
deriving (Eq,Ord,Show)
data St = St { get_tracknum :: !Int, get_acctime :: Integer }
state_zero :: St
state_zero = St { get_tracknum = 0
, get_acctime = 0
}
newtype Table a = Table {
getTable :: ColumnSpecs -> St -> HLines -> (St,HLines,a) }
instance Functor Table where
fmap f ma = Table $ \r s ac ->
let (s1,ac1,a) = getTable ma r s ac in (s1, ac1, f a)
instance Applicative Table where
pure a = Table $ \_ s ac -> (s,ac,a)
mf <*> ma = Table $ \r s ac ->
let (s1,ac1,f) = getTable mf r s ac
(s2,ac2,a) = getTable ma r s1 ac1
in (s2,ac2,f a)
instance Monad Table where
return = pure
ma >>= k = Table $ \r s ac ->
let (s1,ac1,a) = getTable ma r s ac in getTable (k a) r s1 ac1
runTable :: ColumnSpecs -> Table a -> ([String],a)
runTable hdrs ma =
let (_,hf,a) = getTable ma hdrs state_zero emptyH in (fromH hf,a)
execTable :: ColumnSpecs -> Table a -> [String]
execTable hdrs = fst . runTable hdrs
tellFree :: (Int -> Integer -> WString) -> Table ()
tellFree wf = Table $ \_ s ac ->
let next = fromH $ doch $ wf (get_tracknum s) (get_acctime s)
in (s, ac `snocH` next, ())
tellRow :: (Int -> Integer -> [WString]) -> Table ()
tellRow wsf = Table $ \r s ac ->
let next = formatRow r $ wsf (get_tracknum s) (get_acctime s)
in (s, ac `snocH` next, ())
tellBlank :: Table ()
tellBlank = Table $ \_ s ac -> let next = ""
in (s, ac `snocH` next, ())
tellBreak :: Table ()
tellBreak = Table $ \r s ac -> let next = formatBreak r
in (s, ac `snocH` next, ())
localColumns :: ColumnSpecs -> Table a -> Table a
localColumns r1 ma = Table $ \_ s ac -> getTable ma r1 s ac
nextTrack :: Table ()
nextTrack = Table $ \_ s ac -> (upd s, ac, ())
where
upd = (\s n -> s { get_tracknum = n+1, get_acctime = 0}) <*> get_tracknum
arbTrack :: Int -> Table ()
arbTrack n = Table $ \_ _ ac ->
(St { get_tracknum = n, get_acctime = 0}, ac, ())
incrDelta :: Integer -> Table ()
incrDelta dt = Table $ \_ s ac -> (upd s, ac, ())
where
upd = (\s n -> s { get_acctime = n + dt }) <*> get_acctime
formatBreak :: ColumnSpecs -> String
formatBreak spec = replicate (lineLength spec) '-'
formatRow :: ColumnSpecs -> [WString] -> String
formatRow (ColumnSpecs ch fmts) ws = fromH $ doch $ step fmts ws
where
fmt1 = PadL 9
step [] xs = step [fmt1] xs
step _ [] = mempty
step (c:_) [x] = format1 c x
step (c:cs) (x:xs) = let d1 = format1 c x; ds = step cs xs
in sep ch d1 ds
format1 :: ColumnSpec -> WString -> WString
format1 (PadL n) w = if n > width w then padl n w else w
format1 (PadR n) w = if n > width w then padr n w else w
lineLength :: ColumnSpecs -> Int
lineLength (ColumnSpecs _ fmts) = step 0 fmts
where
step w [] = w
step w [x] = w + size x
step w (x:xs) = step (w + size x + 1) xs
size (PadL i) = i
size (PadR i) = i
data WString = WString {
width :: !Int,
doch :: HString }
wstring :: String -> WString
wstring s = WString (length s) (showString s)
outputDoc :: WString -> String
outputDoc = fromH . doch
instance Monoid WString where
mempty = WString 0 id
WString w1 f1 `mappend` WString w2 f2 = WString (w1+w2) (f1 . f2)
#if MIN_VERSION_base(4,11,0)
instance Semigroup WString where
(<>) = mappend
#endif
infixr 6 <+>
(<+>) :: WString -> WString -> WString
(<+>) = sep ' '
sep :: Char -> WString -> WString -> WString
sep ch (WString w1 f1) (WString w2 f2) = WString (1+w1+w2) (f1 . (ch:) . f2)
char :: Char -> WString
char c = WString 1 (c:)
text :: String -> WString
text = wstring
repeatChar :: Int -> Char -> WString
repeatChar n c = WString n (showString $ replicate n c)
padl :: Int -> WString -> WString
padl i d@(WString n f) | i > n = WString i (spaceH (i-n) . f)
| otherwise = d
padr :: Int -> WString -> WString
padr i d@(WString n f) | i > n = WString i (f . spaceH (i-n))
| otherwise = d
hex2 :: Word8 -> WString
hex2 n | n < 0x10 = WString 2 (('0' :) . showHex n)
| otherwise = WString 2 (showHex n)
hex4 :: Word16 -> WString
hex4 n | n < 0x10 = WString 4 (('0':) . ('0':) . ('0':) . showHex n)
| n < 0x100 = WString 4 (('0':) . ('0':) . showHex n)
| n < 0x1000 = WString 4 (('0':) . showHex n)
| otherwise = WString 4 (showHex n)
int :: Int -> WString
int = wstring . show
integral :: (Show a, Integral a) => a -> WString
integral = wstring . show