packages feed

DrHylo-0.0.2: src/DrHylo.hs

module Main where

import System.IO
import System.Console.GetOpt
import System.Environment
import Language.Haskell.Exts.Syntax as Exts
import Language.Haskell.Exts.Parser
import Language.Haskell.Exts.Pretty
import Data.Maybe
import Data.List
import PwPf
import Matching
import FunctorOf
import Hylos
import Language.Pointfree.Pretty
import Language.Pointfree.Syntax as Pf
import Language.Pointwise.Syntax as Pw
import Language.Pointwise.Pretty
import Language.Pointwise.Parser
import Language.Pointwise.Matching
import Generics.Pointless.Combinators
import Control.Monad.State
import Data.Generics.Schemes
import Data.Generics.Aliases

-- Managing Options

data Flag = Input String | Output String | Fixify | Pointwise | Observable
	    deriving Eq

options :: [OptDescr Flag]
options = [Option ['o'] ["output"] (OptArg outp "FILE") "output FILE",
	   Option ['i'] ["input"] (OptArg inp "FILE") "input FILE",
	   Option ['f'] ["fix"] (NoArg Fixify) "use fixpoints instead of hylomorphisms",
	   Option ['w'] ["pointwise"] (NoArg Pointwise) "do not convert to point-free",
           Option ['O'] ["observable"] (NoArg Observable) "generate observable hylomorphisms"
	  ]

inp,outp :: Maybe String -> Flag
outp = Output . fromMaybe "stdout"
inp = Input . fromMaybe "stdin"

parseOpts :: [String] -> IO [Flag]
parseOpts opts = case (getOpt Permute options opts) 
		 of (l,[],[]) -> return l
		    (_,_,errs) -> fail (concat errs ++"\n"++ usageInfo header options)
    where header = "DrHylo derives point-free hylomorphisms from restricted Haskell syntax\n\nUsage: DrHylo [OPTION...]"

getInput :: [Flag] -> IO Handle
getInput [] = return stdin
getInput ((Input i):_) | i=="stdin" = return stdin
		       | otherwise = openFile i ReadMode
getInput (_:l) = getInput l

getOutput :: [Flag] -> IO Handle
getOutput [] = return stdout
getOutput ((Output i):_) | i=="stdout" = return stdout
			 | otherwise = openFile i WriteMode
getOutput (_:l) = getOutput l

fixrequired :: [Flag] -> Bool
fixrequired = elem Fixify

pwrequired :: [Flag] -> Bool
pwrequired = elem Pointwise

obrequired :: [Flag] -> Bool
obrequired = elem Observable

-- Parsing

parse :: String -> IO Module
parse s = case (parseModule s) 
	  of ParseOk m -> return m
	     ParseFailed l d -> fail ((show l)++": "++d)

-- Generation of Observable function contexts

isTypeSig :: String -> Decl -> Bool
isTypeSig name (TypeSig _ x _) = elem (Ident name) x
isTypeSig _ _ = False

getTypeVars :: Exts.Type -> [Name]
getTypeVars = everything (++) ([] `mkQ` getVar)
   where getVar :: Exts.Type -> [Name]
         getVar (TyVar v) = [v]
         getVar _ = []

addTypeSig :: Decl -> Decl
addTypeSig (TypeSig loc names t) = TypeSig loc names (aux t)
   where
   aux (TyForall mb ctx (TyFun a b)) = TyForall mb (ctx++inst typeable a b++inst observable a b) (TyFun a b)
   aux (TyFun a b) = TyForall Nothing (inst typeable a b++inst observable a b) (TyFun a b)
   vars a b = nub $ intersect (getTypeVars a) (getTypeVars b)
   inst cl a b = map (mkInsVar cl) (vars a b)

mkInsVar :: Name -> Name -> Asst
mkInsVar cl n = ClassA (UnQual cl) [TyVar n]

addTypeableObservableIns :: String -> [Decl] -> [Decl]
addTypeableObservableIns n [] = []
addTypeableObservableIns n (d:ds) | isTypeSig n d = addTypeSig d : addTypeableObservableIns n ds
                          | otherwise = d : addTypeableObservableIns n ds

-- From Pointwise to Point-free (or not)

