{-# LANGUAGE TemplateHaskell, PatternGuards, MagicHash #-}
module System.Console.CmdArgs.Quote(
cmdArgsQuote,
(&=#), modes#, cmdArgsMode#, cmdArgs#, enum#
) where
import Language.Haskell.TH
import Control.Arrow
import Control.Monad
import Data.Data
import Data.Maybe
import System.Console.CmdArgs.Implicit
stub name = error $
"System.Console.CmdArgs.Quote." ++ name ++
": this function is provided only for use inside cmdArgsQuote, and should never be called"
(&=#) :: a -> Ann -> a
(&=#) = stub "(&=#)"
modes# :: [a] -> a
modes# = stub "modes#"
cmdArgsMode# :: a -> Mode (CmdArgs a)
cmdArgsMode# = stub "cmdArgsMode#"
cmdArgs# :: a -> IO a
cmdArgs# = stub "cmdArgs#"
enum# :: [a] -> a
enum# = stub "enum#"
cmdArgsQuote :: Q [Dec] -> Q [Dec]
cmdArgsQuote x = do
x <- x
translate $ rename $ simplify $ inline x
translate :: [Dec] -> Q [Dec]
translate = descendBiM f
where
dull = ['Just, 'Left, 'Right, '(:)]
f (RecConE x xs) = return $
let args = [anns (InfixE (Just $ VarE lbl) (ConE '(:=)) (Just val)) as | (lbl,x) <- xs, let (val, as) = asAnns x]
in VarE 'record `AppE` RecConE x [] `AppE` ListE args
f x | (ConE x, xs@(_:_)) <- asApps x, x `notElem` dull = do
names <- forM [1..length xs] $ \i -> newName $ "_" ++ nameBase x ++ show i
let (vals, ass) = unzip $ map asAnns xs
bind = [ValD (VarP name) (NormalB val) [] | (name,val) <- zip names vals]
args = [anns (VarE 'atom `AppE` VarE name) as | (name,as) <- zip names ass]
return $ LetE bind $ VarE 'record `AppE` (ConE x `apps` map VarE names) `AppE` ListE args
f x = descendM f x
apps x [] = x
apps x (y:ys) = apps (x `AppE` y) ys
asApps (AppE x y) = let (a,b) = asApps x in (a,b++[y])
asApps x = (x,[])
anns x [] = x
anns x (a:as) = anns (InfixE (Just x) (VarE '(+=)) (Just a)) as
asAnns (InfixE (Just x) (VarE op) (Just y)) | op == '(+=) = let (a,b) = asAnns x in (a,b++[y])
asAnns (AppE (AppE (VarE op) x) y) | op == '(+=) = let (a,b) = asAnns x in (a,b++[y])
asAnns x = (x, [])
rename :: [Dec] -> [Dec]
rename = transformBi f
where
rep = let f a b c = [(a,c),(b,c)] in concat
[f '(&=) '(&=#) '(+=)
,f 'modes 'modes# 'modes_
,f 'enum 'enum# 'enum_
,f 'cmdArgsMode 'cmdArgsMode# 'cmdArgsMode_
,f 'cmdArgs 'cmdArgs# 'cmdArgs_]
f (VarE x) | Just x <- lookup x rep = VarE x
f x = x
simplify :: [Dec] -> [Dec]
simplify = transformBi f
where
f (AppE (LamE [VarP v] bod) x) = f $ subst v x bod
f x = x
subst v x bod = transform f bod
where f (VarE v2) | v == v2 = x
f x = x
inline :: [Dec] -> [Dec]
inline xs = map (dec $ addEnv xs []) xs
where
newEnv = concatMap $ \x -> case x of
FunD x [Clause ps (NormalB e) ds] -> [(x, LamE ps $ let_ ds e)]
ValD (VarP x) (NormalB e) ds -> [(x, let_ ds e)]
_ -> []
addEnv xs env = without [] (newEnv xs) ++ env
where
without ns new = [(n, exp (new2 ++ env) e) | (n,e) <- new, n `notElem` ns, let new2 = without (n:ns) new]
dec env (FunD n cs) = FunD n $ map (clause env) cs
dec env (ValD p x ds) = ValD p (body (addEnv ds env) x) ds
clause env (Clause ps x ds) = Clause ps (body (addEnv ds env) x) ds
body env (GuardedB xs) = GuardedB $ map (second $ exp env) xs
body env (NormalB x) = NormalB $ exp env x
exp env (LetE ds x) = LetE ds $ exp (addEnv ds env) x
exp env (VarE x) | Just x <- lookup x env = x
exp env x = descend (exp env) x
let_ ds e = if null ds then e else LetE ds e
descendBi :: (Data a, Data b) => (b -> b) -> a -> a
descendBi f x | Just f <- cast f = f x
| otherwise = gmapT (descendBi f) x
descend :: Data a => (a -> a) -> a -> a
descend f = gmapT (descendBi f)
transform :: Data a => (a -> a) -> a -> a
transform f = f . descend (transform f)
transformBi :: (Data a, Data b) => (b -> b) -> a -> a
transformBi f = descendBi (transform f)
descendBiM :: (Data a, Data b, Monad m) => (b -> m b) -> a -> m a
descendBiM f x | Just x <- cast x = liftM (fromJust . cast) $ f x
| otherwise = gmapM (descendBiM f) x
descendM :: (Data a, Monad m) => (a -> m a) -> a -> m a
descendM f = gmapM (descendBiM f)