{-# OPTIONS -fglasgow-exts #-} -- We need an instance String module Stringalike (Stringalike(..)) where import Numeric ( readHex ) import Data.Char ( chr, isHexDigit ) import FileName ( FileName, ps2fn, fp2fn ) import FastPackedString ( PackedString, packString, unpackPS, nilPS, nullPS, lengthPS, reversePS, indexPS, appendPS, concatPS, headPS, tailPS, initPS, lastPS, takePS, dropPS, dropWhilePS, dropWhitePS, breakPS, breakFirstPS, breakLastPS, breakOnPS, breakWhitePS, readIntPS, fromHex2PS, ) class Stringalike s where sal_empty :: s sal_null :: s -> Bool sal_head :: s -> Char sal_last :: s -> Char sal_tail :: s -> s sal_take :: Int -> s -> s sal_drop :: Int -> s -> s sal_reverse :: s -> s sal_concat :: [s] -> s sal_length :: s -> Int sal_index :: s -> Int -> Char sal_dropWhile :: (Char -> Bool) -> s -> s sal_dropWhite :: s -> s sal_breakWhite :: s -> (s, s) sal_readInt :: s -> Maybe (Int, s) sal_break :: (Char -> Bool) -> s -> (s, s) sal_breakFirst :: Char -> s -> Maybe (s, s) sal_breakFirst c xs = case sal_breakOn c xs of (ys, zs) | sal_null zs -> Nothing | otherwise -> Just (ys, sal_tail zs) sal_breakLast :: Char -> s -> Maybe (s, s) sal_breakLast c xs = case sal_breakFirst c (sal_reverse xs) of Nothing -> Nothing Just (ys, zs) -> Just (sal_reverse zs, sal_reverse ys) sal_breakOn :: Char -> s -> (s, s) sal_breakOn c = sal_break (c ==) sal_to_string :: s -> String sal_to_PS :: s -> PackedString sal_fromHex :: s -> s sal_to_fn :: s -> FileName instance Stringalike String where sal_empty = "" sal_null = null sal_head = head sal_last = last sal_tail = tail sal_take = take sal_drop = drop sal_reverse = reverse sal_concat = concat sal_length = length sal_index = (!!) sal_dropWhile = dropWhile sal_dropWhite = dropWhile (`elem` " \n\t\r") sal_breakWhite = break (`elem` " \n\t\r") sal_readInt xs = case reads xs of [(n, s')] -> Just (n, s') _ -> Nothing sal_break = break sal_to_string = id sal_to_PS = packString sal_fromHex "" = "" sal_fromHex [_] = "" -- Should this be an error? sal_fromHex all_cs@(c1:c2:cs) = case readHex [c1, c2] of [(n, "")] -> chr n:sal_fromHex cs _ -> error ("Bad hex characters: " ++ all_cs) sal_to_fn = fp2fn instance Stringalike PackedString where sal_empty = nilPS sal_null = nullPS sal_head = headPS sal_last = lastPS sal_tail = tailPS sal_take = takePS sal_drop = dropPS sal_reverse = reversePS sal_concat = concatPS sal_length = lengthPS sal_index = indexPS sal_dropWhile = dropWhilePS sal_dropWhite = dropWhitePS sal_breakWhite = breakWhitePS sal_readInt = readIntPS sal_break = breakPS sal_breakFirst = breakFirstPS sal_breakLast = breakLastPS sal_breakOn = breakOnPS sal_to_string = unpackPS sal_to_PS = id sal_fromHex = fromHex2PS sal_to_fn = ps2fn -- Invariant: nullPS `notElem` instance Stringalike [PackedString] where sal_empty = [] sal_null = null sal_head (ps:_) = headPS ps sal_head [] = error "sal_head []" sal_last (ps:pss) | null pss = lastPS ps | otherwise = sal_last pss sal_last [] = error "sal_last []" sal_tail (ps:pss) | lengthPS ps == 1 = pss | otherwise = tailPS ps:pss sal_tail [] = error "sal_tail []" sal_take _ [] = [] sal_take 0 _ = [] sal_take n (ps:pss) | n <= lengthPS ps = [takePS n ps] | otherwise = ps:sal_take (n - lengthPS ps) pss sal_drop _ [] = [] sal_drop n (ps:pss) | n == lengthPS ps = pss | n < lengthPS ps = dropPS n ps:pss | otherwise = sal_drop (n - lengthPS ps) pss sal_reverse = reverse . map reversePS sal_concat = concat sal_length = sum . map lengthPS sal_index [] _ = error "sal_index []" sal_index (ps:pss) n | n < lengthPS ps = indexPS ps n | otherwise = sal_index pss (n - lengthPS ps) sal_dropWhile _ [] = [] sal_dropWhile f (ps:pss) = let ps' = dropWhilePS f ps in if nullPS ps' then sal_dropWhile f pss else ps':pss sal_dropWhite [] = [] sal_dropWhite (ps:pss) = let ps' = dropWhitePS ps in if nullPS ps' then sal_dropWhite pss else ps':pss sal_breakWhite [] = ([], []) sal_breakWhite (ps:pss) = case breakWhitePS ps of (xs, ys) | nullPS ys -> case sal_breakWhite pss of (xs', ys') -> (xs:xs', ys') | nullPS xs -> ([], ys:pss) | otherwise -> ([xs], ys:pss) sal_readInt pss = case sal_break f $ sal_dropWhite pss of (xs, ys) -> case readIntPS (concatPS (xs ++ [nulPS])) of Just (n, ys') | len == 0 -> error "readIntPS lost NUL!" | len == 1 -> Just (n, ys) | otherwise -> Just (n, initPS ys':ys) where len = lengthPS ys' Nothing -> Nothing where f c | isHexDigit c = False f '+' = False f '-' = False f 'x' = False f _ = True nulPS = packString "\NUL" sal_break _ [] = ([], []) sal_break f (ps:pss) = case breakPS f ps of (xs, ys) | nullPS ys -> case sal_break f pss of (xs', ys') -> (xs:xs', ys') | nullPS xs -> ([], ys:pss) | otherwise -> ([xs], ys:pss) sal_to_string = concat . map unpackPS sal_to_PS = concatPS sal_fromHex [] = [] sal_fromHex [ps] | lengthPS ps == 1 = [] -- Should this be an error? sal_fromHex (ps1:ps2:pss) | lengthPS ps1 == 1 = sal_fromHex (appendPS ps1 ps2):pss sal_fromHex (ps:pss) | odd (lengthPS ps) = sal_fromHex (initPS ps:packString [lastPS ps]:pss) | otherwise = fromHex2PS ps:sal_fromHex pss sal_to_fn = ps2fn . concatPS