module Debug.Hoed.Render
(CompStmt(..)
,StmtDetails(..)
,stmtRes
,renderCompStmts
,CDS
,eventsToCDS
,noNewlines
,sortOn
) where
import Control.DeepSeq
import Data.Array as Array
import Data.Char (isAlpha)
import Data.List (nub, sort)
import Data.Strict.Tuple
import Debug.Hoed.Compat
import Debug.Hoed.Observe
import GHC.Generics
import Prelude hiding (lookup)
import Text.PrettyPrint.FPretty hiding (sep, (<$>))
import Text.Read
data CompStmt = CompStmt { stmtLabel :: !String
, stmtIdentifier :: !UID
, stmtDetails :: !StmtDetails
}
deriving (Generic)
instance NFData CompStmt
instance Eq CompStmt where c1 == c2 = stmtIdentifier c1 == stmtIdentifier c2
instance Ord CompStmt where
compare c1 c2 = compare (stmtIdentifier c1) (stmtIdentifier c2)
data StmtDetails
= StmtCon { stmtCon :: !String
, stmtPretty :: !String}
| StmtLam { stmtLamArgs :: ![String]
, stmtLamRes :: !String
, stmtPretty :: !String}
deriving (Generic)
instance NFData StmtDetails
stmtRes :: CompStmt -> String
stmtRes = stmtPretty . stmtDetails
instance Show CompStmt where
show = stmtRes
showList eqs eq = unlines (map show eqs) ++ eq
noNewlines :: String -> String
noNewlines = noNewlines' False
noNewlines' :: Bool -> String -> String
noNewlines' _ [] = []
noNewlines' w (s:ss)
| w && (s == ' ' || s == '\n') = noNewlines' True ss
| not w && (s == ' ' || s == '\n') = ' ' : noNewlines' True ss
| otherwise = s : noNewlines' False ss
renderCompStmts :: (?statementWidth::Int) => CDSSet -> [CompStmt]
renderCompStmts = concatMap renderCompStmt
renderCompStmt :: (?statementWidth::Int) => CDS -> [CompStmt]
renderCompStmt (CDSNamed name uid set) = statements
where statements :: [CompStmt]
statements = concatMap (renderNamedTop name uid) output
output = cdssToOutput set
renderCompStmt other = error $ show other
renderNamedTop :: (?statementWidth::Int) => String -> UID -> Output -> [CompStmt]
renderNamedTop name observeUid (OutData cds) = map f pairs
where
f (args, res, Just i) =
CompStmt name i $
StmtLam
(map (prettyW . renderSet) args)
(prettyW $ renderSet res)
(prettyW $ renderNamedFn name (args, res))
f (_, cons, Nothing) =
CompStmt name observeUid $
StmtCon (prettyW $ renderSet cons)
(prettyW $ renderNamedCons name cons)
pairs = (nubSorted . sortOn argAndRes) pairs'
pairs' = findFn [cds]
argAndRes (arg, res, _) = (arg, res)
renderNamedTop name _ other = error $ show other
nubSorted :: Eq a => [a] -> [a]
nubSorted [] = []
nubSorted (a:a':as) | a == a' = nubSorted (a' : as)
nubSorted (a:as) = a : nubSorted as
data CDS = CDSNamed !String !UID !CDSSet
| CDSCons !UID !String ![CDSSet]
| CDSFun !UID !CDSSet !CDSSet
| CDSEntered !UID
| CDSTerminated !UID
| CDSString !String
deriving (Show,Eq,Ord,Generic)
instance NFData CDS
normalizeCDS :: CDS -> CDS
normalizeCDS (CDSString s) = CDSCons 0 (show s) []
normalizeCDS other = other
type CDSSet = [CDS]
eventsToCDS :: [Event] -> CDSSet
eventsToCDS pairs = force $ getChild 0 0
where
res = (!) out_arr
bnds = (0, length pairs)
cons !t !h = h : t
mid_arr :: Array Int [Pair Int CDS]
mid_arr = accumArray cons [] bnds
[ (pnode, (pport :!: res node))
| (Event node (Parent pnode pport) change) <- pairs
, change /= Enter
]
out_arr = array bnds
[ (node,getNode'' node e change)
| e@(Event node _ change) <- pairs
]
getNode'' :: Int -> Event -> Change -> CDS
getNode'' node _e change =
case change of
Observe str -> let chd = normalizeCDS <$> getChild node 0
in CDSNamed str (getId chd node) chd
Enter -> CDSEntered node
Fun -> CDSFun node (normalizeCDS <$> getChild node 0)
(normalizeCDS <$> getChild node 1)
(Cons portc cons)
-> simplifyCons node cons
[getChild node n | n <- [0 .. portc 1]]
getId [] i = i
getId (CDSFun i _ _:_) _ = i
getId (_:cs) i = getId cs i
getChild :: Int -> Int -> CDSSet
getChild pnode pport =
[ content
| pport' :!: content <- (!) mid_arr pnode
, pport == pport'
]
simplifyCons :: UID -> String -> [CDSSet] -> CDS
simplifyCons _ "throw" [[CDSCons _ "ErrorCall" set]]
= CDSCons 0 "error" set
simplifyCons _ ":" [[CDSCons _ (matchChar -> Just !ch) []], [CDSCons _ "[]" []]]
= CDSString [ch]
simplifyCons _ ":" [[CDSCons _ (matchChar -> Just !ch) []], [CDSString s]]
= CDSString (ch:s)
simplifyCons uid con xx = CDSCons uid con (map (map normalizeCDS) xx)
matchChar :: [Char] -> Maybe Char
matchChar ['\'', ch ,'\''] = Just ch
matchChar special@['\'', _, _ ,'\''] = readMaybe special
matchChar _ = Nothing
render :: Int -> Bool -> CDS -> Doc
render prec par (CDSCons _ ":" [cds1,cds2]) =
if par && not needParen
then doc
else paren needParen doc
where
doc = grp (sep <> renderSet' 5 False cds1 <> " : ") <>
renderSet' 4 True cds2
needParen = prec > 4
render _prec _par (CDSCons _ "," cdss) | not (null cdss) =
nest 2 ("(" <> foldl1 (\ a b -> a <> ", " <> b)
(map renderSet cdss) <>
")")
render prec _par (CDSCons _ name cdss)
| _:_ <- name
, (not . isAlpha . head) name && length cdss > 1 =
paren (prec /= 0)
(grp
(renderSet' 10 False (head cdss)
<> sep <> text name
<> nest 2 (foldr (<>) nil
[ if null cds then nil else sep <> renderSet' 10 False cds
| cds <- tail cdss
]
)
)
)
| otherwise =
paren (not (null cdss) && prec /= 0)
( grp
(text name <> nest 2 (foldr (<>) nil
[ sep <> renderSet' 10 False cds
| cds <- cdss
]
)
)
)
renderSet :: CDSSet -> Doc
renderSet = renderSet' 0 False
renderSet' :: Int -> Bool -> CDSSet -> Doc
renderSet' _ _ [] = "_"
renderSet' prec par [cons@CDSCons {}] = render prec par cons
renderSet' _prec _par cdss =
"{ " <> foldl1 (\ a b -> a <> line <>
", " <> b)
(map renderFn pairs) <>
line <> "}"
where
findFn_noUIDs :: CDSSet -> [([CDSSet],CDSSet)]
findFn_noUIDs c = map (\(a,r,_) -> (a,r)) (findFn c)
pairs = nub (sort (findFn_noUIDs cdss))
nub [] = []
nub (a:a':as) | a == a' = nub (a' : as)
nub (a:as) = a : nub as
renderFn :: ([CDSSet],CDSSet) -> Doc
renderFn (args, res)
= grp (nest 3
("\\ " <>
foldr (\ a b -> nest 0 (renderSet' 10 False a) <> sp <> b)
nil
args <> sep <>
"-> " <> renderSet res
)
)
renderNamedCons :: String -> CDSSet -> Doc
renderNamedCons name cons
= text name <> nest 2
( sep <> grp (text "= " <> renderSet cons)
)
renderNamedFn :: String -> ([CDSSet],CDSSet) -> Doc
renderNamedFn name (args,res)
= text name <> nest 2
( sep <> foldr (\ a b -> grp (renderSet' 10 False a) <> sep <> b) nil args
<> sep <> grp ("= " <> align(renderSet res))
)
findFn :: CDSSet -> [([CDSSet],CDSSet, Maybe UID)]
findFn = foldr findFn' []
findFn' :: CDS -> [([CDSSet], CDSSet, Maybe UID)] -> [([CDSSet], CDSSet, Maybe UID)]
findFn' (CDSFun i arg res) rest =
case findFn res of
[(args',res',_)] -> (arg : args', res', Just i) : rest
_ -> ([arg], res, Just i) : rest
findFn' other rest = ([],[other], Nothing) : rest
paren :: Bool -> Doc -> Doc
paren False doc = grp doc
paren True doc = grp ( "(" <> doc <> ")")
data Output = OutLabel String CDSSet [Output]
| OutData CDS
deriving (Eq,Ord,Show)
cdssToOutput :: CDSSet -> [Output]
cdssToOutput = map cdsToOutput
cdsToOutput :: CDS -> Output
cdsToOutput (CDSNamed name _ cdsset)
= OutLabel name res1 res2
where
res1 = [ cdss | (OutData cdss) <- res ]
res2 = [ out | out@OutLabel {} <- res ]
res = cdssToOutput cdsset
cdsToOutput cons@CDSCons {} = OutData cons
cdsToOutput fn@CDSFun {} = OutData fn
nil :: Doc
nil = Text.PrettyPrint.FPretty.empty
grp :: Doc -> Doc
grp = Text.PrettyPrint.FPretty.group
sep :: Doc
sep = softline
sp :: Doc
sp = " "
prettyW :: (?statementWidth::Int) => Doc -> String
prettyW = pretty ?statementWidth