KiCS-prophecy-0.1.0: Curry/Module/Transform.curry
--- Program transformation to compute the oracle.
---
--- @version July 2007
--- new: employ make for modulewise transformation
---
module Transform where
import System (getArgs)
import Directory
import Integer ( maxlist )
import List
import Maybe
import Distribution (getStdLibDir)
import ReadShowTerm
import FlatCurry
import FlatCurryGoodies
import Wrapper
import Make
import AddWorld (addWorld)
import LiftCases (liftCases)
main :: IO ()
main = do
putStrLn "this is the curry prophecy generator"
params <- parseArgs
libdir <- getStdLibDir
maybe done mayCreateDirectory (output params)
transform libdir (force params) (quiet params) (output params) (modulename params)
putStrLn "curry prophecy generator finished"
mayCreateDirectory :: String -> IO ()
mayCreateDirectory dir = do
ex <- doesDirectoryExist dir
unless ex (createDirectory dir)
transform :: String -> Bool -> Bool -> Maybe String -> String -> IO ()
transform libdir force quiet outdir mod = make quiet mod tester (writeTrans outdir)
where
tester = if force then (\ fn _ -> readTypes [fn] >>= return . Just)
else myObsolete
readTypes fns = do
prog <- readFile (head fns)
let typeString = dropWhile (/='[') $ dropWhile (/=']') $ dropWhile (/='[') prog
types = fst $ head $ readsTerm typeString
return $ filter hasHOTypeArg types
obso = obsolete quiet (\ dir -> addFcy . addOrc dir outdir) [addFcy] readTypes
myObsolete path modu = do
ob <- obso path modu
if isJust ob || isJust outdir || not (isPrefixOf libdir path)
then return ob
else do
unless quiet (putStrLn (modu ++ " is a standard library"))
ts <- readTypes [path++addFcy modu]
return (Just ts)
writeTrans :: Maybe String -> String -> [[TypeDecl]] -> Prog -> IO [TypeDecl]
writeTrans outdir path imps prog = do
let fn = addOrc path outdir (progName prog)
(trTypes,trProg) = transProg (concat imps) (liftCases False prog)
res = show trProg
putStrLn ("generating "++fn)
writeFile (fn++".fcy") res
writeFile (fn++".fint") res
return trTypes
transProg :: [TypeDecl] -> Prog -> ([TypeDecl],Prog)
transProg impTypes prog
= (typesToTransform,
updProg newModName
(\imps -> eventMod:oracle:ioexts:progName prog:imps ++ map newModName imps)
(map (transType isTrType) . filter (isTrType . typeName))
(concatMap (transFunc isTrType isTrCons))
(map (updOpName newModNameQ)) prog')
where
prog' = rnmAllVarsInProg (+1) (addWorld prog) -- relies on vars starting from 1
typesToTransform = fst (fixDependence (withHo++impTypes) noHo)
(withHo,noHo) = partition hasHOTypeArg (progTypes prog)
fixDependence knowns unknowns =
case partition (dependsOnTypes (map typeName knowns)) unknowns of
([],indeps) -> (knowns,indeps)
(deps,indeps) -> fixDependence (knowns++deps) indeps
dependsOnTypes ts = trType (\_ _ _ -> any (any (containsTypes ts) . consArgs))
(\_ _ _ -> containsTypes ts)
containsTypes ts = trTypeExpr (\_->False) (\qn args -> or (elem qn ts:args)) (||)
isTrType t = elem t $ map typeName typesToTransform
isTrCons c = elem c $ map consName $ concatMap typeConsDecls
$ filter (not . isTypeSyn) typesToTransform
hasHOTypeArg :: TypeDecl -> Bool
hasHOTypeArg = trType (\_ _ _ cs -> any isFuncType (concatMap consArgs cs))
(\_ _ _ _ -> False)
transType :: (QName -> Bool) -> TypeDecl -> TypeDecl
transType isTr t
| isTr (typeName t) = updTypeName newModNameQ $
updTypeConsDecls (map (updCons newModNameQ id id
(map (rType isTr)))) t
| otherwise = t
when :: (a -> Bool) -> (a -> a) -> a -> a
when p f x = if p x then f x else x
rType :: (QName -> Bool) -> TypeExpr -> TypeExpr
rType _ t@(TVar _) = t
rType isTr (TCons name args) = TCons (when isTr newModNameQ name) (map (rType isTr) args)
rType isTr (FuncType dom ran) =
FuncType tRef (FuncType (updQNamesInTypeExpr (when isTr newModNameQ)
(rType isTr dom))
(rType isTr ran))
transFunc :: (QName -> Bool) -> Options -> FuncDecl -> [FuncDecl]
transFunc isTr opts func@(Func name arity vis t _)
| name==("Global","global") || name `elem` implementedInOracle = []
| isExternal func && isIO typ
= let call | isComplexIO typ = extIO (safeIOResult (extCall arity))
| otherwise = extIO (extCall arity) in
[Func newName arity vis (transFuncType (arity-1) isTr t) $
Rule (reverse [1 .. arity]) $ collapse ref call]
| isExternal func
= [Func newName (arity+1) vis (transFuncType arity isTr t) $
Rule (reverse args) $ collapse ref $ extCall (arity+1)]
| isGlobal func
= if isValue (head (combArgs (funcBody func)))
then [Func newName (arity+1) vis (transFuncType arity isTr t) $
Rule (reverse args) $ collapse ref $ extCall (arity+1)]
else error (fst name ++ '.':snd name ++
": cannot treat global states initialized with \
\unevaluated expression\n\
\possible solution: do some inlining and evaluating.")
| otherwise
= case transExpr opts (funcBody func) nextVar of
Right (ns,vs,exp) -> [updFunc newModNameQ
(+1)
id
(transFuncType arity isTr)
(updRule (++[1])
(const ({-if take 6 (snd name) == "_case_"
then exp
else-} event ns ref vs exp))
id)
func]
Left err -> [Func newName (arity+1) vis (transFuncType arity isTr t) $
Rule args $ errorCall $
fst newName ++ '.':snd newName ++':':err]
where
typ = resultType t
funcVars = allVarsInFunc func
nextVar = if null funcVars then 2 else maxlist funcVars + 1
args = [1..arity+1]
newName = newModNameQ name
isIO x = isTCons x && tConsName x==(prelude,"IO")
isComplexIO (TVar _) = False
isComplexIO (TCons _ as) =
as/=[TCons (prelude,"()") []]
&& not (elem name specialIOs)
isGlobal f = case funcBody f of
Comb FuncCall g [_,_] -> g==("Global","global")
_ -> False
extCall i = Comb FuncCall name (map Var (reverse (tail [1 .. i])))
isValue = trExpr (const False) (const True) (\ x _ xs -> and (isCombTypeConsCall x:xs))
(\ bs x -> and (x:map snd bs)) (\ _ x -> x) (&&) (\_ _ _ -> False) (\_ _ -> False)
transFuncType :: Int -> (QName -> Bool) -> TypeExpr -> TypeExpr
transFuncType n isTr t@(TVar v) =
if v==(-42) || n/=0 then t else FuncType tRef (rType isTr t)
transFuncType 0 isTr t@(TCons _ _) = FuncType tRef (rType isTr t)
transFuncType n isTr t@(FuncType dom ran)
| n == 0 = FuncType tRef (rType isTr t)
| otherwise = FuncType (rType isTr dom) (transFuncType (n-1) isTr ran)
transExpr :: Options -> Expr -> Int -> Either String ([Int],[Int],Expr)
transExpr opts exp n =
case trExpr var lit comb leT freE oR casE branch exp opts n [ref] of
Right (_,ns,vs,e) -> Right (ns,vs [],e)
Left s -> Left s
type Options = QName -> Bool
type T a = Options
-> Int -- next unused reference
-> [Int] -- references to reuse
-> Either
String -- Error for non-supported constructs
(Int -- next unused reference
,[Int] -- references to reuse
,[Int]->[Int] -- references to be introduced
,a) -- monadic result
ret :: a -> T a
ret x _ n ns = Right (n,ns,id,x)
(.>>=) :: T a -> (a -> T b) -> T b
(ta .>>= f) isTr n ns
= case ta isTr n ns of
Right (m,ms,vs,a) -> case f a isTr m ms of
Right (l,ls,ws,b) -> Right (l,ls,vs . ws,b)
Left s -> Left s
Left s -> Left s
sequence :: [T a] -> T [a]
sequence [] = ret []
sequence (tx:txs) = tx .>>= \x -> sequence txs .>>= \xs -> ret (x:xs)
nextRef :: T Int
nextRef _ n [] = Right (n+1,[],(n:),n)
nextRef _ n (m:ms) = Right (n,ms,id,m)
renameCons :: QName -> T QName
renameCons n isTr = ret (when isTr newModNameQ n) isTr
var :: Int -> T Expr
var x = ret (Var x)
lit :: Literal -> T Expr
lit l = ret (Lit l)
comb :: CombType -> QName -> [T Expr] -> T Expr
comb ct@ConsCall name targs
= sequence targs .>>= \args ->
renameCons name .>>= \name' ->
ret (Comb ct name' args)
comb ct@(ConsPartCall m) name targs
= sequence targs .>>= \args ->
renameCons name .>>= \name' ->
ret (partCons m (Comb ct name' args))
comb ct@FuncCall name targs
= sequence targs .>>= \args ->
nextRef .>>= \r ->
let args' = args++[Var r] in
if name `elem` implementedInOracle
then ret (inOraclePartCall (snd name) args')
else ret (Comb ct (newModNameQ name) args')
comb (FuncPartCall m) name targs
= sequence targs .>>= \args ->
ret $ partFunc m $
if name `elem` implementedInOracle
then inOraclePartCall (snd name) args
else pc (newModNameQ name) args
where
pc = Comb (FuncPartCall (m+1))
leT :: [(Int,T Expr)] -> T Expr -> T Expr
leT tbs texp
= sequence (map snd tbs) .>>= \es ->
nextRef .>>= \r isTr n [] -> case texp isTr n [r] of
Right (m,ms,fvs,exp) -> let bs = zip (map fst tbs) es
in Right (m,[],id,Let bs (event ms r (fvs []) exp))
Left s -> Left s
freE :: [Int] -> T Expr -> T Expr
freE vs te = sequence (replicate (length vs) freeVar) .>>= \ es ->
te .>>= \ e ->
ret (Let (zip vs es) e)
where
freeVar = nextRef .>>= (ret . Wrapper.unknown . Var)
oR :: T Expr -> T Expr -> T Expr
oR te1 te2 = te1 .>>= \ e1 ->
te2 .>>= \ e2 ->
ret (FlatCurry.Or e1 e2)
casE :: CaseType -> T Expr -> [Int -> T BranchExpr] -> T Expr
casE ct texp tbs
= texp .>>= \exp ->
nextRef .>>= \cr ->
sequence (map ($cr) tbs) .>>= \bs ->
ret (Case ct exp bs)
branch :: Pattern -> T Expr -> Int -> T BranchExpr
branch pat texp cr isTr n []
= case texp isTr n [cr] of
Right (m,ms,fvs,exp) -> Right (m,[],id,Branch (updPatCons (when isTr newModNameQ) pat)
(event ms cr (fvs []) exp))
Left s -> Left s