packages feed

hs2bf-0.6: Front.hs

-- | Frontend: Haskell -> 'CoreP' desugarer
--
-- design policy: minimize the amount of code
--
-- [0. 'collectModules' :: IO \[Hs\]]
--   Parse necesarry modules.
--
-- [1. 'WeakDesugar' :: Hs -> Hs]
--   Shallow desugaring (including unguarding, if removal)
--
-- [2. 'MidDesugar' :: Hs -> Hs]
--   A little bit deep desugaring (where -> let, pattern(PatBind,Lambda) -> case, merge FunBinds)
--   Introduces dummy variables
--
-- [3. 'mergeModules' :: \[Hs\] -> Hs]
--   Resolve all name references and make them 'UnQual'.
--   Unknown identifiers are detected in this process.
--
-- [4. 'sds' :: Hs -> 'CoreP']
--   Explicit pattern matching
--
-- Dummy variable naming convention:
--
-- * #aa_2... : arguments ('HsMatch')
--
-- * #xa_1... : pattern matching
module Front where
import Control.Arrow
import Control.Exception
import Control.Monad
import Control.Monad.Error
import Data.Char
import Data.Either
import Data.List
import Data.Maybe
import qualified Data.Map as M
import qualified Data.Set as S
import Language.Haskell.Parser
import Language.Haskell.Syntax
import System.Directory
import System.FilePath.Posix

import Util
import Core hiding(multiApp)


-- | Necessary information for translating module name to 'FilePath'.
data ModuleEnv=ModuleEnv [FilePath]

compile :: [HsModule] -> Process Core
compile ms=return $ sds $ mergeModules $ map (mds . wds) ms

-- | Search all necesarry modules and parse them all (if possible).
-- If an parser error occurs, parse as many other files as possible to report further errors.
collectModules :: ModuleEnv -> String -> IO (Process [HsModule])
collectModules env mod=do
    x<-aux env (S.singleton mod) M.empty
    let (ls,rs)=partitionEithers $ M.elems x
    if null ls
        then return $ return $ rs
        else return $ throwError $ concat ls


aux :: ModuleEnv
    -> S.Set String -- ^ modules to be parsed
    -> M.Map String (Either [CompileError] HsModule) -- ^ already parsed module
    -> IO (M.Map String (Either [CompileError] HsModule)) -- ^ all modules
