data-svd-0.1.1.0: src/Data/SVD/Pretty/Box.hs
{-# LANGUAGE RecordWildCards #-}
module Data.SVD.Pretty.Box
( renderFields
) where
import Data.Bits (Bits())
import Data.SVD.Types (Field(..))
import Data.Word (Word8, Word16, Word32, Word64)
import Prettyprinter
import Prettyprinter.Render.Terminal (Color(..), color)
import Text.PrettyPrint.Boxes (Box, (//))
import qualified Text.PrettyPrint.Boxes
import qualified Data.List
import qualified Data.Bits.Pretty
import qualified Data.SVD.Pretty
-- | Render fields as table using boxes
-- If table would be too wide split it into two tables
renderFields
:: ( Bits a
, Num a
, Show a
, Integral a)
=> [(a, Field)]
-> String
renderFields fs | headerSize >= 80 = do
Data.SVD.Pretty.displayPretty
( annotate (color Yellow)
(pretty "MSB")
<> line
)
<> Text.PrettyPrint.Boxes.render
( table
. remap
$ takeBits 16 fs
)
<> Data.SVD.Pretty.displayPretty
( annotate (color Magenta)
(pretty "LSB")
<> line
)
<> Text.PrettyPrint.Boxes.render
( table
. remap
$ dropBits 16 fs
)
where
headerSize =
sum
$ map
(length . showField . snd)
fs
renderFields fs | otherwise =
Text.PrettyPrint.Boxes.render
. table
. remap
$ fs
table :: [[String]] -> Box
table rows =
hSepDeco
Text.PrettyPrint.Boxes.<>
Text.PrettyPrint.Boxes.punctuateH
Text.PrettyPrint.Boxes.top
hSepDeco
(map fmtColumn cols)
Text.PrettyPrint.Boxes.<> hSepDeco
where
cols = Data.List.transpose rows
nrows = length rows
hSepDeco =
Text.PrettyPrint.Boxes.vcat
Text.PrettyPrint.Boxes.left
$ map
Text.PrettyPrint.Boxes.char
(
"+"
<>
(concat $ replicate nrows "|+")
)
fmtColumn :: [String] -> Box
fmtColumn items =
vSepDeco
// Text.PrettyPrint.Boxes.punctuateV
Text.PrettyPrint.Boxes.center2
vSepDeco
(map
Text.PrettyPrint.Boxes.text
items
)
// vSepDeco
where width' = maximum $ map length items
vSepDeco =
Text.PrettyPrint.Boxes.text
$ replicate width' '-'
remap
:: ( Integral x
, Show x
)
=> [(x, Field)]
-> [[String]]
remap fs =
[ map
(showField . snd)
fs
, map
(\(v, f) -> hexFieldVal f v)
fs
]
takeBits
:: Int
-> [(a, Field)]
-> [(a, Field)]
takeBits 0 _ = []
takeBits x (y@(_, f):fs) | x >= fieldBitWidth f = y : (takeBits (x - fieldBitWidth f) fs)
takeBits x (y@(_, f):_fs) | x < fieldBitWidth f = [splitField x y]
where
splitField x' (v, f') =
( v
, f
{ fieldBitWidth = x'
, fieldBitOffset = fieldBitOffset f' + (fieldBitWidth f' - x')
}
)
takeBits _ _ = []
dropBits
:: Int
-> [(a, Field)]
-> [(a, Field)]
dropBits 0 fs = fs
dropBits x ((_, f):fs) | x >= fieldBitWidth f = dropBits (x - fieldBitWidth f) fs
dropBits x (y@(_, f):fs) | x < fieldBitWidth f = (splitField x y):fs
where
splitField x' (v, f') =
( v
, f { fieldBitWidth = fieldBitWidth f' - x' }
)
dropBits _ _ = []
-- | Show `Field` with its range, e.g BRR[15:0] (16 bit wide)
showField :: Field -> String
showField f@Field{..} | fieldReserved =
"◦"
<> fieldRange f
showField f@Field{..} | otherwise =
fieldName
<> fieldRange f
-- | Datasheeeet like
fieldRange :: Field -> String
fieldRange Field{..} | fieldBitWidth == 1 = ""
fieldRange Field{..} | otherwise =
concat
[ "["
, show $ fieldBitWidth - 1
, ":0]"
]
-- | Format field value in hex, padded according to `fieldBitWidth`
hexFieldVal :: (Integral x, Show x) => Field -> x -> String
hexFieldVal _ 0 = "0"
hexFieldVal f x | fieldBitWidth f == 1 = showBit x
where
showBit 0 = "0"
showBit 1 = "1"
showBit y = error $ "Not a bit: " ++ show y
hexFieldVal f x | fieldBitWidth f <= 8 =
Data.Bits.Pretty.showHex (fromIntegral x :: Word8)
hexFieldVal f x | fieldBitWidth f <= 16 =
Data.Bits.Pretty.showHex (fromIntegral x :: Word16)
hexFieldVal f x | fieldBitWidth f <= 32 =
Data.Bits.Pretty.showHex (fromIntegral x :: Word32)
hexFieldVal _ x | otherwise =
Data.Bits.Pretty.showHex (fromIntegral x :: Word64)