------------------------------------------------------------------------ --- This module contains the implementation of the "postcondition" reducer --- which simplifies the postconditions in a list of function declarations --- w.r.t. a given list of theorems. --- --- Note that theorems are standard (EasyCheck) properties having the --- prefix `theorem'`, as --- --- theorem'sortlength xs = length xs <~> length (sort xs) --- --- theorem'sorted xs = always (sorted (sort xs)) --- --- @author Michael Hanus --- @version August 2016 ------------------------------------------------------------------------ module SimplifyPostConds ( simplifyPostConditionsWithTheorems) where import AbstractCurry.Types import AbstractCurry.Select import AbstractCurry.Build import List (last, maximum) import Maybe (maybeToList) import ReadShowTerm (readQTerm) import Rewriting.Files import Rewriting.Term import Rewriting.Position import Rewriting.Substitution import Rewriting.Rules import ContractUsage import TheoremUsage -- Simplify the postconditions contained in the third argument w.r.t. a given -- list of theorems (second argument). -- If the verbosity (first argument) is greater than 1, the details -- about the theorems, simplifcation rules, and reduced postconditions -- are shown. simplifyPostConditionsWithTheorems :: Int -> [CFuncDecl] -> [CFuncDecl] -> IO [CFuncDecl] simplifyPostConditionsWithTheorems verb theofuncs postconds = do theoTRS <- mapIO safeFromFuncDecl theofuncs >>= return . concat if null theoTRS then return postconds else do let simprules = concatMap theoremToSimpRules theoTRS ++ standardSimpRules when (verb>1) $ putStr $ unlines [ "THEOREMS (with existing proof files):", showTRS snd theoTRS, "SIMPLIFICATION RULES (for postcondition reduction):" , showTRS snd simprules] simppostconds <- mapIO (safeSimplifyPostCondition verb simprules) postconds return (concat simppostconds) where safeFromFuncDecl fd = catch (return $!! (snd (fromFuncDecl fd))) (\e -> do putStrLn $ showError e ++ "\nTheorem \"" ++ snd (funcName fd) ++ "\" not used for simplification (too complex)." return []) -- Simplify a single postcondition (third argument) w.r.t. a given -- list of theorems (second argument): safeSimplifyPostCondition :: Int -> TRS QName -> CFuncDecl -> IO [CFuncDecl] safeSimplifyPostCondition verb simprules fundecl = catch (simplifyPostCondition verb simprules fundecl) (\e -> do putStrLn $ showError e ++ "\nPostcondition \"" ++ snd (funcName fundecl) ++ "\" not simplified (too complex)." return [fundecl]) simplifyPostCondition :: Int -> TRS QName -> CFuncDecl -> IO [CFuncDecl] simplifyPostCondition verb simprules (CFunc qn ar vis texp rs) = simplifyPostCondition verb simprules (CmtFunc "" qn ar vis texp rs) simplifyPostCondition verb simprules fdecl@(CmtFunc cmt qn ar vis texp rules) = if isPostCondName (snd qn) then do redrules <- mapIO (simplifyRule verb simprules qn) rules return $ if all isTrivial redrules then [] else [CmtFunc cmt qn ar vis texp redrules] else return [fdecl] -- Translate property theorem into simplification rules. theoremToSimpRules :: Rule QName -> [Rule QName] theoremToSimpRules (_, TermVar _) = error $ "theoremToSimpRules: variable in rhs" theoremToSimpRules rl@(_, TermCons qf args) | qf == easyCheck "-=-" || qf == easyCheck "<~>" = [(TermCons (pre "==") args, trueTerm), (TermCons (pre "==") (reverse args), trueTerm)] | qf == easyCheck "always" = [(head args, trueTerm)] | otherwise = [rl] where easyCheck f = ("Test.EasyCheck",f) isTrivial :: CRule -> Bool isTrivial (CRule _ rhs) = case rhs of CSimpleRhs exp [] -> exp == constF (pre "True") _ -> False -- To avoid infinite loops during simplification, we define a maximum number -- of allowed simplification steps: maxSimpSteps :: Int maxSimpSteps = 100 -- Simplify a rule of a postcondition. simplifyRule :: Int -> TRS QName -> QName -> CRule -> IO CRule simplifyRule verb simprules qn crule@(CRule rpats _) = do (id $!! (lhs,rhs)) `seq` done -- in order to raise a fromRule error here! unless (null trs) $ error $ "simplifyRule: cannot handle local TRS:\n" ++ showTRS snd trs when (verb > 1 ) $ putStrLn $ unlines ["POSTCONDITION: " ++ showRule snd (lhs,rhs), "POSTCONDEXP: " ++ showTerm snd postcondexp, "SIMPLIFIEDEXP: " ++ showTerm snd simpterm, "SIMPPOSTCOND: " ++ showRule snd simppostcond ] return (simpleRule rpats (term2acy (concatMap varsOfPat rpats) simppostrhs)) where ((lhs,rhs),trs) = fromRule qn crule postcondexp = postCondition2Term lhs rhs simpterm = simplifyTerm maxSimpSteps simprules postcondexp simppostrhs = postConditionTermToRule lhs simpterm simppostcond = (lhs, simppostrhs) --- Transform a post-condition rule into a term by substituting --- the last argument variable by the function call. postCondition2Term :: Term QName -> Term QName -> Term QName postCondition2Term (TermVar _) _ = error "postCondition2Term: variable term" postCondition2Term (TermCons (mn,fn) args) rhs = let TermVar i = last args fcall = TermCons (mn, fromPostCondName fn) (take (length args - 1) args) fcallsubst = extendSubst emptySubst i fcall in applySubst fcallsubst rhs --- Transform (simplified) post-condition back into rule by replacing --- function call by the last argument variable. by the function call. postConditionTermToRule :: Term QName -> Term QName -> Term QName postConditionTermToRule (TermVar _) _ = error "postConditionTermToRule: variable term" postConditionTermToRule (TermCons (mn,fn) args) term = let TermVar i = last args fcall = TermCons (mn, fromPostCondName fn) (take (length args - 1) args) in replaceAllTerms (fcall, TermVar i) term replaceAllTerms :: Rule QName -> Term QName -> Term QName replaceAllTerms (lhs,rhs) term = if null oneStep then term else replaceAllTerms (lhs,rhs) (head oneStep) where oneStep = [ replaceTerm term p rhs | p <- positions term, (term |> p) == lhs ] ------------------------------------------------------------------------ simplifyTerm :: Int -> TRS QName -> Term QName -> Term QName simplifyTerm maxsteps simprules term = if null oneStep || maxsteps==0 then term else simplifyTerm (maxsteps-1) simprules (head oneStep) where oneStep = [ replaceTerm term p (applySubst sub rhs) | p <- positions term, rule <- simprules, let vMax = maximum (0: tVars term) + 1, let (lhs,rhs) = renameRuleVars vMax rule, sub <- maybeToList (match (term |> p) lhs) ] -- match t1 t2 = sub iff sub(t2) = t1 match :: Term QName -> Term QName -> Maybe (Subst QName) match = matchTerm emptySubst where matchTerm sub t1 (TermVar i) = maybe (Just (extendSubst sub i t1)) (\t2 -> matchTerm sub t1 t2) (lookupSubst sub i) matchTerm sub (TermCons f1 args1) (TermCons f2 args2) = if f1 /= f2 then Nothing else matchArgs sub args1 args2 matchTerm _ (TermVar _) (TermCons _ _) = Nothing matchArgs _ (_:_) [] = Nothing matchArgs _ [] (_:_) = Nothing matchArgs sub [] [] = Just sub matchArgs sub (x:xs) (y:ys) = maybe Nothing (\s -> matchArgs s xs ys) (matchTerm sub x y) -- Some additional simplifcation rules (based on Prelude definitions): standardSimpRules :: TRS QName standardSimpRules = [ (TermCons (pre "&&") [trueTerm, x1], x1) , (TermCons (pre "&&") [x1, trueTerm], x1) ] where x1 = TermVar 1 trueTerm :: Term QName trueTerm = TermCons (pre "True") [] ------------------------------------------------------------------------ --- Translate terms into AbstractCurry expressions -- to be extended term2acy :: [CVarIName] -> Term QName -> CExpr term2acy cvars (TermVar i) = maybe (error "term2acy: cannot find variable") (\s -> CVar (i,s)) (lookup i cvars) term2acy cvars (TermCons (mn,fn) args) | null args && head mn == '%' = CLit (const2literal (tail mn, fn)) | otherwise = foldl CApply (CSymbol (mn,fn)) (map (term2acy cvars) args) const2literal :: QName -> CLiteral const2literal sl = case sl of ("i",s) -> CIntc (readQTerm s) ("f",s) -> CFloatc (readQTerm s) ("c",s) -> CCharc (head s) ("s",s) -> CStringc s _ -> error "const2literal: unknown literal" ------------------------------------------------------------------------