CurryInfo: currybrowse-3.0.0 / BrowserAnalysis

classes:

              
documentation:
-----------------------------------------------------------------------------
-- Definition of the various analyses contained in the browser.
--
-- To modify or extend the analysis functionality of the browser,
-- add access to a new analysis here and recompile the browser.
-----------------------------------------------------------------------------
name:
BrowserAnalysis
operations:
allFunctionAnalyses functionAnalyses moduleAnalyses viewDependencyGraph
sourcecode:
module BrowserAnalysis
  ( moduleAnalyses, allFunctionAnalyses, functionAnalyses
  , viewDependencyGraph )
 where

import Data.List            ( nub, (\\) )

import FlatCurry.Types
import FlatCurry.Goodies    ( funcName )
import FlatCurry.Show       ( showFlatFunc, showFlatProg )

import AddTypes             ( addTypeSignatures )
import Analysis.Types       ( AOutFormat(..) )
import CASS.Server          ( analyzeFunctionForBrowser )
import CASS.Registry        ( functionAnalysisInfos )
import Data.GraphViz
import FlatCurry.ShowIntMod
import System.CurryPath     ( runModuleActionQuiet )

import BrowserAnalysisTypes
import CurryBrowseAnalysis.Overlapping
import CurryBrowseAnalysis.PatternComplete
import CurryBrowseAnalysis.SolutionComplete
import CurryBrowseAnalysis.Nondeterminism
import CurryBrowseAnalysis.Dependency
import CurryBrowseAnalysis.Indeterminism
import CurryBrowseAnalysis.CalledByAnalysis
import CurryBrowseAnalysis.Linearity

infix 1 `showWith`,`showWithMsg`

-------------------------------------------------------------------------------
-- The list of all available analyses for individual modules.
-- Each analysis must return a string representation of its analysis result
-- or an IO action to show the result.
moduleAnalyses :: [(String, ModuleAnalysis ModuleAnalysisResult)]
moduleAnalyses =
 [("Interface",
   InterfaceAnalysis (\int -> ContentsResult CurryProg (showInterface False int)))
  --("Write Interface",
  -- InterfaceAnalysis (\int -> ModuleAction (putStrLn (showInterface False int)))),
  --("Read source file",
  -- SourceCodeAnalysis (\fname -> readFile fname >>= \prog ->
  --                               return (ContentsResult CurryProg prog))),
  ,("Curry code (generated from FlatCurry)",
   FlatCurryAnalysis (\prog -> ContentsResult CurryProg (showCurryModule prog)))
  ,("Source program with type signatures added", SourceCodeAnalysis addTypes)
  ,("FlatCurry code",
   FlatCurryAnalysis (\prog -> ContentsResult CurryProg (showFlatCurry prog)))
  ,("FlatCurry expression",
   FlatCurryAnalysis (\prog -> ContentsResult FlatCurryExp (showFlatProg prog)))
  ]

addTypes :: String -> IO ModuleAnalysisResult
addTypes fname
  | take 7 (reverse fname) == "yrrucl."
  = return (ContentsResult OtherText "Can't add types to literate programs")
  | otherwise
  = do prog <- runModuleActionQuiet addTypeSignatures fname
       return (ContentsResult CurryProg prog)

