{-# LANGUAGE UndecidableInstances #-} module Data.Repa.Nice.Tabulate ( tab , tabulate , Str (..) , Tok (..)) where import Data.Repa.Nice.Present as A import Data.Repa.Nice.Display as A import Data.List as L import qualified Data.Text as T import Data.Text (Text) import Data.Maybe -- | Print a nested value to the console in tabular form. -- -- The first two layers of nesting are displayed as rows and columns. -- Numeric data is right-justified, while the rest is left-justified. -- -- @ -- > tab [[10, 20, 302], [40, 50], [60, 7001, 80, 90 :: Int]] -- 10 20 302 -- 40 50 -- 60 7001 80 90 -- @ -- -- Deeper layers of nesting are preserved in the output: -- -- @ -- > tab [[[10], [20, 21]], [[30, 31], [40, 41, 41], [50 :: Int]]] -- [10] [20,21] -- [30,31] [40,41,41] [50] -- @ -- -- By default, strings are printed as lists of characters: -- -- @ -- > tab [[("red", 10), ("green", 20), ("blue", 30)], [("grey", 40), ("white", 50 :: Int)]] -- ([\'r\',\'e\',\'d\'],10) ([\'g\',\'r\',\'e\',\'e\',\'n\'],20) ([\'b\',\'l\',\'u\',\'e\'],30) -- ([\'g\',\'r\',\'e\',\'y\'],40) ([\'w\',\'h\',\'i\',\'t\',\'e\'],50) -- @ -- -- If you want double-quotes then wrap the strings with a @Str@ constructor: -- -- @ -- > tab [[(Str "red", 10), (Str "green", 20), (Str "blue", 30)], [(Str "grey", 40), (Str "white", 50 :: Int)]] -- ("red",10) ("green",20) ("blue",30) -- ("grey",40) ("white",50) -- @ -- -- If you don't want any quotes then wrap them with a @Tok@ constructor: -- -- @ -- > tab [[(Tok "red", 10), (Tok "green", 20), (Tok "blue", 30)], [(Tok "grey", 40), (Tok "white", 50 :: Int)]] -- (red,10) (green,20) (blue,30) -- (grey,40) (white,50) -- @ -- tab :: Presentable a => a -> IO () tab val = putStrLn $ T.unpack $ tabulate val -- | Display a nested value in tabular form. -- tabulate :: Presentable a => a -> Text tabulate xx = let pp = present xx in case depth pp of 0 -> flatten pp 1 -> let Just pss = strip1 pp in tabulate1 $ map flatten pss _ -> let Just pss = strip2 pp in tabulate2 $ map (map flatten) pss tabulate1 :: [Text] -> Text tabulate1 strs = let d = mconcat $ map takeDisplay strs in T.intercalate (T.pack "\n") $ L.map (display d) strs tabulate2 :: [[Text]] -> Text tabulate2 strss = let -- Decide how to display a single column. displayOfCol c = mconcat $ mapMaybe (\line -> if c >= length line then Nothing else Just (takeDisplay (line !! c))) $ strss -- How many columns we have. nCols = maximum $ L.map L.length strss -- Decide how to display all the columns. displays = L.map displayOfCol [0.. nCols - 1] makeLine line = T.intercalate (T.pack " ") $ L.zipWith display displays line in T.intercalate (T.pack "\n") $ L.map makeLine strss