pwpfModule :: [Flag] -> [(String,Pw.Term)] -> Module -> Module
pwpfModule f c (Module loc name pragmas warnings exports imports decls) = Module loc name pragmas' warnings exports imports decls''
   where
   (decls',obs) = (id >< catMaybes) $ unzip $ map aux decls
   decls'' = if (obrequired f) then foldr addTypeableObservableIns decls' obs else decls'
   pragmaNames = if (obrequired f) then ["TypeFamilies,","DeriveDataTypeable"] else ["TypeFamilies"]
   pragmas' = LanguagePragma loc (map Ident pragmaNames) : pragmas
   aux d = case pwpfDecl f c d 
      of Just (d',mb) -> (d',mb)
         Nothing -> (d,Nothing)

consts :: [(String,Pw.Term)]
consts = [("[]", In (Inl Unit)),(":", Lam "h" (Lam "t" (In (Inr (Pw.Var "h" :&: Pw.Var "t")))))]

pwpfDecl :: [Flag] -> [(String,Pw.Term)] -> Decl -> Maybe (Decl,Maybe String)
pwpfDecl f d (PatBind loc (PVar (Ident name)) (UnGuardedRhs rhs) (BDecls [])) =
    do pw <- hs2pw rhs
       pw0 <- return (step (replace (d++consts) pw))
       pw1 <- evalStateT (nomatch pw0) 0
       pw2 <- return (if (name `elem` free pw1) 
		      then Pw.Fix (Lam name pw1)
		      else pw1)
       pw3 <- return (subst (map (\v -> (v, Pw.Const v)) (free pw2)) pw2)
       (rhs',ob) <- return (if (pwrequired f)
		       then (pw2hs pw3,Nothing)
		       else if (not (fixrequired f)) && (derivable pw3)
		            then let (Pw.Fix (Lam nam (Lam x z))) = pw3 
		                     t   = fun z nam
		                     a   = Lam "__" (alg z nam (Pw.Var "__"))
		                     c   = Lam x (coa z nam)
                                     hyl = if (obrequired f) then HyloO else Hylo
		                 in (pf2hs (hyl (Pf.Fix t) (unpoint (pwpf [] a)) (unpoint (pwpf [] c))),Just name)
		            else (pf2hs (unpoint (pwpf [] pw3)),Nothing))
       return (PatBind loc (PVar (Ident name)) (UnGuardedRhs rhs') (BDecls []),ob)
pwpfDecl _ _ _ = fail "The transformation must be applied to simple declarations"


-- Handle imports

loc0 :: SrcLoc
loc0 = SrcLoc "" 0 0

mkImportDecl :: String -> ImportDecl
mkImportDecl n = ImportDecl loc0 (ModuleName n) False False Nothing Nothing

getImportName :: ImportDecl -> String
getImportName (ImportDecl _ (ModuleName n) _ _ _ _) = n

handleImports :: Bool -> Module -> Module
handleImports b (Module loc name pragmas warnings exports imports decls) =
    let aux True  = ["Generics.Pointless.Combinators", "Generics.Pointless.Functors", "Generics.Pointless.RecursionPatterns", "Data.Typeable", "Debug.Observe", "Generics.Pointless.Observe.Functors", "Generics.Pointless.Observe.RecursionPatterns"]
        aux False = ["Generics.Pointless.Combinators", "Generics.Pointless.Functors", "Generics.Pointless.RecursionPatterns"]
	aux' = aux b \\ (map getImportName imports) 
	imports' = imports++(map mkImportDecl aux')
    in Module loc name pragmas warnings exports imports' decls


-- Main

main :: IO ()
main = do opts <- getArgs
	  flags <- parseOpts opts
          let ob = obrequired flags
	  ihandle <- getInput flags
          ohandle <- getOutput flags
          source <- hGetContents ihandle
	  hsModule <- parse source
	  hsModule0 <- return (casificate hsModule)
	  hsModule1 <- return (functorOfInst ob hsModule0)
	  hsModule2 <- return (pwpfModule flags (getCtx hsModule1) hsModule1)
          hPutStrLn ohandle (prettyPrint (handleImports ob hsModule2))
          hClose ihandle
          hClose ohandle