{-# Language ApplicativeDo #-} {-# Language LambdaCase #-} {-# Language MagicHash #-} {-# Language TemplateHaskell #-} {-# Language ViewPatterns #-} {-# OPTIONS_HADDOCK hide,not-home #-} module Data.Ruin.QQ ( expQQ, expQQA, pars, patQQ, rna, rnaA, rpat, ) where import Data.Maybe (catMaybes) import GHC.Prim (Proxy#,proxy#) import qualified Language.Haskell.TH as TH import Language.Haskell.TH.Quote (QuasiQuoter(..)) import Text.Parsec (parse) import Data.Ruin.All import Data.Ruin.Hoid import Data.Ruin.Internal import Data.Ruin.QQ.Parser pars :: (QQ -> TH.Q a) -> String -> TH.Q a pars k s = either (fail . show) k $ parse pQQ "rna quasiquote" s expQQ :: QQ -> TH.ExpQ expQQ (MkQQ typename binders) = case typename of Nothing -> e Just s -> [e| prto (proxy# :: Proxy# $(TH.conT (TH.mkName s))) $e |] where e = foldr TH.appE val (catMaybes seqs) (seqs,vals) = unzip $ map mk binders val = tupE vals mk (strictness,var,field) = ( if strictness then Just [e| seq $v |] else Nothing , [e| dub (mkLabel :: Label $(TH.litT (TH.strTyLit field))) $v |] ) where v = TH.varE (TH.mkName var) expQQA :: QQ -> TH.ExpQ expQQA (MkQQ typename binders) = foldl app [e| pure $fun |] binders where app f (_,var,_) = [e| $f <*> $(TH.varE (TH.mkName var)) |] fun = do (seqs,pats,vals) <- unzip3 <$> mapM mk binders let e = tupE vals let result = case typename of Nothing -> e Just s -> [e| prto (proxy# :: Proxy# $(TH.conT (TH.mkName s))) $e |] TH.lamE pats $ foldr TH.appE result (catMaybes seqs) mk (strictness,var,field) = do n <- TH.newName (if "_" == var then "x" else var) let v = TH.varE n return ( if strictness then Just [e| seq $v |] else Nothing , TH.varP n , [e| dub (mkLabel :: Label $(TH.litT (TH.strTyLit field))) $v |] ) patQQ :: QQ -> TH.PatQ patQQ (MkQQ typename binders) = case typename of Nothing -> tp Just s -> [p| (rup . phoid (proxy# :: Proxy# $(TH.conT (TH.mkName s))) -> $tp) |] where tp = tupP $ map mk binders mk (strictness,var,field) = bang p where bang = if strictness then TH.bangP else id p = [p| (undub (mkLabel :: Label $(TH.litT (TH.strTyLit field))) -> $v) |] v = if "_" == var then TH.wildP else TH.varP (TH.mkName var) tupE :: [TH.ExpQ] -> TH.ExpQ tupE [e] = [e| MkTup1 $e |] tupE es = TH.tupE es tupP :: [TH.PatQ] -> TH.PatQ tupP [p] = [p| MkTup1 $p |] tupP ps = TH.tupP ps ----- -- | Named arguments for functions. -- -- @ -- (\\['rna'|x] -> x) :: 'Tup1' ("x" ':@' a) -> a -- -- (\\['rna'|x y] -> (x,y)) :: ("x" ':@' a,"y" ':@' b) -> (a,b) -- -- (\\['rna'|y x] -> (x,y)) :: ("y" ':@' b,"x" ':@' a) -> (a,b) -- @ -- -- And so on. The 'Has' and 'Build' classes support such tuples up to -- 8 components. -- -- There are four pieces of special syntax, none of which can be -- escaped. -- -- * A @\@@ allows a different variable name than the field name. -- -- @ -- (\\f ['rna'|l\@x|] ['rna'|r\@x|] -> f l r) -- :: (a -> b -> c) -> 'Tup1' ("x" ':@' a) -> 'Tup1' ("x" ':@' b) -> c -- @ -- -- * A name of @_@ is a wildcard pattern. -- -- * A leading word of the form @(\...\)@ adds a -- prefix and/or suffix to all of the variable names. This affects -- even the names given with @\@@ syntax. It does not affect -- wildcards. -- -- @ -- (\\['rna'|(...L) x y|] ['rna'|(r_...') x y|] -> xL == r_x' && yL == r_y') -- :: (Eq a,Eq b) => ("x" ':@' a,"y" ':@' b) -> ("x" ':@' a,"y" ':@' b) -> Bool -- @ -- -- * A @!@ at the beginning of the pattern makes it strict. -- -- @ -- -- strict -- (\\['rna'|!x|] -> Just x) :: 'Tup1' ("x" ':@' a) -> Maybe a -- -- -- strict -- (\\['rna'|!x\@foo|] -> Just x) :: 'Tup1' ("foo" ':@' a) -> Maybe a -- -- -- strict -- (\\['rna'|!x\@!|] -> Just x) :: 'Tup1' ("!" ':@' a) -> Maybe a -- -- -- not strict -- (\\['rna'|x\@!|] -> Just x) :: 'Tup1' ("!" ':@' a) -> Maybe a -- @ -- -- Note a @~@ pattern for binding would be redundant, since the -- bindings are ultimately variable bindings. Though it may be -- useful to apply a tilde pattern to the entire quasiquote. -- -- * A leading word that is capitalized is interpreted as the name of -- a record type and is ascribed via 'rfrom'. -- -- @ -- data XY x y = MkXY {x :: x,y :: y} -- -- (\\['rna'| XY x y|] -> (x,y)) :: XY t t1 -> (t, t1) -- @ -- -- When both are present, the type name must precede the prefix -- and/or suffix. -- -- 'rna' also works as an expression. All of the sugar except -- wildcards is supported in the dual way. rna :: QuasiQuoter rna = QuasiQuoter (pars expQQ) (pars patQQ) nope nope where nope = fail "The `rna' quasiquoter only creates expressions or patterns." -- | 'rnaA' is like 'rna', but: -- -- * it only works for expressions, -- -- * it only works inside an 'Applicative'. rnaA :: QuasiQuoter rnaA = QuasiQuoter (pars expQQA) nope nope nope where nope = fail "The `rnaA' quasiquoter only creates expressions." ----- -- | 'rpat' is like 'rna', but it only works for patterns. rpat :: QuasiQuoter rpat = QuasiQuoter nope (pars patQQ) nope nope where nope = fail "The `rpat' quasiquoter only creates patterns."