Commit 298002c8 authored by Michael Hanus's avatar Michael Hanus
Browse files

Fruther tools moved

parent 40bc96d3
......@@ -9,5 +9,7 @@ browser/BrowserGUI
-- Definition of the datatypes for the various analyses contained in the browser.
module AnalysisTypes(FunctionAnalysis(..),AnalysisResult(..),
ContentsKind(..)) where
import FlatCurry
-- Types for analyzing functions:
-- Interface of various kinds of function analyses:
data FunctionAnalysis a =
LocalAnalysis (FuncDecl -> a)
| LocalDataAnalysis ([TypeDecl] -> FuncDecl -> a)
| GlobalAnalysis ([FuncDecl] -> [(QName,a)])
| GlobalDataAnalysis ([TypeDecl] -> [FuncDecl] -> [(QName,a)])
-- The possible results of a function analysis:
data AnalysisResult =
MsgResult String -- a message to be shown in the browser
| ActionResult (IO ()) -- an I/O action to show the result externally
-- Types for analyzing complete modules:
data ModuleAnalysis a =
InterfaceAnalysis (Prog -> a) -- analysis based on the module interface
| FlatCurryAnalysis (Prog -> a) -- analysis based on the FlatCurry representation
| SourceCodeAnalysis (String -> IO a) -- analysis based on the module's source file
-- (argument is the name of the source file)
-- The possible results of a module analysis:
data ModuleAnalysisResult =
ContentsResult ContentsKind String -- a program to be shown in main contents window
| ModuleAction (IO ()) -- an I/O action to show the result externally
-- Kind of contents produced as the result of a module analysis
-- and shown in the main content window:
data ContentsKind =
CurryProg -- Curry source code
| LCurryProg -- Literate Curry source code
| FlatCurryExp -- FlatCurry expression
| OtherText -- some other text
-- 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.
module BrowserAnalysis(moduleAnalyses,allFunctionAnalyses,functionAnalyses) where
import AnalysisTypes
import FlatCurry
import FlatCurryGoodies
import FlatCurryShow(showFlatFunc)
import Overlapping
import PatternComplete
import SolutionComplete
import Nondeterminism
import Dependency
import Indeterminism
import CalledByAnalysis
import Linearity
import AddTypes
import ShowFlatCurry
import List(intersperse)
import Imports
import FileGoodies(stripSuffix)
import ShowGraph
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 =
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 (showCurryMod False prog))),
("Source program with type signatures added", SourceCodeAnalysis addTypes),
("FlatCurry code",
FlatCurryAnalysis (\prog -> ContentsResult CurryProg (showCurryMod True 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 <- addTypeSignatures (stripSuffix 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:"),
("Overlapping rules",
LocalAnalysis isOverlappingFunction `showWithMsg` showOverlap),
("Right-linear rules",
LocalAnalysis hasRightLinearRules `showWithMsg` showLinear),
GlobalAnalysis analyseRightLinearity `showWithMsg` showLinearity),
("Pattern completeness",
LocalDataAnalysis analyseCompleteness `showWithMsg` showComplete),
("Totally defined",
GlobalDataAnalysis analyseTotallyDefined `showWithMsg` showComplete),
("Solution complete",
GlobalAnalysis analyseSolutionComplete `showWithMsg` showOpComplete),
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>>>"),
"Meaning of function markings:\n\n"++
"ND>>> : nondeterministic operation\n\n"++
"unmarked: deterministic operation",
GlobalAnalysis analyseNondeterminism `showWith` showBool "ND>>>" ""),
"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>>>" ""),
"Meaning of function markings:\n\n"++
"SET>>> : set-valued operation\n\n"++
"unmarked: single-valued operation",
GlobalAnalysis analyseSetValued `showWith` showBool "SET>>>" ""),
"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 [] _ = True -- this case should not occur
isExternal (Func g _ _ _ rule : gs) f = if f==g then isExternalRule rule
else isExternal gs f
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 =
(map (\(f,gs)->(showLocalName f,
if isExt f then extAttrs else [],
map showLocalName gs))
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")]
-- 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 >> done))
withAction (LocalDataAnalysis ana) =
LocalDataAnalysis (\types f -> ActionResult (ana types f >> done))
withAction (GlobalAnalysis ana) =
GlobalAnalysis (\funs -> map (\(name,res)->(name,ActionResult (res >> done)))
(ana funs))
withAction (GlobalDataAnalysis ana) =
GlobalDataAnalysis (\types funs -> map (\(name,res)->(name,ActionResult (res>>done)))
(ana types funs))
-- A simple example for a global function analysis of type IO:
printFuncName :: [FuncDecl] -> [(QName,IO ())]
printFuncName = map (\fdecl -> (funcName fdecl, putStrLn (unqualifiedName fdecl)))
This diff is collapsed.
How to extend CurryBrowser:
CurryBrowser is designed as a generic analysis tool for declarative
programs. In particular, it provides some infrastructure to integrate
new program analyses implemented in Curry.
The ideas of this infrastructure are described in this paper:
M. Hanus: A Generic Analysis Environment for Declarative Programs
Proc. of the International Workshop on Curry and Functional Logic
Programming (WCFLP 2005), pp. 43-48, ACM Press, 2005
In the current implementation of CurryBrowser, the following steps
are necessary to integrate a new program analysis (here, BROWSERDIR
denotes the implementation directory of CurryBrowser):
1. Implement the new program analysis in a Curry module as a function
of one of the types used in the module BROWSERDIR/AnalysesTypes.curry.
For instance, a global analyses could be of type
myana :: [FuncDecl] -> [(QName,a)]
Example analyses can be found in the directory BROWSERDIR/analysis.
2. Include the new analysis in the file BROWSERDIR/BrowserAnalysis.curry,
i.e., import the module containing the new analysis and add the
corresponding analysis function in one of the list constants
specifying the current analyses (e.g., functionAnalyses,
allFunctionAnalyses, moduleAnalyses).
3. Run "make" in BROWSERDIR.
If everything runs well, the new analysis is available with the next
invocation of CurryBrowser.
If your analysis is of general interest for Curry users, please sent it
to Michael Hanus <> so that it can be included
in future distributions of the CurryBrowser.
-- Some useful operations on FlatCurry programs:
module FlatCurryGoodies where
import FlatCurry
moduleName (Prog mod _ _ _ _) = mod
importsOfProg :: Prog -> [String]
importsOfProg (Prog _ imps _ _ _) = imps
typesOfProg (Prog _ _ types _ _) = types
funcsOfProg (Prog _ _ _ funcs _) = funcs
funcName (Func fname _ _ _ _) = fname
unqualifiedName (Func fname _ _ _ _) = snd fname
funcModule (Func fname _ _ _ _) = fst fname
isPublic :: FuncDecl -> Bool
isPublic (Func _ _ vis _ _) = vis==Public
How to use CurryBrowser:
CurryBrowser is a tool to browse through the modules and
functions of programs written in the declarative multi-paradigm
language Curry.
It contains operations to analyze the properties of functions
of a Curry module. Moreover, it is constructed in a way so that
new analyzers can be easily connected to CurryBrowser
(see the source code of module "BrowserAnalysis" for details).
Start CurryBrowser:
CurryBrowser is part of the Curry systems PAKCS and KiCS2
and can be started in two ways:
- In the command shell via the command: [[pakcs|kics2]/bin/]currybrowse mod
- In the PAKCS/KiCS2 environment after loading the module "mod" and typing
the command ":browse"
Here, "mod" is the name of the main module of a Curry application.
After the start, CurryBrowser loads the interfaces of the main
module and all imported modules before a GUI is created.
The GUI of CurryBrowser:
Selecting modules and imports:
The column "Select module" shows the hierarchical structure of
modules and their imports. If a module name is prefixed by "+",
its imports are not shown. Clicking on such a module name will
open their imports, i.e., all directly imported modules are
shown below. The imported module names are indented so that
the hierarchy is directly visible.
After selecting a module in the list of modules, its source code,
interface, or various other formats of the module can be shown
in the main (right) text area (see menu "Show selected module as...").
Furthermore, various properties of all exported functions defined in
the selected module can be analyzed (see menu "Analyse selected module").
If the function list box contains all functions of the selected
module, then all of them are analyzed by this menu, otherwise
the exported ones are loaded before analyzing them.
Finally, the list of exported or all functions defined in the
selected module or various other list of functions (see menu
"Select functions...") can be shown in the left lower "Functions"
Selecting functions:
After selecting and showing a list of functions in the left lower
column, individually selected functions can be analyzed by selecting
a function analysis (see menu "Select analysis...") from the list of
available analysis.
Analyzing functions:
The results of the analysis of individually selected functions
are shown in the lower right text area, and the results of analyzing
complete modules are shown by prefixes in the function list box
(the explanation about these prefixes are shown in the lower right
text area).
Note that the analyses require different amounts of time.
Local analyses are usually fast since they are based on looking
on the code of an indiviual function only, whereas global analyses
require the consideration of all functions occurring on the module
and their (directly and indirectly) imported modules.
Focusing functions:
If the button "focus in code" is checked, individually selected
functions are focused in the source code, i.e., whenever a function
is selected in the function list box, the source code of the
corresponding module is shown in the right text area
where the focus is put on the definition of the function.
Note that, if you select a function that has been introduced
by some compiler transformations (e.g., lambda lifting),
the source code cannot be focused on this function.
The analyses of CurryBrowser:
The following analysis are currently available:
Curry code (local analysis):
Show the intermediate FlatCurry code in (almost) Curry syntax,
whenever possible.
FlatCurry code (local analysis):
Show the intermediate FlatCurry code in human-readable syntax.
This is useful to see the pattern matching in the form of case
FlatCurry expression (local analysis):
Show the intermediate FlatCurry code as an expression according
to the FlatCurry representation in the Curry module "FlatCurry".
Calls directly (local analysis):
Which functions are directly called in the rules defining this
function? Since the called functions are shown qualified with
their module name, the result is useful to spot the directly
called functions in larger applications.
Depends on (global analysis):
On which functions does the current function depend, i.e., what
are the functions that might be called (directly or indirectly)
when this function is executed?
Depends on externals (global analysis):
On which external functions (i.e., functions that are not
defined in Curry) does the current function depend, i.e., what
are the external functions that might be called (directly or
indirectly) when this function is executed?
Dependency graph (global analysis):
Show the complete dependency graph of the selected function
(with the graph visualization tool "dot").
Local dependency graph (global analysis):
Show the dependency graph of the selected function local
to the current module, i.e., do not show dependencies
that are defined in imported modules. Thus, imported functions
are leaves of the dependency graph that are shown in a different
Called by (global analysis):
Which functions of the current module call this function
directly or indirectly?
Overlapping rules (local analysis):
Is the function defined by overlapping left-hand sides?
Right-linear rules (local analysis):
Is the function defined by right-linear rules?
Right-linearity (global analysis):
Is the function defined by right-linear rules and does it only depend
on functions defined by right-linear rules?
Pattern completeness (local analysis):
Is the pattern matching exhaustive, i.e., is the function reducible
for any (well-typed) ground constructor arguments?
Totally defined (global analysis):
Is the function totally defined, i.e., does the function evaluate to a value
for any (well-typed) ground constructor arguments?
Solution complete (global analysis):
Is the function operationally complete, i.e., does its execution
not suspend for any arguments?
Nondeterministic (global analysis):
Is the function nondeterministic, i.e., is it directly or indirectly
defined by overlapping rules so that it might deliver
(nondeterministically) two values for the same ground constructor
Set-valued (global analysis):
Is the function set-valued, i.e., is it directly or indirectly defined
by overlapping rules or rules containing extra variables so that
an application to some ground constructor arguments is equal to
different values?
Purity (global analysis):
Is the function pure (referentially transparent), i.e., is it ensured
that it delivers always the same values for the same ground constructor
arguments at each time and all schedulings of the evaluation?
This might not be the case if committed choice or sending via ports
is executed.
-- A library to get the import structures of a program.
module Imports(getImportedInterfaces,moduleImports,readFlatCurryFileInLoadPath,
import FlatCurry
import FlatCurryGoodies
import FlatCurryRead
import FileGoodies
import System(getArgs)
import Distribution(getLoadPath,addCurrySubdir)
import Directory
import Maybe
-- Get all interfaces (i.e., main and all indirectly imported modules) of a program:
getImportedInterfaces :: String -> IO [(String,InterfaceOrFlatProg)]
getImportedInterfaces mod = do
args <- getArgs
imps <- readFlatCurryIntWithImports (if null args
then mod
else dirName(head args)++"/"++mod)
return (map (\prog -> (moduleName prog, IF prog)) imps)
-- Extract a module and its imports:
moduleImports (Prog mod imps _ _ _) = (mod,imps)