packages feed

hat-2.7.0.0: tools/Pretty.hs

module Pretty (PrettyOption(..), graphForNode, makeGraph) where

-- #if ( defined(__GLASGOW_HASKELL__) && __GLASGOW_HASKELL__ < 604 ) \
--    || ( defined(__NHC__) && __NHC__ < 118 )
-- #define empty emptySet
-- #define toList setToList
-- #define insert addToSet
-- #define member elementOf
-- #endif

import LowLevel      (FileNode(..),nil,getParentNode,getSrcRef)
import CommonUI      (Options(..),Keep(..))
import NodeExp       (NodeExp(..),fullEval,flatEval,branches,result
                     ,removeResultCycles,removeNonResultCycles
                     ,nodeExp2SExp,getNode,CondType(..),getFirstCaredNode
                     ,flatEvalText,nodeExpForNode,fullEvalText,(===))
import SExp          (QName(..),showQN,prettySExp)
import qualified Data.Set as Set (empty, toList, insert, Set(..),member)
import ADT           (ADT(..),detectCycles,trustModule,trustIO,trustConstant
                     ,foldHiddens,trustUnevaluated,displayTrees)
import EDT           (buildEDT)
import SrcRef        (SrcRef(..), readSrcRef)

import Data.Char     (chr)
import Data.List     (sort)

import System.IO.Unsafe (unsafePerformIO)

-- #if ( defined(__GLASGOW_HASKELL__) && __GLASGOW_HASKELL__ < 604 ) \
--     || ( defined(__NHC__) && __NHC__ < 118 )
-- addToSet :: Ord a => a -> Set.Set a -> Set.Set a
-- addToSet = flip Set.addToSet
-- setToList = Set.setToList
-- #else
addToSet :: Ord a => a -> Set.Set a -> Set.Set a
addToSet = Set.insert
setToList = Set.toList
-- #endif

data PrettyOption = ShowDecOffsets
                  | ShowHexOffsets
                  | ShowParents
                  | MkEDT
                  | MkFDT
                  | ShowSrcPoses
                  | ShowVersion
                  | ShowModule String
                  deriving Eq

parents :: [PrettyOption] -> Bool
parents = elem ShowParents

makeGraph :: NodeExp -> [PrettyOption] -> String
makeGraph n o = if MkEDT `elem` o
                  then
                    "digraph edt\n" ++
                    "    {\n" ++
                    "    ordering=out; rankstep=0.1;\n" ++
                    (indent 4 (graphForEDT n o)) ++
                    "    }\n"
                  else
                    "digraph trace\n" ++
                    "    {\n" ++
                    "    ordering=out; rankstep=0.1;\n" ++
                    (indent 4 (graphForNode n o)) ++
                    "    }\n"

indent :: Int -> String -> String
indent n = unlines . (map ((++) (take n (repeat ' ')))) . lines

graphForNode :: NodeExp -> [PrettyOption] -> String
graphForNode x o = (dotNodes y o) ++ (dotEdges y o)
                   where
                     y = moveToSet (removeNonResultCycles (removeResultCycles x)) Set.empty

graphForEDT :: NodeExp -> [PrettyOption] -> String
graphForEDT x o =
  (concat (map (flip dotADTNodes o) y)) ++
  (concat (map (flip dotADTEdges o) y)) 
  where
    y = (  trustConstant (Plain "otherwise")
         . trustModule "Prelude"
         . detectCycles
         . trustUnevaluated
         . trustIO
         . foldHiddens
         . buildEDT) $ removeNonResultCycles $ removeResultCycles x

moveToSet :: NodeExp -> Set.Set NodeExp -> Set.Set NodeExp
moveToSet n s
  | n `Set.member` s = s
  | otherwise = addToSet n $ foldr moveToSet s $ branches n

{-moveToSet' :: NodeExp -> Set.Set NodeExp -> Set.Set NodeExp
moveToSet' node@(NExpApp _ fun args res) set
  | (isVCycleOrApp res) && (getNode node) == (getNode res) && isId fun
    = if addToSet node (foldr moveToSet set args)
  | isId fun
    = addToSet node (moveToSet res (foldr moveToSet set args))
  | otherwise
    = addToSet node (moveToSet fun
                      (moveToSet res
                        (foldr moveToSet set args)))
moveToSet' node@(NExpConstUse _ def res) set
  = addToSet node (moveToSet res set)
moveToSet' node@(NExpConstDef _ def res) set
  = addToSet node (moveToSet res set)
moveToSet' node@(NExpCond _ _ cnd res) set
  = addToSet node (moveToSet cnd (moveToSet res set))
moveToSet' node@(NExpFieldExp _ field mappings) set
  = addToSet node (moveToSet field (foldr (moveToSet . snd) set mappings))
moveToSet' node@(NExpProjection _ res) set
  = addToSet node (moveToSet res set)
moveToSet' node@(NExpForward _ res) set
  = moveToSet res set
moveToSet' node@(NExpHidden _ children res) set
  = addToSet node (moveToSet res (foldr moveToSet set children))
moveToSet' node set
  = addToSet node set-}

