packages feed

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