{-# LANGUAGE TemplateHaskell #-} module Language.LaTeX.Builder.QQ (-- * Quasi Quoters frQQ,frQQFile,str,strFile,istr,tex,texFile,texm,texmFile,qm,qmFile,qp,qpFile, keys,keysFile, -- * Building new Quasi Quoters mkQQ, mkQQnoIndent, mkQQgen, mkQQFile, indent, noIndent, stripIndentQQ, -- * Misc functions used by the frquotes expander of «...» frTop, frAntiq, ) where import Data.List import Data.Char import Data.Functor import Language.Haskell.TH (Q, Exp, Name, appE, varE, stringE, litP, stringL, valD, varP, sigD, mkName, normalB, conE) import Language.Haskell.TH.Quote import Language.Haskell.TH.Syntax (Lift(..)) import Language.LaTeX.Types (Key(..)) import Language.LaTeX.Builder.Internal (rawTex, rawPreamble, rawMath) import Language.LaTeX.Builder.Math (mstring) import Language.LaTeX.Builder (hstring) frTop :: a -> a frTop = id frAntiq :: a -> a frAntiq = id frQQ,frQQFile,str,strFile,istr,tex,texFile,qm,qmFile,qp,qpFile, keys, keysFile :: QuasiQuoter quasiQuoter :: String -> QuasiQuoter quasiQuoter qqName = QuasiQuoter (err "expressions") (err "patterns") -- if GHC7 (err "types") (err "declarations") -- endif where err kind _ = fail $ qqName ++ ": not available in " ++ kind stripIndentQQ :: String -> Q String stripIndentQQ = fmap unlines' . skipFirst (mapM dropBar . dropLastWhen null . map (dropWhile isSpace)) . lines where unlines' = intercalate "\n" skipFirst _ [] = return [] skipFirst f (x:xs) = (x :) <$> f xs dropLastWhen _ [] = [] dropLastWhen p (x:xs) | null xs && p x = [] | otherwise = x:dropLastWhen p xs dropBar ('|':xs) = return xs dropBar [] = fail "stripIndentQQ: syntax error '|' expected after spaces (unexpected empty string)" dropBar (c:_) = fail $ "stripIndentQQ: syntax error '|' expected after spaces (unexpected "++show c++")" str = (quasiQuoter "str"){ quoteExp = stringE , quotePat = litP . stringL } strFile = quoteFile str mkQQgen :: (String -> Q Exp) -> String -> Name -> QuasiQuoter mkQQgen pre qqName qqFun = (quasiQuoter qqName){ quoteExp = appE (varE qqFun) . pre } mkQQ :: String -> Name -> QuasiQuoter mkQQ = mkQQgen indent mkQQnoIndent :: String -> Name -> QuasiQuoter mkQQnoIndent = mkQQgen lift mkQQFile :: (String -> Q Exp) -> String -> Name -> (QuasiQuoter, QuasiQuoter) mkQQFile pre qqName qqFun = (mkQQgen pre qqName qqFun , quoteFile (mkQQgen lift qqName qqFun)) indent, noIndent :: String -> Q Exp indent = (lift =<<) . stripIndentQQ noIndent = lift -- istr ≡ mkQQ "istr" 'id istr = (quasiQuoter "istr"){ quoteExp = (stringE =<<) . stripIndentQQ } -- istrFile makes no sense, use strFile instead -- | Quasiquoter for raw TeX in math mode texm, texmFile :: QuasiQuoter (frQQ, frQQFile) = mkQQFile noIndent "frQQ" 'hstring (tex, texFile) = mkQQFile indent "tex" 'rawTex (texm, texmFile) = mkQQFile indent "texm" 'rawMath (qm, qmFile) = mkQQFile indent "qm" 'mstring (qp, qpFile) = mkQQFile indent "qp" 'rawPreamble keys = (quasiQuoter "keys"){ quoteDec = fs } where fs = sequence . concatMap f . words clean = filter isAlphaNum f x = [sigD n [t|Key|] ,valD (varP n) (normalB (appE (conE 'MkKey) $ stringE x)) [] ] where n = mkName (clean x) keysFile = quoteFile keys