X-Git-Url: http://git.megacz.com/?p=ghc-hetmet.git;a=blobdiff_plain;f=compiler%2Futils%2FUtil.lhs;h=8cfdf839819a30da11e10e7d8aecce331d406f88;hp=3eff34b3d53824800da7fe69a8d9f9067944c511;hb=8e3f4465c2a85e6328df52939c9e2429dc63aaca;hpb=046ee54f048ddd721dcee41916d6a6f68db3b15b diff --git a/compiler/utils/Util.lhs b/compiler/utils/Util.lhs index 3eff34b..8cfdf83 100644 --- a/compiler/utils/Util.lhs +++ b/compiler/utils/Util.lhs @@ -6,146 +6,123 @@ \begin{code} module Util ( + debugIsOn, - -- general list processing - zipEqual, zipWithEqual, zipWith3Equal, zipWith4Equal, + -- general list processing + zipEqual, zipWithEqual, zipWith3Equal, zipWith4Equal, zipLazy, stretchZipWith, - mapFst, mapSnd, - mapAndUnzip, mapAndUnzip3, - nOfThem, filterOut, + mapFst, mapSnd, + mapAndUnzip, mapAndUnzip3, + nOfThem, filterOut, partitionWith, splitEithers, + foldl1', - lengthExceeds, lengthIs, lengthAtLeast, - listLengthCmp, atLength, equalLength, compareLength, + lengthExceeds, lengthIs, lengthAtLeast, + listLengthCmp, atLength, equalLength, compareLength, - isSingleton, only, singleton, - notNull, snocView, + isSingleton, only, singleton, + notNull, snocView, - isIn, isn'tIn, + isIn, isn'tIn, - -- for-loop - nTimes, + -- for-loop + nTimes, - -- sorting - sortLe, sortWith, + -- sorting + sortLe, sortWith, on, - -- transitive closures - transitiveClosure, + -- transitive closures + transitiveClosure, - -- accumulating - mapAccumL, mapAccumR, mapAccumB, - foldl2, count, all2, - - takeList, dropList, splitAtList, split, + -- accumulating + foldl2, count, all2, - -- comparisons - isEqual, eqListBy, - thenCmp, cmpList, prefixMatch, suffixMatch, maybePrefixMatch, - removeSpaces, + takeList, dropList, splitAtList, split, - -- strictness - foldl', seqList, + -- comparisons + isEqual, eqListBy, + thenCmp, cmpList, maybePrefixMatch, + removeSpaces, - -- pairs - unzipWith, + -- strictness + seqList, - global, consIORef, + -- pairs + unzipWith, - -- module names - looksLikeModuleName, - - toArgs, + global, consIORef, - -- Floating point stuff - readRational, + -- module names + looksLikeModuleName, - -- IO-ish utilities - createDirectoryHierarchy, - doesDirNameExist, - modificationTimeIfExists, + getCmd, toCmdArgs, toArgs, - later, handleDyn, handle, + -- Floating point stuff + readRational, - -- Filename utils - Suffix, - splitFilename, suffixOf, basenameOf, joinFileExt, - splitFilenameDir, joinFileName, - splitFilename3, - splitLongestPrefix, - replaceFilenameSuffix, directoryOf, filenameOf, - replaceFilenameDirectory, - escapeSpaces, isPathSeparator, - parseSearchPath, - normalisePath, platformPath, pgmPath, + -- IO-ish utilities + createDirectoryHierarchy, + doesDirNameExist, + modificationTimeIfExists, + + later, handleDyn, handle, + + -- Filename utils + Suffix, + splitLongestPrefix, + escapeSpaces, + parseSearchPath, + Direction(..), reslash, ) where #include "HsVersions.h" -import Panic ( panic, trace ) -import FastTypes +import Panic 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.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 ) +import FastTypes #endif -import Control.Monad ( when ) +import Control.Monad ( unless ) import SYSTEM_IO_ERROR as IO ( catch, isDoesNotExistError ) -import System.Directory ( doesDirectoryExist, createDirectory, +import System.Directory ( doesDirectoryExist, createDirectory, getModificationTime ) -import Data.Char ( isUpper, isAlphaNum, isSpace, ord, isDigit ) -import Data.Ratio ( (%) ) -import System.Time ( ClockTime ) +import System.FilePath hiding ( searchPathSeparator ) +import Data.Char ( isUpper, isAlphaNum, isSpace, ord, isDigit ) +import Data.Ratio ( (%) ) +import System.Time ( ClockTime ) infixr 9 `thenCmp` \end{code} %************************************************************************ -%* * -\subsection{The Eager monad} -%* * +%* * +\subsection{-DDEBUG} +%* * %************************************************************************ -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) +debugIsOn :: Bool +#ifdef DEBUG +debugIsOn = True +#else +debugIsOn = False #endif \end{code} %************************************************************************ -%* * +%* * \subsection{A for loop} -%* * +%* * %************************************************************************ \begin{code} @@ -157,17 +134,31 @@ nTimes n f = f . nTimes (n-1) f \end{code} %************************************************************************ -%* * +%* * \subsection[Utils-lists]{General list processing} -%* * +%* * %************************************************************************ \begin{code} filterOut :: (a->Bool) -> [a] -> [a] -- Like filter, only reverses the sense of the test -filterOut p [] = [] +filterOut _ [] = [] filterOut p (x:xs) | p x = filterOut p xs - | otherwise = x : filterOut p xs + | otherwise = x : filterOut p xs + +partitionWith :: (a -> Either b c) -> [a] -> ([b], [c]) +partitionWith _ [] = ([],[]) +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 @@ -175,10 +166,10 @@ are of equal length. Alastair Reid thinks this should only happen if DEBUGging on; hey, why not? \begin{code} -zipEqual :: String -> [a] -> [b] -> [(a,b)] -zipWithEqual :: String -> (a->b->c) -> [a]->[b]->[c] -zipWith3Equal :: String -> (a->b->c->d) -> [a]->[b]->[c]->[d] -zipWith4Equal :: String -> (a->b->c->d->e) -> [a]->[b]->[c]->[d]->[e] +zipEqual :: String -> [a] -> [b] -> [(a,b)] +zipWithEqual :: String -> (a->b->c) -> [a]->[b]->[c] +zipWith3Equal :: String -> (a->b->c->d) -> [a]->[b]->[c]->[d] +zipWith4Equal :: String -> (a->b->c->d->e) -> [a]->[b]->[c]->[d]->[e] #ifndef DEBUG zipEqual _ = zip @@ -186,23 +177,23 @@ zipWithEqual _ = zipWith zipWith3Equal _ = zipWith3 zipWith4Equal _ = zipWith4 #else -zipEqual msg [] [] = [] +zipEqual _ [] [] = [] zipEqual msg (a:as) (b:bs) = (a,b) : zipEqual msg as bs -zipEqual msg as bs = panic ("zipEqual: unequal lists:"++msg) +zipEqual msg _ _ = panic ("zipEqual: unequal lists:"++msg) zipWithEqual msg z (a:as) (b:bs)= z a b : zipWithEqual msg z as bs -zipWithEqual msg _ [] [] = [] -zipWithEqual msg _ _ _ = panic ("zipWithEqual: unequal lists:"++msg) +zipWithEqual _ _ [] [] = [] +zipWithEqual msg _ _ _ = panic ("zipWithEqual: unequal lists:"++msg) zipWith3Equal msg z (a:as) (b:bs) (c:cs) - = z a b c : zipWith3Equal msg z as bs cs -zipWith3Equal msg _ [] [] [] = [] -zipWith3Equal msg _ _ _ _ = panic ("zipWith3Equal: unequal lists:"++msg) + = z a b c : zipWith3Equal msg z as bs cs +zipWith3Equal _ _ [] [] [] = [] +zipWith3Equal msg _ _ _ _ = panic ("zipWith3Equal: unequal lists:"++msg) zipWith4Equal msg z (a:as) (b:bs) (c:cs) (d:ds) - = z a b c d : zipWith4Equal msg z as bs cs ds -zipWith4Equal msg _ [] [] [] [] = [] -zipWith4Equal msg _ _ _ _ _ = panic ("zipWith4Equal: unequal lists:"++msg) + = z a b c d : zipWith4Equal msg z as bs cs ds +zipWith4Equal _ _ [] [] [] [] = [] +zipWith4Equal msg _ _ _ _ _ = panic ("zipWith4Equal: unequal lists:"++msg) #endif \end{code} @@ -210,22 +201,27 @@ zipWith4Equal msg _ _ _ _ _ = panic ("zipWith4Equal: unequal lists:"++msg) -- zipLazy is lazy in the second list (observe the ~) zipLazy :: [a] -> [b] -> [(a,b)] -zipLazy [] ys = [] -zipLazy (x:xs) ~(y:ys) = (x,y) : zipLazy xs ys +zipLazy [] _ = [] +-- We want to write this, but with GHC 6.4 we get a warning, so it +-- doesn't validate: +-- zipLazy (x:xs) ~(y:ys) = (x,y) : zipLazy xs ys +-- so we write this instead: +zipLazy (x:xs) zs = let y : ys = zs + in (x,y) : zipLazy xs ys \end{code} \begin{code} stretchZipWith :: (a -> Bool) -> b -> (a->b->c) -> [a] -> [b] -> [c] --- (stretchZipWith p z f xs ys) stretches ys by inserting z in +-- (stretchZipWith p z f xs ys) stretches ys by inserting z in -- the places where p returns *True* -stretchZipWith p z f [] ys = [] +stretchZipWith _ _ _ [] _ = [] stretchZipWith p z f (x:xs) ys | p x = f x z : stretchZipWith p z f xs ys | otherwise = case ys of - [] -> [] - (y:ys) -> f x y : stretchZipWith p z f xs ys + [] -> [] + (y:ys) -> f x y : stretchZipWith p z f xs ys \end{code} @@ -238,21 +234,19 @@ mapSnd f xys = [(x, f y) | (x,y) <- xys] mapAndUnzip :: (a -> (b, c)) -> [a] -> ([b], [c]) -mapAndUnzip f [] = ([],[]) +mapAndUnzip _ [] = ([], []) mapAndUnzip f (x:xs) - = let - (r1, r2) = f x - (rs1, rs2) = mapAndUnzip f xs + = let (r1, r2) = f x + (rs1, rs2) = mapAndUnzip f xs in (r1:rs1, r2:rs2) mapAndUnzip3 :: (a -> (b, c, d)) -> [a] -> ([b], [c], [d]) -mapAndUnzip3 f [] = ([],[],[]) +mapAndUnzip3 _ [] = ([], [], []) mapAndUnzip3 f (x:xs) - = let - (r1, r2, r3) = f x - (rs1, rs2, rs3) = mapAndUnzip3 f xs + = let (r1, r2, r3) = f x + (rs1, rs2, rs3) = mapAndUnzip3 f xs in (r1:rs1, r2:rs2, r3:rs3) \end{code} @@ -274,8 +268,8 @@ atLength :: ([a] -> b) -> [a] -> Int -> b -atLength atLenPred atEndPred ls n - | n < 0 = atEndPred n +atLength atLenPred atEndPred ls n + | n < 0 = atEndPred n | otherwise = go n ls where go n [] = atEndPred n @@ -293,8 +287,8 @@ lengthAtLeast = atLength notNull (== 0) lengthIs :: [a] -> Int -> Bool lengthIs = atLength null (==0) -listLengthCmp :: [a] -> Int -> Ordering -listLengthCmp = atLength atLen atEnd +listLengthCmp :: [a] -> Int -> Ordering +listLengthCmp = atLength atLen atEnd where atEnd 0 = EQ atEnd x @@ -305,23 +299,23 @@ listLengthCmp = atLength atLen atEnd atLen _ = GT equalLength :: [a] -> [b] -> Bool -equalLength [] [] = True +equalLength [] [] = True equalLength (_:xs) (_:ys) = equalLength xs ys -equalLength xs ys = False +equalLength _ _ = False compareLength :: [a] -> [b] -> Ordering -compareLength [] [] = EQ +compareLength [] [] = EQ compareLength (_:xs) (_:ys) = compareLength xs ys -compareLength [] _ys = LT -compareLength _xs [] = GT +compareLength [] _ = LT +compareLength _ [] = GT ---------------------------- singleton :: a -> [a] singleton x = [x] isSingleton :: [a] -> Bool -isSingleton [x] = True -isSingleton _ = False +isSingleton [_] = True +isSingleton _ = False notNull :: [a] -> Bool notNull [] = False @@ -333,49 +327,62 @@ only [a] = a #else only (a:_) = a #endif +only _ = panic "Util: only" \end{code} Debugging/specialising versions of \tr{elem} and \tr{notElem} \begin{code} -isIn, isn'tIn :: (Eq a) => String -> a -> [a] -> Bool +isIn, isn'tIn :: Eq a => String -> a -> [a] -> Bool # ifndef DEBUG -isIn msg x ys = elem__ x ys -isn'tIn msg x ys = notElem__ x ys +isIn _msg x ys = elem__ x ys +isn'tIn _msg x ys = notElem__ x ys --these are here to be SPECIALIZEd (automagically) -elem__ _ [] = False -elem__ x (y:ys) = x==y || elem__ x ys +elem__ :: Eq a => a -> [a] -> Bool +elem__ _ [] = False +elem__ x (y:ys) = x == y || elem__ x ys -notElem__ x [] = True -notElem__ x (y:ys) = x /= y && notElem__ x ys +notElem__ :: Eq a => a -> [a] -> Bool +notElem__ _ [] = True +notElem__ x (y:ys) = x /= y && notElem__ x ys # else /* DEBUG */ isIn msg x ys - = elem (_ILIT 0) x ys + = elem (_ILIT(0)) x ys where - elem i _ [] = False + elem _ _ [] = False elem i x (y:ys) - | i ># _ILIT 100 = trace ("Over-long elem in " ++ msg) $ - x `List.elem` (y:ys) - | otherwise = x == y || elem (i +# _ILIT(1)) x ys + | i ># _ILIT(100) = trace ("Over-long elem in " ++ msg) + (x `List.elem` (y:ys)) + | otherwise = x == y || elem (i +# _ILIT(1)) x ys isn'tIn msg x ys - = notElem (_ILIT 0) x ys + = notElem (_ILIT(0)) x ys where - notElem i x [] = True + notElem _ _ [] = True notElem i x (y:ys) - | i ># _ILIT 100 = trace ("Over-long notElem in " ++ msg) $ - x `List.notElem` (y:ys) + | i ># _ILIT(100) = trace ("Over-long notElem in " ++ msg) + (x `List.notElem` (y:ys)) | otherwise = x /= y && notElem (i +# _ILIT(1)) x ys # endif /* DEBUG */ \end{code} +foldl1' was added in GHC 6.4 + +\begin{code} +#if defined(__GLASGOW_HASKELL__) && __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} -%* * +%* * %************************************************************************ \begin{display} @@ -415,7 +422,7 @@ Carsten \begin{code} group :: (a -> a -> Bool) -> [a] -> [[a]] --- Given a <= function, group finds maximal contiguous up-runs +-- Given a <= function, group finds maximal contiguous up-runs -- or down-runs in the input list. -- It's stable, in the sense that it never re-orders equal elements -- @@ -423,35 +430,36 @@ group :: (a -> a -> Bool) -> [a] -> [[a]] -- From: Andy Gill -- Here is a `better' definition of group. -group p [] = [] +group _ [] = [] group p (x:xs) = group' xs x x (x :) where group' [] _ _ s = [s []] - group' (x:xs) x_min x_max s - | x_max `p` x = group' xs x_min x (s . (x :)) - | not (x_min `p` x) = group' xs x x_max ((x :) . s) - | otherwise = s [] : group' xs x x (x :) - -- NB: the 'not' is essential for stablity - -- x `p` x_min would reverse equal elements + group' (x:xs) x_min x_max s + | x_max `p` x = group' xs x_min x (s . (x :)) + | not (x_min `p` x) = group' xs x x_max ((x :) . s) + | otherwise = s [] : group' xs x x (x :) + -- NB: the 'not' is essential for stablity + -- x `p` x_min would reverse equal elements generalMerge :: (a -> a -> Bool) -> [a] -> [a] -> [a] -generalMerge p xs [] = xs -generalMerge p [] ys = ys -generalMerge p (x:xs) (y:ys) | x `p` y = x : generalMerge p xs (y:ys) - | otherwise = y : generalMerge p (x:xs) ys +generalMerge _ xs [] = xs +generalMerge _ [] ys = ys +generalMerge p (x:xs) (y:ys) | x `p` y = x : generalMerge p xs (y:ys) + | otherwise = y : generalMerge p (x:xs) ys -- gamma is now called balancedFold balancedFold :: (a -> a -> a) -> [a] -> a -balancedFold f [] = error "can't reduce an empty list using balancedFold" -balancedFold f [x] = x +balancedFold _ [] = error "can't reduce an empty list using balancedFold" +balancedFold _ [x] = x balancedFold f l = balancedFold f (balancedFold' f l) balancedFold' :: (a -> a -> a) -> [a] -> [a] balancedFold' f (x:y:xs) = f x y : balancedFold' f xs -balancedFold' f xs = xs +balancedFold' _ xs = xs -generalNaturalMergeSort p [] = [] +generalNaturalMergeSort :: (a -> a -> Bool) -> [a] -> [a] +generalNaturalMergeSort _ [] = [] generalNaturalMergeSort p xs = (balancedFold (generalMerge p) . group p) xs #if NOT_USED @@ -472,129 +480,68 @@ sortLe le = generalNaturalMergeSort le sortWith :: Ord b => (a->b) -> [a] -> [a] sortWith get_key xs = sortLe le xs where - x `le` y = get_key x < get_key y + 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} %************************************************************************ -%* * +%* * \subsection[Utils-transitive-closure]{Transitive closure} -%* * +%* * %************************************************************************ This algorithm for transitive closure is straightforward, albeit quadratic. \begin{code} -transitiveClosure :: (a -> [a]) -- Successor function - -> (a -> a -> Bool) -- Equality predicate - -> [a] - -> [a] -- The transitive closure +transitiveClosure :: (a -> [a]) -- Successor function + -> (a -> a -> Bool) -- Equality predicate + -> [a] + -> [a] -- The transitive closure transitiveClosure succ eq xs = go [] xs where - go done [] = done + go done [] = done go done (x:xs) | x `is_in` done = go done xs - | otherwise = go (x:done) (succ x ++ xs) + | otherwise = go (x:done) (succ x ++ xs) - x `is_in` [] = False + _ `is_in` [] = False x `is_in` (y:ys) | eq x y = True - | otherwise = x `is_in` ys + | otherwise = x `is_in` ys \end{code} %************************************************************************ -%* * +%* * \subsection[Utils-accum]{Accumulating} -%* * +%* * %************************************************************************ -@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} foldl2 :: (acc -> a -> b -> acc) -> acc -> [a] -> [b] -> acc -foldl2 k z [] [] = z +foldl2 _ z [] [] = z foldl2 k z (a:as) (b:bs) = foldl2 k (k z a b) as bs +foldl2 _ _ _ _ = panic "Util: foldl2" all2 :: (a -> b -> Bool) -> [a] -> [b] -> Bool --- True if the lists are the same length, and +-- True if the lists are the same length, and -- all corresponding elements satisfy the predicate -all2 p [] [] = True +all2 _ [] [] = True all2 p (x:xs) (y:ys) = p x y && all2 p xs ys -all2 p xs ys = False +all2 _ _ _ = False \end{code} Count the number of times a predicate is true \begin{code} count :: (a -> Bool) -> [a] -> Int -count p [] = 0 +count _ [] = 0 count p (x:xs) | p x = 1 + count p xs - | otherwise = count p xs + | otherwise = count p xs \end{code} @splitAt@, @take@, and @drop@ but with length of another @@ -603,7 +550,7 @@ list giving the break-off point: \begin{code} takeList :: [b] -> [a] -> [a] takeList [] _ = [] -takeList (_:xs) ls = +takeList (_:xs) ls = case ls of [] -> [] (y:ys) -> y : takeList xs ys @@ -622,26 +569,27 @@ splitAtList (_:xs) (y:ys) = (y:ys', ys'') (ys', ys'') = splitAtList xs ys snocView :: [a] -> Maybe ([a],a) - -- Split off the last element + -- Split off the last element snocView [] = Nothing snocView xs = go [] xs - where - -- Invariant: second arg is non-empty - go acc [x] = Just (reverse acc, x) - go acc (x:xs) = go (x:acc) xs + where + -- Invariant: second arg is non-empty + go acc [x] = Just (reverse acc, x) + go acc (x:xs) = go (x:acc) xs + go _ [] = panic "Util: snocView" split :: Char -> String -> [String] split c s = case rest of - [] -> [chunk] - _:rest -> chunk : split c rest + [] -> [chunk] + _:rest -> chunk : split c rest where (chunk, rest) = break (==c) s \end{code} %************************************************************************ -%* * +%* * \subsection[Utils-comparison]{Comparisons} -%* * +%* * %************************************************************************ \begin{code} @@ -653,31 +601,27 @@ isEqual LT = False thenCmp :: Ordering -> Ordering -> Ordering {-# INLINE thenCmp #-} -thenCmp EQ any = any -thenCmp other any = other +thenCmp EQ ordering = ordering +thenCmp ordering _ = ordering eqListBy :: (a->a->Bool) -> [a] -> [a] -> Bool -eqListBy eq [] [] = True +eqListBy _ [] [] = True eqListBy eq (x:xs) (y:ys) = eq x y && eqListBy eq xs ys -eqListBy eq xs ys = False +eqListBy _ _ _ = False cmpList :: (a -> a -> Ordering) -> [a] -> [a] -> Ordering -- `cmpList' uses a user-specified comparer -cmpList cmp [] [] = EQ -cmpList cmp [] _ = LT -cmpList cmp _ [] = GT +cmpList _ [] [] = EQ +cmpList _ [] _ = LT +cmpList _ _ [] = GT cmpList cmp (a:as) (b:bs) = case cmp a b of { EQ -> cmpList cmp as bs; xxx -> xxx } \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,44 +629,16 @@ 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} %************************************************************************ -%* * +%* * \subsection[Utils-pairs]{Pairs} -%* * +%* * %************************************************************************ -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,52 +667,60 @@ consIORef var x = do Module names: \begin{code} +looksLikeModuleName :: String -> Bool looksLikeModuleName [] = False looksLikeModuleName (c:cs) = isUpper c && go cs where go [] = True - go ('.':cs) = looksLikeModuleName cs - go (c:cs) = (isAlphaNum c || c == '_') && go cs + go ('.':cs) = looksLikeModuleName cs + go (c:cs) = (isAlphaNum c || c == '_') && go cs \end{code} Akin to @Prelude.words@, but acts like the Bourne shell, treating -quoted strings and escaped characters within the input as solid blocks -of characters. Doesn't raise any exceptions on malformed escapes or -quoting. +quoted strings as Haskell Strings, and also parses Haskell [String] +syntax. \begin{code} -toArgs :: String -> [String] -toArgs "" = [] -toArgs s = - case dropWhile isSpace s of -- drop initial spacing - [] -> [] -- empty, so no more tokens - rem -> let (tok,aft) = token rem [] in tok : toArgs aft +getCmd :: String -> Either String -- Error + (String, String) -- (Cmd, Rest) +getCmd s = case break isSpace $ dropWhile isSpace s of + ([], _) -> Left ("Couldn't find command in " ++ show s) + res -> Right res + +toCmdArgs :: String -> Either String -- Error + (String, [String]) -- (Cmd, Args) +toCmdArgs s = case getCmd s of + Left err -> Left err + Right (cmd, s') -> case toArgs s' of + Left err -> Left err + Right args -> Right (cmd, args) + +toArgs :: String -> Either String -- Error + [String] -- Args +toArgs str + = case dropWhile isSpace str of + s@('[':_) -> case reads s of + [(args, spaces)] + | all isSpace spaces -> + Right args + _ -> + Left ("Couldn't read " ++ show str ++ "as [String]") + s -> toArgs' s where - -- Grab a token off the string, given that the first character exists and - -- isn't whitespace. The second argument is an accumulator which has to be - -- reversed at the end. - token [] acc = (reverse acc,[]) -- out of characters - token ('\\':c:aft) acc -- escapes - = token aft ((escape c) : acc) - token (q:aft) acc | q == '"' || q == '\'' -- open quotes - = let (aft',acc') = quote q aft acc in token aft' acc' - token (c:aft) acc | isSpace c -- unescaped, unquoted spacing - = (reverse acc,aft) - token (c:aft) acc -- anything else goes in the token - = token aft (c:acc) - - -- Get the appropriate character for a single-character escape. - escape 'n' = '\n' - escape 't' = '\t' - escape 'r' = '\r' - escape c = c - - -- Read into accumulator until a quote character is found. - quote qc = - let quote' [] acc = ([],acc) - quote' ('\\':c:aft) acc = quote' aft ((escape c) : acc) - quote' (c:aft) acc | c == qc = (aft,acc) - quote' (c:aft) acc = quote' aft (c:acc) - in quote' + toArgs' s = case dropWhile isSpace s of + [] -> Right [] + ('"' : _) -> case reads s of + [(arg, rest)] + -- rest must either be [] or start with a space + | all isSpace (take 1 rest) -> + case toArgs' rest of + Left err -> Left err + Right args -> Right (arg : args) + _ -> + Left ("Couldn't read " ++ show s ++ "as String") + s' -> case break isSpace s' of + (arg, s'') -> case toArgs' s'' of + Left err -> Left err + Right args -> Right (arg : args) \end{code} -- ----------------------------------------------------------------------------- @@ -804,24 +728,23 @@ toArgs s = \begin{code} readRational__ :: ReadS Rational -- NB: doesn't handle leading "-" -readRational__ r = do +readRational__ r = do (n,d,s) <- readFix r (k,t) <- readExp s return ((n%1)*10^^(k-d), t) where readFix r = do - (ds,s) <- lexDecDigits r - (ds',t) <- lexDotDigits s - return (read (ds++ds'), length ds', t) + (ds,s) <- lexDecDigits r + (ds',t) <- lexDotDigits s + return (read (ds++ds'), length ds', t) readExp (e:s) | e `elem` "eE" = readExp' s - readExp s = return (0,s) + readExp s = return (0,s) readExp' ('+':s) = readDec s - readExp' ('-':s) = do - (k,t) <- readDec s - return (-k,t) - readExp' s = readDec s + readExp' ('-':s) = do (k,t) <- readDec s + return (-k,t) + readExp' s = readDec s readDec s = do (ds,r) <- nonnull isDigit s @@ -844,43 +767,42 @@ readRational top_s where read_me s = case (do { (x,"") <- readRational__ s ; return x }) of - [x] -> x - [] -> error ("readRational: no parse:" ++ top_s) - _ -> error ("readRational: ambiguous parse:" ++ top_s) + [x] -> x + [] -> error ("readRational: no parse:" ++ top_s) + _ -> error ("readRational: ambiguous parse:" ++ 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) - createDirectory 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 + _ -> doesDirectoryExist (takeDirectory fpath) -- ----------------------------------------------------------------------------- -- Exception utils +later :: IO b -> IO a -> IO a later = flip finally 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 @@ -888,52 +810,9 @@ handle h f = f `Exception.catch` \e -> case e of modificationTimeIfExists :: FilePath -> IO (Maybe ClockTime) modificationTimeIfExists f = do (do t <- getModificationTime f; return (Just t)) - `IO.catch` \e -> if isDoesNotExistError e - 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 + `IO.catch` \e -> if isDoesNotExistError e + then return Nothing + else ioError e -- split a string at the last character where 'pred' is True, -- returning a pair of strings. The first component holds the string @@ -948,36 +827,13 @@ splitLongestPrefix :: String -> (Char -> Bool) -> (String,String) splitLongestPrefix str pred | null r_pre = (str, []) | otherwise = (reverse (tail r_pre), reverse r_suf) - -- 'tail' drops the char satisfying '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 + -- 'tail' drops the char satisfying 'pred' + where (r_suf, r_pre) = break pred (reverse str) 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 @@ -991,10 +847,10 @@ parseSearchPath path = split path split :: String -> [String] split s = case rest' of - [] -> [chunk] + [] -> [chunk] _:rest -> chunk : split rest where - chunk = + chunk = case chunk' of #ifdef mingw32_HOST_OS ('\"':xs@(_:_)) | last xs == '\"' -> init xs @@ -1003,9 +859,9 @@ parseSearchPath path = split path (chunk', rest') = break (==searchPathSeparator) s --- | A platform-specific character used to separate search path strings in --- environment variables. The separator is a colon (\":\") on Unix and Macintosh, --- and a semicolon (\";\") on the Windows operating system. +-- | A platform-specific character used to separate search path strings in +-- environment variables. The separator is a colon (\":\") on Unix and +-- Macintosh, and a semicolon (\";\") on the Windows operating system. searchPathSeparator :: Char #if mingw32_HOST_OS || mingw32_TARGET_OS searchPathSeparator = ';' @@ -1013,38 +869,16 @@ searchPathSeparator = ';' 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 +data Direction = Forwards | Backwards + +reslash :: Direction -> FilePath -> FilePath +reslash d = f + where f ('/' : xs) = slash : f xs + f ('\\' : xs) = slash : f xs + f (x : xs) = x : f xs + f "" = "" + slash = case d of + Forwards -> '/' + Backwards -> '\\' \end{code} +