-----------------------------------------------------------------------------
-- The list of all available analyses for individual functions.
-- Each analysis must return a string or an IO action representation of its
-- analysis result.
functionAnalyses :: [(String, FunctionAnalysis AnalysisResult)]
functionAnalyses =
 [("Curry code",        LocalAnalysis     (MsgResult . showFuncDeclAsCurry)),
  --("Print Curry code",  withAction (LocalAnalysis (putStr . showFuncDeclAsCurry))),
  --("Print func name",   withAction (GlobalAnalysis printFuncName)),
  ("FlatCurry code",    LocalAnalysis     (MsgResult . showFuncDeclAsFlatCurry)),
  ("FlatCurry expression",LocalAnalysis   (MsgResult . showFlatFunc)),
  ("Calls directly",    LocalAnalysis     callsDirectly `showWithMsg`
       showQDep "Calls the following functions directly in the right-hand sides:"),
  ("Depends on",        GlobalAnalysis    indirectlyDependent `showWithMsg`
       showQDep "Depends on the following functions:"),
  ("Depends on externals", GlobalAnalysis  externalDependent `showWithMsg`
       showQDep "Depends on the following external functions:"),
  ("Dependency graph (DOT)", withAction (GlobalAnalysis viewFuncDepGraphs)),
  ("Local dependency graph (DOT)", withAction (GlobalAnalysis viewFuncLocalDepGraphs)),
  ("Called by",         GlobalAnalysis    calledBy `showWithMsg`
       showDep "Is called by the following functions of the current module:")] ++
 map (\ (aname,atitle) -> (atitle++" (CASS)", withCASS aname))
     functionAnalysisInfos ++
 [("Overlapping rules",
   LocalAnalysis     isOverlappingFunction   `showWithMsg` showOverlap),
  ("Right-linear rules",
   LocalAnalysis     hasRightLinearRules     `showWithMsg` showLinear),
  ("Right-linearity",
   GlobalAnalysis    analyseRightLinearity   `showWithMsg` showLinearity),
  ("Pattern completeness",
   LocalDataAnalysis analyseCompleteness     `showWithMsg` showComplete),
  ("Totally defined",
   GlobalDataAnalysis analyseTotallyDefined  `showWithMsg` showComplete),
  ("Solution complete",
   GlobalAnalysis    analyseSolutionComplete `showWithMsg` showOpComplete),
  ("Nondeterministic",
   GlobalAnalysis    analyseNondeterminism   `showWithMsg` showNondet),
  ("Set-valued",        GlobalAnalysis    analyseSetValued `showWithMsg` showSetValued),
  ("Purity",            GlobalAnalysis    analyseIndeterminism `showWithMsg` showIndet)]

-----------------------------------------------------------------------------
-- The list of all available analyses for sets of functions.
-- Each analysis must return a short(!) string representation (no more than a few chars)
-- of its analysis result that is prefixed to the function name in the list
-- of function. The second (String) component of each analysis entry is a short
-- explanation of the used prefixes.
allFunctionAnalyses :: [(String, String, FunctionAnalysis String)]
allFunctionAnalyses =
 [("Overlapping rules",
   "Meaning of function markings:\n\n"++
   "OVL>>>  : defining rules overlap\n\n"++
   "unmarked: no overlapping rules",
   LocalAnalysis     isOverlappingFunction `showWith` showBool "OVL>>>" ""),
  ("Pattern completeness",
   "Meaning of function markings:\n\n"++
   "INCMP>>> : possibly incompletely defined operation\n\n"++
   "unmarked : completely defined operation",
   LocalDataAnalysis analyseCompleteness   `showWith` showCompleteS),
  ("Totally defined",
   "Meaning of function markings:\n\n"++
   "PARTIAL>>> : possibly partially defined operation\n\n"++
   "unmarked : totally defined operation",
   GlobalDataAnalysis analyseTotallyDefined `showWith` showTotally),
  ("Solution complete",
   "Meaning of function markings:\n\n"++
   "SUSP>>> : operation may suspend\n\n"++
   "unmarked: operation does not suspend",
   GlobalAnalysis    analyseSolutionComplete `showWith` showBool "" "SUSP>>>"),
  ("Nondeterministic",
   "Meaning of function markings:\n\n"++
   "ND>>>   : nondeterministic operation\n\n"++
   "unmarked: deterministic operation",
   GlobalAnalysis    analyseNondeterminism `showWith` showBool "ND>>>" ""),
  ("Right-linearity",
   "Meaning of function markings:\n\n"++
   "RL>>>   : defined by right-linear rules and depend only on\n"++
   "          right-linear functions\n\n"++
   "unmarked: possibly non-right-linear",
   GlobalAnalysis    analyseRightLinearity `showWith` showBool "RL>>>" ""),
  ("Set-valued",
   "Meaning of function markings:\n\n"++
   "SET>>>  : set-valued operation\n\n"++
   "unmarked: single-valued operation",
   GlobalAnalysis    analyseSetValued      `showWith` showBool "SET>>>" ""),
  ("Purity",
   "Meaning of function markings:\n\n"++
   "IMP>>>  : impure (indeterministic) operation\n\n"++
   "unmarked: referentially transparent operation",
   GlobalAnalysis    analyseIndeterminism  `showWith` showBool "IMP>>>" "")]

