{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedRecordDot #-}
{-# LANGUAGE TupleSections #-}
module Language.Nanopass.QQ
( deflang
, defpass
) where
import Language.Nanopass.LangDef
import Nanopass.Internal.Representation
import Prelude hiding (mod)
import Data.Functor ((<&>))
import Language.Haskell.TH (Q, Dec)
import Language.Haskell.TH.Quote (QuasiQuoter(..))
import Language.Nanopass.Xlate (mkXlate)
import Nanopass.Internal.Validate (validateLanguage)
import Nanopass.Internal.Parser (Loc(..),parseLanguage,parsePass)
import qualified Data.Text.Lazy as LT
import qualified Language.Haskell.TH as TH
import qualified Text.Pretty.Simple as PP
deflang :: QuasiQuoter
deflang :: QuasiQuoter
deflang = (FilePath -> Q Exp)
-> (FilePath -> Q Pat)
-> (FilePath -> Q Type)
-> (FilePath -> Q [Dec])
-> QuasiQuoter
QuasiQuoter (FilePath -> FilePath -> Q Exp
forall {m :: * -> *} {p} {a}. MonadFail m => FilePath -> p -> m a
bad FilePath
"expression") (FilePath -> FilePath -> Q Pat
forall {m :: * -> *} {p} {a}. MonadFail m => FilePath -> p -> m a
bad FilePath
"pattern") (FilePath -> FilePath -> Q Type
forall {m :: * -> *} {p} {a}. MonadFail m => FilePath -> p -> m a
bad FilePath
"type") FilePath -> Q [Dec]
go
where
go :: String -> Q [Dec]
go :: FilePath -> Q [Dec]
go FilePath
input = do
Loc
loc <- Q Loc
TH.location Q Loc -> (Loc -> Loc) -> Q Loc
forall (f :: * -> *) a b. Functor f => f a -> (a -> b) -> f b
<&> \Loc
l -> Loc
{ file :: FilePath
file = Loc
l.loc_filename
, line :: Int
line = (Int, Int) -> Int
forall a b. (a, b) -> a
fst Loc
l.loc_start
, col :: Int
col = (Int, Int) -> Int
forall a b. (a, b) -> b
snd Loc
l.loc_start
}
case (Loc, FilePath) -> Either Error ParseResult
parseLanguage (Loc
loc, FilePath
input) of
(Right (Left Language 'Unvalidated UpName
def)) -> case Language 'Unvalidated UpName
-> Either Error (Language 'Valid UpName)
validateLanguage Language 'Unvalidated UpName
def of
Right Language 'Valid UpName
ok -> Define [Dec] -> Q [Dec]
forall a. Define a -> Q a
runDefine (Define [Dec] -> Q [Dec]) -> Define [Dec] -> Q [Dec]
forall a b. (a -> b) -> a -> b
$ Language 'Valid UpName -> Define [Dec]
defineLang Language 'Valid UpName
ok
Left Error
err -> FilePath -> Q [Dec]
forall a. FilePath -> Q a
forall (m :: * -> *) a. MonadFail m => FilePath -> m a
fail (FilePath -> Q [Dec]) -> FilePath -> Q [Dec]
forall a b. (a -> b) -> a -> b
$ (Text -> FilePath
LT.unpack (Text -> FilePath) -> (Error -> Text) -> Error -> FilePath
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Error -> Text
forall a. Show a => a -> Text
PP.pShow) Error
err
(Right (Right LangMod
mod)) -> LangMod -> Q [Dec]
runModify LangMod
mod
Left Error
err -> FilePath -> Q [Dec]
forall a. FilePath -> Q a
forall (m :: * -> *) a. MonadFail m => FilePath -> m a
fail (FilePath -> Q [Dec]) -> FilePath -> Q [Dec]
forall a b. (a -> b) -> a -> b
$ (Text -> FilePath
LT.unpack (Text -> FilePath) -> (Error -> Text) -> Error -> FilePath
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Error -> Text
forall a. Show a => a -> Text
PP.pShow) Error
err
bad :: FilePath -> p -> m a
bad FilePath
ctx p
_ = FilePath -> m a
forall a. FilePath -> m a
forall (m :: * -> *) a. MonadFail m => FilePath -> m a
fail (FilePath -> m a) -> FilePath -> m a
forall a b. (a -> b) -> a -> b
$ FilePath
"`deflang` quasiquoter cannot be used in " FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ FilePath
ctx FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ FilePath
" context,\n\
\it can only appear as part of declarations."
defpass :: QuasiQuoter
defpass :: QuasiQuoter
defpass = (FilePath -> Q Exp)
-> (FilePath -> Q Pat)
-> (FilePath -> Q Type)
-> (FilePath -> Q [Dec])
-> QuasiQuoter
QuasiQuoter (FilePath -> FilePath -> Q Exp
forall {m :: * -> *} {p} {a}. MonadFail m => FilePath -> p -> m a
bad FilePath
"expression") (FilePath -> FilePath -> Q Pat
forall {m :: * -> *} {p} {a}. MonadFail m => FilePath -> p -> m a
bad FilePath
"pattern") (FilePath -> FilePath -> Q Type
forall {m :: * -> *} {p} {a}. MonadFail m => FilePath -> p -> m a
bad FilePath
"type") FilePath -> Q [Dec]
go
where
go :: FilePath -> Q [Dec]
go FilePath
input = do
Loc
loc <- Q Loc
TH.location Q Loc -> (Loc -> Loc) -> Q Loc
forall (f :: * -> *) a b. Functor f => f a -> (a -> b) -> f b
<&> \Loc
l -> Loc
{ file :: FilePath
file = Loc
l.loc_filename
, line :: Int
line = (Int, Int) -> Int
forall a b. (a, b) -> a
fst Loc
l.loc_start
, col :: Int
col = (Int, Int) -> Int
forall a b. (a, b) -> b
snd Loc
l.loc_start
}
case (Loc, FilePath) -> Either Error Pass
parsePass (Loc
loc, FilePath
input) of
Right Pass
ok -> do
Language 'Valid UpDotName
l1 <- UpDotName -> Q (Language 'Valid UpDotName)
reifyLang Pass
ok.sourceLang.name
Language 'Valid UpDotName
l2 <- UpDotName -> Q (Language 'Valid UpDotName)
reifyLang Pass
ok.targetLang.name
Language 'Valid UpDotName -> Language 'Valid UpDotName -> Q [Dec]
mkXlate Language 'Valid UpDotName
l1 Language 'Valid UpDotName
l2
Left Error
err -> FilePath -> Q [Dec]
forall a. FilePath -> Q a
forall (m :: * -> *) a. MonadFail m => FilePath -> m a
fail (FilePath -> Q [Dec]) -> FilePath -> Q [Dec]
forall a b. (a -> b) -> a -> b
$ (Text -> FilePath
LT.unpack (Text -> FilePath) -> (Error -> Text) -> Error -> FilePath
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Error -> Text
forall a. Show a => a -> Text
PP.pShow) Error
err
bad :: FilePath -> p -> m a
bad FilePath
ctx p
_ = FilePath -> m a
forall a. FilePath -> m a
forall (m :: * -> *) a. MonadFail m => FilePath -> m a
fail (FilePath -> m a) -> FilePath -> m a
forall a b. (a -> b) -> a -> b
$ FilePath
"`defpass` quasiquoter cannot be used in a " FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ FilePath
ctx FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ FilePath
"context,\n\
\it can only appear as part of declarations."