crucible-0.7: src/Lang/Crucible/Utils/Structural.hs
{-|
Module : Lang.Crucible.Utils.Structural
Copyright : (c) Galois, Inc 2013-2016
License : BSD3
Maintainer : Joe Hendrix <jhendrix@galois.com>
This module declares template Haskell primitives so that it is easier
to work with GADTs that have many constructors.
-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeOperators #-}
module Lang.Crucible.Utils.Structural
( structuralPretty
) where
import Data.Char (toLower)
import Language.Haskell.TH
import Language.Haskell.TH.Datatype
import Prettyprinter (brackets)
import Data.Parameterized.TH.GADT
import Data.Parameterized.TraversableFC
import Lang.Crucible.Utils.PrettyPrint (ppFn, commas)
------------------------------------------------------------------------
-- Contructor cases
-- | @structuralPretty tp@ generates a function with the type
-- @forall f ann. (forall x. f x -> Doc ann) -> (forall x. tp f x -> Doc ann)@
-- suitable for instantiating the @PrettyApp@ class.
structuralPretty :: TypeQ -> [(TypePat, ExpQ)] -> ExpQ
structuralPretty tpq pats0 = do
d <- lookupDataType' =<< asTypeCon "structuralPretty" =<< tpq
pp <- newName "pp"
a <- newName "a"
let pats = assocTypePats (dataParamTypes d) pats0
lamE [varP pp, varP a] $
caseE (varE a) (matchPretty pats (varE pp) <$> datatypeCons d)
matchPretty :: (Type -> Q (Maybe ExpQ)) -- ^ Pattern match functions
-> ExpQ
-> ConstructorInfo
-> MatchQ
matchPretty matchPat pp con = do
let nm = constructorName con
tps = constructorFields con
(pat,nms) <- conPat con "x"
let vars = varE <$> nms
let nm' = case nameBase nm of
c:r -> toLower c : r
[] -> error "matchPretty given constructor with empty name."
let mkPP0 v tp = do
me <- matchPat tp
case me of
Nothing -> mkPP v tp
Just f -> [| $(f) $(pp) $(v)|]
mkPP v ConT{} = [| viaShow $(v) |]
mkPP v (AppT VarT{} _) = appE pp v
mkPP v (AppT (ConT cnm) _)
| nameBase cnm `elem` [ "Vector" ]
= [| brackets (commas (fmap $(pp) $(v))) |]
mkPP v (AppT (AppT (ConT cnm) _) _)
| nameBase cnm `elem` [ "Assignment" ]
= [| brackets (commas (toListFC $(pp) $(v))) |]
mkPP v _ = [| viaShow $(v) |]
--mkPP _ tp = error $ "Unsupported type " ++ show tp ++ " with " ++ nameBase nm
let rhs = [| ppFn $(litE (stringL nm')) $(listE (zipWith mkPP0 vars tps)) |]
match (pure pat) (normalB rhs) []