module Language.Haskell.Liquid.UX.QuasiQuoter (
lq
, LiquidQuote(..)
) where
import SrcLoc (SrcSpan)
import Data.Data
import Data.List
import qualified Data.Text as T
import Language.Haskell.TH.Lib
import Language.Haskell.TH.Syntax
import Language.Haskell.TH.Quote
import Text.Parsec.Pos
import Text.PrettyPrint.HughesPJ
import Language.Fixpoint.Types hiding (Error, Loc, SrcSpan)
import qualified Language.Fixpoint.Types as F
import Language.Haskell.Liquid.GHC.Misc (fSrcSpan)
import Language.Haskell.Liquid.Parse
import Language.Haskell.Liquid.Types
import Language.Haskell.Liquid.Types.RefType
import Language.Haskell.Liquid.UX.Tidy
lq :: QuasiQuoter
lq = QuasiQuoter
{ quoteExp = bad
, quotePat = bad
, quoteType = bad
, quoteDec = lqDec
}
where
bad = fail "`lq` quasiquoter can only be used as a top-level declaration"
lqDec :: String -> Q [Dec]
lqDec src = do
pos <- locSourcePos <$> location
case singleSpecP pos src of
Left err -> throwErrorInQ $ errorToUserError err
Right spec -> do
prg <- pragAnnD ModuleAnnotation $
conE 'LiquidQuote `appE` dataToExpQ' spec
case mkSpecDecs spec of
Left err ->
throwErrorInQ err
Right decs ->
return $ prg : decs
throwErrorInQ :: UserError -> Q a
throwErrorInQ err =
fail . showpp =<< runIO (errorWithContext err)
mkSpecDecs :: BPspec -> Either UserError [Dec]
mkSpecDecs (Asrt (name, ty)) =
return . SigD (symbolName name)
<$> simplifyBareType name (quantifyFreeRTy $ val ty)
mkSpecDecs (LAsrt (name, ty)) =
return . SigD (symbolName name)
<$> simplifyBareType name (quantifyFreeRTy $ val ty)
mkSpecDecs (Asrts (names, (ty, _))) =
(\t -> (`SigD` t) . symbolName <$> names)
<$> simplifyBareType (head names) (quantifyFreeRTy $ val ty)
mkSpecDecs (Alias rta) =
return . (TySynD name tvs) <$> simplifyBareType lsym (rtBody rta)
where
lsym = F.Loc (rtPos rta) (rtPosE rta) (rtName rta)
name = symbolName $ rtName rta
tvs = PlainTV . symbolName <$> rtTArgs rta
mkSpecDecs _ =
Right []
symbolName :: Symbolic s => s -> Name
symbolName = mkName . symbolString . symbol
simplifyBareType :: LocSymbol -> BareType -> Either UserError Type
simplifyBareType s t = case simplifyBareType' t of
Simplified t' ->
Right t'
FoundExprArg l ->
Left $ ErrTySpec l (pprint $ val s) (pprint t) $ text
"Found expression argument in bad location in type"
FoundHole ->
Left $ ErrTySpec (fSrcSpan s) (pprint $ val s) (pprint t) $ text
"Can't write LiquidHaskell type with hole in a quasiquoter"
simplifyBareType' :: BareType -> Simpl Type
simplifyBareType' = simplifyBareType'' ([], [])
simplifyBareType'' :: ([BTyVar], [BareType]) -> BareType -> Simpl Type
simplifyBareType'' ([], []) (RVar v _) =
return $ VarT $ symbolName v
simplifyBareType'' ([], []) (RAppTy t1 t2 _) =
AppT <$> simplifyBareType' t1 <*> simplifyBareType' t2
simplifyBareType'' ([], []) (RFun _ i o _) =
(\x y -> ArrowT `AppT` x `AppT` y)
<$> simplifyBareType' i <*> simplifyBareType' o
simplifyBareType'' ([], []) (RApp cc as _ _) =
let c = btc_tc cc
c' | isFun c = ArrowT
| isTuple c = TupleT (length as)
| isList c = ListT
| otherwise = ConT $ symbolName c
in foldl' AppT c' <$> sequenceA (filterExprArgs $ simplifyBareType' <$> as)
simplifyBareType'' _ (RExprArg e) =
FoundExprArg $ fSrcSpan e
simplifyBareType'' _ (RHole _) =
FoundHole
simplifyBareType'' s(RAllP _ t) =
simplifyBareType'' s t
simplifyBareType'' s (RAllS _ t) =
simplifyBareType'' s t
simplifyBareType'' s (RAllE _ _ t) =
simplifyBareType'' s t
simplifyBareType'' s (REx _ _ t) =
simplifyBareType'' s t
simplifyBareType'' s (RRTy _ _ _ t) =
simplifyBareType'' s t
simplifyBareType'' (tvs, cls) (RFun _ i o _)
| isClassType i = simplifyBareType'' (tvs, i : cls) o
simplifyBareType'' (tvs, cls) (RAllT tv t) =
simplifyBareType'' (ty_var_value tv : tvs, cls) t
simplifyBareType'' (tvs, cls) t =
ForallT (PlainTV . symbolName <$> reverse tvs)
<$> mapM simplifyBareType' (reverse cls)
<*> simplifyBareType' t
data Simpl a = Simplified a
| FoundExprArg SrcSpan
| FoundHole
deriving (Functor)
instance Applicative Simpl where
pure = Simplified
Simplified f <*> Simplified x = Simplified $ f x
_ <*> FoundExprArg l = FoundExprArg l
_ <*> FoundHole = FoundHole
FoundExprArg l <*> _ = FoundExprArg l
FoundHole <*> _ = FoundHole
instance Monad Simpl where
return = Simplified
Simplified x >>= f = f x
FoundExprArg l >>= _ = FoundExprArg l
FoundHole >>= _ = FoundHole
filterExprArgs :: [Simpl a] -> [Simpl a]
filterExprArgs = filter check
where
check (FoundExprArg _) = False
check _ = True
newtype LiquidQuote = LiquidQuote { liquidQuoteSpec :: BPspec }
deriving (Data, Typeable)
locSourcePos :: Loc -> SourcePos
locSourcePos loc =
newPos (loc_filename loc) (fst $ loc_start loc) (snd $ loc_start loc)
dataToExpQ' :: Data a => a -> Q Exp
dataToExpQ' = dataToExpQ (const Nothing `extQ` textToExpQ)
textToExpQ :: T.Text -> Maybe ExpQ
textToExpQ text = Just $ varE 'T.pack `appE` stringE (T.unpack text)
extQ :: (Typeable a, Typeable b) => (a -> q) -> (b -> q) -> a -> q
extQ f g a = maybe (f a) g (cast a)