-- This function is useful to integrate an existing program analysis
-- into the browser by providing a transformation of the analysis results
-- into strings:
showWith :: FunctionAnalysis a -> (a->String) -> FunctionAnalysis String
showWith (LocalAnalysis ana) showresult =
   LocalAnalysis (\f -> showresult (ana f))
showWith (LocalDataAnalysis ana) showresult =
   LocalDataAnalysis (\types f -> showresult (ana types f))
showWith (GlobalAnalysis ana) showresult =
   GlobalAnalysis (\funs -> map (\(name,res)->(name,showresult res)) (ana funs))
showWith (GlobalDataAnalysis ana) showresult =
   GlobalDataAnalysis (\types funs -> map (\(name,res)->(name,showresult res))
                                          (ana types funs))

showWithMsg :: FunctionAnalysis a -> (a->String) -> FunctionAnalysis AnalysisResult
showWithMsg (LocalAnalysis ana) showresult =
   LocalAnalysis (\f -> MsgResult (showresult (ana f)))
showWithMsg (LocalDataAnalysis ana) showresult =
   LocalDataAnalysis (\types f -> MsgResult (showresult (ana types f)))
showWithMsg (GlobalAnalysis ana) showresult =
   GlobalAnalysis (\funs -> map (\(name,res)->(name,MsgResult (showresult res)))
                                (ana funs))
showWithMsg (GlobalDataAnalysis ana) showresult =
   GlobalDataAnalysis (\types funs -> map (\(name,res)->(name,MsgResult (showresult res)))
                                          (ana types funs))

-- Shows a Boolean result:
showBool :: String -> String -> Bool -> String
showBool t _ True  = t
showBool _ f False = f

-- Shows the result of the overlapping analysis.
showOverlap :: Bool -> String
showOverlap True  = "Overlapping"
showOverlap False = "Not Overlapping"

-- Shows the result of the right-linear rules analysis.
showLinear :: Bool -> String
showLinear True  = "Defined by right-linear rules"
showLinear False = "Definition contains non-right-linear rules"

-- Shows the result of the right-linearity analysis.
showLinearity :: Bool -> String
showLinearity True  = "Defined by functions with right-linear rules"
showLinearity False = "Defined by functions containing non-right-linear rules"

-- Shows the result of the completeness analysis.
showComplete :: CompletenessType -> String
showComplete Complete     = "completely defined (i.e., reducible on all constructors)"
showComplete InComplete   = "incompletely defined"
showComplete InCompleteOr =
                    "incompletely defined in each disjunction (but might be complete)"

showCompleteS :: CompletenessType -> String
showCompleteS Complete     = ""
showCompleteS InComplete   = "INCMP>>>"
showCompleteS InCompleteOr = "INCMP>>>"

-- Shows the result of the totally-defined analysis.
showTotallyDefined :: CompletenessType -> String
showTotallyDefined Complete     = "totally defined (i.e., reducible to a value)"
showTotallyDefined InComplete   = "partially defined"
showTotallyDefined InCompleteOr = "partially defined"

showTotally :: CompletenessType -> String
showTotally Complete     = ""
showTotally InComplete   = "PARTIAL>>>"
showTotally InCompleteOr = "PARTIAL>>>"

-- Shows the result of the operational completeness analysis.
showOpComplete :: Bool -> String
showOpComplete True  = "All solutions can be computed"
showOpComplete False = "Evaluation might suspend"

-- Shows the result of the indeterminism analysis.
showIndet :: Bool -> String
showIndet True  = "Impure (indeterministic) operation"
showIndet False = "Referentially transparent"

-- Shows the result of the non-determinism analysis.
showNondet :: Bool -> String
showNondet True  = "Operation might be nondeterministic"
showNondet False = "Deterministic operation"

-- Shows the result of the set-valued analysis.
showSetValued :: Bool -> String
showSetValued True  = "Operation might be set-valued"
showSetValued False = "Single-valued operation"

