{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE Rank2Types #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE DefaultSignatures #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE ConstrainedClassMethods #-} {-# LANGUAGE MultiParamTypeClasses #-} -- | Module implements the default methods for Tabulate module Text.PrettyPrint.Tabulate ( Tabulate(..) , Boxable(..) , CellValueFormatter )where import Data.Data import Data.Typeable import Data.Generics.Aliases import GHC.Generics as G import GHC.Show import qualified Data.Map as Map import qualified Text.PrettyPrint.Boxes as B import qualified Data.List as List import Text.Printf import qualified Data.Vector as V -- | Future change to support providing custom formatting functions data TablizeValueFormat = T {floatValueFormat::Maybe (Float -> String), stringValueFormat::Maybe (String -> String), integerValueFormat::Maybe (Integer -> String), intValueFormat::Maybe (Int -> String), doubleValueFormat::Maybe (Double -> String)} -- | Default TabulateValueFormat getDefaultTabulateValueFormat = T {floatValueFormat=Nothing, stringValueFormat=Nothing, integerValueFormat=Nothing, intValueFormat=Nothing, doubleValueFormat=Nothing} -- | The Generalized class that implements the print feature -- for any type that derives Generics and Data class GTabulate f where -- | Print with default style gprintTable :: f a -> [B.Box] -- | For future, will be able to print with provided style gprintTableWithStyle :: TablizeValueFormat -> f a -> [B.Box] --- | The instance class for Unit type instance GTabulate U1 where gprintTable _ = [] gprintTableWithStyle _ _ = [] -- | Any records or product types -- | Nested algebraic types are printed using their respective Show methods instance (GTabulate a, GTabulate b) => GTabulate (a :*: b) where gprintTable (a :*: b) = gprintTable a ++ gprintTable b gprintTableWithStyle style (a :*: b) = gprintTableWithStyle style a ++ gprintTableWithStyle style b -- | Sum types instance (GTabulate a, GTabulate b) => GTabulate (a :+: b) where gprintTable (L1 x) = gprintTable x gprintTable (R1 x) = gprintTable x gprintTableWithStyle style (L1 x) = gprintTableWithStyle style x gprintTableWithStyle style (R1 x) = gprintTableWithStyle style x -- | instance (GTabulate a) => GTabulate (M1 i c a) where gprintTable (M1 x) = gprintTable x gprintTableWithStyle style (M1 x) = gprintTableWithStyle style x -- | The leaf method that actually creates the Boxes that will -- be used to render the boxes as a table. instance (CellValueFormatter a) => GTabulate (K1 i a) where gprintTable (K1 x) = [B.text $ ppFormatter x] gprintTableWithStyle style (K1 x) = [B.text $ ppFormatterWithStyle style x] -- | Class that implements formatting using printf. -- Default instances for String, Char, Int, Integer, Double and Float -- are provided. For types that are not an instance of this class -- `show` is used. class CellValueFormatter a where -- Function that can be implemented by each instance ppFormatter :: a -> String -- Future support for this signature will be added ppFormatterWithStyle :: TablizeValueFormat -> a -> String -- Default instance of function for types that do -- do not have their own instance default ppFormatter :: (Show a) => a -> String ppFormatter x = show x -- Future support. default ppFormatterWithStyle :: (Show a) => TablizeValueFormat -> a -> String ppFormatterWithStyle _ x = "default_" ++ show x instance CellValueFormatter Integer where ppFormatter x = printf "%d" x ppFormatterWithStyle style x = case integerValueFormat style of Just f -> f x Nothing -> ppFormatter x instance CellValueFormatter Int where ppFormatter x = printf "%d" x ppFormatterWithStyle style x = case intValueFormat style of Just f -> f x Nothing -> ppFormatter x instance CellValueFormatter Float where ppFormatter x = printf "%14.9g" x ppFormatterWithStyle style x = case floatValueFormat style of Just f -> f x Nothing -> ppFormatter x instance CellValueFormatter String where ppFormatter x = printf "%s" x ppFormatterWithStyle style x = case stringValueFormat style of Just f -> f x Nothing -> ppFormatter x instance CellValueFormatter Double where ppFormatter x = printf "%14.9g" x ppFormatterWithStyle style x = case doubleValueFormat style of Just f -> f x Nothing -> ppFormatter x instance CellValueFormatter Bool -- | Perform the final alignment of all -- | generated [[B.Box]] alignBox :: [[B.Box]] -> B.Box alignBox b = B.hsep 5 B.left cols where cols = List.map (B.vcat B.left) $ List.transpose b -- | Helper method that detects an algebraic data type isAlgRepConstr t = case dataTypeRep . dataTypeOf $ t of AlgRep [_] -> True _ -> False -- | Class that can be derived by a 'Traversable' to create -- a list of 'Box' values and print as a Table. -- Default instances for List, Map and Vector are already provided. class Boxable b where toBox :: (Data a, G.Generic a, GTabulate(Rep a)) => b a -> [[B.Box]] --toBoxWithStyle :: (Data a, G.Generic a, GTabulate(Rep a)) => TablizeValueFormat -> b a -> [[B.Box]] printTable :: (Data a, G.Generic a, GTabulate(Rep a)) => b a -> IO () --printTableWithStyle :: (Data a, G.Generic a, GTabulate(Rep a)) => TablizeValueFormat -> b a -> IO () instance Boxable [] where toBox a = case a of [] -> [[B.nullBox]] x:xs -> gprintTable (from x):toBox xs -- | Prints a "List" as a table. Called by "ppTable" -- | Need not be called directly printTable m = do let r = head $ m let header = constrFields . toConstr $ r let header_box = List.map (B.text) header B.printBox $ alignBox $ header_box:toBox m instance Boxable V.Vector where toBox v = V.toList $ fmap (\x -> (gprintTable (from x))) v -- | Prints a "Vector" as a table. Called by "ppTable" -- | Need not be called directly printTable m = do let r = m V.! 0 let header = constrFields . toConstr $ r let header_box = List.map (B.text) header B.printBox $ alignBox $ header_box:toBox m instance (Show k) => Boxable (Map.Map k) where -- | Returns a Map of Boxed values toBox m = Map.elems (Map.mapWithKey (\k v -> B.text (show k):gprintTable (from v)) m) -- | Prints a "Map" as a table. Called by "ppTable" -- | Need not be called directly printTable m = do let r = head . Map.elems $ m let header = constrFields . toConstr $ r let header_box = "Key":List.map (B.text) header B.printBox $ alignBox $ header_box:toBox m -- | The elements in a Traverserable should be an instance of Tabulate to be displayed in a tabular format class (Data a) => Tabulate a where -- | Generic function that will be provided by the GTabulate class. ppTable :: (Boxable f) => f a -> IO() --ppTableWithStyle :: TablizeValueFormat -> f a -> IO() default ppTable :: (Boxable f, G.Generic a, GTabulate (Rep a)) => f a -> IO () ppTable x = printTable x --default ppTableWithStyle :: (Boxable f, G.Generic a, GTabulate (Rep a)) => TablizeValueFormat -> f a -> IO () --ppTableWithStyle x = printTableWithStyle x