module Fay.Compiler.Desugar
(desugar
) where
import Fay.Exts.NoAnnotation (unAnn)
import Fay.Types (CompileError (..))
import Control.Applicative
import Control.Monad.Error
import Control.Monad.Reader
import Data.Data (Data)
import qualified Data.Generics.Uniplate.Data as U
import Data.Maybe
import Data.Typeable (Typeable)
import Language.Haskell.Exts.Annotated hiding (binds, loc)
import Prelude hiding (exp)
data DesugarReader = DesugarReader { readerNameDepth :: Int }
newtype Desugar a = Desugar
{ unDesugar :: (ReaderT DesugarReader
(ErrorT CompileError IO))
a
} deriving ( MonadReader DesugarReader
, MonadError CompileError
, MonadIO
, Monad
, Functor
, Applicative
)
runDesugar :: Desugar a -> IO (Either CompileError a)
runDesugar m = runErrorT (runReaderT (unDesugar m) (DesugarReader 0))
withScopedTmpName :: (Data l, Typeable l) => l -> (Name l -> Desugar a) -> Desugar a
withScopedTmpName l f = do
n <- asks readerNameDepth
local (\r -> DesugarReader $ readerNameDepth r + 1) $
f $ Ident l $ "$gen" ++ show n
desugar :: (Data l, Typeable l) => Module l -> IO (Either CompileError (Module l))
desugar md = runDesugar $
checkEnum md
>> desugarSection md
>>= desugarListComp
>>= return . desugarTupleCon
>>= return . desugarPatParen
>>= return . desugarFieldPun
>>= return . desugarPatFieldPun
>>= desugarDo
>>= desugarTupleSection
desugarSection :: (Data l, Typeable l) => Module l -> Desugar (Module l)
desugarSection = transformBiM $ \ex -> case ex of
LeftSection l e q -> withScopedTmpName l $ \tmp ->
return $ Lambda l [PVar l tmp] (InfixApp l e q (Var l (UnQual l tmp)))
RightSection l q e -> withScopedTmpName l $ \tmp ->
return $ Lambda l [PVar l tmp] (InfixApp l (Var l (UnQual l tmp)) q e)
_ -> return ex
desugarDo :: (Data l, Typeable l) => Module l -> Desugar (Module l)
desugarDo = transformBiM $ \ex -> case ex of
Do _ stmts -> maybe (throwError EmptyDoBlock) return $ foldl desugarStmt' Nothing (reverse stmts)
_ -> return ex
desugarStmt' :: Maybe (Exp l) -> (Stmt l) -> Maybe (Exp l)
desugarStmt' inner stmt =
maybe initStmt subsequentStmt inner
where
initStmt = case stmt of
Qualifier _ exp -> Just exp
LetStmt{} -> error "UnsupportedLet"
_ -> error "InvalidDoBlock"
subsequentStmt inner' = case stmt of
Generator loc pat exp -> desugarGenerator loc pat inner' exp
Qualifier s exp -> Just $ InfixApp s exp
(QVarOp s $ UnQual s $ Symbol s ">>")
inner'
LetStmt _ (BDecls s binds) -> Just $ Let s (BDecls s binds) inner'
LetStmt _ _ -> error "UnsupportedLet"
RecStmt{} -> error "UnsupportedRecursiveDo"
desugarGenerator :: l -> Pat l -> Exp l -> Exp l -> Maybe (Exp l)
desugarGenerator s pat inner' exp =
Just $ InfixApp s
exp
(QVarOp s $ UnQual s $ Symbol s ">>=")
(Lambda s [pat] (inner'))
desugarTupleCon :: (Data l, Typeable l) => Module l -> Module l
desugarTupleCon = transformBi $ \ex -> case ex of
Var _ (Special _ t@TupleCon{}) -> fromTupleCon ex t
Con _ (Special _ t@TupleCon{}) -> fromTupleCon ex t
_ -> ex
where
fromTupleCon :: Exp l -> SpecialCon l -> Exp l
fromTupleCon e s = fromMaybe e $ case s of
TupleCon l b n -> Just $ Lambda l params body
where
names = take n $ map (Ident l . ("$gen" ++) . show) [(1::Int)..]
params = PVar l <$> names
body = Tuple l b (Var l . UnQual l <$> names)
_ -> Nothing
desugarTupleSection :: (Data l, Typeable l) => Module l -> Desugar (Module l)
desugarTupleSection = transformBiM $ \ex -> case ex of
TupleSection l _ mes -> do
(names, lst) <- genSlotNames l mes (varNames l)
return $ Lambda l (map (PVar l) names) (Tuple l Unboxed lst)
_ -> return ex
where
varNames :: l -> [Name l]
varNames l = map (\i -> Ident l ("$gen_" ++ show i)) [0::Int ..]
genSlotNames :: l -> [Maybe (Exp l)] -> [Name l] -> Desugar ([Name l], [Exp l])
genSlotNames _ [] _ = return ([], [])
genSlotNames l (Nothing : rest) ns = do
(rn, re) <- genSlotNames l rest (tail ns)
return (head ns : rn, Var l (UnQual l (head ns)) : re)
genSlotNames l (Just e : rest) ns = do
(rn, re) <- genSlotNames l rest ns
return (rn, e : re)
desugarPatParen :: (Data l, Typeable l) => Module l -> Module l
desugarPatParen = transformBi $ \pt -> case pt of
PParen _ p -> p
_ -> pt
desugarFieldPun :: (Data l, Typeable l) => Module l -> Module l
desugarFieldPun = transformBi $ \f -> case f of
FieldPun l n -> let dn = UnQual l n in FieldUpdate l dn (Var l dn)
_ -> f
desugarPatFieldPun :: (Data l, Typeable l) => Module l -> Module l
desugarPatFieldPun = transformBi $ \pf -> case pf of
PFieldPun l n -> PFieldPat l (UnQual l n) (PVar l n)
_ -> pf
desugarListComp :: (Data l, Typeable l) => Module l -> Desugar (Module l)
desugarListComp = transformBiM $ \ex -> case ex of
ListComp l exp stmts -> desugarListComp' l exp stmts
_ -> return ex
where
desugarListComp' l e [] = return (List l [ e ])
desugarListComp' l e (QualStmt _ (Generator _ p e2) : stmts) = do
nested <- desugarListComp' l e stmts
withScopedTmpName l $ \f ->
return (Let l (BDecls l [ FunBind l [
Match l f [ p ] (UnGuardedRhs l nested) Nothing
, Match l f [ PWildCard l ] (UnGuardedRhs l (List l [])) Nothing
]]) (App l (App l (Var l (Qual l (ModuleName l "$Prelude") (Ident l "concatMap"))) (Var l (UnQual l f))) e2))
desugarListComp' l e (QualStmt _ (Qualifier _ e2) : stmts) = do
nested <- desugarListComp' l e stmts
return (If l e2 nested (List l []))
desugarListComp' l e (QualStmt _ (LetStmt _ bs) : stmts) = do
nested <- desugarListComp' l e stmts
return (Let l bs nested)
desugarListComp' _ _ (_ : _) =
error "UnsupportedListComprehension"
checkEnum :: (Data l, Typeable l) => Module l -> Desugar ()
checkEnum = mapM_ f . universeBi
where
f ex = case ex of
e@(EnumFrom _ e1) -> checkIntOrUnknown e [e1]
e@(EnumFromTo _ e1 e2) -> checkIntOrUnknown e [e1,e2]
e@(EnumFromThen _ e1 e2) -> checkIntOrUnknown e [e1,e2]
e@(EnumFromThenTo _ e1 e2 e3) -> checkIntOrUnknown e [e1,e2,e3]
_ -> return ()
checkIntOrUnknown :: Exp l -> [Exp l] -> Desugar ()
checkIntOrUnknown exp es = when (not $ any isIntOrUnknown es) (throwError . UnsupportedEnum $ unAnn exp)
isIntOrUnknown :: Exp l -> Bool
isIntOrUnknown e = case e of
Con {} -> False
Lit _ Int{} -> True
Lit {} -> False
Tuple {} -> False
List {} -> False
EnumFrom {} -> False
EnumFromTo {} -> False
EnumFromThen {} -> False
EnumFromThenTo {} -> False
_ -> True
transformBi :: U.Biplate (from a) (to a) => (to a -> to a) -> from a -> from a
transformBi = U.transformBi
universeBi :: U.Biplate (from a) (to a) => from a -> [to a]
universeBi = U.universeBi
transformBiM :: (Monad m, U.Biplate (from a) (to a)) => (to a -> m (to a)) -> from a -> m (from a)
transformBiM = U.transformBiM