Commit 0e105d52 authored by Michael Hanus 's avatar Michael Hanus

CPM updated

parent 25732641
......@@ -763,8 +763,11 @@ which is used by the \code{update} command to download the
index of all repositories.
Default value:
\begin{lstlisting}
https://git.ps.informatik.uni-kiel.de/curry-packages/cpm-index.git
https://www-ps.informatik.uni-kiel.de/~cpm/PACKAGES/INDEX.tar.gz
\end{lstlisting}
One can also provide more than one URL which are tried in sequential order
until one index could be downloaded.
In this case, the URLs should be separated by a vertical bar (\ccode{|}).
\item[\fbox{\code{PACKAGE_TARFILES_URL}}]
The URL prefix to the directory containing the ``tar'' files of all packages.
......@@ -781,6 +784,11 @@ is downloaded from
\begin{lstlisting}
https://www-ps.informatik.uni-kiel.de/~cpm/PACKAGES/cpm-2.2.0.tar.gz
\end{lstlisting}
One can also provide more than one URL prefix
which are tried in sequential order until the package could be downloaded
from one of them.
In this case, the URL prefixes should be separated
by a vertical bar (\ccode{|}).
\end{description}
%
......@@ -788,7 +796,7 @@ The CPM command
\begin{lstlisting}
> cypm config
\end{lstlisting}
show the current values of the configuration options.
shows the current values of the configuration options.
Note that one write the option names also in lowercase or omit
the underscores. For instance, one can write \code{currybin}
......
......@@ -6,7 +6,7 @@
module CPM.Config
( Config ( Config, packageInstallDir, binInstallDir, repositoryDir
, appPackageDir, packageIndexURLs, packageTarFilesURL
, appPackageDir, packageIndexURLs, packageTarFilesURLs
, homePackageDir, curryExec
, compilerVersion, compilerBaseVersion, baseVersion )
, readConfigurationWith, defaultConfig
......@@ -31,15 +31,15 @@ import CPM.FileUtil ( ifFileExists )
import CPM.Helpers ( strip )
--- The default URL prefix to the directory containing tar files of all packages
packageTarFilesDefaultURL :: String
packageTarFilesDefaultURL =
"https://www-ps.informatik.uni-kiel.de/~cpm/PACKAGES/"
packageTarFilesDefaultURLs :: [String]
packageTarFilesDefaultURLs =
["https://www-ps.informatik.uni-kiel.de/~cpm/PACKAGES"]
--- The default location of the central package index.
packageIndexDefaultURLs :: [String]
packageIndexDefaultURLs =
[packageTarFilesDefaultURL ++ "INDEX.tar.gz",
"https://git.ps.informatik.uni-kiel.de/curry-packages/cpm-index.git"]
map (++"/INDEX.tar.gz") packageTarFilesDefaultURLs ++
["https://git.ps.informatik.uni-kiel.de/curry-packages/cpm-index.git"]
-- If you have an ssh access to git.ps.informatik.uni-kiel.de:
--["ssh://git@git.ps.informatik.uni-kiel.de:55055/curry-packages/cpm-index.git"]
......@@ -55,8 +55,8 @@ data Config = Config {
, appPackageDir :: String
--- URLs tried for downloading the package index
, packageIndexURLs :: [String]
--- URL prefix to the directory containing tar files of all packages
, packageTarFilesURL :: String
--- URL prefixes to the directory containing tar files of all packages
, packageTarFilesURLs :: [String]
--- The directory where the default home package is stored
, homePackageDir :: String
--- The executable of the Curry system used to compile and check packages
......@@ -78,7 +78,7 @@ defaultConfig = Config
, repositoryDir = "$HOME/.cpm/index"
, appPackageDir = ""
, packageIndexURLs = packageIndexDefaultURLs
, packageTarFilesURL = packageTarFilesDefaultURL
, packageTarFilesURLs = packageTarFilesDefaultURLs
, homePackageDir = ""
, curryExec = Dist.installDir </> "bin" </> Dist.curryCompiler
, compilerVersion = ( Dist.curryCompiler
......@@ -102,7 +102,7 @@ showConfiguration cfg = unlines
, "APP_PACKAGE_PATH : " ++ appPackageDir cfg
, "HOME_PACKAGE_PATH : " ++ homePackageDir cfg
, "PACKAGE_INDEX_URL : " ++ intercalate "|" (packageIndexURLs cfg)
, "PACKAGE_TARFILES_URL : " ++ packageTarFilesURL cfg
, "PACKAGE_TARFILES_URL : " ++ intercalate "|" (packageTarFilesURLs cfg)
]
--- Shows the compiler version in the configuration.
......@@ -270,16 +270,18 @@ stripProps = map ((map toUpper . filter (/='_') . strip) *** strip)
--- record with a value for that option.
keySetters :: [(String, String -> Config -> Config)]
keySetters =
[ ("APPPACKAGEPATH" , \v c -> c { appPackageDir = v })
, ("BASEVERSION" , \v c -> c { baseVersion = v })
, ("BININSTALLPATH" , \v c -> c { binInstallDir = v })
, ("CURRYBIN" , \v c -> c { curryExec = v })
, ("HOMEPACKAGEPATH" , \v c -> c { homePackageDir = v })
, ("PACKAGEINDEXURL" , \v c -> c { packageIndexURLs = [v] })
, ("PACKAGETARFILESURL" , \v c -> c { packageTarFilesURL = v })
, ("PACKAGEINSTALLPATH" , \v c -> c { packageInstallDir = v })
, ("REPOSITORYPATH" , \v c -> c { repositoryDir = v })
[ ("APPPACKAGEPATH" , \v c -> c { appPackageDir = v })
, ("BASEVERSION" , \v c -> c { baseVersion = v })
, ("BININSTALLPATH" , \v c -> c { binInstallDir = v })
, ("CURRYBIN" , \v c -> c { curryExec = v })
, ("HOMEPACKAGEPATH" , \v c -> c { homePackageDir = v })
, ("PACKAGEINDEXURL" , \v c -> c { packageIndexURLs = breakURLs v })
, ("PACKAGETARFILESURL" , \v c -> c { packageTarFilesURLs = breakURLs v })
, ("PACKAGEINSTALLPATH" , \v c -> c { packageInstallDir = v })
, ("REPOSITORYPATH" , \v c -> c { repositoryDir = v })
]
where
breakURLs = splitOn "|"
--- Sequentially applies a list of functions that transform a value to a value
--- of that type (i.e. a fold). Each function can error out with a Left, in
......
......@@ -122,7 +122,7 @@ runELM elmact = do
(|>=) :: IO (ErrorLogger a) -> (a -> IO (ErrorLogger b)) -> IO (ErrorLogger b)
a |>= f = do
(msgs, err) <- a
mapIO showLogEntry msgs
mapM showLogEntry msgs
case err of
Right v -> do (msgs', err') <- f v
return (msgs', err')
......@@ -137,7 +137,7 @@ mapEL :: (a -> IO (ErrorLogger b)) -> [a] -> IO (ErrorLogger [b])
mapEL _ [] = succeedIO []
mapEL f (x:xs) = do
(msgs, err) <- f x
mapIO showLogEntry msgs
mapM showLogEntry msgs
case err of
Right v -> do
(msgs', xs') <- mapEL f xs
......@@ -151,7 +151,7 @@ foldEL :: (a -> b -> IO (ErrorLogger a)) -> a -> [b] -> IO (ErrorLogger a)
foldEL _ z [] = succeedIO z
foldEL f z (x:xs) = do
(msgs, err) <- f z x
mapIO showLogEntry msgs
mapM showLogEntry msgs
case err of
Right v -> foldEL f v xs
Left m -> return $ ([], Left m)
......@@ -256,7 +256,7 @@ errorMessage msg = (log Error msg |> succeedIO ()) >> done
fromErrorLogger :: IO (ErrorLogger a) -> IO a
fromErrorLogger a = do
(msgs, err) <- a
mapIO showLogEntry msgs
mapM showLogEntry msgs
case err of
Left m -> showLogEntry m >> exitWith 1
Right v -> return v
......
......@@ -63,7 +63,7 @@ cpmBanner :: String
cpmBanner = unlines [bannerLine,bannerText,bannerLine]
where
bannerText =
"Curry Package Manager <curry-lang.org/tools/cpm> (version of 14/08/2020)"
"Curry Package Manager <curry-lang.org/tools/cpm> (version of 12/10/2020)"
bannerLine = take (length bannerText) (repeat '-')
main :: IO ()
......
......@@ -73,8 +73,10 @@ installPackageSourceTo pkg (Http url) installdir = do
else do
tmpdir <- tempDir
let tmppkgfile = tmpdir </> pkgfile
c <- inTempDir $ showExecCmd $ "curl -f -s -S -o " ++ tmppkgfile ++
" " ++ quote url
ll <- getLogLevel
c <- inTempDir $ showExecCmd $
"curl -f -s " ++ (if ll==Debug then "-S" else "")
++ " -o " ++ tmppkgfile ++ " " ++ quote url
if c == 0
then installPkgFromFile pkg tmppkgfile pkgDir True
else do cleanTempDir
......
......@@ -31,7 +31,7 @@ import List
import Maybe (isJust)
import FilePath
import CPM.Config ( Config, packageInstallDir, packageTarFilesURL )
import CPM.Config ( Config, packageInstallDir, packageTarFilesURLs )
import CPM.ErrorLogger
import CPM.FileUtil ( cleanTempDir, copyDirectory, inTempDir
, recreateDirectory, inDirectory
......@@ -120,14 +120,22 @@ acquireAndInstallPackage cfg pkg = do
if pkgDirExists
then logMsg Info $ "Package '" ++ packageId pkg ++
"' already installed, skipping"
else do
let stdurl = packageTarFilesURL cfg ++ packageId pkg ++ ".tar.gz"
logMsg Info ("Installing package from " ++ stdurl)
(msgs,err) <- execIO $ installPackageSourceTo pkg (Http stdurl)
(packageInstallDir cfg)
case err of
Right _ -> toELM $ return (msgs,err)
Left _ -> toELM $ acquireAndInstallPackageFromSource cfg pkg
else tryInstallFromURLs (packageTarFilesURLs cfg)
where
tryInstallFromURLs [] = toELM $ failIO "No URLs for installations"
tryInstallFromURLs (url:urls) = do
let stdurl = url ++ "/" ++ packageId pkg ++ ".tar.gz"
logMsg Info $ "Installing package from " ++ stdurl
(msgs,err) <- execIO $ installPackageSourceTo pkg (Http stdurl)
(packageInstallDir cfg)
case err of
Left _ -> if null urls
then toELM $ failIO downloadError
else tryInstallFromURLs urls
Right _ -> toELM $ acquireAndInstallPackageFromSource cfg pkg
downloadError =
"Package downloading failed. Use option '-v debug' for more infos."
--- Acquires a package from the source specified in its specification and
--- installs it to the global package cache.
......@@ -135,9 +143,9 @@ acquireAndInstallPackageFromSource :: Config -> Package -> IO (ErrorLogger ())
acquireAndInstallPackageFromSource cfg reppkg =
readPackageFromRepository cfg reppkg |>= \pkg ->
case source pkg of
Nothing -> failIO $ "No source specified for " ++ packageId pkg
Just s -> log Info ("Installing package '" ++ packageId pkg ++ "'...") |>
installFromSource cfg pkg s
Nothing -> failIO $ "No source specified for " ++ packageId pkg
Just s -> log Info ("Installing package '" ++ packageId pkg ++ "'...") |>
installFromSource cfg pkg s
------------------------------------------------------------------------------
--- Installs a package from the given package source to the global package
......
......@@ -19,7 +19,7 @@ import Database.CDBI.Connection
import System.Path ( fileInPath )
import Text.CSV
import CPM.Config ( Config, packageTarFilesURL, readConfigurationWith
import CPM.Config ( Config, packageTarFilesURLs, readConfigurationWith
, repositoryDir )
import CPM.ErrorLogger
import CPM.FileUtil ( cleanTempDir, inTempDir, quote, tempDir
......@@ -56,16 +56,28 @@ installRepositoryDB :: Config -> Bool -> Bool -> IO (ErrorLogger ())
installRepositoryDB cfg False writecsv = writeRepositoryDB cfg False writecsv
installRepositoryDB cfg True writecsv = do
let sqlitefile = repositoryCacheDB cfg
dburl = packageTarFilesURL cfg ++ "/REPOSITORY_CACHE.db"
whenFileExists sqlitefile (removeFile sqlitefile)
c <- inTempDir $ showExecCmd $
"curl -f -s -o " ++ quote sqlitefile ++ " " ++ quote dburl
c <- tryDownloadFromURLs sqlitefile (packageTarFilesURLs cfg)
"REPOSITORY_CACHE.db"
dbexists <- doesFileExist sqlitefile
if c == 0 && dbexists
then if writecsv then saveDBAsCSV cfg
else succeedIO ()
else writeRepositoryDB cfg True writecsv
--- Tries to download some target file (first argument) from a list of
--- base URLs where the source file (third argument) is located.
--- Returns 0 if the download was successfull.
tryDownloadFromURLs :: String -> [String] -> String -> IO Int
tryDownloadFromURLs _ [] _ = return 1
tryDownloadFromURLs target (baseurl:baseurls) file = do
let sourceurl = baseurl ++ "/" ++ file
rc <- inTempDir $ showExecCmd $
"curl -f -s -o " ++ quote target ++ " " ++ quote sourceurl
if rc == 0
then return 0
else tryDownloadFromURLs target baseurls file
--- Writes the repository database with the current repository index.
--- It is generated either from the CSV file `REPOSITORY_CACHE.csv`
--- downloaded from the tar files URL (if the second argument is `True`)
......@@ -79,11 +91,10 @@ writeRepositoryDB cfg usecache writecsv = do
createNewDB sqlitefile
tmpdir <- tempDir
let csvfile = tmpdir </> "cachedb.csv"
csvurl = packageTarFilesURL cfg ++ "/REPOSITORY_CACHE.csv"
showExecCmd $ "/bin/rm -f " ++ csvfile
c <- if usecache
then inTempDir $ showExecCmd $
"curl -f -s -o " ++ csvfile ++ " " ++ quote csvurl
then tryDownloadFromURLs csvfile (packageTarFilesURLs cfg)
"REPOSITORY_CACHE.csv"
else return 1
csvexists <- doesFileExist csvfile
pkgentries <- if c == 0 && csvexists
......
......@@ -32,18 +32,18 @@ overlapAnalysis = simpleFuncAnalysis "Overlapping" isOverlappingFunction
isOverlappingFunction :: FuncDecl -> Bool
isOverlappingFunction (Func _ _ _ _ (Rule _ e)) = orInExpr e
isOverlappingFunction (Func f _ _ _ (External _)) = f==("Prelude","?")
isOverlappingFunction (Func f _ _ _ (External _)) = f == ("Prelude","?")
-- Check an expression for occurrences of OR:
orInExpr :: Expr -> Bool
orInExpr (Var _) = False
orInExpr (Lit _) = False
orInExpr (Comb _ f es) = f==(pre "?") || any orInExpr es
orInExpr (Free _ e) = orInExpr e
orInExpr (Let bs e) = any orInExpr (map snd bs) || orInExpr e
orInExpr (Or _ _) = True
orInExpr (Var _) = False
orInExpr (Lit _) = False
orInExpr (Comb _ f es) = f == (pre "?") || any orInExpr es
orInExpr (Free _ e) = orInExpr e
orInExpr (Let bs e) = any orInExpr (map snd bs) || orInExpr e
orInExpr (Or _ _) = True
orInExpr (Case _ e bs) = orInExpr e || any orInBranch bs
where orInBranch (Branch _ be) = orInExpr be
where orInBranch (Branch _ be) = orInExpr be
orInExpr (Typed e _) = orInExpr e
-- Show overlapping information as a string.
......
......@@ -70,8 +70,12 @@ rev :: [a] -> [a]
rev [] = []
rev (x:xs) = append (rev xs) [x]
fromTo :: Int -> Int -> [Int]
fromTo m n | m <= n = m : fromTo (m+1) n
| otherwise = []
main :: Int -> Int -> [Int]
main x y = rev [x .. y]
main m n = rev (fromTo m n)
\end{curry}
%
CASS supports three different usage modes to analyze this program.
......@@ -88,6 +92,7 @@ when CASS is started for the first time.}
\begin{curry}
> cass Demand Rev
append : demanded arguments: 1
fromTo : demanded arguments: 1,2
main : demanded arguments: 1,2
rev : demanded arguments: 1
\end{curry}
......@@ -110,7 +115,7 @@ application implemented in Curry. Since CASS is implemented in Curry,
one can import the modules of the CASS implementation and
use the CASS interface operations to start an analysis and use the
computed results. For instance, CASS provides an operation
(defined in the module \code{AnalysisServer})
(defined in the module \code{CASS.Server})
\begin{curry}
analyzeGeneric :: Analysis a -> String -> IO (Either (ProgInfo a) String)
\end{curry}
......@@ -119,15 +124,23 @@ given in the second argument). The result is either the analysis
information computed for this module or an error message in case of
some execution error.
The modules of the CASS implementation are stored in the directory
\code{\cyshome/currytools/CASS} and the modules implementing
the various program analyses are stored in
\code{\cyshome/currytools/analysis}.
Hence, one should add these directories to the Curry load path
when using CASS in API mode.
The CASS module \code{GenericProgInfo} contains operations
to access the analysis information computed by CASS.
In order to use CASS via the API mode in a Curry program,
one has to use the package \code{cass} by the Curry package manager CPM
(the subsequent explanation assumes familiarity with the basic
features of CPM):
\begin{enumerate}
\item
Add the dependency on package \code{cass} and also on
package \code{cass-analysis}, which contains some base definitions,
in the package specification file \code{package.json}.
\item
Install these dependencies by \ccode{cypm install}.
\end{enumerate}
Then you can import in your application the modules
provided by CASS.
The module \code{Analysis.ProgInfo} (from package \code{cass-analysis})
contains operations to access the analysis information computed by CASS.
For instance, the operation
\begin{curry}
lookupProgInfo:: QName -> ProgInfo a -> Maybe a
......@@ -135,16 +148,16 @@ lookupProgInfo:: QName -> ProgInfo a -> Maybe a
returns the information about a given qualified name in the
analysis information, if it exists.
As a simple example, consider the demand analysis which is implemented
in the module \code{Demandedness} by the following operation:
in the module \code{Analysis.Demandedness} by the following operation:
\begin{curry}
demandAnalysis :: Analysis DemandedArgs
\end{curry}
\code{DemendedArgs} is just a type synonym for \code{[Int]}.
We can use this analysis in the following simple program:
\begin{currynomath}
import AnalysisServer (analyzeGeneric)
import GenericProgInfo (lookupProgInfo)
import Demandedness (demandAnalysis)
import CASS.Server ( analyzeGeneric )
import Analysis.ProgInfo ( lookupProgInfo )
import Analysis.Demandedness ( demandAnalysis )
demandedArgumentsOf :: String -> String -> IO [Int]
demandedArgumentsOf modname fname = do
......@@ -168,7 +181,8 @@ The server mode of CASS can be used in an application implemented in
some language that does not have a direct interface to Curry.
In this case, one can connect to CASS via
some socket using a simple communication protocol that is specified
in the file \code{\cyshome/currytools/CASS/Protocol.txt} and sketched below.
in the file \code{Protocol.txt} (in package \code{cass})
and sketched below.
To start CASS in the server mode, one has to execute the command
\begin{curry}
......@@ -240,15 +254,16 @@ Overlapping Text
Deterministic XML
...
> AnalyzeModule Demand Text Rev
ok 3
ok 4
append : demanded arguments: 1
fromTo : demanded arguments: 1,2
main : demanded arguments: 1,2
rev : demanded arguments: 1
> AnalyzeModule Demand CurryTerm Rev
ok 1
[(("Rev","append"),"demanded arguments: 1"),(("Rev","main"),"demanded arguments: 1,2"),(("Rev","rev"),"demanded arguments: 1")]
[(("Rev","append"),"demanded arguments: 1"),(("Rev","fromTo"),"demanded arguments: 1,2"),(("Rev","main"),"demanded arguments: 1,2"),(("Rev","rev"),"demanded arguments: 1")]
> AnalyzeModule Demand XML Rev
ok 19
ok 24
<?xml version="1.0" standalone="yes"?>
<results>
......@@ -257,6 +272,11 @@ ok 19
<name>append</name>
<result>demanded arguments: 1</result>
</operation>
<operation>
<module>Rev</module>
<name>fromTo</name>
<result>demanded arguments: 1,2</result>
</operation>
<operation>
<module>Rev</module>
<name>main</name>
......@@ -277,8 +297,8 @@ Connection closed by foreign host.
\subsection{Implementing Program Analyses}
Each program analysis accessible by CASS must be registered
in the CASS module \code{Registry}. The registered analysis
must contain an operation of type
in the CASS module \code{CASS.Registry}.
An analysis to be registered must contain an operation of type
\begin{curry}
Analysis a
\end{curry}
......@@ -292,8 +312,8 @@ is defined by overlapping rules.
In order to add a new analysis to CASS, one has to implement
a corresponding analysis operation, registering it in the module
\code{Registry} (in the constant \code{registeredAnalysis})
and compile the modified CASS implementation.
\code{CASS.Registry} (in the constant \code{registeredAnalysis})
and compile/install the modified CASS implementation.
An analysis is implemented as a mapping from Curry programs
represented in FlatCurry into the analysis result.
......@@ -304,18 +324,18 @@ import FlatCurry.Types
$\ldots$
isOverlappingFunction :: FuncDecl -> Bool
isOverlappingFunction (Func _ _ _ _ (Rule _ e)) = orInExpr e
isOverlappingFunction (Func f _ _ _ (External _)) = f==("Prelude","?")
isOverlappingFunction (Func f _ _ _ (External _)) = f == ("Prelude","?")
-- Check an expression for occurrences of Or:
orInExpr :: Expr -> Bool
orInExpr (Var _) = False
orInExpr (Lit _) = False
orInExpr (Comb _ f es) = f==(pre "?") || any orInExpr es
orInExpr (Comb _ f es) = f == (pre "?") || any orInExpr es
orInExpr (Free _ e) = orInExpr e
orInExpr (Let bs e) = any orInExpr (map snd bs) || orInExpr e
orInExpr (Or _ _) = True
orInExpr (Case _ e bs) = orInExpr e || any orInBranch bs
where orInBranch (Branch _ be) = orInExpr be
where orInBranch (Branch _ be) = orInExpr be
orInExpr (Typed e _) = orInExpr e
\end{curry}
%
......@@ -335,7 +355,7 @@ simpleFuncAnalysis :: String -> (FuncDecl -> a) -> Analysis a
The arguments are the analysis name and the actual analysis function.
Hence, the ``overlapping rules'' analysis can be specified as
\begin{curry}
import Analysis
import Analysis.Types
$\ldots$
overlapAnalysis :: Analysis Bool
overlapAnalysis = simpleFuncAnalysis "Overlapping" isOverlappingFunction
......@@ -387,8 +407,8 @@ by the following operation:
detFunc :: FuncDecl -> [(QName,Deterministic)] -> Deterministic
detFunc (Func f _ _ _ (Rule _ e)) calledFuncs =
if orInExpr e || freeVarInExpr e || any (==NDet) (map snd calledFuncs)
then NDet
else Det
then NDet
else Det
\end{curry}
Thus, it computes the abstract value \code{NDet}
if the function itself is defined by overlapping rules or
......
......@@ -6,5 +6,9 @@ rev :: [a] -> [a]
rev [] = []
rev (x:xs) = append (rev xs) [x]
fromTo :: Int -> Int -> [Int]
fromTo m n | m <= n = m : fromTo (m+1) n
| otherwise = []
main :: Int -> Int -> [Int]
main x y = rev [x .. y]
main m n = rev (fromTo m n)
......@@ -32,18 +32,18 @@ overlapAnalysis = simpleFuncAnalysis "Overlapping" isOverlappingFunction
isOverlappingFunction :: FuncDecl -> Bool
isOverlappingFunction (Func _ _ _ _ (Rule _ e)) = orInExpr e
isOverlappingFunction (Func f _ _ _ (External _)) = f==("Prelude","?")
isOverlappingFunction (Func f _ _ _ (External _)) = f == ("Prelude","?")
-- Check an expression for occurrences of OR:
orInExpr :: Expr -> Bool
orInExpr (Var _) = False
orInExpr (Lit _) = False
orInExpr (Comb _ f es) = f==(pre "?") || any orInExpr es
orInExpr (Free _ e) = orInExpr e
orInExpr (Let bs e) = any orInExpr (map snd bs) || orInExpr e
orInExpr (Or _ _) = True
orInExpr (Var _) = False
orInExpr (Lit _) = False
orInExpr (Comb _ f es) = f == (pre "?") || any orInExpr es
orInExpr (Free _ e) = orInExpr e
orInExpr (Let bs e) = any orInExpr (map snd bs) || orInExpr e
orInExpr (Or _ _) = True
orInExpr (Case _ e bs) = orInExpr e || any orInBranch bs
where orInBranch (Branch _ be) = orInExpr be
where orInBranch (Branch _ be) = orInExpr be
orInExpr (Typed e _) = orInExpr e
-- Show overlapping information as a string.
......
......@@ -70,8 +70,12 @@ rev :: [a] -> [a]
rev [] = []
rev (x:xs) = append (rev xs) [x]
fromTo :: Int -> Int -> [Int]
fromTo m n | m <= n = m : fromTo (m+1) n
| otherwise = []
main :: Int -> Int -> [Int]
main x y = rev [x .. y]
main m n = rev (fromTo m n)
\end{curry}
%
CASS supports three different usage modes to analyze this program.
......@@ -88,6 +92,7 @@ when CASS is started for the first time.}
\begin{curry}
> cass Demand Rev
append : demanded arguments: 1
fromTo : demanded arguments: 1,2
main : demanded arguments: 1,2
rev : demanded arguments: 1
\end{curry}
......@@ -110,7 +115,7 @@ application implemented in Curry. Since CASS is implemented in Curry,
one can import the modules of the CASS implementation and
use the CASS interface operations to start an analysis and use the
computed results. For instance, CASS provides an operation
(defined in the module \code{AnalysisServer})
(defined in the module \code{CASS.Server})
\begin{curry}
analyzeGeneric :: Analysis a -> String -> IO (Either (ProgInfo a) String)
\end{curry}
......@@ -119,15 +124,23 @@ given in the second argument). The result is either the analysis
information computed for this module or an error message in case of
some execution error.
The modules of the CASS implementation are stored in the directory
\code{\cyshome/currytools/CASS} and the modules implementing
the various program analyses are stored in
\code{\cyshome/currytools/analysis}.
Hence, one should add these directories to the Curry load path
when using CASS in API mode.
The CASS module \code{GenericProgInfo} contains operations
to access the analysis information computed by CASS.
In order to use CASS via the API mode in a Curry program,
one has to use the package \code{cass} by the Curry package manager CPM
(the subsequent explanation assumes familiarity with the basic
features of CPM):
\begin{enumerate}
\item
Add the dependency on package \code{cass} and also on
package \code{cass-analysis}, which contains some base definitions,
in the package specification file \code{package.json}.
\item
Install these dependencies by \ccode{cypm install}.
\end{enumerate}
Then you can import in your application the modules
provided by CASS.
The module \code{Analysis.ProgInfo} (from package \code{cass-analysis})
contains operations to access the analysis information computed by CASS.
For instance, the operation
\begin{curry}
lookupProgInfo:: QName -> ProgInfo a -> Maybe a
......@@ -135,16 +148,16 @@ lookupProgInfo:: QName -> ProgInfo a -> Maybe a
returns the information about a given qualified name in the
analysis information, if it exists.
As a simple example, consider the demand analysis which is implemented
in the module \code{Demandedness} by the following operation:
in the module \code{Analysis.Demandedness} by the following operation:
\begin{curry}
demandAnalysis :: Analysis DemandedArgs
\end{curry}
\code{DemendedArgs} is just a type synonym for \code{[Int]}.
We can use this analysis in the following simple program:
\begin{currynomath}
import AnalysisServer (analyzeGeneric)
import GenericProgInfo (lookupProgInfo)
import Demandedness (demandAnalysis)
import CASS.Server ( analyzeGeneric )
import Analysis.ProgInfo ( lookupProgInfo )
import Analysis.Demandedness ( demandAnalysis )
demandedArgumentsOf :: String -> String -> IO [Int]
demandedArgumentsOf modname fname = do
......@@ -168,7 +181,8 @@ The server mode of CASS can be used in an application implemented in
some language that does not have a direct interface to Curry.
In this case, one can connect to CASS via
some socket using a simple communication protocol that is specified
in the file \code{\cyshome/currytools/CASS/Protocol.txt} and sketched below.
in the file \code{Protocol.txt} (in package \code{cass})
and sketched below.
To start CASS in the server mode, one has to execute the command
\begin{curry}
......@@ -240,15 +254,16 @@ Overlapping Text
Deterministic XML
...
> AnalyzeModule Demand Text Rev
ok 3
ok 4
append : demanded arguments: 1
fromTo : demanded arguments: 1,2
main : demanded arguments: 1,2