sourcecode:
|
{- |
Author : Kai-Oliver Prott
Version : March 2025
Operations to add analysis information
to the CurryDoc declarations.
-}
module CurryDoc.Info.Analysis
( addAnaInfoToCurryDocDecls, addShortAnaInfoToCurryDocDecls ) where
import CurryDoc.Data.AnaInfo
import CurryDoc.Data.CurryDoc
import CurryDoc.Info.Goodies
import AbstractCurry.Types
import AbstractCurry.Select
import Data.List ( find, partition, isPrefixOf )
-- | Adds analysis information to suitable `CurryDocDecls`.
addAnaInfoToCurryDocDecls :: AnaInfo -> [COpDecl] -> [CFuncDecl]
-> [CurryDocDecl] -> [CurryDocDecl]
addAnaInfoToCurryDocDecls ai cop funs =
map (addAnaInfoToCurryDocDecl ai cop funs)
-- | Adds short analysis information to suitable `CurryDocDecls`.
addShortAnaInfoToCurryDocDecls :: [COpDecl] -> [CFuncDecl]
-> [CurryDocDecl] -> [CurryDocDecl]
addShortAnaInfoToCurryDocDecls cop funs =
map (addShortAnaInfoToCurryDocDecl cop funs)
-- | Recursively descends the declarations and fills in any `AnalysisInfo`.
addAnaInfoToCurryDocDecl :: AnaInfo -> [COpDecl] -> [CFuncDecl] -> CurryDocDecl
-> CurryDocDecl
addAnaInfoToCurryDocDecl _ _ _ d@(CurryDocTypeDecl _ _ _ _) = d
addAnaInfoToCurryDocDecl _ cop _ (CurryDocClassDecl a b c d e f) =
CurryDocClassDecl a b c d (map (addPrecedenceInfoToCurryDocDecl cop) e) f
addAnaInfoToCurryDocDecl ai cop funs (CurryDocFunctionDecl n qty sig _ cs) =
CurryDocFunctionDecl n qty sig (createAnalysisInfoFun ai cop funs n) cs
addAnaInfoToCurryDocDecl _ cop _ (CurryDocDataDecl idt vs ins ex cns cs) =
CurryDocDataDecl idt vs ins ex
(map (addPrecedenceInfoToCurryDocCons cop) cns) cs
addAnaInfoToCurryDocDecl _ cop _ (CurryDocNewtypeDecl idt vs ins cns cs) =
CurryDocNewtypeDecl idt vs ins
(fmapMaybe (addPrecedenceInfoToCurryDocCons cop) cns) cs
where fmapMaybe _ Nothing = Nothing
fmapMaybe f (Just x) = Just (f x)
addShortAnaInfoToCurryDocDecl :: [COpDecl] -> [CFuncDecl] -> CurryDocDecl
-> CurryDocDecl
addShortAnaInfoToCurryDocDecl _ _ d@(CurryDocTypeDecl _ _ _ _) = d
addShortAnaInfoToCurryDocDecl cop _ (CurryDocClassDecl a b c d e f) =
CurryDocClassDecl a b c d (map (addPrecedenceInfoToCurryDocDecl cop) e) f
addShortAnaInfoToCurryDocDecl cop funs (CurryDocFunctionDecl n qty sig _ cs) =
CurryDocFunctionDecl n qty sig (createShortAnalysisInfoFun cop funs n) cs
addShortAnaInfoToCurryDocDecl cop _ (CurryDocDataDecl idt vs ins ex cns cs) =
CurryDocDataDecl idt vs ins ex
(map (addPrecedenceInfoToCurryDocCons cop) cns) cs
addShortAnaInfoToCurryDocDecl cop _ (CurryDocNewtypeDecl idt vs ins cns cs) =
CurryDocNewtypeDecl idt vs ins
(fmapMaybe (addPrecedenceInfoToCurryDocCons cop) cns) cs
where fmapMaybe _ Nothing = Nothing
fmapMaybe f (Just x) = Just (f x)
addPrecedenceInfoToCurryDocCons :: [COpDecl] -> CurryDocCons -> CurryDocCons
addPrecedenceInfoToCurryDocCons cop (CurryDocConstr c tys _ cs) =
CurryDocConstr c tys (createPrecInfo cop c) cs
addPrecedenceInfoToCurryDocCons cop (CurryDocConsOp c ty1 ty2 _ cs) =
CurryDocConsOp c ty1 ty2 (createPrecInfo cop c) cs
addPrecedenceInfoToCurryDocCons cop (CurryDocRecord c tys fs _ cs) =
CurryDocRecord c tys fs' (createPrecInfo cop c) cs
where fs' = map (addPrecedenceInfoToField cop) fs
addPrecedenceInfoToField :: [COpDecl] -> CurryDocField -> CurryDocField
addPrecedenceInfoToField cop (CurryDocField n ty _ cs) =
CurryDocField n ty (createPrecInfo cop n) cs
addPrecedenceInfoToCurryDocDecl :: [COpDecl] -> CurryDocDecl -> CurryDocDecl
addPrecedenceInfoToCurryDocDecl cop d = case d of
CurryDocFunctionDecl n qty sig _ cs ->
CurryDocFunctionDecl n qty sig (createPrecInfo cop n) cs
_ -> d
-- | Full analysis for functions.
createAnalysisInfoFun :: AnaInfo -> [COpDecl] -> [CFuncDecl] -> QName
-> AnalysisInfo
createAnalysisInfoFun ai cop funs n = AnalysisInfo
(getNondetInfo ai n)
(getIndetInfo ai n)
(getOpCompleteInfo ai n)
(getExternalInfo funs n)
(getCompleteInfo ai n)
(genPrecedenceInfo cop n)
(genPropertyInfo funs n)
-- | Short analysis for functions.
createShortAnalysisInfoFun :: [COpDecl] -> [CFuncDecl] -> QName -> AnalysisInfo
createShortAnalysisInfoFun cop funs n = ShortAnalysisInfo
(getExternalInfo funs n)
(genPrecedenceInfo cop n)
(genPropertyInfo funs n)
-- | Gets external status of a function by checking the number of rules.
getExternalInfo :: [CFuncDecl] -> QName -> Bool
getExternalInfo [] _
= error "CurryDoc.Comment.getExternalInfo: Function not found!"
getExternalInfo (CFunc n _ _ _ [] : fs) n'
| n =~= n' = True
| otherwise = getExternalInfo fs n'
getExternalInfo (CmtFunc _ n _ _ _ [] : fs) n'
| n =~= n' = True
| otherwise = getExternalInfo fs n'
getExternalInfo (CFunc n _ _ _ (_:_) : fs) n'
| n =~= n' = False
| otherwise = getExternalInfo fs n'
getExternalInfo (CmtFunc _ n _ _ _ (_:_) : fs) n'
| n =~= n' = False
| otherwise = getExternalInfo fs n'
-- | Only creates a precedence info (e.g. for fields and constructors).
createPrecInfo :: [COpDecl] -> QName -> AnalysisInfo
createPrecInfo cop n = PrecedenceInfo (genPrecedenceInfo cop n)
-- | Looks up the precedence for a given name.
genPrecedenceInfo :: [COpDecl] -> QName -> Maybe (CFixity, Int)
genPrecedenceInfo [] _ = Nothing
genPrecedenceInfo (COp m fix prec : cop) n
| n =~= m = Just (fix, prec)
| otherwise = genPrecedenceInfo cop n
-- | Checks for properties of a function.
-- (Code from Michael Hanus, modified by Kai Prott)
genPropertyInfo :: [CFuncDecl] -> QName -> [(Property, (QName, CRule))]
genPropertyInfo funs n = getContracts ++ concatMap getProp props
where
fprops = takeWhile isPropSpecFun $
tail $ dropWhile (not . (=~=n) . funcName) funs
(specs, props) = partition isSpecFun fprops
getContracts = getContract (snd n ++ "'pre") PreSpec
++ getContract (snd n ++ "'post") PostSpec
++ getContract (snd n ++ "'spec") Spec
getContract fn typ =
maybe [] (getRule typ)
(find (\fd -> snd (funcName fd) == fn) specs)
getRule typ f@(CFunc _ ar _ (CQualType _ ftype) rules) =
map (\rule -> (typ, (funcName f, etaExpand ar (length (argTypes ftype)) rule))) rules
getRule typ f@(CmtFunc _ _ ar _ (CQualType _ ftype) rules) =
map (\rule -> (typ, (funcName f, etaExpand ar (length (argTypes ftype)) rule))) rules
etaExpand arity tarity rule = case rule of
CRule ps (CSimpleRhs exp ldecls) ->
if arity == tarity
then rule
else let evars = map (\i -> (i,"x"++show i)) [(arity+1) .. tarity]
in CRule (ps ++ map CPVar evars)
(CSimpleRhs (foldl CApply exp (map CVar evars)) ldecls)
_ -> rule -- don't do it for complex rules
getProp propdecl = map (\rule -> (Prop, (funcName propdecl, rule))) (funcRules propdecl)
isPropSpecFun :: CFuncDecl -> Bool
isPropSpecFun fdecl = isPropFun fdecl || isSpecFun fdecl
-- | Is a function definition a property function?
isPropFun :: CFuncDecl -> Bool
isPropFun fdecl = fst (funcName fdecl) /= easyCheckModule
&& isPropType ty && funcVis fdecl == Private
where
isPropType :: CTypeExpr -> Bool
isPropType ct = ct =~~= CTCons (easyCheckModule,"Prop") -- I/O test?
|| ct =~~= CTCons (propModule,"Prop") -- I/O test?
|| resultType ct =~~= CTCons (easyCheckModule,"Prop")
|| resultType ct =~~= CTCons (propModule,"Prop")
easyCheckModule = "Test.EasyCheck"
propModule = "Test.Prop.Types"
CQualType _ ty = funcType fdecl
-- | Is a function definition part of a specification, i.e.,
-- a full specification (suffix 'spec), a precondition (suffix 'pre),
-- or a postcondition (suffix 'post)?
isSpecFun :: CFuncDecl -> Bool
isSpecFun fdecl =
let rfname = reverse (snd (funcName fdecl))
in any (`isPrefixOf` rfname) ["ceps'","erp'","tsop'"]
&& funcVis fdecl == Private
|