diff options
Diffstat (limited to 'utils/check-ppr')
-rw-r--r-- | utils/check-ppr/Main.hs | 219 | ||||
-rw-r--r-- | utils/check-ppr/README | 20 | ||||
-rw-r--r-- | utils/check-ppr/check-ppr.cabal | 32 | ||||
-rw-r--r-- | utils/check-ppr/ghc.mk | 18 |
4 files changed, 289 insertions, 0 deletions
diff --git a/utils/check-ppr/Main.hs b/utils/check-ppr/Main.hs new file mode 100644 index 0000000000..c61b0e6d4c --- /dev/null +++ b/utils/check-ppr/Main.hs @@ -0,0 +1,219 @@ +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE RankNTypes #-} + +import Data.Data hiding (Fixity) +import Data.List +import Bag +import FastString +import NameSet +import SrcLoc +import HsSyn +import OccName hiding (occName) +import GHC hiding (moduleName) +import Var +import DynFlags +import Outputable hiding (space) +import System.Environment( getArgs ) +import System.Exit +import System.FilePath + +import qualified Data.ByteString as B +import qualified Data.Map as Map + +main::IO() +main = do + args <- getArgs + case args of + [libdir,fileName] -> testOneFile libdir fileName + _ -> putStrLn "invoke with the libdir and a file to parse." + +testOneFile :: FilePath -> String -> IO () +testOneFile libdir fileName = do + p <- parseOneFile libdir fileName + let + origAst = showAstData 0 (pm_parsed_source p) + pped = pragmas ++ "\n" ++ pp (pm_parsed_source p) + anns = pm_annotations p + pragmas = getPragmas anns + + newFile = dropExtension fileName <.> "ppr" <.> takeExtension fileName + astFile = fileName <.> "ast" + + writeFile astFile origAst + writeFile newFile pped + + p' <- parseOneFile libdir newFile + + let newAstStr = showAstData 0 (pm_parsed_source p') + + if origAst == newAstStr + then do + -- putStrLn "ASTs matched" + exitSuccess + else do + putStrLn "AST Match Failed" + putStrLn "\n===================================\nOrig\n\n" + putStrLn origAst + putStrLn "\n===================================\nNew\n\n" + putStrLn newAstStr + exitFailure + + +parseOneFile :: FilePath -> FilePath -> IO ParsedModule +parseOneFile libdir fileName = do + let modByFile m = + case ml_hs_file $ ms_location m of + Nothing -> False + Just fn -> fn == fileName + runGhc (Just libdir) $ do + dflags <- getSessionDynFlags + let dflags2 = dflags `gopt_set` Opt_KeepRawTokenStream + _ <- setSessionDynFlags dflags2 + addTarget Target { targetId = TargetFile fileName Nothing + , targetAllowObjCode = True + , targetContents = Nothing } + _ <- load LoadAllTargets + graph <- getModuleGraph + let + modSum = case filter modByFile graph of + [x] -> x + xs -> error $ "Can't find module, got:" + ++ show (map (ml_hs_file . ms_location) xs) + parseModule modSum + +getPragmas :: ApiAnns -> String +getPragmas anns = pragmaStr + where + tokComment (L _ (AnnBlockComment s)) = s + tokComment (L _ (AnnLineComment s)) = s + tokComment _ = "" + + comments = case Map.lookup noSrcSpan (snd anns) of + Nothing -> [] + Just cl -> map tokComment $ sortLocated cl + pragmas = filter (\c -> isPrefixOf "{-#" c ) comments + pragmaStr = intercalate "\n" pragmas + +pp :: (Outputable a) => a -> String +pp a = showPpr unsafeGlobalDynFlags a + + +-- | Show a GHC AST with SrcSpan's blanked out, to avoid comparing locations, +-- only structure +showAstData :: Data a => Int -> a -> String +showAstData n = + generic + `ext1Q` list + `extQ` string `extQ` fastString `extQ` srcSpan + `extQ` bytestring + `extQ` name `extQ` occName `extQ` moduleName `extQ` var `extQ` dataCon + `extQ` bagName `extQ` bagRdrName `extQ` bagVar `extQ` nameSet + `extQ` fixity + `ext2Q` located + where generic :: Data a => a -> String + generic t = indent n ++ "(" ++ showConstr (toConstr t) + ++ space (unwords (gmapQ (showAstData (n+1)) t)) ++ ")" + space "" = "" + space s = ' ':s + indent i = "\n" ++ replicate i ' ' + string = show :: String -> String + fastString = ("{FastString: "++) . (++"}") . show + :: FastString -> String + bytestring = show :: B.ByteString -> String + list l = indent n ++ "[" + ++ intercalate "," (map (showAstData (n+1)) l) + ++ "]" + + name = ("{Name: "++) . (++"}") . showSDocDebug_ . ppr + :: Name -> String + occName = ("{OccName: "++) . (++"}") . OccName.occNameString + moduleName = ("{ModuleName: "++) . (++"}") . showSDoc_ . ppr + :: ModuleName -> String + + srcSpan :: SrcSpan -> String + srcSpan _ss = "{ "++ "ss" ++"}" + + var = ("{Var: "++) . (++"}") . showSDocDebug_ . ppr + :: Var -> String + dataCon = ("{DataCon: "++) . (++"}") . showSDoc_ . ppr + :: DataCon -> String + + bagRdrName:: Bag (Located (HsBind RdrName)) -> String + bagRdrName = ("{Bag(Located (HsBind RdrName)): "++) . (++"}") + . list . bagToList + bagName :: Bag (Located (HsBind Name)) -> String + bagName = ("{Bag(Located (HsBind Name)): "++) . (++"}") + . list . bagToList + bagVar :: Bag (Located (HsBind Var)) -> String + bagVar = ("{Bag(Located (HsBind Var)): "++) . (++"}") + . list . bagToList + + nameSet = ("{NameSet: "++) . (++"}") . list . nameSetElemsStable + + fixity = ("{Fixity: "++) . (++"}") . showSDoc_ . ppr + :: Fixity -> String + + located :: (Data b,Data loc) => GenLocated loc b -> String + located (L ss a) = + indent n ++ "(" + ++ case cast ss of + Just (s :: SrcSpan) -> + srcSpan s + Nothing -> "nnnnnnnn" + ++ showAstData (n+1) a + ++ ")" + +showSDoc_ :: SDoc -> String +showSDoc_ = showSDoc unsafeGlobalDynFlags + +showSDocDebug_ :: SDoc -> String +showSDocDebug_ = showSDocDebug unsafeGlobalDynFlags + +-- --------------------------------------------------------------------- + +-- Copied from syb for the test + + +-- | The type constructor for queries +newtype Q q x = Q { unQ :: x -> q } + +-- | Extend a generic query by a type-specific case +extQ :: ( Typeable a + , Typeable b + ) + => (a -> q) + -> (b -> q) + -> a + -> q +extQ f g a = maybe (f a) g (cast a) + +-- | Type extension of queries for type constructors +ext1Q :: (Data d, Typeable t) + => (d -> q) + -> (forall e. Data e => t e -> q) + -> d -> q +ext1Q def ext = unQ ((Q def) `ext1` (Q ext)) + + +-- | Type extension of queries for type constructors +ext2Q :: (Data d, Typeable t) + => (d -> q) + -> (forall d1 d2. (Data d1, Data d2) => t d1 d2 -> q) + -> d -> q +ext2Q def ext = unQ ((Q def) `ext2` (Q ext)) + +-- | Flexible type extension +ext1 :: (Data a, Typeable t) + => c a + -> (forall d. Data d => c (t d)) + -> c a +ext1 def ext = maybe def id (dataCast1 ext) + + + +-- | Flexible type extension +ext2 :: (Data a, Typeable t) + => c a + -> (forall d1 d2. (Data d1, Data d2) => c (t d1 d2)) + -> c a +ext2 def ext = maybe def id (dataCast2 ext) diff --git a/utils/check-ppr/README b/utils/check-ppr/README new file mode 100644 index 0000000000..ac0eb55977 --- /dev/null +++ b/utils/check-ppr/README @@ -0,0 +1,20 @@ + +This programme is intended to be used by any GHC developers working on the AST +and/or pretty printer by providing a way to check that the same AST is generated +from the pretty printed AST as from the original source. + +i.e., it checks whether + + parse (ppr (parse s)) === parse s + + +This utility is also intended to be used in tests, so that when new features are +added the ability to round-trip the AST via ppr is tested. + +Usage + +In a test Makefile + + $(CHECK_PPR) "`'$(TEST_HC)' $(TEST_HC_OPTS) --print-libdir | tr -d '\r'`" FileToParse.hs + +See examples in (REPO_HOME)/testsuite/tests/printer/Makefile diff --git a/utils/check-ppr/check-ppr.cabal b/utils/check-ppr/check-ppr.cabal new file mode 100644 index 0000000000..96863e58fa --- /dev/null +++ b/utils/check-ppr/check-ppr.cabal @@ -0,0 +1,32 @@ +Name: check-ppr +Version: 0.1 +Copyright: XXX +License: BSD3 +-- XXX License-File: LICENSE +Author: XXX +Maintainer: XXX +Synopsis: A utilities for checking the consistency of GHC's pretty printer +Description: + This utility is used to check the consistency of the GHC pretty printer, by + parsing a file, pretty printing it, and then re-parsing the pretty printed + version. See @utils/check-ppr/README@ in GHC's source distribution for + details. +Category: Development +build-type: Simple +cabal-version: >=1.10 + +Executable check-ppr + Default-Language: Haskell2010 + + Main-Is: Main.hs + + Ghc-Options: -Wall + + Build-Depends: base >= 4 && < 5, + bytestring, + containers, + -- Cabal >= 1.25 && <1.27, + Cabal >= 1.24 && <1.27, + directory, + filepath, + ghc diff --git a/utils/check-ppr/ghc.mk b/utils/check-ppr/ghc.mk new file mode 100644 index 0000000000..189b447171 --- /dev/null +++ b/utils/check-ppr/ghc.mk @@ -0,0 +1,18 @@ +# ----------------------------------------------------------------------------- +# +# (c) 2009 The University of Glasgow +# +# This file is part of the GHC build system. +# +# To understand how the build system works and how to modify it, see +# http://ghc.haskell.org/trac/ghc/wiki/Building/Architecture +# http://ghc.haskell.org/trac/ghc/wiki/Building/Modifying +# +# ----------------------------------------------------------------------------- + +utils/check-ppr_USES_CABAL = YES +utils/check-ppr_PACKAGE = check-ppr +utils/check-ppr_dist-install_PROGNAME = check-ppr +utils/check-ppr_dist-install_INSTALL = NO +utils/check-ppr_dist-install_INSTALL_INPLACE = YES +$(eval $(call build-prog,utils/check-ppr,dist-install,2)) |