{-# language TemplateHaskell #-} {-# language ViewPatterns #-} {-# language PatternSynonyms #-} module DependencyInjector ( module DependencyInjector, -- module Assembler, ) where -- import Assembler import Control.Monad import Language.Haskell.TH import Common import Data.List as L import Language.Haskell.Meta (parseExp) import Data.Either import System.IO.Unsafe import qualified Data.Set as Set assemble :: Deps -> Q Exp assemble = convertDepsViaTuple .> return assembleSimple :: Deps -> Q Exp assembleSimple = convertDepsToExp .> return convertDepsToExp :: Deps -> Exp convertDepsToExp = id .> mapDepNames (parseExp .> either errF id) -- .> mapChildren reverse .> convertDepsToExp' where errF = ("Error parsing: " ++) .> error convertDepsToExp' :: DepsG Exp -> Exp convertDepsToExp' d@(Dep{kind=Monadic, cs=[]}) = -- AppE (VarE $ mkName "unsafePerformIO") $ convertDepsToExp' d{kind=Pure} -- error "xxx" convertDepsToExp' $ depOP (VarE $ 'unsafePerformIO) [d{kind=Pure}] convertDepsToExp' (getDep -> (_, name, [])) = name -- convertDepsToExp' d@(Dep{kind=Monadic}) = -- AppE (VarE $ mkName "unsafePerformIO") $ convertDepsToExp' d{kind=Pure} convertDepsToExp' d@(Dep{kind=Monadic, cs=(_:_)}) = error "Children not yet supported in Monadic deps" convertDepsToExp' (getDep -> (_, name, x:xs)) = convertDepsToExp' (depOP (AppE name (convertDepsToExp' x)) xs) data DepsG a = Dep {name :: a, src :: DepSrc, kind :: DepKind, cs :: [DepsG a]} deriving (Show, Eq) data DepSrc = Original | Replaced deriving (Show, Eq) -- [ ] TODO consider makeing `override` handle the tuple too making the -- distinction between original and overridden dependencies obsolete data DepKind = Pure | Monadic deriving (Show, Eq) type Deps = DepsG String -- [ ] TODO conder redefining as -- type Deps = DepsG Exp -- or -- type Deps = DepsG ExpQ instance Ord a => Ord (DepsG a) where compare (getDep -> (_, n1, _)) (getDep -> (_, n2, _)) = compare n1 n2 mapDepNames :: (a -> b) -> DepsG a -> DepsG b mapDepNames f (getDep -> (c, n, xs)) = c (f n) (map (mapDepNames f) xs) -- mapDeps :: (DepsG a -> DepsG b) -> DepsG a -> DepsG b mapDeps f d | (cons, n, xs) <- getDep d = f $ cons n (map (mapDeps f) xs) mapChildren f (getDep -> (c, n, xs)) = c n (f $ map (mapChildren f) xs) override :: String -> String -> Deps -> Deps override a b d = if d == res then error errMsg else res where res = mapDeps (overrideDep a b) d errMsg = a ++ " not found while trying to override: " ++ (take 200 $ show d) -- If you would like to be able to do what this error prevents, -- pelase contact the maintainer of this package. overrideName a b n | n == a = b | otherwise = n overrideDep a b d@(getDep -> (c, n, ds)) = if n == a then d{name=b, src=Replaced} else d getContentOfNextLine :: Q String getContentOfNextLine = do loc <- location -- runIO $ print loc line <- runIO $ do file <- readFile $ loc_filename loc let (start, _) = loc_start loc l = file $> lines $> drop start $> head return l return line getContentOfNextLines :: Q String getContentOfNextLines = do loc <- location -- runIO $ print loc line <- runIO $ do file <- readFile $ loc_filename loc let (start, _) = loc_start loc l = file $> lines $> drop start $> take 10 $> unlines return l return line getContentOfFile :: Q String getContentOfFile = do loc <- location runIO $ readFile $ loc_filename loc getContentOfFollowingFnLine :: Q String getContentOfFollowingFnLine = getContentOfNextLines >>= findFirstFnDecLine .> return getContentOfNextLineLit = getContentOfNextLine $> fmap (StringL .> LitE) parseLineToDeps :: String -> (String, String, [String], [String]) parseLineToDeps line = (name, nameD, deps, args) where ws = words line name = head ws args = takeWhile (/= "=") $ tail ws nameD = d name deps = map d args d n = n ++ "D" inj :: Q [Dec] inj = injI getContentOfFollowingFnLine injI getContentOfFollowingFnLine = do getContentOfFollowingFnLine >>= parseLineToDeps .> return >>= injDecs injectableI = injI injLeaf = injectableLeaf injectableLeaf :: String -> Q [Dec] injectableLeaf name = injDecs (name, nameD name, [], []) injDecs (name, nameD, depsD, deps) = [d| $identD = $consDep $nameStr $(nce "Original") $(nce "Pure") $listLiteral $(return $ VarP $ mkName $ nameT $ name) = $(return $ TupE $ map (VarE . mkName) (name : map (++ "T") deps)) $(return $ VarP $ mkName $ name ++ "A") = $(return $ convertDepsToExp $ depOP name (map (mapDepNames (++ "A")) (map (flip depOP []) deps))) $(return $ VarP $ mkName $ (++ "I") $ name) = $(return $ VarE $ mkName $ name) |] where identD :: Q Pat identD = return $ VarP $ mkName nameD nameStr :: Q Exp nameStr = return $ (StringL .> LitE) name listLiteral :: Q Exp listLiteral = return $ ListE $ map (mkName .> VarE) depsD consDep :: Q Exp consDep = return $ ConE $ mkName "Dep" -- ne = return . VarE . mkName nce = return . ConE . mkName nameD = (++ "D") nameT = (++ "T") r x = x .> return convertDepsViaTuple deps@(name -> n) = LetE [ValD (tuplePattern deps) (NormalB (VarE $ mkName $ n ++ "T")) []] (convertDepsToExp deps) tuplePattern d@(getDep -> (_, n, ds)) = tuplePattern' Set.empty d n ds $> fst tuplePattern' set d n [] = inSet set d $ \set -> (wrapNameFor d, set) tuplePattern' set d n ds = inSet set d $ \set -> let (ds', set') = foldr f ([], set) ds -- f (getDep -> (_, n, ds)) (ls, set) = isSet set n $ \set -> () f d@(getDep -> (_, n, ds)) (ls, set) = let (d', set') = tuplePattern' set d n ds in (d':ls, set') -- inSetOrInsert n set (WildP:ls, set) $ \set -> ( set) in (TupP $ (wrapNameFor d) : ds', set') inSetOrInsert a set o1 o2 = if a `Set.member` set then o1 else o2 $ a `Set.insert` set inSet set d x = if d `Set.member` set then (WildP, set) -- else x else x $ d `Set.insert` set wrapNameFor (Dep n Original _ _) = VarP $ mkName n wrapNameFor (Dep _ Replaced _ _) = WildP getDepName (getDep -> (_, n, _)) = n getDepDs (getDep -> (_, _, ds)) = ds getDep (Dep n s p ds) = ((\n' ds' -> Dep n' s p ds'), n, ds) -- functions for injG injG :: Q [Dec] injG = injIG getContentOfFollowingFnLine injIG getContentOfFollowingFnLine = do getContentOfFollowingFnLine >>= parseLineToDepsG .> return >>= injDecsG 'Pure injMG :: Q [Dec] injMG = do getContentOfFollowingFnLine >>= parseLineToDepsG .> return >>= injDecsG 'Monadic injAllG :: Q [Dec] injAllG = do getContentOfFile >>= lines .> groupByIndentation .> filter (concat .> words .> uncons .> maybe False (fst .> ("I" `isSuffixOf`))) .> map (unlines .> parseLineToDepsG .> injDecsG 'Pure) .> sequence .> fmap concat -- parseLineToDepsG :: String -> (String, String, [String], [String]) parseLineToDepsG ls = (name, nameI, nameD, deps, args) where line = findFirstFnDecLine ls ws = words line name = nameI $> removeIname nameI = head ws args = map (reverse .> takeWhile (/= '@') .> reverse) $ takeWhile (/= "=") $ tail ws nameD = d name deps = map d args d n = n ++ "D" groupByIndentation = id .> groupBy (const $ (" " `isPrefixOf`)) joinIndentedLines = id .> groupByIndentation .> map (intercalate "") findFirstFnDecLine ls = ls $> lines $> joinIndentedLines $> L.find (("=" `L.isInfixOf`) `andf` (("=>" `L.isInfixOf`) .> not)) $> maybe (error $ "Couldn't find function definition:\n" ++ ls) id orf :: (a -> Bool) -> (a -> Bool) -> a -> Bool orf f g x = f x || g x andf :: (a -> Bool) -> (a -> Bool) -> a -> Bool andf f g x = f x && g x removeIname n = n $> reverse .> f .> reverse where f ('I':(a@(_:_))) = a f _ = error $ "Name must end with `I` suffix. e.g. `fooI` or `barI`: " ++ n depOP n ds = Dep n Original Pure ds injDecsG n (name, nameI, nameD, depsD, deps) = [d| $identD = $consDep $nameStr Original $(return $ ConE $ n) $listLiteral :: Deps $(return $ VarP $ mkName $ nameT $ name) = $(return $ TupE $ map (mkName .> VarE) ((name ++ "I") : map (++ "T") deps)) $(return $ VarP $ mkName $ name ++ "A") = -- $(assemble $ depOP nameI (map (flip depOP []) deps)) $(return $ convertDepsToExp $ depOP nameI (map (flip depOP []) deps)) $(return $ VarP $ mkName $ name) = $(if n == 'Pure then return $ VarE $ mkName $ name ++ "A" else return $ AppE (VarE 'unsafePerformIO) (VarE $ mkName $ name ++ "A") ) |] where identD :: Q Pat identD = return $ VarP $ mkName nameD nameStr :: Q Exp nameStr = name $> StringL $> LitE $> return listLiteral :: Q Exp listLiteral = return $ ListE $ map (mkName .> VarE) depsD consDep :: Q Exp consDep = return $ ConE $ mkName "Dep" injP :: Q Pat injP = injG >>= transposeDecsToPE .> fst .> return injE :: Q Exp injE = injG >>= transposeDecsToPE .> snd .> return transposeDecsToPE :: [Dec] -> (Pat, Exp) transposeDecsToPE decs = (pats, exps) where pats = TupP $ map (\(ValD p e _) -> p) decs exps = TupE $ map (\(ValD p (NormalB e) _) -> e) decs