Retrie/ExactPrint/Annotated.hs (144 lines of code) (raw):
-- Copyright (c) Facebook, Inc. and its affiliates.
--
-- This source code is licensed under the MIT license found in the
-- LICENSE file in the root directory of this source tree.
--
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE DeriveDataTypeable #-}
module Retrie.ExactPrint.Annotated
( -- * Annotated
Annotated
, astA
, seedA
-- ** Synonyms
, AnnotatedHsDecl
, AnnotatedHsExpr
, AnnotatedHsType
, AnnotatedImport
, AnnotatedImports
, AnnotatedModule
, AnnotatedPat
, AnnotatedStmt
-- ** Operations
, pruneA
, graftA
, transformA
, trimA
, setEntryDPA
, printA
, printA'
, showAstA
-- * Internal
, unsafeMkA
) where
import Control.Monad.State.Lazy hiding (fix)
import Data.Default as D
import Data.Functor.Identity
import Language.Haskell.GHC.ExactPrint hiding
( -- cloneT
-- setEntryDP
-- , setEntryDPT
-- , transferEntryDPT
transferEntryDP
)
-- import Language.Haskell.GHC.ExactPrint.ExactPrint (ExactPrint(..))
import Language.Haskell.GHC.ExactPrint.Utils
import Retrie.GHC
import Retrie.SYB
-- Annotated -----------------------------------------------------------------
type AnnotatedHsDecl = Annotated (LHsDecl GhcPs)
type AnnotatedHsExpr = Annotated (LHsExpr GhcPs)
type AnnotatedHsType = Annotated (LHsType GhcPs)
type AnnotatedImport = Annotated (LImportDecl GhcPs)
type AnnotatedImports = Annotated [LImportDecl GhcPs]
type AnnotatedModule = Annotated (Located HsModule)
type AnnotatedPat = Annotated (LPat GhcPs)
type AnnotatedStmt = Annotated (LStmt GhcPs (LHsExpr GhcPs))
-- | 'Annotated' packages an AST fragment with the annotations necessary to
-- 'exactPrint' or 'transform' that AST.
data Annotated ast = Annotated
{ astA :: ast
-- ^ Examine the actual AST.
, seedA :: Int
-- ^ Name supply used by ghc-exactprint to generate unique locations.
}
deriving instance (Data ast) => Data (Annotated ast)
instance Functor Annotated where
fmap f Annotated{..} = Annotated{astA = f astA, ..}
instance Foldable Annotated where
foldMap f = f . astA
instance Traversable Annotated where
traverse f Annotated{..} =
(\ast -> Annotated{astA = ast, ..}) <$> f astA
instance Default ast => Default (Annotated ast) where
def = Annotated D.def 0
instance (Data ast, Monoid ast) => Semigroup (Annotated ast) where
(<>) = mappend
instance (Data ast, Monoid ast) => Monoid (Annotated ast) where
mempty = Annotated mempty 0
mappend a1 (Annotated ast2 _) =
runIdentity $ transformA a1 $ \ ast1 ->
mappend ast1 <$> return ast2
-- | Construct an 'Annotated'.
-- This should really only be used in the parsing functions, hence the scary name.
-- Don't use this unless you know what you are doing.
unsafeMkA :: ast -> Int -> Annotated ast
unsafeMkA = Annotated
-- | Transform an 'Annotated' thing.
transformA
:: Monad m => Annotated ast1 -> (ast1 -> TransformT m ast2) -> m (Annotated ast2)
transformA (Annotated ast seed) f = do
(ast',seed',_) <- runTransformFromT seed (f ast)
return $ Annotated ast' seed'
-- | Graft an 'Annotated' thing into the current transformation.
-- The resulting AST will have proper annotations within the 'TransformT'
-- computation. For example:
--
-- > mkDeclList :: IO (Annotated [LHsDecl GhcPs])
-- > mkDeclList = do
-- > ad1 <- parseDecl "myId :: a -> a"
-- > ad2 <- parseDecl "myId x = x"
-- > transformA ad1 $ \ d1 -> do
-- > d2 <- graftA ad2
-- > return [d1, d2]
--
graftA :: (Data ast, Monad m) => Annotated ast -> TransformT m ast
graftA (Annotated x _) = return x
-- | Encapsulate something in the current transformation into an 'Annotated'
-- thing. This is the inverse of 'graftT'. For example:
--
-- > splitHead :: Monad m => Annotated [a] -> m (Annotated a, Annotated [a])
-- > splitHead l = fmap astA $ transformA l $ \(x:xs) -> do
-- > y <- pruneA x
-- > ys <- pruneA xs
-- > return (y, ys)
--
pruneA :: (Data ast, Monad m) => ast -> TransformT m (Annotated ast)
pruneA ast = Annotated ast <$> gets id
-- | Trim the annotation data to only include annotations for 'ast'.
-- (Usually, the annotation data is a superset of what is necessary.)
-- Also freshens all source locations, so filename information
-- in annotation keys is discarded.
--
-- Note: not commonly needed, but useful if you want to inspect the annotation
-- data directly and don't want to wade through a mountain of output.
trimA :: Data ast => Annotated ast -> Annotated ast
trimA = runIdentity . transformA nil . const . graftA
where
nil :: Annotated ()
nil = mempty
setEntryDPA :: (Default an)
=> Annotated (LocatedAn an ast) -> DeltaPos -> Annotated (LocatedAn an ast)
setEntryDPA (Annotated ast s) dp = Annotated (setEntryDP ast dp) s
-- | Exactprint an 'Annotated' thing.
printA :: (Data ast, ExactPrint ast) => Annotated ast -> String
printA (Annotated ast _) = exactPrint ast
`debug` ("printA:" ++ showAst ast)
printA' :: (Data ast, ExactPrint ast) => Annotated ast -> String
printA' (Annotated ast _) = "[" ++ exactPrint ast ++ "]\n" ++ showAst ast
-- | showAst an 'Annotated' thing.
showAstA :: (Data ast, ExactPrint ast) => Annotated ast -> String
showAstA (Annotated ast _) = showAst ast