packages feed

data-svd-0.1.0.0: src/Data/SVD/Pretty.hs

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}

module Data.SVD.Pretty
  (
  -- * Helpers
    ppList
  , displayPretty
  , displayCompact
  -- * Shorthand
  , displayDevice
  , displayDeviceInfo
  , displayPeripheral
  , displayRegister
  , displayMemMap
  , displayMemMapCompact
  , displayDevISR
  , displayISRs
  -- * Pretty printers
  , ppDevice
  , ppPeriph
  , ppReg
  , ppHex
  -- ** Interrupts
  , ppDevISR
  , ppISR
  -- ** Terse output
  , ppDeviceInfo
  , ppPeriphName
  , shortField
  -- ** MemMap
  , ppMem
  -- * Who knows
  , printSetFields
  , printSetField
  , showField
  , fieldRange
  , hexFieldVal
  )
  where

import Data.Char (toLower)
import Data.SVD.Types
import Data.Word
import Prettyprinter
import Prettyprinter.Render.String
import Prettyprinter.Render.Terminal (AnsiStyle, Color(..), color)

import qualified Data.Bits.Pretty
import qualified Data.SVD.Util
import qualified Data.Text
import qualified Prettyprinter.Render.Terminal

-- * Helpers

ppList :: (a -> Doc AnsiStyle) -> [a] -> Doc AnsiStyle
ppList pp x = vcat $ map pp x

displayPretty :: Doc AnsiStyle -> String
displayPretty =
    Data.Text.unpack
  . Prettyprinter.Render.Terminal.renderStrict
  . layoutPretty defaultLayoutOptions

displayCompact :: Doc AnsiStyle -> String
displayCompact =
    renderString
  . layoutCompact

-- * Shorthand

displayDevice :: Device -> String
displayDevice = displayPretty . ppDevice

displayDeviceInfo :: Device -> String
displayDeviceInfo = displayPretty . ppDeviceInfo

displayPeripheral :: Peripheral -> String
displayPeripheral = displayPretty . ppPeriph

displayRegister :: Register -> String
displayRegister = displayPretty . ppReg

displayMemMap :: [(String, String)] -> String
displayMemMap = displayPretty . ppList ppMem

displayMemMapCompact :: [(String, String)] -> String
displayMemMapCompact = displayCompact . ppList ppMem

displayDevISR :: Device -> String
displayDevISR = displayPretty . ppDevISR

displayISRs :: [Interrupt] -> String
displayISRs = displayPretty . ppList ppISR

-- * Pretty printers

ppDevice :: Device -> Doc AnsiStyle
ppDevice Device{..} =
  (annotate (color Red) $ pretty deviceName)
  <> line
  <> indent 2 (ppList ppPeriph devicePeripherals)

ppPeriph :: Peripheral -> Doc AnsiStyle
ppPeriph Peripheral{..} =
      hardline
  <>  annotate (color Yellow)
        (pretty periphName)
  <+> annotate (color White)
        (ppHex periphBaseAddress)
  <+> annotate (color Magenta)
        (pretty periphDescription)
  <>  line
  <>  indent 2 (ppList ppReg periphRegisters)
  <>  maybe
        mempty
        (\x ->
          indent 2
           $   line
           <>  pretty ("Derived from" :: String)
           <+> pretty x
        )
        periphDerivedFrom

ppReg :: Register -> Doc AnsiStyle
ppReg Register{..} =
  hardline
  <>  annotate (color Blue)
        (pretty regName)
  <+> annotate (color White)
        (ppHex regAddressOffset)
  <+> annotate (color Cyan)
        (pretty '-' <+> (pretty regDescription))
  <>  line
  <>  indent 2
        (ppList ppField regFields)

ppHex :: Int -> Doc AnsiStyle
ppHex = pretty . Data.Bits.Pretty.formatHex

rpad :: Int -> String -> String
rpad m xs = take m $ xs ++ repeat ' '

ppField :: Field -> Doc AnsiStyle
ppField Field{..} =
      annotate (color Green)
        (pretty $ rpad 25 fieldName)
  <+> pretty ("::" :: String)
  <+> ppWidthPad 7 fieldBitWidth
  <+> annotate
        (color Cyan)
        (pretty $ " -- " ++ fieldDescription)

ppWidthPad
  :: Int
  -> Int
  -> Doc AnsiStyle
ppWidthPad m 1 = pretty $ rpad m "Bit"
ppWidthPad m x = pretty $ rpad m $ "Bits " ++ show x

-- ** Interrupts

ppDevISR :: Device -> Doc AnsiStyle
ppDevISR Device{..} = ppList ppPeriphISR devicePeripherals

ppPeriphISR :: Peripheral -> Doc AnsiStyle
ppPeriphISR Peripheral{..} =
  indent 2 (ppList ppISR periphInterrupts)
--  <//> (maybe empty (\x -> string "Derived from" <+> string x) periphDerivedFrom)

ppISR :: Interrupt -> Doc AnsiStyle
ppISR Interrupt{..} =
  indent 2
    (
          "|"
      <+> pretty interruptName
      <>  " -- " <> pretty interruptValue <+> pretty interruptDescription
    )

-- ** Terse output

ppDeviceInfo :: Device -> Doc AnsiStyle
ppDeviceInfo Device{..} =
     annotate (color Red)
       (pretty deviceName)
  <> line
  <> indent 2
       (ppList ppPeriphName devicePeripherals)

ppPeriphName :: Peripheral -> Doc AnsiStyle
ppPeriphName Peripheral{..} =
  annotate (color Yellow)
    (pretty periphName)

shortField :: Field -> String
shortField Field{..} = unwords [
  fieldName
  , "offset"
  , show fieldBitOffset
  , "width"
  , show fieldBitWidth ]

-- ** MemMap

ppMem :: (String, String) -> Doc AnsiStyle
ppMem (addr, periph) =
     name <> " :: Integer"
  <> line
  <> name
  <> " = "
  <> pretty addr
  where
    name = pretty (map toLower periph) <> "_periph_base"

-- | Print currently set (non-zero) fields
printSetFields :: (Show a, Eq a, Num a) => [(a, Field)] -> String
printSetFields =
    unlines
  . map printSetField
  . Data.SVD.Util.filterSet

printSetField :: (Show a, Eq a, Num a) => (a, Field) -> String
printSetField (_, f) | fieldBitWidth f == 1 = concat ["Bit ", show (fieldBitOffset f), " ", fieldName f]
printSetField (v, f) | otherwise = concat [
    "Bits ["
  , show (fieldBitOffset f)
  , ":"
  , show (fieldBitOffset f + fieldBitWidth f - 1)
  , "]"
  , " "
  , fieldName f
  , " value ", show v]

-- | 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)