-- Shows the result of a dependency analysis with title.
showQDep :: String -> [QName] -> String
showQDep title fnames = title ++ "\n" ++ unlines (map (\(m,n)->m++"."++n) fnames)

-- Shows the result of a dependency analysis with title without qualifiers.
showDep :: String -> [QName] -> String
showDep title fnames = title ++ "\n" ++ unlines (map snd fnames)

-- Visualize the result of the dependency graph analysis.
viewFuncDepGraphs :: [FuncDecl] -> [(QName,IO ())]
viewFuncDepGraphs fdecls =
  map (\(f,fgraph)->(f,showDGraph f (isExternal fdecls) fgraph))
      (dependencyGraphs fdecls)

isExternal :: [FuncDecl] -> QName -> Bool
isExternal [] _ = True -- this case should not occur
isExternal (Func g _ _ _ rule : gs) f = if f==g then isExternalRule rule
                                                else isExternal gs f
 where
   isExternalRule (Rule _ _) = False
   isExternalRule (External _) = True

-- Visualize the result of the local dependency graph analysis.
viewFuncLocalDepGraphs :: [FuncDecl] -> [(QName,IO ())]
viewFuncLocalDepGraphs fdecls =
  map (\(f,fgraph)->(f,showDGraph f (\(m,_)->m/=fst f) fgraph))
      (localDependencyGraphs fdecls)

showDGraph :: QName -> (QName->Bool) -> [(QName,[QName])] -> IO ()
showDGraph (mod,_) isExt fnames =
   viewDependencyGraph
       (map (\(f,gs)->(showLocalName f,
                       if isExt f then extAttrs else [],
                       map showLocalName gs))
            fnames)
 where
  showLocalName (m,g) = if m==mod then g else m++'.':g

  -- dot attributes for visualization of external function nodes:
  extAttrs = [("style","filled"),("color",".7 .3 1.0")]

viewDependencyGraph :: [(String,[(String,String)],[String])] -> IO ()
viewDependencyGraph deps = viewDotGraph $ dgraph "dependencies" nodes edges
 where
  nodes = map (\ (n,a,_) -> Node n a) deps ++
          map (\ n -> Node n [])
              (concatMap (\ (_,_,ts) -> ts) deps \\ map (\ (n,_,_) -> n) deps)
  edges = map (\ (s,t) -> Edge s t [])
              (nub (concatMap (\ (p,_,ds) -> map (\d -> (p,d)) ds) deps))

--------------------------------------------------------------------------------
-- Auxiliary operation to integrate a CASS analysis for an individual
-- operation.
withCASS :: String -> FunctionAnalysis AnalysisResult
withCASS ananame =
  LocalAnalysis (\f -> ActionResult (analyzeFunctionWithCASS f))
 where
   analyzeFunctionWithCASS (Func f _ _ _ _) =
     analyzeFunctionForBrowser ananame f AText

--------------------------------------------------------------------------------
-- This function is useful to integrate an existing program analysis
-- with result type (IO a) into the browser by providing a transformation
-- of the analysis results.
withAction :: FunctionAnalysis (IO _) -> FunctionAnalysis AnalysisResult
withAction (LocalAnalysis ana) =
   LocalAnalysis (\f -> ActionResult (ana f >> return ""))
withAction (LocalDataAnalysis ana) =
   LocalDataAnalysis (\types f -> ActionResult (ana types f >> return ""))
withAction (GlobalAnalysis ana) =
   GlobalAnalysis
     (\funs -> map (\(name,res) -> (name,ActionResult (res >> return "")))
                   (ana funs))
withAction (GlobalDataAnalysis ana) =
   GlobalDataAnalysis
     (\types funs -> map (\ (name,res) -> (name,ActionResult (res>>return "")))
                         (ana types funs))

-- A simple example for a global function analysis of type IO:
printFuncName :: [FuncDecl] -> [(QName,IO ())]
printFuncName =
  map (\fdecl -> (funcName fdecl, putStrLn (snd (funcName fdecl))))
types:

              
unsafe:
unsafe due to modules Control.Search.AllValues Control.Search.Unsafe CASS.Registry Analysis.NondetOps System.IO.Unsafe Analysis.UnsafeModule