isId :: NodeExp -> Bool
isId (NExpForward _ r) = isId r
isId (NExpIdentifier _ _ _) = True
isId _ = False

isVCycleOrApp (NExpResultCycle _) = True
isVCycleOrApp (NExpApp _ _ _ _) = True
isVCycleOrApp _ = False

dotNodes :: Set.Set NodeExp -> [PrettyOption] -> String
dotNodes s o
  = {-concatMap makeNode (setToList s)-}
    concatMap (\s -> "{rank=same; " ++ concatMap makeNode s ++ "}\n") (groupSets s)
    where
      groupSets :: Set.Set NodeExp -> [[NodeExp]]
      groupSets s = groupSets' $ sort $ setToList s
      
      groupSets' :: [NodeExp] -> [[NodeExp]]
      groupSets' [] = []
      groupSets' (x:xs) =
        (collected:groupSets' (filter (not . (`elem` collected)) xs))
        where
          collected = collectRs x []
      
      collectRs n v = if n `elem` v then []
                                    else n:collectRs (result n) (n:v)
            
      makeNode :: NodeExp -> String
      makeNode node@(NExpApp id fun args res)
        | (isVCycleOrApp res) && (getNode node) == (getNode res) && isId fun
          = mkNode id "ValApp" $ quote (flatEvalText 100 fun)
        | isId fun
          = let pos = getSrcRef id
            in mkNode id "Application" $ quote (flatEvalText 100 fun ++ if ShowSrcPoses `elem` o && pos /= LowLevel.nil then " - " ++ (formatSrcRef $ readSrcRef $ pos) else "")
        | otherwise
          = mkNode id "Application" ""
      makeNode node@(NExpConstUse id def res)
        = mkNode id "Constant Use" $ quote (showQN False def ++  if ShowSrcPoses `elem` o && pos /= LowLevel.nil then " - " ++ (formatSrcRef $ readSrcRef $ pos) else "")
          where pos = getSrcRef id
      makeNode node@(NExpConstDef id def res)
        = mkNode id "Constant Def" $ quote $ showQN False def
      makeNode node@(NExpIdentifier id _ _)
        = mkNode id "Identifier" $ quote (flatEvalText 100 node ++ if ShowSrcPoses `elem` o && pos /= LowLevel.nil then " - " ++ (formatSrcRef $ readSrcRef $ pos) else "")
          where pos = getSrcRef id
      makeNode node@(NExpLiteral id name)
        = mkNode id "Literal" $ quote (name ++ if ShowSrcPoses `elem` o && pos /= LowLevel.nil then " - " ++ (formatSrcRef $ readSrcRef $ pos) else "")
          where pos = getSrcRef id
      makeNode node@(NExpHidden id children res)
        = mkNode id "Hidden" ""
      makeNode node@(NExpProjection id res)
        = mkNode id "Projection" $ quote $ if ShowSrcPoses `elem` o && pos /= LowLevel.nil then formatSrcRef $ readSrcRef $ pos else ""
          where pos = getSrcRef id
      makeNode node@(NExpCond id gType cnd res)
        | gType == IfCond    = mkNode id "If" ""
        | gType == CaseCond  = mkNode id "Case" ""
        | gType == GuardCond = mkNode id "Guard" ""
      makeNode node@(NExpFieldExp id field mappings)
        = mkNode id "Field Expression" $ quote $ if ShowSrcPoses `elem` o then formatSrcRef $ readSrcRef $ getSrcRef id else ""
      makeNode node@(NExpInterrupt)
        = mkNode nil "^C" ""
      makeNode node@(NExpUneval)
        = mkNode nil "Unevaluated" ""
      makeNode node@(NExpBottom)
        = mkNode nil "_|_" ""
      makeNode node@(NExpNotShown)
        = mkNode nil "Not Shown" ""
      makeNode _ = ""
      
      mkNode :: FileNode -> String -> String -> String
      mkNode id lab ""
        | lab == "^C"          = insertOffset id
                                              "int [shape=diamond, label=\""
                                              "^C\"];\n"
        | lab == "_|_"         = insertOffset id
                                              "bot [shape=diamond, label=\""
                                              "_|_\"];\n"
        | lab == "Not Shown"   = insertOffset id
                                              "ns [shape=diamond, label=\""
                                              "Not Shown\"];\n"
        | otherwise
          = insertOffset id
                         ("x" ++ (show id)
                          ++ " [shape=box, label=\"")
                         (lab
                          ++ "\", fontname=\"Monaco\", fontsize=10];\n")
      mkNode id lab desc
        | lab == "ValApp" || lab == "Literal" || lab == "Identifier"
          = insertOffset id
                         ("x" ++ (show id)
                          ++ " [shape=ellipse, label=\"")
                         (desc
                          ++ "\", fontname=\"Monaco\", fontsize=10];\n")
        | otherwise
          = insertOffset id
                         ("x" ++ (show id)
                          ++ " [shape=record, label=\"{")
                         (lab
                          ++ " | "
                          ++ desc
                          ++ "}\", fontname=\"Monaco\", fontsize=10];\n")
      
      insertOffset :: FileNode -> String -> String -> String
      insertOffset node@(FileNode offset) start end
        = start
          ++ (if ShowDecOffsets `elem` o then show offset ++ " "
              else "")
          ++ (if ShowHexOffsets `elem` o then "0x" ++ show node ++ " "
              else "")
          ++ end
      
      writeRank :: FileNode -> String
      writeRank (FileNode x) = (show x)

dotADTNodes :: ADT -> [PrettyOption] -> String
dotADTNodes adt@(Branch _ node _ others) o
  = makeNode node ++ (concat $ map ((flip dotADTNodes) o) others)
    where
      makeNode :: NodeExp -> String
      makeNode node@(NExpApp id fun args res)
        = mkNode id "" $ filter (/= '\n') $ quote $ (flatEvalText 100 node ++ " ~> " ++ fullEvalText 100 node)
      makeNode node@(NExpConstUse id def res)
        = mkNode id "" $ filter (/= '\n') $ quote $ (showQN False def ++ " ~> " ++ fullEvalText 100 node)
      makeNode node@(NExpConstDef id def res)
        = mkNode id "" $ filter (/= '\n') $ quote $ (showQN False def ++ " ~> " ++ fullEvalText 100 node)
      makeNode node@(NExpInterrupt)
        = mkNode nil "^C" ""
      makeNode node@(NExpUneval)
        = mkNode nil "Unevaluated" ""
      makeNode node@(NExpBottom)
        = mkNode nil "_|_" ""
      makeNode node@(NExpNotShown)
        = mkNode nil "Not Shown" ""
      makeNode _ = ""
      
      mkNode :: FileNode -> String -> String -> String
      mkNode id lab ""
        | lab == "^C"          = insertOffset id
                                              "int [shape=diamond, label=\""
                                              "^C\"];\n"
        | lab == "_|_"         = insertOffset id
                                              "bot [shape=diamond, label=\""
                                              "_|_\"];\n"
        | lab == "Not Shown"   = insertOffset id
                                              "ns [shape=diamond, label=\""
                                              "Not Shown\"];\n"
        | otherwise
          = insertOffset id
                         ("x" ++ (show id)
                          ++ " [shape=box, label=\"")
                         (lab
                          ++ "\", fontname=\"Monaco\", fontsize=10];\n")
      mkNode id lab desc
        = insertOffset id
                       ("x" ++ (show id)
                       ++ " [shape=box, label=\"")
                       (desc
                       ++ "\", fontname=\"Monaco\", fontsize=10];\n")
      
      insertOffset :: FileNode -> String -> String -> String
      insertOffset node@(FileNode offset) start end
        = start
          ++ (if ShowDecOffsets `elem` o then show offset ++ " "
              else "")
          ++ (if ShowHexOffsets `elem` o then "0x" ++ show node ++ " "
              else "")
          ++ end
dotADTNodes adt@(Cycle _ nodes) o = ""

formatSrcRef :: SrcRef -> String
formatSrcRef SrcRef {filename = f, line = sl, column = sc, lineend = el, columnend = ec} =
  f ++ ":" ++ show sl ++ ":" ++ show sc ++ "-" ++ show el ++ ":" ++ show ec

dotEdges :: Set.Set NodeExp -> [PrettyOption] -> String
dotEdges s o
  = makeEdges (setToList s)
    where
      makeEdges :: [NodeExp] -> String
      makeEdges [] = ""
      makeEdges (x:xs) = makeEdge x ++ makeEdges xs
      
      makeEdge :: NodeExp -> String
      makeEdge node@(NExpApp id fun args res)
        | (isVCycleOrApp res) && (getNode node) == (getNode res) && isId fun
          =    (foldr ((++) . ((flip (mkEdge id)) "Arg")) "" args)
            ++ if (parents o) then (mkEdge (getParentNode id)
                                           node
                                           "Par")
                              else ""
        | isId fun
          =    (foldr ((++) . ((flip (mkEdge id)) "Arg")) "" args)
            ++ mkEdge id res "Res"
            ++ if (parents o) then (mkEdge (getParentNode id)
                                           node
                                           "Par")
                              else ""
        | otherwise
          =    mkEdge id fun "Fun"
            ++ (foldr ((++) . ((flip (mkEdge id)) "Arg")) "" args)
            ++ mkEdge id res "Res"
            ++ if (parents o) then (mkEdge (getParentNode id)
                                           node
                                           "Par")
                              else ""
      makeEdge node@(NExpConstUse id def res)
        =    mkEdge id res "Res"
          ++ if (parents o) then (mkEdge (getParentNode id)
                                         node
                                         "Par")
                            else ""
      makeEdge node@(NExpConstDef id def res)
        =    mkEdge id res "Res"
          ++ if (parents o) then (mkEdge (getParentNode id)
                                         node
                                         "Par")
                            else ""
      makeEdge node@(NExpCond id gType cnd res)
        =    mkEdge id cnd "Cond"
          ++ mkEdge id res "Res"
      {-makeEdge node@(NExpFieldExp id field mappings)
      -}
      makeEdge node@(NExpHidden id children res)
        =    (foldr ((++) . ((flip (mkEdge id)) "Chld")) "" children)
          ++ mkEdge id res "Res"
      makeEdge node@(NExpProjection id res)
        = mkEdge id res "Res"
      makeEdge node = ""

      mkEdge :: Show a => a -> NodeExp -> String -> String
      mkEdge src dst lbl
        =    "x" ++ (show src)
          ++ " -> "
          ++ "x" ++ (show (getNode (findChild dst)))
          ++ " ["
          ++ lineStyle lbl
          ++ "];\n"
          where
            lineStyle :: String -> String
            lineStyle "Fun"   = "style=dashed"
            lineStyle "Const" = "style=dashed"
            lineStyle "Res"   = "style=bold"
            lineStyle "Par"   = "style=dotted, dir=back"
            lineStyle "Arg"   = "arrowhead=inv"
            lineStyle "Cond"  = ""
            lineStyle "Chld"  = "arrowhead=inv"
            lineStyle x       = "label=\"" ++ x ++ "\""

dotADTEdges :: ADT -> [PrettyOption] -> String
dotADTEdges adt opts =
  concat $ zipWith mkADTEdge starts ends
  where
    (starts,ends) = unzip $ setToList $ edgeSet
    edgeSet = adtEdgeSet adt Set.empty

foldrM1 :: (a -> IO ()) -> [a] -> IO ()
foldrM1 f = foldr (\x y -> y >> f x) (return ())

adtEdgeSet :: ADT -> Set.Set (ADT,ADT) -> Set.Set (ADT,ADT)
adtEdgeSet adt@(Branch _ exp _ others) s
  = foldr (\x y -> addToSet (adt,x) y) addedSet others
    where
      addedSet = doSetAdds others
      doSetAdds [] = s
      doSetAdds (x:xs) = adtEdgeSet x (doSetAdds xs)
adtEdgeSet adt@(Cycle _ adts) s
  = foldr (\x y -> addToSet (adt,x) y) s adts

mkADTEdge :: ADT -> ADT -> String
mkADTEdge src@(Branch _ exp _ _) dst@(Branch _ exp' _ _)
  =    "x" ++ (show (getNode exp))
    ++ " -> "
    ++ "x" ++ (show (getNode exp')) ++ "\n"
mkADTEdge src@(Branch _ exp _ _) dst@(Cycle tag exps)
  = concatMap (mkADTEdge src) exps
mkADTEdge _ _ = ""

findChild :: NodeExp -> NodeExp
findChild (NExpForward _ res) = findChild res
-- findChild (NExpHidden _ children res) = findChild res
-- findChild (NExpProjection _ res) = findChild res
findChild x = x

bracket :: String -> String
bracket x = '(' : (x ++ ")")

quote :: String -> String
quote [] = []
quote (x:xs)
  | x == '{'   = "\\{" ++ quote xs
  | x == '}'   = "\\}" ++ quote xs
  | x == '"'   = "\\\"" ++ quote xs
  | x == '\\'  = "\\\\" ++ quote xs
  | x == '<'   = "\\<" ++ quote xs
  | x == '>'   = "\\>" ++ quote xs
  | x == '\27' = quote $ drop 1 $ dropWhile (/='m') xs
  | otherwise  = x : (quote xs)