module Util.Extra(module Util.Extra, trace) where -- FIXME: Some of this stuff needs moving out into compiler specific bits import Char import List import Maybe import IO (hPutStr,stderr) import SysDeps (trace) import System exitFail :: IO a exitFail = exitWith (ExitFailure 1) space :: Int -> String space n = if n == 0 then "" else ' ':space (n-1) fst3 :: (a, b, c) -> a fst3 (a,_,_) = a snd3 :: (a, b, c) -> b snd3 (_,a,_) = a thd3 :: (a, b, c) -> c thd3 (_,_,a) = a sndOf :: a -> b -> b sndOf a b = b makeDouble :: Integer -> Double -> Int -> Double makeDouble i f e = (fromIntegral i +f) * (10.0 ^^ e) mapListSnd :: (a -> b) -> [(c,a)] -> [(c,b)] mapListSnd f = map (mapSnd f) foldls :: (a -> b -> a) -> a -> [b] -> a foldls f z [] = z foldls f z (x:xs) = let z' = f z x in seq z' (foldl f z' xs) split :: Eq a => [a] -> a -> [[a]] split cs sep = split' cs sep [] where split' [] s acc = [reverse acc] split' (c:cs) s acc | c == s = (reverse acc) : split' cs s [] | otherwise = split' cs s (c:acc) strace :: String -> a -> a strace msg c = if length msg == 0 then c else trace msg c warning :: String -> a -> a warning s v = trace ("Warning: "++s) v --warning s v = v fstOf :: a -> b -> a fstOf a b = a safeTail :: [a] -> [a] safeTail [] = [] safeTail (x:xs) = xs snub :: Eq a => [a] -> [a] snub [] = [] snub (x:xs) = x:snub (filter (/=x) xs) pair :: a -> b -> (a, b) pair x y = (x,y) triple :: a -> b -> c -> (a, b, c) triple x y z = (x,y,z) isLeft :: Either a b -> Bool isLeft (Left a) = True isLeft _ = False isRight :: Either a b -> Bool isRight (Right a) = True isRight _ = False dropLeft :: Either a b -> a dropLeft (Left a) = a dropRight :: Either a b -> b dropRight (Right a) = a dropEither :: Either a a -> a dropEither (Left x) = x dropEither (Right x) = x mapPair :: (a -> b) -> (c -> d) -> (a, c) -> (b, d) mapFst :: (a -> b) -> (a, o) -> (b, o) mapSnd :: (a -> b) -> (o, a) -> (o, b) mapPair f g (x,y) = (f x,g y) mapFst f (x,y) = (f x, y) mapSnd g (x,y) = ( x,g y) findLeft :: [Either e a] -> Either e [a] findLeft l = f [] l where f a [] = Right (reverse a) f a (Left e:r) = Left e f a (Right x:r) = f (x:a) r -- | Isn't this just @(\f -> findLeft . map f)@? eitherMap :: (a -> Either e b) -> [a] -> Either e [b] eitherMap f [] = Right [] eitherMap f (x:xs) = case f x of Left err -> Left err Right x' -> case eitherMap f xs of Left err -> Left err Right xs' -> Right (x':xs') jRight :: Int -> [Char] -> [Char] jRight n s = case length s of ns -> if ns > n then s else space (n-ns) ++ s jLeft :: Int -> [Char] -> [Char] jLeft n s = case length s of ns -> if ns > n then s else s ++ space (n-ns) -- | Take a function and a list and return a list of spans in which -- the function returns the same value for each element. partitions :: Eq b => (a -> b) -> [a] -> [[a]] partitions f [] = [] partitions f (x:xs) = gB f (f x) [x] xs where gB f v a [] = [reverse a] gB f v a (x:xs) = if f x == v then gB f v (x:a) xs else reverse a : gB f (f x) [x] xs ---------- mix :: String -> [String] -> String mix s [] = "" mix s xs = foldl1 (\x y-> x ++ s ++ y) xs mixSpace, mixComma, mixLine :: [String] -> String mixSpace = mix " " mixComma = mix "," mixLine = mix "\n" mixCommaAnd :: [String] -> String mixCommaAnd [] = "" mixCommaAnd [x] = x mixCommaAnd [x,y] = x ++ " and " ++ y mixCommaAnd (x:xs) = x ++ ", " ++ mixCommaAnd xs rep 0 c = [] rep n c = c:rep (n-1) c ----------------- assoc :: Eq a => a -> [(a,b)] -> b assoc a [] = error "assoc!" assoc a ((k,v):kvs) = if a == k then v else assoc a kvs assocDef :: Eq a => [(a,b)] -> b -> a -> b assocDef [] d a = d assocDef ((k,v):kvs) d a = if a == k then v else assocDef kvs d a ------------------- -- | abstract type for storing the position of a syntactic construct in a file, -- that is, line and column number of both start and end positions. data Pos = P !Int !Int -- line * 10000 + column of start, line * 10000 + column of end -- both lines and column start at 1 -- allow lines and coluns 0 to mark nonexisting position type Line = Int type Column = Int -- | used in STGcode to get encoded start position -- STGcode should be changed so that this function can disappear pos2Int :: Pos -> Int pos2Int (P s _) = s toPos :: Line -> Column -> Line -> Column -> Pos toPos l1 c1 l2 c2 = P (l1*10000 + c1) (l2*10000 + c2) -- | create a virtual position out of a real one insertPos :: Pos -> Pos insertPos (P s e) = P s 0 noPos :: Pos noPos = P 0 0 mergePos :: Pos -> Pos -> Pos -- ^ combines positions by determining minimal one that covers both -- positions may or may not overlap -- does not assume that first pos really earlier -- nonexisting positions are ignored mergePos (P s1 e1) (P s2 e2) = if e1 == 0 then P s2 e2 else if e2 == 0 then P s1 e1 else P (min s1 s2) (max e1 e2) mergePoss :: [Pos] -> Pos -- ^ merge a list of positions mergePoss = foldr mergePos noPos fromPos :: Pos -> (Line,Column,Line,Column) fromPos (P s e) = let l1 = s `div` 10000 c1 = s - l1*10000 l2 = e `div` 10000 c2 = e - l2*10000 in (l1,c1,l2,c2) strPos :: Pos -> String strPos p = case fromPos p of (0,0,0,0) -> "nopos" (l1,c1,0,0) -> show l1 ++ ':' : show c1 (l1,c1,l2,c2) | l1==l2 && c1==c2 -> show l1 ++ ':' : show c1 (l1,c1,l2,c2) -> show l1 ++ ':' : show c1 ++ '-' : show l2 ++ ':' : show c2 instance Show Pos where show p = strPos p instance Eq Pos where P s1 e1 == P s2 e2 = (s1 == s2) && (e1 == e2) instance Ord Pos where -- for ordering error messages of parser -- and determining minimum of two positions -- nonexisting positions are avoided {- P s1 e1 > P s2 e2 = s1 > s2 || (s1 == s2 && e1 > e2) min (P s1 e1) (P s2 e2) = if e1 == 0 then if e2 == 0 then if s1 <= s2 then P s1 e1 else P s2 e2 else P s2 e2 else if e2 == 0 then P s1 e1 else if (s1 < s2) || (s1 == s2 && e1 <= e2) then P s1 e1 else P s2 e2 -} compare (P s1 e1) (P s2 e2) = compare (s1,e1) (s2,e2) -------------------- data SplitIntegral = SplitPos [Int] | SplitZero | SplitNeg [Int] splitIntegral :: (Integral n) => n -> SplitIntegral splitIntegral n = if n < 0 then SplitNeg (split' (-n)) else if n == 0 then SplitZero else SplitPos (split' n) where split' n = if n == 0 then [] else fromInteger (toInteger (n `mod` 256)) : split' (n `div` 256) -------------------- type Set a = [a] emptySet :: Set a emptySet = [] singletonSet :: a -> Set a singletonSet a = [a] listSet :: Eq a => [a] -> Set a listSet xs = (nub xs) unionSet :: Eq a => Set a -> Set a -> Set a unionSet xs ys = unionSet' xs ys where unionSet' [] ys = ys unionSet' (x:xs) ys | x `elem` ys = unionSet' xs ys | otherwise = x:unionSet' xs ys removeSet :: Eq a => Set a -> Set a -> Set a removeSet xs ys = filter (`notElem` ys) xs --------------------- strChr' :: Char -> Char -> String strChr' del '\\' = "\\\\" strChr' del '\n' = "\\n" strChr' del '\t' = "\\t" strChr' del c = if isPrint c then if c == del then "\\" ++ [c] else [c] else "\\o" ++ map (toEnum . (+(fromEnum '0'))) (ctoo (fromEnum c)) where ctoo c = [(c `div` 64),(c `div` 8) `mod` 8,c `mod` 8] strChr :: Char -> String strChr c = "'" ++ strChr' '\'' c ++ "'" strStr :: String -> String strStr s = "\"" ++ concatMap (strChr' '"') s ++ "\"" ----------------------- showErr :: FilePath -> (Pos,String,[String]) -> String showErr file (pos,token,strs) = "Error: " ++ file ++ "(" ++ strPos pos ++ ") Found " ++ token ++ case nub strs of [] -> " but no token can be accepted here." [x] -> " but expected a " ++ x xs -> " but expected one of " ++ mix " " xs ------------------------ -- | Given a list of filenames, return filename and its content of first file -- that was read successfully (intention: other filenames may not exist) -- FIXME, wouldn't doesFileExist be better here? readFirst :: [String] -> IO (String,String) readFirst [] = do hPutStr stderr "Fail no filenames, probably no -I or -P" exitFail readFirst [x] = do finput <- readFile x return (x,finput) readFirst (x:xs) = catch (do finput <- readFile x return (x,finput)) (\ _ -> readFirst xs) ------------------------ -- * Test integers for their size bounds isByte :: Int -> Bool isByte c = c >= -0x80 && c <= 0x7f isUByte :: Int -> Bool isUByte c = c >= 0x00 && c <= 0xff isShort :: Int -> Bool isShort c = c >= -0x8000 && c <= 0x7fff isUShort :: Int -> Bool isUShort c = c >= 0x00 && c <= 0xffff isInt :: Int -> Bool isInt c = c >= -0x80000000 && c <= 0x7fffffff