zmidi-core-0.5.0: src/ZMidi/Core/Internal/SimpleFormat.hs
{-# OPTIONS -Wall #-}
--------------------------------------------------------------------------------
-- |
-- Module : ZMidi.Core.Internal.SimpleFormat
-- Copyright : (c) Stephen Tetley 2010-2012
-- License : BSD3
--
-- Maintainer : Stephen Tetley <stephen.tetley@gmail.com>
-- Stability : unstable
-- Portability : As per dependencies.
--
-- Simple line-oriented formatting combinators.
--
--------------------------------------------------------------------------------
module ZMidi.Core.Internal.SimpleFormat
(
Doc
, width
, output
, cat
, sep
, char
, text
, repeatChar
, padl
, padr
, hex2
, hex4
, integral
) where
import Data.Monoid
import Data.Word
import Numeric
-- | Strings are represented as Hughes lists
--
-- ShowS is a Hughes list representation specialized to Strings
--
type H a = [a] -> [a]
type HString = H Char
-- | Make a HString of spaces.
--
spaceH :: Int -> HString
spaceH n = showString $ replicate n ' '
fromH :: H a -> [a]
fromH = ($ [])
-- | Docs represent a single line - they should not contain
-- newlines.
--
data Doc = Doc {
-- | Width of the doc.
width :: !Int,
-- | Internal representation.
doch :: HString }
-- | Make a literal Doc from a String.
--
doc :: String -> Doc
doc s = Doc (length s) (showString s)
-- | Unwrap a Doc making a String.
--
output :: Doc -> String
output = fromH . doch
instance Monoid Doc where
mempty = Doc 0 id
Doc i1 f1 `mappend` Doc i2 f2 = Doc (i1+i2) (f1 . f2)
infixr 6 `cat`
-- | Concatenate - no space.
--
cat :: Doc -> Doc -> Doc
cat = mappend
infixr 6 `sep`
-- | Concatenate - with space.
--
sep :: Doc -> Doc -> Doc
sep (Doc i1 f1) (Doc i2 f2) = Doc (1+i1+i2) (f1 . (' ':) . f2)
-- | Make a Doc from a Char.
--
char :: Char -> Doc
char c = Doc 1 (c:)
-- | Make a Doc from a String.
--
text :: String -> Doc
text = doc
-- | Repeat the Char /n/ times to make a Doc.
--
repeatChar :: Int -> Char -> Doc
repeatChar n c = Doc n (showString $ replicate n c)
-- | Pad the left with space.
--
padl :: Int -> Doc -> Doc
padl i d@(Doc n f) | i > n = Doc i (spaceH (i-n) . f)
| otherwise = d
-- | Pad the right with space.
--
padr :: Int -> Doc -> Doc
padr i d@(Doc n f) | i > n = Doc i (f . spaceH (i-n))
| otherwise = d
-- | Show as a two digit hex number.
--
hex2 :: Word8 -> Doc
hex2 n | n < 0x10 = Doc 2 (('0' :) . showHex n)
| otherwise = Doc 2 (showHex n)
-- | Show as a four digit hex number.
--
hex4 :: Word16 -> Doc
hex4 n | n < 0x10 = Doc 4 (('0':) . ('0':) . ('0':) . showHex n)
| n < 0x100 = Doc 4 (('0':) . ('0':) . showHex n)
| n < 0x1000 = Doc 4 (('0':) . showHex n)
| otherwise = Doc 4 (showHex n)
-- | Show an Integral value as a base 10 number.
--
integral :: (Show a, Integral a) => a -> Doc
integral = doc . show