aux env s m=case S.minView s of
    Nothing -> return m
    Just (s0,s') -> do x<-parseModule1 env s0
                       aux env (flip S.difference (M.keysSet m) $ S.union s' $ S.fromList $ either (const []) collectDep x) (M.insert s0 x m)



parseModule1 :: ModuleEnv -> String -> IO (Either [CompileError] HsModule)
parseModule1 env mod=do
    x<-modToPath env mod
    case x of
        Nothing -> return $ Left [CompileError "frontend" "" ("module "++mod++" not found\n")]
        Just ph -> do x<-readFile ph
                      case parseModuleWithMode (ParseMode ph) x of
                          ParseFailed loc msg -> return $ Left [CompileError "frontend" (show loc) msg]
                          ParseOk x           -> return $ Right x


collectDep :: HsModule -> [String]
collectDep (HsModule _ _ _ is _)="Prelude":map (uM . importModule) is
    where uM (Module x)=x

-- | convert module name to full file path.
modToPath :: ModuleEnv -> String -> IO (Maybe FilePath)
modToPath (ModuleEnv dirs) name=
    handle (\(SomeException _)->return Nothing) $ liftM Just $ firstM doesFileExist $ map (</>(name++".hs")) dirs

-- | Returns first element which satisfies the predicate.
firstM :: Monad m => (a -> m Bool) -> [a] -> m a
firstM f []=fail "firstM: not found"
firstM f (x:xs)=do
    e<-f x
    if e then return x else firstM f xs




-- | /strong/ desugar
-- Replaces all implicit pattern matching with explicit cases.
-- Explicit modification of SrcLoc for error reporting in later process.
sds :: HsModule -> Core
sds (HsModule _ _ _ _ decls)=Core (map convDataDecl ds) (map convFunDecl fs)
    where
        ds=filter isDataDecl decls
        fs=filter isFunBind decls






isDataDecl (HsDataDecl _ _ _ _ _ _)=True
isDataDecl _=False

isFunBind (HsFunBind _)=True
isFunBind _=False

isPatBind (HsPatBind _ _ _ _)=True
isPatBind _=False


convDataDecl :: HsDecl -> CrData
convDataDecl (HsDataDecl loc ctx (HsIdent name) vars cons derv)=
    CrData name [] $ map convDataCon cons

convDataCon :: HsConDecl -> (CrName,[(Bool,CrType)])
convDataCon (HsConDecl loc (HsIdent name) ts)=(name,map f ts)
    where f (HsBangedTy ty)=(True,convType ty)
          f (HsUnBangedTy ty)=(False,convType ty)

convFunDecl :: HsDecl -> CrProc
convFunDecl (HsFunBind [HsMatch loc (HsIdent n) args (HsUnGuardedRhs e) []])
    =CrProc n (map f args) (convExp e)
    where
        f (HsPVar (HsIdent x))=x
        f x=error $ show x


convDecl d=error $ "convDecl:ERROR:"++show d

convExp :: HsExp -> CrExpr
convExp (HsLambda loc as e)=CrLm (map f as) (convExp e)
    where
        f (HsPVar (HsIdent x))=x
convExp (HsVar (UnQual (HsIdent x)))=CrVar x
convExp (HsApp e0 e1)=CrApp (convExp e0) (convExp e1)
convExp e@(HsCase _ _)=convFullCase e
-- convExp (HsLit (HsInt n))=error "convExp: int"-- CrA (h,Nothing) $ CrInt $ fromIntegral n
convExp (HsLit (HsInt n))=CrByte $ fromIntegral n
convExp (HsLit (HsChar ch))=CrByte $ fromIntegral $ ord ch
convExp (HsLet bs e)=CrLet True (map (toVP . convFunDecl) bs) (convExp e)
    where
        toVP (CrProc v [] e)=(v,e)
        toVP (CrProc v as e)=(v,CrLm as e)
-- convExp h (HsExpTyeSig
convExp e=error $ "ERROR:convExp:"++show e


convType :: HsType -> CrType
convType (HsTyVar (HsIdent x))=CrTyVar x
convType (HsTyCon (UnQual (HsIdent x)))=CrTyCon x
convType (HsTyApp t0 t1)=CrTyApp (convType t0) (convType t1)
convType t=error $ "convType: "++show t


-- | Convert 'HsCase'(desugared) to 'CrExpr'
--
-- Sort 'HsAlt's by constructor and use 'procPartialCase' for each constructor.
--
-- One of the following:
--
-- * HsAlt (HsPApp ...
--
-- * HsAlt (HsPVar ...
convFullCase :: HsExp -> CrExpr
convFullCase (HsCase e as)=case md of
    Nothing    -> CrCase (convExp e) cs
    Just (v,f) -> CrLet False [(v,convExp e)] $ CrCase (CrVar v) $ ("",[],convExp f):cs
    where
        cs=mapMaybe cv $ M.assocs $ M.map (second $ map $ second $ convExp) mn
        
        (mn,md)=sortAlts as
        cv (cons,(arity,cs))=let vs=take arity $ stringSeq "#x"
                             in do{x<-convSeqCase (liftM (convExp . snd) md) (map CrVar vs) cs; return (cons,vs,x)}
            



-- | Transform case of multiple vars. Note that constructors are already removed by 'procFullCase'.
--
-- example:
--  
--  > case v1 v2 v3 of
--  >    p11 p12 p13 -> e1
--  >    p21 p22 p23 -> e2
--  >    _           -> fail
--
--  to
--
--  > case v1 v2 v3 of
--  >    p11 p12 p13 -> e1
--  >    _ -> case v1 v2 v3 of
--  >             p21 p22 p23 -> e2
--  >             _ -> fail
convSeqCase :: Maybe CrExpr -> [CrExpr] -> [([HsPat],CrExpr)] -> Maybe CrExpr
convSeqCase fail vs []=fail
convSeqCase fail vs ((ps,e):as)=return $ convAndCase (convSeqCase fail vs as) e (zip vs ps)

-- | Transform multiple vars to
-- 
-- >case v1 v2 v3 of
-- >    p1 u2 p3 -> succ
-- 
-- to
-- 
-- >case v1 of
-- >    p1 -> let u2=v2 in
-- >          case v3 of
-- >              p3 -> succ
-- >              _  -> fail(other 'HsAlt')
-- >    _ -> fail(other 'HsAlt')
--
convAndCase :: Maybe CrExpr -> CrExpr -> [(CrExpr,HsPat)] -> CrExpr
convAndCase fail succ []=succ
convAndCase fail succ ((v,pat):cs)=case pat of
    HsPVar (HsIdent p) ->
        CrLet False [(p,v)] next
    HsPApp (UnQual (HsIdent n)) args ->
        let das=take (length args) $ stringSeq "#x"
        in CrCase v $ maybe [] (\x->[("",[],x)]) fail++[(n,das,convAndCase fail next $ zip (map CrVar das) args)]
    where
        next=convAndCase fail succ cs


-- | Sort ['HsAlt'] by constructors.
sortAlts :: [HsAlt] -> (M.Map String (Int,[([HsPat],HsExp)]),Maybe (String,HsExp))
sortAlts []=(M.empty,Nothing)
sortAlts ((HsAlt loc pat (HsUnGuardedAlt e) []):as)=
    case pat of
        HsPApp (UnQual (HsIdent n)) args -> 
            (M.insertWith merge n (length args,[(args,e)]) mn,md)
        HsPVar (HsIdent v) -> (M.empty,Just (v,e))
        x -> error $ "sortAlts:"++show x
    where
        merge (n0,xs0) (n1,xs1)=if n0==n1 then (n0,xs0++xs1) else error $ "Unmatched arity@"++show loc
        (mn,md)=sortAlts as



showLoc :: SrcLoc -> LocHint
showLoc (SrcLoc file line col)=concat [file,":",show line,":",show col,":"]
-- convExp (HsCase e as)=CrCase 










-- | Merge 'HsModule's. TODO: implement qualification resolver.
mergeModules :: [HsModule] -> HsModule
mergeModules ms=HsModule (SrcLoc "<whole>" 0 0) (Module "<whole>") Nothing [] (concatMap f ms)
    where f (HsModule _ _ _ _ decls)=decls


-- | /medium/ desugaring
--
-- * introduces dummy variables
--
-- * 'HsPat' is handled manually(not via 'mds').
--
--  You might find that 'HsLambda','HsPatBind'->'HsCase' conversion here duplicates 'sds'.
-- But they're completely different because 'sds' considers evaluation sequence of 'HsAlt's.
class MidDesugar a where
    mds :: a -> a

instance MidDesugar HsModule where
    mds (HsModule loc mod exp imp ds)=HsModule loc mod exp imp (eliminatePBind $ map mds ds)

instance MidDesugar HsDecl where
    mds (HsFunBind ms)=mergeMatches $ map mds ms
    mds (HsPatBind loc pat (HsUnGuardedRhs e) decls)
        =HsPatBind loc pat (HsUnGuardedRhs $ mds $ moveDecls e decls) []
    mds d=d

instance MidDesugar HsExp where
    mds (HsCase e als)=HsCase (mds e) (map mds als)
    mds (HsLet decls e)=HsLet (eliminatePBind $ map mds decls) (mds e)
    mds (HsLambda loc ps e)=eliminatePLambda $ HsLambda loc ps (mds e)
    mds e=e

instance MidDesugar HsMatch where
    mds (HsMatch loc name ps (HsUnGuardedRhs e) decls)
        =HsMatch loc name ps (HsUnGuardedRhs $ mds $ moveDecls e decls) []

instance MidDesugar HsAlt where
    mds (HsAlt loc pat (HsUnGuardedAlt e) decls)
        =HsAlt loc pat (HsUnGuardedAlt $ mds $ moveDecls e decls) []


-- | Generate let with given decls and 'HsExp'
moveDecls :: HsExp -> [HsDecl] -> HsExp
moveDecls e []=e
moveDecls e ds=HsLet ds e


-- | Merge multiple 'HsMatch' in HsFunBind into one.
mergeMatches :: [HsMatch] -> HsDecl
mergeMatches []=error "Front: mergeMatches: empty [HsMatch] found!"
-- mergeMatches [m]=HsFunBind [m]
mergeMatches ms=HsFunBind [HsMatch loc0 n0 (map HsPVar args) (HsUnGuardedRhs expr) []]
    where
        HsMatch loc0 n0 ps0 _ _=head ms
        args=map (HsIdent . ("#a"++) . show) [0..length ps0-1]
        expr=HsCase (multiApp (stdTuple $ length args) $ map (HsVar . UnQual) args) $ map genAlt ms
        
        genAlt (HsMatch loc _ ps (HsUnGuardedRhs e) [])=HsAlt loc (wds $ HsPTuple ps) (HsUnGuardedAlt e) []

-- | Eliminate pattern matching in lambda arguments.
eliminatePLambda :: HsExp -> HsExp
eliminatePLambda (HsLambda loc ps e)=HsLambda loc (map fst vars) (f (map snd vars) e)
    where
        cat :: HsPat -> HsName -> (HsPat,Maybe (HsExp,HsPat))
        cat p@(HsPVar v) _=(p,Nothing)
        cat p r=(HsPVar r,Just (HsVar (UnQual r),p))
        
        vars=zipWith cat ps (map HsIdent $ stringSeq "#x")
        
        f :: [Maybe (HsExp,HsPat)] -> HsExp -> HsExp
        f [] e=e
        f (Nothing:vs) e=f vs e
        f ((Just (v,p)):vs) e=HsCase v [HsAlt loc p (HsUnGuardedAlt $ f vs e) []]


-- | Eliminate 'HsPatBind'
-- x:xs=f
-- ->
-- #t0=f
-- x=case #t0 of x:xs -> x
-- xs=case #t0 of x:xs -> x
--
eliminatePBind :: [HsDecl] -> [HsDecl]
eliminatePBind ds=concat $ zipWith (convertPBind True) (stringSeq "#x") $ map convSimple ds


-- | Convert obvious 'HsPatBind' to 'HsFunBind'
convSimple :: HsDecl -> HsDecl
convSimple (HsPatBind loc (HsPVar n) rhs [])=HsFunBind [HsMatch loc n [] rhs []]
convSimple d=d

-- | Convert 'HsPatBind' to ['HsFunBind'] recursively.
-- operation:
--
--   from: T x y=z
--
--   to: #=z; x=case # of T x y -> x; y=case # of T x y -> y
--
convertPBind :: Bool -> String -> HsDecl -> [HsDecl]
convertPBind _ _ (HsPatBind loc (HsPVar n) rhs [])=[HsFunBind [HsMatch loc n [] rhs []]]
convertPBind flag prefix (HsPatBind loc p0@(HsPApp n args) rhs [])
    |flag      = pre:concat (zipWith3 f vars p0s args)
    |otherwise = concat (zipWith3 f vars p0s args)
    where
        pre=HsFunBind [HsMatch loc (HsIdent prefix) [] rhs []]
        
        vars=map (((prefix++"_")++) . show) [0..]
        p0s=map (HsPApp n) $ change1 (map (HsPVar . HsIdent) vars) args
        
        f _ _ p@(HsPVar (HsIdent n))=[genDecl n p0]
        f v p0' p=genDecl v p0':convertPBind False v (HsPatBind loc p (HsUnGuardedRhs (stdVar v)) [])
        
        genDecl :: String -> HsPat -> HsDecl
        genDecl v p=HsFunBind [HsMatch loc (HsIdent v) [] (HsUnGuardedRhs e) []]
            where e=HsCase (stdVar prefix) [HsAlt loc p (HsUnGuardedAlt (stdVar v)) []]

convertPBind _ prefix x=[x]


-- | Literally convert 'HsPat' to 'HsExp'.
pat2con :: HsPat -> Maybe HsExp
pat2con (HsPVar n)=return $ HsVar (UnQual n)
pat2con (HsPApp n vs)=liftM (multiApp $ HsVar n) (mapM pat2con vs)
pat2con HsPWildCard=Nothing







-- | /weak/ desugaring
class WeakDesugar a where
    wds :: a -> a

instance WeakDesugar HsModule where
    wds (HsModule loc mod exp imp ds)=HsModule loc mod exp imp (map wds ds)

instance WeakDesugar HsDecl where
    wds (HsFunBind ms)=HsFunBind $ map wds ms
    wds (HsPatBind loc pat rhs decls)=HsPatBind loc (wds pat) (wds rhs) (map wds decls)
    wds (HsTypeSig loc ns ty)=HsTypeSig loc ns ty
    wds (HsDataDecl loc [] n vs cdecls [])=HsDataDecl loc [] n vs cdecls []
    wds (HsDataDecl _ _ _ _ _ _)=error "WeakDesugar: HsDataDecl: context/deriving is not supported"
    wds (HsInfixDecl loc assoc lv ops)=HsInfixDecl loc assoc lv ops

instance WeakDesugar HsExp where
    wds (HsApp e0 e1)=HsApp (wds e0) (wds e1)
    wds (HsInfixApp e0 op e1)=HsApp (HsApp (wds $ opToExp op) (wds e0)) (wds e1)
    wds (HsNegApp e)=HsApp (HsVar (UnQual (HsIdent "negate"))) e
    wds (HsParen e)=wds e
    wds (HsLeftSection e op)=HsApp (opToExp op) (wds e)
    wds (HsRightSection op e)=HsApp (HsApp (HsVar (UnQual (HsIdent "flip"))) (opToExp op)) (wds e)
    wds (HsIf c e0 e1)=HsCase (wds c)
        [HsAlt wdsDummySrc (HsPApp (UnQual (HsIdent "True" )) []) (HsUnGuardedAlt (wds e0)) []
        ,HsAlt wdsDummySrc (HsPApp (UnQual (HsIdent "False")) []) (HsUnGuardedAlt (wds e1)) []]
    wds (HsCase e als)=HsCase (wds e) (map wds als)
    wds (HsLet decls e)=HsLet (map wds decls) (wds e)
    wds (HsLambda loc ps e)=HsLambda loc (map wds ps) (wds e)
    wds (HsEnumFrom e)=HsApp (stdVar "enumFrom") (wds e)
    wds (HsEnumFromTo e0 e1)=HsApp (HsApp (stdVar "enumFromTo") (wds e0)) (wds e1)
    wds (HsEnumFromThen e0 e1)=HsApp (HsApp (stdVar "enumFromThen") (wds e0)) (wds e1)
    wds (HsEnumFromThenTo e0 e1 e2)=HsApp (HsApp (HsApp (stdVar "enumFromThen") (wds e0)) (wds e1)) (wds e2)
    wds (HsCon f)=wds $ HsVar f
    wds (HsVar (Special HsCons))=stdVar "XCons"
    wds (HsVar (UnQual (HsSymbol v)))=HsVar (UnQual (HsIdent v))
    wds (HsVar v)=HsVar v
    wds (HsTuple es)=multiApp (stdTuple $ length es) (map wds es)
    wds (HsList es)=foldr (\x y->multiApp (stdVar "XCons") [wds x,y]) (stdVar "XNil") es
    wds (HsLit (HsString s))=wds $ HsList $ map (HsLit . HsChar) s
    wds l@(HsLit _)=l
    wds e=error $ "WeakDesugar:unsupported expression:"++show e

instance WeakDesugar HsMatch where
    wds (HsMatch loc name ps rhs decls)=HsMatch loc (wds name) (map wds ps) (wds rhs) (map wds decls)


instance WeakDesugar HsName where
    wds (HsSymbol x)=HsIdent x
    wds n=n

instance WeakDesugar HsQName where
    wds (Qual mod n)=Qual mod (wds n)
    wds (UnQual n)=UnQual (wds n)
    wds (Special sc)=UnQual $ HsIdent n
        where n=case sc of
                    HsUnitCon->"XT0"
                    HsListCon-> "XNil"
                    HsFunCon-> "XApp"
                    HsTupleCon n-> "XT"++show n
                    HsCons-> "XCons"

instance WeakDesugar HsPat where
    wds (HsPInfixApp p0 q p1)=HsPApp (wds q) [wds p0,wds p1]
    wds (HsPParen p)=wds p
    wds (HsPList ps)=foldr (\x y->HsPApp c [x,y]) n $ map wds ps
        where c=UnQual (HsIdent "XCons"); n=HsPApp (UnQual $ HsIdent "XNil") []
    wds (HsPTuple ps)=HsPApp (UnQual (HsIdent $ "XT"++show (length ps))) (map wds ps)
    wds (HsPApp n ps)=HsPApp (wds n) (map wds ps)
    wds (HsPVar n)=HsPVar $ wds n
    wds (HsPWildCard)=HsPWildCard
--    wds p=p
    wds p=error $ "WeakDesugar:unsupported HsPat:"++show p


instance WeakDesugar HsAlt where
    wds (HsAlt loc pat al decls)=HsAlt loc (wds pat) (wds al) (map wds decls)

-- | Only HsUnGuardedAlt will remain.
instance WeakDesugar HsGuardedAlts where
    wds (HsUnGuardedAlt e)=HsUnGuardedAlt $ wds e
    wds (HsGuardedAlts als)=HsUnGuardedAlt $ wds $ unguardG (\(HsGuardedAlt _ c e)->(c,e)) als

-- | Only HsUnGuardedRhs will remain.
instance WeakDesugar HsRhs where
    wds (HsUnGuardedRhs e)=HsUnGuardedRhs $ wds e
    wds (HsGuardedRhss rs)=HsUnGuardedRhs $ wds $ unguardG (\(HsGuardedRhs _ c e)->(c,e)) rs


-- | Generic unguarding routine (nested if generation)
unguardG :: (a -> (HsExp,HsExp)) -> [a] -> HsExp
unguardG _ []=HsVar $ UnQual $ HsIdent "undefined"
unguardG f (x:xs)=let (cond,exp)=f x in HsIf cond exp $ unguardG f xs

wdsDummySrc=SrcLoc "<WeakDesugar>" 0 0



stdVar :: String -> HsExp
stdVar=HsVar . UnQual . HsIdent

stdTuple :: Int -> HsExp
stdTuple=stdVar . ("XT"++) . show

opToExp :: HsQOp -> HsExp
opToExp (HsQVarOp n)=HsVar n
opToExp (HsQConOp n)=HsCon n

multiApp :: HsExp -> [HsExp] -> HsExp
multiApp=foldl HsApp