forked from bmillwood/haskell-src-meta
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
preliminary implementation for bmillwood#9 as a prepass
- Loading branch information
Showing
4 changed files
with
159 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
module Language.Haskell.Meta.ExtractQQ where | ||
|
||
import Data.Generics | ||
import Language.Haskell.Exts | ||
import Control.Monad.State | ||
import qualified Language.Haskell.TH as TH | ||
import Language.Haskell.TH.Quote | ||
import Data.Map (Map) | ||
import qualified Data.Map as M | ||
|
||
uniqueStr = "fns6LODHO5Czm0KSz4Do" | ||
uniqueVar ls = Ident (uniqueStr ++ show (length ls)) | ||
|
||
|
||
type QQResult = ([TH.Exp], [TH.Pat], [TH.Type], [TH.Dec]) | ||
|
||
initQQResult :: QQResult | ||
initQQResult = ([],[],[],[]) | ||
|
||
|
||
extractQQ :: Data a => Map String QuasiQuoter -> a -> TH.Q (a, QQResult) | ||
extractQQ qqs ast = runStateT (everywhereM quasiT =<< everywhereM decT ast) | ||
initQQResult | ||
where | ||
|
||
getQQ :: (MonadTrans t, Monad m) | ||
=> (QuasiQuoter -> a -> m b) | ||
-> String | ||
-> a | ||
-> Maybe (t m b) | ||
getQQ extractQ q body = do | ||
quoter <- M.lookup q qqs | ||
Just (lift (extractQ quoter body)) | ||
|
||
-- decT done first because of overlap with QuasiQuote | ||
quasiT, decT :: GenericM (StateT QQResult TH.Q) | ||
quasiT = mkM expT `extM` patT | ||
|
||
-- quasiquotes in types cannot be parsed yet: | ||
-- https://github.com/haskell-suite/haskell-src-exts/issues/117 | ||
|
||
decT = mkM $ \ xs -> fmap concat $ mapM (\x -> case x of | ||
SpliceDecl loc (QuasiQuote q body) | ||
| Just runQQ <- getQQ quoteDec q body -> do | ||
bodies <- runQQ | ||
forM bodies $ \ body' -> do | ||
(e,p,t,d) <- get | ||
put (e,p,t,body' : d) | ||
return (PatBind loc | ||
(PVar (uniqueVar d)) | ||
Nothing | ||
(UnGuardedRhs | ||
(Var (UnQual (uniqueVar d)))) | ||
(BDecls [])) | ||
_ -> return [x]) | ||
xs | ||
|
||
patT x = case x of | ||
PQuasiQuote q body | ||
| Just runQQ <- getQQ quotePat q body -> do | ||
body' <- runQQ | ||
(e,p,t,d) <- get | ||
put (e,body' : p,t,d) | ||
return (PVar (uniqueVar p)) | ||
_ -> return x | ||
|
||
expT x = case x of | ||
QuasiQuote q body | ||
| Just runQQ <- getQQ quoteExp q body -> do | ||
body' <- runQQ | ||
(e,p,t,d) <- get | ||
put (body' : e, p, t, d) | ||
return (Var (UnQual (uniqueVar e))) | ||
_ -> return x | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
module Language.Haskell.Meta.SubstQQ where | ||
|
||
import Language.Haskell.Meta.ExtractQQ | ||
import Language.Haskell.TH | ||
import Language.Haskell.TH.Syntax | ||
import Data.Generics | ||
|
||
import qualified Data.IntMap as M | ||
import Data.IntMap (IntMap) | ||
import Text.Read | ||
|
||
import Data.List | ||
|
||
|
||
lookupN :: Name -> IntMap e -> Maybe e | ||
lookupN (Name (OccName s) NameS) m | ||
| Just n <- readMaybe =<< stripPrefix uniqueStr s = M.lookup n m | ||
lookupN _ _ = Nothing | ||
|
||
expUpdate :: QQResult -> Exp -> Exp | ||
expUpdate (es, _, _, _) = | ||
let m :: IntMap Exp | ||
m = M.fromList $ zip [0 .. ] (reverse es) | ||
in \x -> case x of | ||
VarE n | Just e' <- lookupN n m -> e' | ||
_ -> x | ||
|
||
patUpdate :: QQResult -> Pat -> Pat | ||
patUpdate (_, ps, _, _) = | ||
let m :: IntMap Pat | ||
m = M.fromList $ zip [0 .. ] (reverse ps) | ||
in \x -> case x of | ||
VarP n | Just e' <- lookupN n m -> e' | ||
_ -> x | ||
|
||
decUpdate :: QQResult -> Dec -> Dec | ||
decUpdate (_, _, _, ds) = | ||
let m :: IntMap Dec | ||
m = M.fromList $ zip [0 .. ] (reverse ds) | ||
in \x -> case x of | ||
ValD (VarP n) _ _ | Just e' <- lookupN n m -> e' | ||
_ -> x | ||
|
||
|
||
substQQ :: Data a => QQResult -> a -> a | ||
substQQ qqr = everywhere (mkT (patUpdate qqr) `extT` expUpdate qqr) | ||
. everywhere (mkT (decUpdate qqr)) | ||
-- two traversals because the dec overlaps with the others |