------------------------------------------------------------------------- --- This module contains the datatypes, constructors, and other --- operations to create and process analyses used in the --- generic analysis system. --- --- Each analysis has a name which is used to identify the analysis --- stored in files, when passing analysis information between workers etc. --- --- **Important:** Use the constructor operations to define new analyses --- (instead of the data constructors). --- --- @author Heiko Hoffmann, Michael Hanus --- @version June 2018 ------------------------------------------------------------------------- module Analysis.Types ( Analysis(..) , simpleFuncAnalysis, simpleTypeAnalysis, simpleConstructorAnalysis , dependencyFuncAnalysis, dependencyTypeAnalysis , combinedSimpleFuncAnalysis, combined2SimpleFuncAnalysis , combinedSimpleTypeAnalysis , combinedDependencyFuncAnalysis, combinedDependencyTypeAnalysis , simpleModuleAnalysis, dependencyModuleAnalysis , isSimpleAnalysis, isCombinedAnalysis, isFunctionAnalysis , analysisName, baseAnalysisNames, startValue , AOutFormat(..) ) where import FlatCurry.Types ( Prog, ConsDecl, FuncDecl, TypeDecl, QName ) import FlatCurry.Goodies ( progImports ) import Analysis.Logging ( DLevel(..) ) import Analysis.ProgInfo ( ProgInfo, combineProgInfo, lookupProgInfo ) import Analysis.Files ( getImports, loadCompleteAnalysis, getInterfaceInfos ) --- Datatype representing a program analysis to be used in the --- generic analysis system. The datatype is abstract so that --- one has to use one of the constructor operations to create --- an analysis. data Analysis a = SimpleFuncAnalysis String (FuncDecl -> a) | SimpleTypeAnalysis String (TypeDecl -> a) | SimpleConstructorAnalysis String (ConsDecl -> TypeDecl -> a) | DependencyFuncAnalysis String a (FuncDecl -> [(QName,a)] -> a) | DependencyTypeAnalysis String a (TypeDecl -> [(QName,a)] -> a) | CombinedSimpleFuncAnalysis [String] String Bool (String -> IO (FuncDecl -> a)) | CombinedSimpleTypeAnalysis [String] String Bool (String -> IO (TypeDecl -> a)) | CombinedDependencyFuncAnalysis [String] String Bool a (String -> IO (FuncDecl -> [(QName,a)] -> a)) | CombinedDependencyTypeAnalysis [String] String Bool a (String -> IO (TypeDecl -> [(QName,a)] -> a)) | SimpleModuleAnalysis String (Prog -> a) | DependencyModuleAnalysis String (Prog -> [(String,a)] -> a) --- A simple analysis for functions takes an operation that computes --- some information from a given function declaration. simpleFuncAnalysis :: String -> (FuncDecl -> a) -> Analysis a simpleFuncAnalysis anaName anaFunc = SimpleFuncAnalysis anaName anaFunc --- A simple analysis for types takes an operation that computes --- some information from a given type declaration. simpleTypeAnalysis :: String -> (TypeDecl -> a) -> Analysis a simpleTypeAnalysis anaName anaFunc = SimpleTypeAnalysis anaName anaFunc --- A simple analysis for data constructors takes an operation that computes --- some information for a constructor declaration and its type declaration --- to which it belongs. simpleConstructorAnalysis :: String -> (ConsDecl -> TypeDecl -> a) -> Analysis a simpleConstructorAnalysis anaName anaFunc = SimpleConstructorAnalysis anaName anaFunc --- Construct a function analysis with dependencies. --- The analysis has a name, a start value (representing "no initial --- information") and an operation to process a function declaration --- with analysis information --- for the operations directly called in this function declaration. --- The analysis will be performed by a fixpoint iteration --- starting with the given start value. dependencyFuncAnalysis :: String -> a -> (FuncDecl -> [(QName,a)] -> a) -> Analysis a dependencyFuncAnalysis anaName startval anaFunc = DependencyFuncAnalysis anaName startval anaFunc --- Construct a type analysis with dependencies. --- The analysis has a name, a start value (representing "no initial --- information") and an operation to process a type declaration --- with analysis information --- for the type constructors occurring in the type declaration. --- The analysis will be performed by a fixpoint iteration --- starting with the given start value. dependencyTypeAnalysis :: String -> a -> (TypeDecl -> [(QName,a)] -> a) -> Analysis a dependencyTypeAnalysis anaName startval anaType = DependencyTypeAnalysis anaName startval anaType --- A simple combined analysis for functions. --- The analysis is based on an operation that computes --- some information from a given function declaration --- and information provided by some base analysis. --- The base analysis is provided as the second argument. combinedSimpleFuncAnalysis :: Read b => String -> Analysis b -> (ProgInfo b -> FuncDecl -> a) -> Analysis a combinedSimpleFuncAnalysis ananame baseAnalysis anaFunc = CombinedSimpleFuncAnalysis [analysisName baseAnalysis] ananame True (runWithBaseAnalysis Quiet baseAnalysis anaFunc) --- A simple combined analysis for functions. --- The analysis is based on an operation that computes --- some information from a given function declaration --- and information provided by two base analyses. --- The base analyses are provided as the second and third argument. combined2SimpleFuncAnalysis :: (Read b, Read c) => String -> Analysis b -> Analysis c -> (ProgInfo b -> ProgInfo c -> FuncDecl -> a) -> Analysis a combined2SimpleFuncAnalysis ananame baseAnalysisA baseAnalysisB anaFunc = CombinedSimpleFuncAnalysis [analysisName baseAnalysisA, analysisName baseAnalysisB] ananame True (runWith2BaseAnalyses Quiet baseAnalysisA baseAnalysisB anaFunc) --- A simple combined analysis for types. --- The analysis is based on an operation that computes --- some information from a given type declaration --- and information provided by some base analysis. --- The base analysis is provided as the second argument. combinedSimpleTypeAnalysis :: Read b => String -> Analysis b -> (ProgInfo b -> TypeDecl -> a) -> Analysis a combinedSimpleTypeAnalysis ananame baseAnalysis anaFunc = CombinedSimpleTypeAnalysis [analysisName baseAnalysis] ananame True (runWithBaseAnalysis Quiet baseAnalysis anaFunc) --- A combined analysis for functions with dependencies. --- The analysis is based on an operation that computes --- from information provided by some base analysis --- for each function declaration and information about its --- directly called operation some information for the declared function. --- The analysis will be performed by a fixpoint iteration --- starting with the given start value (fourth argument). --- The base analysis is provided as the second argument. combinedDependencyFuncAnalysis :: Read b => String -> Analysis b -> a -> (ProgInfo b -> FuncDecl -> [(QName,a)] -> a) -> Analysis a combinedDependencyFuncAnalysis ananame baseAnalysis startval anaFunc = CombinedDependencyFuncAnalysis [analysisName baseAnalysis] ananame True startval (runWithBaseAnalysis Quiet baseAnalysis anaFunc) --- A combined analysis for types with dependencies. --- The analysis is based on an operation that computes --- from information provided by some base analysis --- for each type declaration and information about its --- directly used types some information for the declared type. --- The analysis will be performed by a fixpoint iteration --- starting with the given start value (fourth argument). --- The base analysis is provided as the second argument. combinedDependencyTypeAnalysis :: Read b => String -> Analysis b -> a -> (ProgInfo b -> TypeDecl -> [(QName,a)] -> a) -> Analysis a combinedDependencyTypeAnalysis ananame baseAnalysis startval anaType = CombinedDependencyTypeAnalysis [analysisName baseAnalysis] ananame True startval (runWithBaseAnalysis Quiet baseAnalysis anaType) --- Construct a simple analysis for entire modules. --- The analysis has a name and takes an operation that computes --- some information from a given module. simpleModuleAnalysis :: String -> (Prog -> a) -> Analysis a simpleModuleAnalysis anaName anaFunc = SimpleModuleAnalysis anaName anaFunc --- Construct a module analysis which uses analysis information on --- imported modules. --- The analysis has a name and an operation to analyze a module. --- The analysis operation could use already computed information --- of imported modules, represented as a list of module name/information pairs. --- Note that a fixpoint iteration is not necessary --- since module dependencies must be acyclic. dependencyModuleAnalysis :: String -> (Prog -> [(String,a)] -> a) -> Analysis a dependencyModuleAnalysis anaName anaFunc = DependencyModuleAnalysis anaName anaFunc ------------------------------------------------------------------------- --- Is the analysis a simple analysis? --- Otherwise, it is a dependency analysis which requires a fixpoint --- computation to compute the results. isSimpleAnalysis :: Analysis a -> Bool isSimpleAnalysis analysis = case analysis of SimpleFuncAnalysis _ _ -> True SimpleTypeAnalysis _ _ -> True SimpleConstructorAnalysis _ _ -> True CombinedSimpleFuncAnalysis _ _ _ _ -> True CombinedSimpleTypeAnalysis _ _ _ _ -> True _ -> False --- Is the analysis a combined analysis? isCombinedAnalysis :: Analysis a -> Bool isCombinedAnalysis analysis = case analysis of CombinedSimpleFuncAnalysis _ _ _ _ -> True CombinedSimpleTypeAnalysis _ _ _ _ -> True CombinedDependencyFuncAnalysis _ _ _ _ _ -> True CombinedDependencyTypeAnalysis _ _ _ _ _ -> True _ -> False --- Is the analysis a function analysis? --- Otherwise, it is a type or constructor analysis. isFunctionAnalysis :: Analysis a -> Bool isFunctionAnalysis analysis = case analysis of SimpleFuncAnalysis _ _ -> True DependencyFuncAnalysis _ _ _ -> True CombinedSimpleFuncAnalysis _ _ _ _ -> True CombinedDependencyFuncAnalysis _ _ _ _ _ -> True _ -> False --- Name of the analysis to be used in server communication and --- analysis files. analysisName :: Analysis a -> String analysisName (SimpleFuncAnalysis name _ ) = name analysisName (SimpleTypeAnalysis name _ ) = name analysisName (SimpleConstructorAnalysis name _ ) = name analysisName (DependencyFuncAnalysis name _ _) = name analysisName (DependencyTypeAnalysis name _ _) = name analysisName (CombinedSimpleFuncAnalysis _ nameB _ _) = nameB analysisName (CombinedSimpleTypeAnalysis _ nameB _ _) = nameB analysisName (CombinedDependencyFuncAnalysis _ nameB _ _ _) = nameB analysisName (CombinedDependencyTypeAnalysis _ nameB _ _ _) = nameB analysisName (SimpleModuleAnalysis name _) = name analysisName (DependencyModuleAnalysis name _) = name --- Names of the base analyses of a combined analysis. baseAnalysisNames :: Analysis a -> [String] baseAnalysisNames ana = case ana of CombinedSimpleFuncAnalysis bnames _ _ _ -> bnames CombinedSimpleTypeAnalysis bnames _ _ _ -> bnames CombinedDependencyFuncAnalysis bnames _ _ _ _ -> bnames CombinedDependencyTypeAnalysis bnames _ _ _ _ -> bnames _ -> [] --- Start value of a dependency analysis. startValue :: Analysis a -> a startValue ana = case ana of DependencyFuncAnalysis _ startval _ -> startval DependencyTypeAnalysis _ startval _ -> startval CombinedDependencyFuncAnalysis _ _ _ startval _ -> startval CombinedDependencyTypeAnalysis _ _ _ startval _ -> startval _ -> error "Internal error in Analysis.startValue" ------------------------------------------------------------------------- --- The desired kind of output of an analysis result. --- `AText` denotes a standard textual representation. --- `ANote` denotes a short note that is empty in case of irrelevant --- information. For instance, this is used in the CurryBrowser --- to get a quick overview of the analysis results of all operations --- in a module. data AOutFormat = AText | ANote deriving Eq ------------------------------------------------------------------------- --- Loads the results of the base analysis and put it as the first --- argument of the main analysis operation which is returned. runWithBaseAnalysis :: Read a => DLevel -> Analysis a -> (ProgInfo a -> (input -> b)) -> String -> IO (input -> b) runWithBaseAnalysis dl baseAnalysis analysisFunction moduleName = do importedModules <- getImports dl moduleName let baseananame = analysisName baseAnalysis impbaseinfos <- getInterfaceInfos dl baseananame importedModules mainbaseinfos <- loadCompleteAnalysis dl baseananame moduleName let baseinfos = combineProgInfo impbaseinfos mainbaseinfos return (analysisFunction baseinfos) --- Loads the results of the base analysis and put it as the first --- argument of the main analysis operation which is returned. runWith2BaseAnalyses :: (Read a, Read b) => DLevel -> Analysis a -> Analysis b -> (ProgInfo a -> ProgInfo b -> (input -> c)) -> String -> IO (input -> c) runWith2BaseAnalyses dl baseanaA baseanaB analysisFunction moduleName = do importedModules <- getImports dl moduleName let baseananameA = analysisName baseanaA baseananameB = analysisName baseanaB impbaseinfosA <- getInterfaceInfos dl baseananameA importedModules mainbaseinfosA <- loadCompleteAnalysis dl baseananameA moduleName impbaseinfosB <- getInterfaceInfos dl baseananameB importedModules mainbaseinfosB <- loadCompleteAnalysis dl baseananameB moduleName let baseinfosA = combineProgInfo impbaseinfosA mainbaseinfosA baseinfosB = combineProgInfo impbaseinfosB mainbaseinfosB return (analysisFunction baseinfosA baseinfosB)