inferno-core-0.1.0.0: src/Inferno/Utils/QQ/Module.hs
{-# LANGUAGE TemplateHaskell #-}
module Inferno.Utils.QQ.Module where
import Control.Monad.Reader (ReaderT (..))
import Control.Monad.Writer (WriterT (..))
import Data.Data (Proxy (..), cast)
import Data.Generics.Aliases (extQ)
import Data.List (intercalate)
import qualified Data.List.NonEmpty as NEList
import Data.Text (pack)
import Inferno.Infer (closeOverType)
import Inferno.Module (buildPinnedQQModules)
import Inferno.Parse
( QQDefinition (..),
modulesParser,
topLevel,
)
import qualified Inferno.Types.Type as Type
import Inferno.Utils.QQ.Common
( liftText,
location',
mkParseErrorStr,
)
import qualified Language.Haskell.TH.Lib as TH
import Language.Haskell.TH.Quote (QuasiQuoter (..), dataToExpQ)
import Language.Haskell.TH.Syntax (mkName)
import Text.Megaparsec
( ParseErrorBundle (ParseErrorBundle),
PosState (PosState),
State (State),
attachSourcePos,
defaultTabWidth,
errorOffset,
runParser',
)
mkProxy :: a -> Proxy a
mkProxy _ = Proxy
metaToValue :: (Maybe Type.TCScheme, QQDefinition) -> Maybe TH.ExpQ
metaToValue = \case
(Just sch, QQToValueDef x) -> Just [|Left ($(dataToExpQ (\a -> liftText <$> cast a) sch), toValue $(TH.varE (mkName x)))|]
(Nothing, QQToValueDef x) ->
Just [|Left (closeOverType (toType (mkProxy $(TH.varE (mkName x)))), toValue $(TH.varE (mkName x)))|]
(Just sch, QQRawDef x) -> Just [|Left ($(dataToExpQ (\a -> liftText <$> cast a) sch), pure $(TH.varE (mkName x)))|]
(Nothing, QQRawDef _) -> error "QQRawDef must have an explicit type"
(sch, InlineDef e) ->
Just [|Right ($(dataToExpQ (\a -> liftText <$> cast a) sch), $(dataToExpQ (\a -> liftText <$> cast a) e))|]
infernoModules :: QuasiQuoter
infernoModules =
QuasiQuoter
{ quoteExp = \str -> do
l <- location'
let (_, res) =
runParser' (runWriterT $ flip runReaderT (mempty, mempty) $ topLevel $ modulesParser) $
State
(pack str)
0
(PosState (pack str) 0 l defaultTabWidth "")
[]
case res of
Left (ParseErrorBundle errs pos) ->
let errs' = map mkParseErrorStr $ NEList.toList $ fst $ attachSourcePos errorOffset errs pos
in fail $ intercalate "\n\n" errs'
Right (modules, _comments) ->
[|buildPinnedQQModules $(dataToExpQ ((\a -> liftText <$> cast a) `extQ` metaToValue) modules)|],
quotePat = error "infernoModule: Invalid use of this quasi-quoter in pattern context.",
quoteType = error "infernoModule: Invalid use of this quasi-quoter in type context.",
quoteDec = error "infernoModule: Invalid use of this quasi-quoter in top-level declaration context."
}