X-Git-Url: http://git.megacz.com/?a=blobdiff_plain;f=compiler%2Futils%2FUtil.lhs;h=862b46a8e152430679cf96ca01ba5f90b6bc5697;hb=7eb5e29b4a7b6fef55512bc7bf3308e712ca3eba;hp=3eff34b3d53824800da7fe69a8d9f9067944c511;hpb=046ee54f048ddd721dcee41916d6a6f68db3b15b;p=ghc-hetmet.git diff --git a/compiler/utils/Util.lhs b/compiler/utils/Util.lhs index 3eff34b..862b46a 100644 --- a/compiler/utils/Util.lhs +++ b/compiler/utils/Util.lhs @@ -5,6 +5,13 @@ \section[Util]{Highly random utility functions} \begin{code} +{-# OPTIONS -w #-} +-- The above warning supression flag is a temporary kludge. +-- While working on this module you are encouraged to remove it and fix +-- any warnings in the module. See +-- http://hackage.haskell.org/trac/ghc/wiki/Commentary/CodingStyle#Warnings +-- for details + module Util ( -- general list processing @@ -12,7 +19,8 @@ module Util ( zipLazy, stretchZipWith, mapFst, mapSnd, mapAndUnzip, mapAndUnzip3, - nOfThem, filterOut, + nOfThem, filterOut, partitionWith, splitEithers, + foldl1', lengthExceeds, lengthIs, lengthAtLeast, listLengthCmp, atLength, equalLength, compareLength, @@ -26,24 +34,23 @@ module Util ( nTimes, -- sorting - sortLe, sortWith, + sortLe, sortWith, on, -- transitive closures transitiveClosure, -- accumulating - mapAccumL, mapAccumR, mapAccumB, foldl2, count, all2, takeList, dropList, splitAtList, split, -- comparisons isEqual, eqListBy, - thenCmp, cmpList, prefixMatch, suffixMatch, maybePrefixMatch, + thenCmp, cmpList, maybePrefixMatch, removeSpaces, -- strictness - foldl', seqList, + seqList, -- pairs unzipWith, @@ -66,40 +73,38 @@ module Util ( later, handleDyn, handle, -- Filename utils - Suffix, - splitFilename, suffixOf, basenameOf, joinFileExt, - splitFilenameDir, joinFileName, - splitFilename3, + Suffix, splitLongestPrefix, - replaceFilenameSuffix, directoryOf, filenameOf, - replaceFilenameDirectory, - escapeSpaces, isPathSeparator, + escapeSpaces, parseSearchPath, - normalisePath, platformPath, pgmPath, ) where #include "HsVersions.h" -import Panic ( panic, trace ) import FastTypes +#if defined(DEBUG) || __GLASGOW_HASKELL__ < 604 +import Panic +#endif + import Control.Exception ( Exception(..), finally, catchDyn, throw ) import qualified Control.Exception as Exception import Data.Dynamic ( Typeable ) import Data.IORef ( IORef, newIORef ) import System.IO.Unsafe ( unsafePerformIO ) import Data.IORef ( readIORef, writeIORef ) +import Data.List hiding (group) -import qualified Data.List as List ( elem, notElem ) - -#ifndef DEBUG -import Data.List ( zipWith4 ) +import qualified Data.List as List ( elem ) +#ifdef DEBUG +import qualified Data.List as List ( notElem ) #endif -import Control.Monad ( when ) +import Control.Monad ( unless ) import SYSTEM_IO_ERROR as IO ( catch, isDoesNotExistError ) import System.Directory ( doesDirectoryExist, createDirectory, getModificationTime ) +import System.FilePath hiding ( searchPathSeparator ) import Data.Char ( isUpper, isAlphaNum, isSpace, ord, isDigit ) import Data.Ratio ( (%) ) import System.Time ( ClockTime ) @@ -109,41 +114,6 @@ infixr 9 `thenCmp` %************************************************************************ %* * -\subsection{The Eager monad} -%* * -%************************************************************************ - -The @Eager@ monad is just an encoding of continuation-passing style, -used to allow you to express "do this and then that", mainly to avoid -space leaks. It's done with a type synonym to save bureaucracy. - -\begin{code} -#if NOT_USED - -type Eager ans a = (a -> ans) -> ans - -runEager :: Eager a a -> a -runEager m = m (\x -> x) - -appEager :: Eager ans a -> (a -> ans) -> ans -appEager m cont = m cont - -thenEager :: Eager ans a -> (a -> Eager ans b) -> Eager ans b -thenEager m k cont = m (\r -> k r cont) - -returnEager :: a -> Eager ans a -returnEager v cont = cont v - -mapEager :: (a -> Eager ans b) -> [a] -> Eager ans [b] -mapEager f [] = returnEager [] -mapEager f (x:xs) = f x `thenEager` \ y -> - mapEager f xs `thenEager` \ ys -> - returnEager (y:ys) -#endif -\end{code} - -%************************************************************************ -%* * \subsection{A for loop} %* * %************************************************************************ @@ -168,6 +138,22 @@ filterOut :: (a->Bool) -> [a] -> [a] filterOut p [] = [] filterOut p (x:xs) | p x = filterOut p xs | otherwise = x : filterOut p xs + +partitionWith :: (a -> Either b c) -> [a] -> ([b], [c]) +partitionWith f [] = ([],[]) +partitionWith f (x:xs) = case f x of + Left b -> (b:bs, cs) + Right c -> (bs, c:cs) + where + (bs,cs) = partitionWith f xs + +splitEithers :: [Either a b] -> ([a], [b]) +splitEithers [] = ([],[]) +splitEithers (e : es) = case e of + Left x -> (x:xs, ys) + Right y -> (xs, y:ys) + where + (xs,ys) = splitEithers es \end{code} A paranoid @zip@ (and some @zipWith@ friends) that checks the lists @@ -372,6 +358,16 @@ isn'tIn msg x ys # endif /* DEBUG */ \end{code} +foldl1' was added in GHC 6.4 + +\begin{code} +#if __GLASGOW_HASKELL__ < 604 +foldl1' :: (a -> a -> a) -> [a] -> a +foldl1' f (x:xs) = foldl' f x xs +foldl1' _ [] = panic "foldl1'" +#endif +\end{code} + %************************************************************************ %* * \subsubsection[Utils-Carsten-mergesort]{A mergesort from Carsten} @@ -473,6 +469,10 @@ sortWith :: Ord b => (a->b) -> [a] -> [a] sortWith get_key xs = sortLe le xs where x `le` y = get_key x < get_key y + +on :: (a -> a -> Ordering) -> (b -> a) -> b -> b -> Ordering +on cmp sel = \x y -> sel x `cmp` sel y + \end{code} %************************************************************************ @@ -507,72 +507,6 @@ transitiveClosure succ eq xs %* * %************************************************************************ -@mapAccumL@ behaves like a combination -of @map@ and @foldl@; -it applies a function to each element of a list, passing an accumulating -parameter from left to right, and returning a final value of this -accumulator together with the new list. - -\begin{code} -mapAccumL :: (acc -> x -> (acc, y)) -- Function of elt of input list - -- and accumulator, returning new - -- accumulator and elt of result list - -> acc -- Initial accumulator - -> [x] -- Input list - -> (acc, [y]) -- Final accumulator and result list - -mapAccumL f b [] = (b, []) -mapAccumL f b (x:xs) = (b'', x':xs') where - (b', x') = f b x - (b'', xs') = mapAccumL f b' xs -\end{code} - -@mapAccumR@ does the same, but working from right to left instead. Its type is -the same as @mapAccumL@, though. - -\begin{code} -mapAccumR :: (acc -> x -> (acc, y)) -- Function of elt of input list - -- and accumulator, returning new - -- accumulator and elt of result list - -> acc -- Initial accumulator - -> [x] -- Input list - -> (acc, [y]) -- Final accumulator and result list - -mapAccumR f b [] = (b, []) -mapAccumR f b (x:xs) = (b'', x':xs') where - (b'', x') = f b' x - (b', xs') = mapAccumR f b xs -\end{code} - -Here is the bi-directional version, that works from both left and right. - -\begin{code} -mapAccumB :: (accl -> accr -> x -> (accl, accr,y)) - -- Function of elt of input list - -- and accumulator, returning new - -- accumulator and elt of result list - -> accl -- Initial accumulator from left - -> accr -- Initial accumulator from right - -> [x] -- Input list - -> (accl, accr, [y]) -- Final accumulators and result list - -mapAccumB f a b [] = (a,b,[]) -mapAccumB f a b (x:xs) = (a'',b'',y:ys) - where - (a',b'',y) = f a b' x - (a'',b',ys) = mapAccumB f a' b xs -\end{code} - -A strict version of foldl. - -\begin{code} -foldl' :: (a -> b -> a) -> a -> [b] -> a -foldl' f z xs = lgo z xs - where - lgo z [] = z - lgo z (x:xs) = (lgo $! (f z x)) xs -\end{code} - A combination of foldl with zip. It works with equal length lists. \begin{code} @@ -672,12 +606,8 @@ cmpList cmp (a:as) (b:bs) \end{code} \begin{code} -prefixMatch :: Eq a => [a] -> [a] -> Bool -prefixMatch [] _str = True -prefixMatch _pat [] = False -prefixMatch (p:ps) (s:ss) | p == s = prefixMatch ps ss - | otherwise = False - +-- This (with a more general type) is Data.List.stripPrefix from GHC 6.8. +-- This definition can be removed once we require at least 6.8 to build. maybePrefixMatch :: String -> String -> Maybe String maybePrefixMatch [] rest = Just rest maybePrefixMatch (_:_) [] = Nothing @@ -685,9 +615,6 @@ maybePrefixMatch (p:pat) (r:rest) | p == r = maybePrefixMatch pat rest | otherwise = Nothing -suffixMatch :: Eq a => [a] -> [a] -> Bool -suffixMatch pat str = prefixMatch (reverse pat) (reverse str) - removeSpaces :: String -> String removeSpaces = reverse . dropWhile isSpace . reverse . dropWhile isSpace \end{code} @@ -698,31 +625,6 @@ removeSpaces = reverse . dropWhile isSpace . reverse . dropWhile isSpace %* * %************************************************************************ -The following are curried versions of @fst@ and @snd@. - -\begin{code} -#if NOT_USED -cfst :: a -> b -> a -- stranal-sem only (Note) -cfst x y = x -#endif -\end{code} - -The following provide us higher order functions that, when applied -to a function, operate on pairs. - -\begin{code} -#if NOT_USED -applyToPair :: ((a -> c),(b -> d)) -> (a,b) -> (c,d) -applyToPair (f,g) (x,y) = (f x, g y) - -applyToFst :: (a -> c) -> (a,b)-> (c,b) -applyToFst f (x,y) = (f x,y) - -applyToSnd :: (b -> d) -> (a,b) -> (a,d) -applyToSnd f (x,y) = (x,f y) -#endif -\end{code} - \begin{code} unzipWith :: (a -> b -> c) -> [(a, b)] -> [c] unzipWith f pairs = map ( \ (a, b) -> f a b ) pairs @@ -751,6 +653,7 @@ consIORef var x = do Module names: \begin{code} +looksLikeModuleName :: String -> Bool looksLikeModuleName [] = False looksLikeModuleName (c:cs) = isUpper c && go cs where go [] = True @@ -853,17 +756,20 @@ readRational top_s -- Create a hierarchy of directories createDirectoryHierarchy :: FilePath -> IO () +createDirectoryHierarchy dir | isDrive dir = return () -- XXX Hack createDirectoryHierarchy dir = do b <- doesDirectoryExist dir - when (not b) $ do - createDirectoryHierarchy (directoryOf dir) + unless b $ do + createDirectoryHierarchy (takeDirectory dir) createDirectory dir ----------------------------------------------------------------------------- -- Verify that the 'dirname' portion of a FilePath exists. -- doesDirNameExist :: FilePath -> IO Bool -doesDirNameExist fpath = doesDirectoryExist (directoryOf fpath) +doesDirNameExist fpath = case takeDirectory fpath of + "" -> return True -- XXX Hack + dir -> doesDirectoryExist (takeDirectory fpath) -- ----------------------------------------------------------------------------- -- Exception utils @@ -874,13 +780,9 @@ handleDyn :: Typeable ex => (ex -> IO a) -> IO a -> IO a handleDyn = flip catchDyn handle :: (Exception -> IO a) -> IO a -> IO a -#if __GLASGOW_HASKELL__ < 501 -handle = flip Exception.catchAllIO -#else handle h f = f `Exception.catch` \e -> case e of ExitException _ -> throw e _ -> h e -#endif -- -------------------------------------------------------------- -- check existence & modification time at the same time @@ -892,49 +794,6 @@ modificationTimeIfExists f = do then return Nothing else ioError e --- -------------------------------------------------------------- --- Filename manipulation - --- Filenames are kept "normalised" inside GHC, using '/' as the path --- separator. On Windows these functions will also recognise '\\' as --- the path separator, but will generally construct paths using '/'. - -type Suffix = String - -splitFilename :: String -> (String,Suffix) -splitFilename f = splitLongestPrefix f (=='.') - -basenameOf :: FilePath -> String -basenameOf = fst . splitFilename - -suffixOf :: FilePath -> Suffix -suffixOf = snd . splitFilename - -joinFileExt :: String -> String -> FilePath -joinFileExt path "" = path -joinFileExt path ext = path ++ '.':ext - --- "foo/bar/xyzzy.ext" -> ("foo/bar", "xyzzy.ext") -splitFilenameDir :: String -> (String,String) -splitFilenameDir str - = let (dir, rest) = splitLongestPrefix str isPathSeparator - (dir', rest') | null rest = (".", dir) - | otherwise = (dir, rest) - in (dir', rest') - --- "foo/bar/xyzzy.ext" -> ("foo/bar", "xyzzy", ".ext") -splitFilename3 :: String -> (String,String,Suffix) -splitFilename3 str - = let (dir, rest) = splitFilenameDir str - (name, ext) = splitFilename rest - in (dir, name, ext) - -joinFileName :: String -> String -> FilePath -joinFileName "" fname = fname -joinFileName "." fname = fname -joinFileName dir "" = dir -joinFileName dir fname = dir ++ '/':fname - -- split a string at the last character where 'pred' is True, -- returning a pair of strings. The first component holds the string -- up (but not including) the last character for which 'pred' returned @@ -952,32 +811,10 @@ splitLongestPrefix str pred where (r_suf, r_pre) = break pred (reverse str) -replaceFilenameSuffix :: FilePath -> Suffix -> FilePath -replaceFilenameSuffix file suf = basenameOf file `joinFileExt` suf - --- directoryOf strips the filename off the input string, returning --- the directory. -directoryOf :: FilePath -> String -directoryOf = fst . splitFilenameDir - --- filenameOf strips the directory off the input string, returning --- the filename. -filenameOf :: FilePath -> String -filenameOf = snd . splitFilenameDir - -replaceFilenameDirectory :: FilePath -> String -> FilePath -replaceFilenameDirectory path dir = dir `joinFileName` filenameOf path - escapeSpaces :: String -> String escapeSpaces = foldr (\c s -> if isSpace c then '\\':c:s else c:s) "" -isPathSeparator :: Char -> Bool -isPathSeparator ch = -#ifdef mingw32_TARGET_OS - ch == '/' || ch == '\\' -#else - ch == '/' -#endif +type Suffix = String -------------------------------------------------------------- -- * Search path @@ -1012,39 +849,4 @@ searchPathSeparator = ';' #else searchPathSeparator = ':' #endif - ------------------------------------------------------------------------------ --- Convert filepath into platform / MSDOS form. - --- We maintain path names in Unix form ('/'-separated) right until --- the last moment. On Windows we dos-ify them just before passing them --- to the Windows command. --- --- The alternative, of using '/' consistently on Unix and '\' on Windows, --- proved quite awkward. There were a lot more calls to platformPath, --- and even on Windows we might invoke a unix-like utility (eg 'sh'), which --- interpreted a command line 'foo\baz' as 'foobaz'. - -normalisePath :: String -> String --- Just changes '\' to '/' - -pgmPath :: String -- Directory string in Unix format - -> String -- Program name with no directory separators - -- (e.g. copy /y) - -> String -- Program invocation string in native format - -#if defined(mingw32_HOST_OS) ---------------------- Windows version ------------------ -normalisePath xs = subst '\\' '/' xs -pgmPath dir pgm = platformPath dir ++ '\\' : pgm -platformPath p = subst '/' '\\' p - -subst a b ls = map (\ x -> if x == a then b else x) ls -#else ---------------------- Non-Windows version -------------- -normalisePath xs = xs -pgmPath dir pgm = dir ++ '/' : pgm -platformPath stuff = stuff --------------------------------------------------------- -#endif \end{code}