zeroth 2008.10.28 → 2009.6.23
raw patch · 16 files changed
+573/−297 lines, 16 filesdep +Cabaldep +derivedep +filepathdep ~basedep ~haskell-src-extsbuild-type:Customsetup-changednew-uploader
Dependencies added: Cabal, derive, filepath, hskeleton, monoid-record, syb, template-haskell
Dependency ranges changed: base, haskell-src-exts
Files
- Distribution/ZeroTH.hs +40/−0
- LICENSE +2/−1
- Language/Haskell/TH/ZeroTH.hs +241/−0
- Language/Haskell/TH/ZeroTH/Comments.hs +25/−0
- Language/Haskell/TH/ZeroTH/Config.hs +13/−0
- Language/Haskell/TH/ZeroTH/Helper.hs +20/−0
- ListUtils.hs +13/−0
- Main.hs +9/−0
- Setup.hs +0/−2
- Setup.lhs +4/−0
- ppsrc/Language/Haskell/TH/ZeroTH/GetOpt.hs +176/−0
- src/Comments.hs +0/−23
- src/Main.hs +0/−30
- src/Setup.hs +0/−114
- src/Zeroth.hs +0/−113
- zeroth.cabal +30/−14
+ Distribution/ZeroTH.hs view
@@ -0,0 +1,40 @@+module Distribution.ZeroTH where++import Data.Foldable ( fold )+import Data.Monoid ( mempty )+import Distribution.Package ( PackageIdentifier(..) )+import Distribution.PackageDescription ( PackageDescription(..) )+import Distribution.Simple ( Args, defaultMainWithHooks, simpleUserHooks, postConf )+import Distribution.Simple.LocalBuildInfo ( LocalBuildInfo )+import Distribution.Simple.Setup ( ConfigFlags(..) )+import System.Directory ( createDirectoryIfMissing )+import System.FilePath ( (</>), takeDirectory )++import Language.Haskell.TH.ZeroTH ( zeroTH )+import Language.Haskell.TH.ZeroTH.GetOpt ( mkConfig, tempCpphsArgs', tempCpphsPath', tempDropImport', TempFlags, tempGHCArgs', tempGHCPath', tempInputFile', tempOutputFile' )++zeroTHCabalMain :: Maybe [String] -> [String] -> [FilePath] -> IO ()+zeroTHCabalMain dropIm extraCpphsArgs relevantFiles+ = defaultMainWithHooks $ simpleUserHooks { postConf = zeroTHPostConf dropIm extraCpphsArgs relevantFiles }++zeroTHPostConf :: Maybe [String] -> [String] -> [FilePath] -> Args -> ConfigFlags -> PackageDescription -> LocalBuildInfo -> IO ()+zeroTHPostConf dropIm extraCpphsArgs relevantFiles _+ (ConfigFlags { configProgramPaths = paths, configProgramArgs = args })+ (PackageDescription { package = PackageIdentifier { pkgVersion = version } })+ _ = let ppFlags = tempCpphsArgs' (("-Dversion=" ++ show version) : extraCpphsArgs ++ fold (lookup "cpphs" args))+ . maybe id tempCpphsPath' (lookup "cpphs" paths)+ . maybe id tempDropImport' dropIm+ . tempGHCArgs' ("--make" : fold (lookup "ghc" args))+ $ maybe id tempGHCPath' (lookup "ghc" paths) mempty+ in mapM_ (ppZeroTH ppFlags) relevantFiles++-- | Run zeroTH as a preprocessor on the given file+ppZeroTH :: TempFlags -> FilePath -> IO ()+ppZeroTH flags inputFile = do+ let outputFile = ppOutputDir </> inputFile+ createDirectoryIfMissing True $ takeDirectory outputFile+ zeroTH =<< mkConfig (tempInputFile' inputFile $ tempOutputFile' outputFile flags)++-- | The directory in which generated files are written+ppOutputDir :: FilePath+ppOutputDir = "ppsrc"
LICENSE view
@@ -1,4 +1,5 @@-Copyright (c) 2004-2005, David Himmelstrup+Copyright (c) 2004-2008, David Himmelstrup+Copyright (c) 2009, Robin Green All rights reserved. Redistribution and use in source and binary forms,
+ Language/Haskell/TH/ZeroTH.hs view
@@ -0,0 +1,241 @@+module Language.Haskell.TH.ZeroTH+ ( prettyPrintAll, zeroTH, zeroTHInternal+ ) where++import Language.Haskell.Exts+import System.Process ( runInteractiveProcess, waitForProcess )+import System.IO ( hClose, hGetContents, hPutStr, hPutStrLn, openTempFile, stderr )+import System.Directory ( removeFile, getTemporaryDirectory )+import System.Exit ( ExitCode (..) )+import Control.Applicative ( (<$>), (<*>) )+import Control.Monad ( guard, when )+import Data.Foldable ( fold )+import Data.Generics.Aliases ( mkT )+import Data.Generics.Schemes ( everywhere, listify )+import Data.List ( (\\), delete, intersperse, isInfixOf, isPrefixOf, nub, stripPrefix )+import Data.Maybe ( catMaybes, fromMaybe, mapMaybe )++import Language.Haskell.TH.ZeroTH.Config ( Config(..) )+import Language.Haskell.TH.ZeroTH.Comments ( Location, parseComments, mixComments )+import Language.Haskell.TH.ZeroTH.Helper ( idPrefix )+import ListUtils ( replaceAll )++readFromFile :: FilePath -> IO String+readFromFile "-" = getContents+readFromFile path = readFile path++writeToFile :: FilePath -> String -> IO ()+writeToFile "-" d = putStr d+writeToFile path d = writeFile path d++zeroTH :: Config -> IO ()+zeroTH = (=<<) . writeToFile . outputFile <*> (prettyPrintAll <$>) . zeroTHInternal++data ZeroTHOutput+ = ZeroTHOutput { originalSource :: String+ , combinedOutput :: Module+ , thOutput :: [(Location, String)]+ }++zeroTHInternal :: Config -> IO ZeroTHOutput+zeroTHInternal c+ = do input <- readFromFile $ inputFile c+ tmpDir <- getTemporaryDirectory+ (inputFile2, tmpHandle) <- case inputFile c of+ "-" -> openTempFile tmpDir "TH.cpphs.zeroth"+ _ -> return (inputFile c, undefined)+ when (inputFile c == "-") $ hPutStr tmpHandle input >> hClose tmpHandle+ let exts = readExtensions input+ hPutStrLn stderr $ "extensions: " ++ show exts+ let firstLine = head $ lines input+ shouldRunCpphs = "-cpp" `elem` ghcArgs c || " -cpp " `isInfixOf` firstLine || CPP `elem` (fold exts)+ thInput <- if shouldRunCpphs then preprocessCpphs (cpphsPath c) (["--noline","-DHASTH"]++cpphsArgs c) inputFile2+ else return input+ zerothInput <- if shouldRunCpphs then preprocessCpphs (cpphsPath c) ("--noline" : cpphsArgs c) inputFile2+ else return input+ (thData, qualImports) <- case parseFileContents thInput of+ ParseOk m -> unzip <$> runTH (ghcPath c) ((if wholeFile c then id else onlySplices) m) (ghcArgs c)+ e -> error $ show e ++ '\n' : thInput+ let reattach :: [Decl] -> [Decl]+ reattach (SpliceDecl sLoc _ : t) = (parseDecls . fromMaybe err $ lookup (location sLoc) thData) ++ t+ where+ err = error $ "Could not find splice at " ++ show (location sLoc) ++ " in " ++ show thData+ reattach x = x+ combinedData <- case parseFileContents zerothInput of+ ParseOk (Module loc m pragmas mWarn exports im decls)+ -> return (Module loc m pragmas mWarn exports (postProcessImports (dropImport c) im $ concat qualImports)+ (everywhere (mkT reattach) decls))+ e -> error $ show e ++ '\n' : zerothInput+ when (inputFile c == "-") $ removeFile inputFile2+ return ZeroTHOutput { originalSource = input+ , combinedOutput = combinedData+ , thOutput = thData+ }+ where parseDecls s = case parseFileContents s of+ ParseOk (Module _ _ _ _ _ _ decls) -> decls+ e -> error $ show e ++ '\n' : s+ onlySplices (Module loc m pragmas mWarn exports im decls) = Module loc m pragmas mWarn exports im $ listify isSplice decls+ isSplice (SpliceDecl _ _) = True+ isSplice _ = False++prettyPrintAll :: ZeroTHOutput -> String+prettyPrintAll out = unlines . mixComments (parseComments $ originalSource out) . numberAndPrettyPrint $ combinedOutput out++location :: SrcLoc -> Location+location sLoc = (srcLine sLoc, srcColumn sLoc)++numberAndPrettyPrint :: Module -> [(Location, String)]+numberAndPrettyPrint (Module mLoc m prags mbWarn exports imp decls)+ = (nAndPPrag =<< prags)+ ++ (location mLoc, concat $ "module "+ : prettyPrint m+ : catMaybes [ ppWarnText <$> mbWarn+ , (\es -> " (" ++ concat (intersperse ", " $ prettyPrint <$> es) ++ ")") <$> exports+ ]+ ++ [" where"])+ : (((\i -> (location (importLoc i), prettyPrint i)) <$> imp) ++ (nAndPDec =<< decls))+ where nAndPDec d@(TypeDecl loc _ _ _) = [(location loc, prettyPrint d)]+ nAndPDec d@(DataDecl loc _ _ _ _ _ _) = [(location loc, prettyPrint d)]+ nAndPDec d@(GDataDecl loc _ _ _ _ _ _ _) = [(location loc, prettyPrint d)]+ nAndPDec d@(InfixDecl loc _ _ _) = [(location loc, prettyPrint d)]+ nAndPDec d@(ClassDecl loc _ _ _ _ _) = [(location loc, prettyPrint d)]+ nAndPDec d@(InstDecl loc _ _ _ _) = [(location loc, prettyPrint d)]+ nAndPDec d@(DefaultDecl loc _) = [(location loc, prettyPrint d)]+ nAndPDec d@(SpliceDecl loc _) = [(location loc, prettyPrint d)]+ nAndPDec d@(TypeSig loc _ _) = [(location loc, prettyPrint d)]+ nAndPDec (FunBind matches) = (\match@(Match loc _ _ _ _ _) -> (location loc, prettyPrint match)) <$> matches+ nAndPDec d@(PatBind loc _ _ _ _) = [(location loc, prettyPrint d)]+ nAndPDec d@(ForImp loc _ _ _ _ _) = [(location loc, prettyPrint d)]+ nAndPDec d@(ForExp loc _ _ _ _) = [(location loc, prettyPrint d)]+ nAndPDec d@(DataFamDecl loc _ _ _ _) = [(location loc, prettyPrint d)]+ nAndPDec d@(DataInsDecl loc _ _ _ _) = [(location loc, prettyPrint d)]+ nAndPDec d@(DeprPragmaDecl loc _) = [(location loc, prettyPrint d)]+ nAndPDec d@(DerivDecl loc _ _ _) = [(location loc, prettyPrint d)]+ nAndPDec d@(GDataInsDecl loc _ _ _ _ _) = [(location loc, prettyPrint d)]+ nAndPDec d@(InlineSig loc _ _ _) = [(location loc, prettyPrint d)]+ nAndPDec d@(InstSig loc _ _ _) = [(location loc, prettyPrint d)]+ nAndPDec d@(RulePragmaDecl loc _) = [(location loc, prettyPrint d)]+ nAndPDec d@(SpecInlineSig loc _ _ _ _) = [(location loc, prettyPrint d)]+ nAndPDec d@(SpecSig loc _ _) = [(location loc, prettyPrint d)]+ nAndPDec d@(TypeFamDecl loc _ _ _) = [(location loc, prettyPrint d)]+ nAndPDec d@(TypeInsDecl loc _ _) = [(location loc, prettyPrint d)]+ nAndPDec d@(WarnPragmaDecl loc _) = [(location loc, prettyPrint d)]+ nAndPPrag (LanguagePragma loc names)+ | null filteredNames = []+ | otherwise = [(location loc, prettyPrint $ LanguagePragma loc filteredNames)]+ where+ filteredNames = names \\ (Ident <$> unwantedLanguageOptions)+ nAndPPrag p@(IncludePragma loc _) = [(location loc, prettyPrint p)]+ nAndPPrag p@(CFilesPragma loc _) = [(location loc, prettyPrint p)]+ nAndPPrag (OptionsPragma loc mt s) = [(location loc, prettyPrint . OptionsPragma loc mt $ filterOptions s)]+ filterOptions optStr = foldr (\opt -> replaceAll (" -" ++ opt ++ " ") " ") optStr $ "cpp" : "fth" : (('X' :) <$> unwantedLanguageOptions)+ unwantedLanguageOptions = ["CPP", "TemplateHaskell"]++ppWarnText :: WarningText -> String+ppWarnText (DeprText s) = "{-# DEPRECATED" ++ s ++ "#-}"+ppWarnText (WarnText s) = "{-# WARNING" ++ s ++ "#-}"++-- Removes TH imports, and adds any qualified imports needed by generated TH code+postProcessImports :: [String] -> [ImportDecl] -> [String] -> [ImportDecl]+postProcessImports dropPrefixes oldImports qNames+ = nub $ removeTH+ ++ mapMaybe (\q -> do guard . not $ any (maybe False (\(ModuleName m) -> m == q) . importAs) removeTH+ return ImportDecl { importLoc = emptySrcLoc+ , importModule = ModuleName q+ , importQualified = True+ , importSrc = False+ , importAs = Nothing+ , importPkg = Nothing+ , importSpecs = Nothing })+ qNames+ where+ removeTH = filter (not . (\(ModuleName m) -> any (`isPrefixOf` m) dropPrefixes) . importModule) oldImports++preprocessCpphs :: FilePath -- ^ Path to cpphs+ -> [String]+ -> String+ -> IO String+preprocessCpphs cpphs args inputFilename+ = do (inH,outH,_,pid) <- runInteractiveProcess cpphs (inputFilename:args) Nothing Nothing+ hClose inH+ output <- hGetContents outH+ length output `seq` hClose outH+ eCode <- waitForProcess pid+ case eCode of+ ExitFailure err -> error $ "Failed to run cpphs: " ++ show err+ ExitSuccess -> return output++runTH :: FilePath -- ^ Path to GHC+ -> Module + -> [String]+ -> IO ([((Location,String),[String])])+runTH ghc (Module _ _ pragmas _ _ imports decls) ghcOpts+ = do tmpDir <- getTemporaryDirectory+ (tmpInPath,tmpInHandle) <- openTempFile tmpDir "TH.source.zeroth.hs"+ hPutStr tmpInHandle realM+ hClose tmpInHandle+ let args = [tmpInPath]++ghcOpts++extraOpts+ --putStrLn $ "Module:\n" ++ realM+ --putStrLn $ "Running: " ++ unwords (ghc:args)+ (inH,outH,errH,pid) <- runInteractiveProcess ghc args Nothing Nothing+ hClose inH+ output <- hGetContents outH+ --putStrLn $ "TH Data:\n" ++ output+ length output `seq` hClose outH+ errMsg <- hGetContents errH+ length errMsg `seq` hClose errH+ eCode <- waitForProcess pid+ -- removeFile tmpInPath+ let check :: [(((Location,String),[String]), String)] -> ((Location,String),[String])+ check [(ret,_)] = ret+ check _ = error $ "Failed to parse result:\n" ++ output+ case eCode of+ ExitFailure err -> error (unwords (ghc:args) ++ ": failure: " ++ show err ++ ":\n" ++ errMsg)+ ExitSuccess -> return . mapMaybe (fmap (check . reads) . stripPrefix idPrefix) $ lines output+ where pp :: (Pretty a) => a -> String+ pp = prettyPrintWithMode (defaultMode{layout = PPInLine})+ realM = unlines $ (pp . disableWarnings <$> pragmas)+ ++ ["module ZerothTemp where"]+ ++ (pp <$> imports)+ ++ ["import qualified " ++ helperModule]+ ++ (prettyPrint <$> everywhere (mkT editSplice) decls)+ helperModule = "Language.Haskell.TH.ZeroTH.Helper"+ editSplice :: Decl -> Decl+ editSplice (SpliceDecl loc splice)+ = SpliceDecl loc+ . ParenSplice+ . App (App (Var . Qual (ModuleName helperModule) $ Ident "helper")+ (Paren $ spliceToExp splice))+ . Tuple+ $ Lit . Int . fromIntegral <$> [ srcLine loc, srcColumn loc ]+ editSplice x = x+ spliceToExp (ParenSplice e) = e+ spliceToExp _ = error "TH: FIXME!"+ extraOpts = ["-w"]+ extraOpts' = (' ' :) =<< extraOpts+ disableWarnings (OptionsPragma loc Nothing s) = OptionsPragma loc Nothing $ s ++ extraOpts' -- Turn off all warnings (works for GHC)+ disableWarnings (OptionsPragma loc (Just GHC) s) = OptionsPragma loc (Just GHC) $ s ++ extraOpts'+ disableWarnings (LanguagePragma loc xs) = LanguagePragma loc $ delete (Ident "CPP") xs+ disableWarnings x = x++emptySrcLoc :: SrcLoc+emptySrcLoc = SrcLoc "" 0 0++{-++module Test where+$(test) -- line 2+$(jalla) -- line 3+svend = svend+++-------------------------------++module Main where+import Language.Haskell.TH+main = do decs <- sequence [runQ test+ ,runQ jalla]+ mapM_ (putStrLn.pprint) (zip decs [2,3])+++-}
+ Language/Haskell/TH/ZeroTH/Comments.hs view
@@ -0,0 +1,25 @@+module Language.Haskell.TH.ZeroTH.Comments where++type Location = (Int, Int)++type Comment = (Location,String)++mixComments :: [Comment] -> [(Location,String)] -> [String]+mixComments c [] = map (showComment.lines.snd) c+mixComments [] i = map snd i+mixComments c@(((cl, _),cc):cs) i@(((il, _),ic):is)+ | cl <= il = showComment (lines cc):mixComments cs i+ | otherwise = ic:mixComments c is++showComment :: [String] -> String+showComment [x] = '-':'-':' ':x+showComment c = "{- " ++ unlines c ++ " -}"++-- FIXME!+parseComments :: String -> [Comment]+parseComments input = parseComments' (lines input) 1++parseComments' :: [String] -> Int -> [Comment]+parseComments' [] _ = []+parseComments' (('-':'-':c):xs) l = ((l, 1), c):parseComments' xs (l+1)+parseComments' (_:xs) l = parseComments' xs (l+1)
+ Language/Haskell/TH/ZeroTH/Config.hs view
@@ -0,0 +1,13 @@+module Language.Haskell.TH.ZeroTH.Config where++data Config+ = Config+ { ghcPath :: FilePath+ , cpphsPath :: FilePath+ , inputFile :: FilePath+ , outputFile :: FilePath+ , ghcArgs :: [String]+ , cpphsArgs :: [String]+ , dropImport :: [String]+ , wholeFile :: Bool+ } deriving Show
+ Language/Haskell/TH/ZeroTH/Helper.hs view
@@ -0,0 +1,20 @@+module Language.Haskell.TH.ZeroTH.Helper ( helper, idPrefix ) where++import Control.Applicative ( (<$>) )+import Data.Generics.Schemes ( listify )+import Data.Maybe ( fromJust, isJust )+import Language.Haskell.TH.Ppr ( pprint )+import Language.Haskell.TH.Syntax ( Dec, nameModule, Q, runIO )+import System.IO ( hFlush, stdout )++import Language.Haskell.TH.ZeroTH.Comments ( Location )++idPrefix :: String+idPrefix = "ZEROTH OUTPUT: "++helper :: Q [Dec] -> Location -> Q [Dec]+helper splice loc = do+ decls <- splice+ runIO $ do putStrLn $ idPrefix ++ show ((loc, unlines $ pprint <$> decls), map (fromJust . nameModule) $ listify (isJust . nameModule) decls)+ hFlush stdout+ return decls
+ ListUtils.hs view
@@ -0,0 +1,13 @@+module ListUtils where++import Data.List ( intercalate, isPrefixOf, unfoldr )++-- | Replace a substring with a replacement string throughout a list+replaceAll :: Eq a => [a] -> [a] -> [a] -> [a]+replaceAll [] newSub = intercalate newSub . map return+replaceAll oldSub newSub = concat . unfoldr replace+ where+ replace list = do+ (h:t) <- return list+ return $ if oldSub `isPrefixOf` list then (newSub, drop len list) else ([h], t)+ len = length oldSub
+ Main.hs view
@@ -0,0 +1,9 @@+module Main ( main ) where++import Language.Haskell.TH.ZeroTH.GetOpt ( myParseArgs, mkConfig )+import Language.Haskell.TH.ZeroTH ( zeroTH )++import System.Environment ( getArgs )++main :: IO ()+main = zeroTH =<< mkConfig =<< myParseArgs =<< getArgs
− Setup.hs
@@ -1,2 +0,0 @@-import Distribution.Simple-main = defaultMain
+ Setup.lhs view
@@ -0,0 +1,4 @@+#!/usr/bin/env runhaskell+> import Distribution.ZeroTH+> main = zeroTHCabalMain (Just ["Data.Derive"]) ["--hashes"] ["Language/Haskell/TH/ZeroTH/GetOpt.hs"]+
+ ppsrc/Language/Haskell/TH/ZeroTH/GetOpt.hs view
@@ -0,0 +1,176 @@+{-# OPTIONS_GHC+ -Wall -fno-warn-missing-signatures -pgmP cpphs -optP --hashes -optP --cpp + #-}+module Language.Haskell.TH.ZeroTH.GetOpt where+import Control.Applicative ((<$>))+import Data.List (isPrefixOf)+import Data.Maybe (fromMaybe)+import Data.Monoid (Any(..), Last(..), Monoid(..))+import Data.Monoid.Record (addP)+import System.Console.GetOpt (ArgDescr(..), OptDescr(..))+import System.Console.GetOpt.Skeleton (mParseArgs)+import System.Console.GetOpt.StandardOpts (StandardFlag, stdOpts)+import System.Directory (findExecutable)+import System.Info (os)+import Distribution.Version (Version(..))+import Language.Haskell.TH.ZeroTH.Config (Config(..))+ +getExecutable :: String -> Maybe FilePath -> IO FilePath+getExecutable _ (Just path) = return path+getExecutable name (Nothing)+ = fromMaybe (error errMsg) <$> findExecutable name+ where errMsg = "Couldn't find: " ++ name+ +mkConfig :: TempFlags -> IO Config+mkConfig tmpFlags+ = do ghcPath' <- getExecutable "ghc" . getLast $+ tempGHCPath tmpFlags+ cpphsPath' <- getExecutable "cpphs" . getLast $+ tempCpphsPath tmpFlags+ return+ Config{ghcPath = ghcPath', cpphsPath = cpphsPath',+ inputFile = fromMaybe "-" . getLast $ tempInputFile tmpFlags,+ outputFile = fromMaybe "-" . getLast $ tempOutputFile tmpFlags,+ ghcArgs = tempGHCArgs tmpFlags `orElse` defaultGhcArgs,+ cpphsArgs = tempCpphsArgs tmpFlags,+ dropImport = tempDropImport tmpFlags `orElse` defaultDrop,+ wholeFile = not . getAny $ tempJustSplices tmpFlags}+ where defaultGhcArgs+ = ["-fno-code", "-o", nullFile, "-ohi", nullFile]+ nullFile+ | "mingw" `isPrefixOf` os = "NUL:"+ | otherwise = "/dev/null"+ defaultDrop = ["Language.Haskell.TH"]+ +orElse :: [] a -> [] a -> [] a+orElse [] theDefault = theDefault+orElse x _ = x+ +data TempFlags = TempFlags{tempGHCPath :: Last FilePath,+ tempInputFile :: Last FilePath, tempOutputFile :: Last FilePath,+ tempCpphsPath :: Last FilePath, tempGHCArgs :: [] String,+ tempCpphsArgs :: [] String, tempDropImport :: [] String,+ tempJustSplices :: Any, tempStdFlag :: Last StandardFlag}+ +instance Monoid TempFlags where+ mempty+ = TempFlags mempty mempty mempty mempty mempty mempty mempty mempty+ mempty+ mappend (TempFlags x1 x2 x3 x4 x5 x6 x7 x8 x9)+ (TempFlags y1 y2 y3 y4 y5 y6 y7 y8 y9)+ = TempFlags (mappend x1 y1) (mappend x2 y2) (mappend x3 y3)+ (mappend x4 y4)+ (mappend x5 y5)+ (mappend x6 y6)+ (mappend x7 y7)+ (mappend x8 y8)+ (mappend x9 y9)+setTempGHCPath a0 b0+ = TempFlags a0 (tempInputFile b0) (tempOutputFile b0)+ (tempCpphsPath b0)+ (tempGHCArgs b0)+ (tempCpphsArgs b0)+ (tempDropImport b0)+ (tempJustSplices b0)+ (tempStdFlag b0)+setTempInputFile a0 b0+ = TempFlags (tempGHCPath b0) a0 (tempOutputFile b0)+ (tempCpphsPath b0)+ (tempGHCArgs b0)+ (tempCpphsArgs b0)+ (tempDropImport b0)+ (tempJustSplices b0)+ (tempStdFlag b0)+setTempOutputFile a0 b0+ = TempFlags (tempGHCPath b0) (tempInputFile b0) a0+ (tempCpphsPath b0)+ (tempGHCArgs b0)+ (tempCpphsArgs b0)+ (tempDropImport b0)+ (tempJustSplices b0)+ (tempStdFlag b0)+setTempCpphsPath a0 b0+ = TempFlags (tempGHCPath b0) (tempInputFile b0) (tempOutputFile b0)+ a0+ (tempGHCArgs b0)+ (tempCpphsArgs b0)+ (tempDropImport b0)+ (tempJustSplices b0)+ (tempStdFlag b0)+setTempGHCArgs a0 b0+ = TempFlags (tempGHCPath b0) (tempInputFile b0) (tempOutputFile b0)+ (tempCpphsPath b0)+ a0+ (tempCpphsArgs b0)+ (tempDropImport b0)+ (tempJustSplices b0)+ (tempStdFlag b0)+setTempCpphsArgs a0 b0+ = TempFlags (tempGHCPath b0) (tempInputFile b0) (tempOutputFile b0)+ (tempCpphsPath b0)+ (tempGHCArgs b0)+ a0+ (tempDropImport b0)+ (tempJustSplices b0)+ (tempStdFlag b0)+setTempDropImport a0 b0+ = TempFlags (tempGHCPath b0) (tempInputFile b0) (tempOutputFile b0)+ (tempCpphsPath b0)+ (tempGHCArgs b0)+ (tempCpphsArgs b0)+ a0+ (tempJustSplices b0)+ (tempStdFlag b0)+setTempJustSplices a0 b0+ = TempFlags (tempGHCPath b0) (tempInputFile b0) (tempOutputFile b0)+ (tempCpphsPath b0)+ (tempGHCArgs b0)+ (tempCpphsArgs b0)+ (tempDropImport b0)+ a0+ (tempStdFlag b0)+setTempStdFlag a0 b0+ = TempFlags (tempGHCPath b0) (tempInputFile b0) (tempOutputFile b0)+ (tempCpphsPath b0)+ (tempGHCArgs b0)+ (tempCpphsArgs b0)+ (tempDropImport b0)+ (tempJustSplices b0)+ a0+-- XXX: Use Data.Derive to generate these instead+tempGHCPath' = addP tempGHCPath setTempGHCPath+tempInputFile' = addP tempInputFile setTempInputFile+tempOutputFile' = addP tempOutputFile setTempOutputFile+tempCpphsPath' = addP tempCpphsPath setTempCpphsPath+tempGHCArgs' = addP tempGHCArgs setTempGHCArgs+tempCpphsArgs' = addP tempCpphsArgs setTempCpphsArgs+tempDropImport' = addP tempDropImport setTempDropImport+tempJustSplices' = addP tempJustSplices setTempJustSplices+tempStdFlag' = addP tempStdFlag setTempStdFlag+ +globalOptions :: [] (OptDescr (TempFlags -> TempFlags))+globalOptions+ = stdOpts tempStdFlag' +++ [Option "" ["only-splices"] (NoArg $ tempJustSplices' True)+ "Only pass the splices to GHC, not the whole file (for faster processing)",+ Option "w" ["ghc"] (ReqArg tempGHCPath' "PATH") "Use this GHC",+ Option "" ["cpphs"] (ReqArg tempCpphsPath' "PATH")+ "Use this cpphs",+ Option "i" ["input"] (ReqArg tempInputFile' "PATH") "Input file",+ Option "o" ["output"] (ReqArg tempOutputFile' "PATH")+ "Output file",+ Option "" ["ghc-args"] (ReqArg (tempGHCArgs' . words) "Arguments")+ "Arguments to GHC",+ Option "" ["cpphs-args"]+ (ReqArg (tempCpphsArgs' . words) "Arguments")+ "Arguments to cpphs",+ Option "d" ["drop-import"]+ (ReqArg (tempDropImport' . words) "Prefix")+ "Any import that starts with this prefix will be removed"]+ +myParseArgs :: [] String -> IO TempFlags+myParseArgs+ = mParseArgs+ Version{versionBranch = [2009, 6, 23], versionTags = []}+ globalOptions+ tempStdFlag
− src/Comments.hs
@@ -1,23 +0,0 @@-module Comments where--type Comment = (Int,String)--mixComments :: [Comment] -> [(Int,String)] -> [String]-mixComments c [] = map (showComment.lines) (map snd c)-mixComments [] i = map snd i-mixComments c@((cl,cc):cs) i@((il,ic):is)- | cl <= il = showComment (lines cc):mixComments cs i- | otherwise = ic:mixComments c is--showComment :: [String] -> String-showComment [x] = '-':'-':' ':x-showComment c = "{- " ++ unlines c ++ " -}"---- FIXME!-parseComments :: String -> [Comment]-parseComments input = parseComments' (lines input) 1--parseComments' :: [String] -> Int -> [Comment]-parseComments' [] _ = []-parseComments' (('-':'-':c):xs) l = (l,c):parseComments' xs (l+1)-parseComments' (_:xs) l = parseComments' xs (l+1)
− src/Main.hs
@@ -1,30 +0,0 @@-module Main ( main ) where--import Zeroth ( zeroth )-import Setup ( parseArgs, ConfigFlags (..), mkConfigFlags )--import System.Environment ( getArgs )-import System.IO ( hGetContents, stdin, stdout, hPutStr )--readFromFile :: FilePath -> IO String-readFromFile "-" = hGetContents stdin-readFromFile path = readFile path--writeToFile :: FilePath -> String -> IO ()-writeToFile "-" d = hPutStr stdout d-writeToFile path d = writeFile path d----main :: IO ()-main = do args <- getArgs- configFlags <- mkConfigFlags =<< parseArgs args- input <- readFromFile (configInputFile configFlags)- output <- zeroth (configGHCPath configFlags)- (configCpphsPath configFlags)- (configGHCArgs configFlags)- (configCpphsArgs configFlags)- input- writeToFile (configOutputFile configFlags) output--
− src/Setup.hs
@@ -1,114 +0,0 @@-module Setup where--import System.Console.GetOpt ( ArgDescr (..), OptDescr (..), usageInfo, getOpt', ArgOrder (..) )-import System.Environment ( getProgName )-import System.Exit ( exitWith, ExitCode (..) )-import System.Directory ( findExecutable )-import Data.Maybe ( fromMaybe )--data ConfigFlags- = ConfigFlags- { configGHCPath :: FilePath- , configInputFile :: FilePath- , configOutputFile :: FilePath- , configCpphsPath :: FilePath- , configGHCArgs :: [String]- , configCpphsArgs :: [String]- } deriving Show--getExecutable :: String -> Maybe FilePath -> IO FilePath-getExecutable _ (Just path) = return path-getExecutable name Nothing = fmap (fromMaybe (error errMsg)) (findExecutable name)- where errMsg = "Couldn't find: "++name- - --mkConfigFlags :: TempFlags -> IO ConfigFlags-mkConfigFlags tmpFlags- = do ghcPath <- getExecutable "ghc" (tempGHCPath tmpFlags)- cpphsPath <- getExecutable "cpphs" (tempCpphsPath tmpFlags)- return (ConfigFlags- { configGHCPath = ghcPath- , configCpphsPath = cpphsPath- , configInputFile = tempInputFile tmpFlags- , configOutputFile = tempOutputFile tmpFlags- , configGHCArgs = tempGHCArgs tmpFlags- , configCpphsArgs = tempCpphsArgs tmpFlags})--data TempFlags- = TempFlags- { tempGHCPath :: Maybe FilePath- , tempInputFile :: FilePath- , tempOutputFile :: FilePath- , tempCpphsPath :: Maybe FilePath- , tempGHCArgs :: [String]- , tempCpphsArgs :: [String]- } deriving Show--data Flag- = WithGHC FilePath- | InputFile FilePath- | OutputFile FilePath- | WithCpphs FilePath- | GHCArgs String- | CpphsArgs String- | HelpFlag---- We don't want to use elem, because that imposes Eq a-hasHelpFlag :: [Flag] -> Bool-hasHelpFlag flags = not . null $ [ () | HelpFlag <- flags ]---emptyTempFlags :: TempFlags-emptyTempFlags =- TempFlags- { tempGHCPath = Nothing- , tempInputFile = "-"- , tempOutputFile = "-"- , tempCpphsPath = Nothing- , tempGHCArgs = []- , tempCpphsArgs = []- }---globalOptions :: [OptDescr Flag]-globalOptions =- [ Option "h?" ["help"] (NoArg HelpFlag) "Show this help text"- , Option "w" ["ghc"] (ReqArg WithGHC "PATH") "Use this GHC"- , Option "" ["cpphs"] (ReqArg WithCpphs "PATH") "Use this cpphs"- , Option "i" ["input"] (ReqArg InputFile "PATH") "Input file"- , Option "o" ["output"] (ReqArg OutputFile "PATH") "Output file"- , Option "" ["ghc-args"] (ReqArg GHCArgs "Arguments") "Arguments to GHC"- , Option "" ["cpphs-args"] (ReqArg CpphsArgs "Arguments") "Arguments to cpphs"- ]--printHelp :: IO ()-printHelp =- do pname <- getProgName- let syntax_line = concat [ "Usage: ", pname- , " [FLAGS]\n"]- putStrLn (usageInfo syntax_line globalOptions)--parseArgs :: [String] -> IO TempFlags-parseArgs args =- case getOpt' RequireOrder globalOptions args of- (flags, _, _, []) | hasHelpFlag flags -> do- printHelp- exitWith ExitSuccess- (flags, [], _, []) -> return (mkTempFlags flags emptyTempFlags)- (_, _, _, errs) -> do putStrLn "Errors:"- mapM_ putStrLn errs- exitWith (ExitFailure 1)--mkTempFlags :: [Flag] -> TempFlags -> TempFlags-mkTempFlags = updateCfg- where updateCfg [] t = t- updateCfg (fl:flags) t = updateCfg flags $- case fl of- WithGHC path -> t { tempGHCPath = Just path }- InputFile path -> t { tempInputFile = path }- OutputFile path -> t { tempOutputFile = path }- WithCpphs path -> t { tempCpphsPath = Just path }- GHCArgs args -> t { tempGHCArgs = tempGHCArgs t ++ words args }- CpphsArgs args -> t { tempCpphsArgs = tempCpphsArgs t ++ words args }- _ -> error $ "Unexpected flag!"
− src/Zeroth.hs
@@ -1,113 +0,0 @@-module Zeroth- ( zeroth- ) where--import Language.Haskell.Exts hiding ( comments )-import System.Process ( runInteractiveProcess, waitForProcess )-import System.IO ( hPutStr, hClose, hGetContents, openTempFile )-import System.Directory ( removeFile, getTemporaryDirectory )-import System.Exit ( ExitCode (..) )-import Data.Maybe ( mapMaybe )--import Comments ( parseComments, mixComments )--zeroth :: FilePath -- ^ Path to GHC- -> FilePath -- ^ Path to cpphs- -> [String] -- ^ GHC options- -> [String] -- ^ cpphs options- -> String -- ^ Input data- -> IO String-zeroth ghcPath cpphsPath ghcOpts cpphsOpts input- = do thInput <- preprocessCpphs cpphsPath (["--noline","-DHASTH"]++cpphsOpts) input- zerothInput <- preprocessCpphs cpphsPath (["--noline"]++cpphsOpts) input- thData <- case parseModule thInput of- ParseOk m@(HsModule _ _ _ _ decls) -> runTH ghcPath m ghcOpts (mapMaybe getTH decls)- e -> error (show e)- zerothData <- case parseModule zerothInput of- ParseOk (HsModule loc m exports im decls) -> return (HsModule loc m exports im (filter delTH decls))- e -> error (show e)- return (prettyPrint zerothData ++ "\n" ++ unlines (mixComments comments thData))- where getTH (HsSpliceDecl l s) = Just (l,s)- getTH _ = Nothing- delTH (HsSpliceDecl _ _) = False- delTH _ = True- comments = parseComments input--preprocessCpphs :: FilePath -- ^ Path to cpphs- -> [String]- -> String- -> IO String-preprocessCpphs cpphs args input- = do tmpDir <- getTemporaryDirectory- (tmpPath,tmpHandle) <- openTempFile tmpDir "TH.cpphs.zeroth"- hPutStr tmpHandle input- hClose tmpHandle- (inH,outH,_,pid) <- runInteractiveProcess cpphs (tmpPath:args) Nothing Nothing- hClose inH- output <- hGetContents outH- length output `seq` hClose outH- eCode <- waitForProcess pid- removeFile tmpPath- case eCode of- ExitFailure err -> error $ "Failed to run cpphs: " ++ show err- ExitSuccess -> return output--runTH :: FilePath -- ^ Path to GHC- -> HsModule - -> [String]- -> [(SrcLoc,HsSplice)]- -> IO [(Int,String)]-runTH ghcPath (HsModule _ _ _ imports _) ghcOpts th- = do tmpDir <- getTemporaryDirectory- (tmpInPath,tmpInHandle) <- openTempFile tmpDir "TH.source.zeroth.hs"- hPutStr tmpInHandle realM- hClose tmpInHandle- let args = [tmpInPath,"-e","main","-fth","-fglasgow-exts"]++ghcOpts- --putStrLn $ "Module:\n" ++ realM- --putStrLn $ "Running: " ++ unwords (ghcPath:args)- (inH,outH,errH,pid) <- runInteractiveProcess ghcPath args Nothing Nothing- hClose inH- output <- hGetContents outH- --putStrLn $ "TH Data:\n" ++ output- length output `seq` hClose outH- errMsg <- hGetContents errH- length errMsg `seq` hClose errH- eCode <- waitForProcess pid- -- removeFile tmpInPath- case eCode of- ExitFailure err -> error (unwords (ghcPath:args) ++ ": failure: " ++ show err)- ExitSuccess | not (null errMsg) -> error (unwords (ghcPath:args) ++ ": failure:\n" ++ errMsg)- | otherwise -> case reads output of- [(ret,_)] -> return ret- _ -> error $ "Failed to parse result:\n"++output- where thImport = HsImportDecl emptySrcLoc (Module "Language.Haskell.TH") False Nothing Nothing- emptySrcLoc = SrcLoc "" 0 0- pp :: (Pretty a) => a -> String- pp = prettyPrintWithMode (defaultMode{layout = PPInLine})- realM = unlines $ ["module Main ( main ) where"]- ++ map pp (thImport:imports)- ++ ["main = do decls <- sequence $ " ++ pp (HsList splices)]- ++ [" print $ map (\\(l,d) -> (l,pprint d)) (zip "++ pp (HsList lineNums) ++" decls)"]- splices = flip map th $ \(_src,splice) -> HsApp (HsVar (UnQual (HsIdent "runQ"))) (HsParen (spliceToExp splice))- lineNums = flip map th $ \(loc,_splice) -> HsLit (HsInt (fromIntegral (srcLine loc)))- spliceToExp (HsParenSplice e) = e- spliceToExp _ = error "TH: FIXME!"--{---module Test where-$(test) -- line 2-$(jalla) -- line 3-svend = svend------------------------------------module Main where-import Language.Haskell.TH-main = do decs <- sequence [runQ test- ,runQ jalla]- mapM_ (putStrLn.pprint) (zip decs [2,3])----}
zeroth.cabal view
@@ -1,22 +1,38 @@ Name: zeroth-Version: 2008.10.28+Version: 2009.6.23+Cabal-version: >= 1.6 License: BSD3 License-file: LICENSE-Maintainer: Lemmih <lemmih@gmail.com>-Build-type: Simple-Build-depends: base, haskell-src-exts, directory, process-Copyright: 2005-2008 Lemmih <lemmih@gmail.com>+Maintainer: Robin Green <greenrd@greenrd.org>+Build-type: Custom+Copyright: 2005-2008 Lemmih <lemmih@gmail.com>, 2009 Robin Green <greenrd@greenrd.org> Category: Language-Synopsis: ZeroTH - remove unnecessary TH depdendencies.+Synopsis: ZeroTH - remove unnecessary TH dependencies Description: TemplateHaskell is fairly useful for generating new- Haskell programs. This, however, incur a dependency on+ Haskell code. This, however, can incur a dependency on TH on subsequent uses where none theoretically should exist. ZeroTH solves this by scanning a file for top-level TH- declarations, evaluates them, and then puts the generated- code back.+ declarations, evaluates them, removes TH imports, and outputs+ the resulting code.+Bug-reports: mailto:greenrd@greenrd.org+Stability: experimental+Tested-with: GHC == 6.10.3+Extra-tmp-files: ppsrc/Language/Haskell/TH/ZeroTH/GetOpt.hs -Executable: zeroth-Main-is: Main.hs-Other-modules: Comments, Setup, Zeroth-GHC-Options: -Wall-Hs-Source-Dirs: src+Source-repository head+ Type: darcs+ Location: http://patch-tag.com/publicrepos/zerothDarcs2++Library+ Build-depends: base >= 4, base < 5, haskell-src-exts >= 1.0.0, directory, process, syb, template-haskell, hskeleton >= 0.1, hskeleton < 0.2, derive, monoid-record >= 0.1, monoid-record < 0.2, Cabal >= 1.6, Cabal < 1.7, filepath+ Exposed-modules: Language.Haskell.TH.ZeroTH.Config, Language.Haskell.TH.ZeroTH.GetOpt, Language.Haskell.TH.ZeroTH, Language.Haskell.TH.ZeroTH.Helper, Language.Haskell.TH.ZeroTH.Comments, Distribution.ZeroTH+ Other-modules: ListUtils+ GHC-options: -Wall+ Hs-source-dirs: ppsrc, .++Executable zeroth+ Build-depends: base >= 4, base < 5, haskell-src-exts >= 1.0.0, directory, process, syb, template-haskell, hskeleton >= 0.1, hskeleton < 0.2, derive, monoid-record >= 0.1, monoid-record < 0.2, Cabal >= 1.6, Cabal < 1.7, filepath+ Main-is: Main.hs+ Other-modules: Language.Haskell.TH.ZeroTH.Comments, ListUtils, Language.Haskell.TH.ZeroTH.Config, Language.Haskell.TH.ZeroTH.GetOpt, Language.Haskell.TH.ZeroTH, Language.Haskell.TH.ZeroTH.Helper, Distribution.ZeroTH+ GHC-options: -Wall+ Hs-source-dirs: ppsrc, .