module System.FilePath.Glob.Optimize (optimize) where
import Data.List (find, sortBy)
import System.FilePath (isPathSeparator, isExtSeparator)
import System.FilePath.Glob.Base
import System.FilePath.Glob.Utils
( isLeft, fromLeft
, increasingSeq
, addToRange, overlap)
optimize :: Pattern -> Pattern
optimize = liftP (fin . go . pre)
where
pre (ExtSeparator:PathSeparator:xs) = pre xs
pre xs = xs
fin [] = []
fin (x:y:xs) | isLiteral x && isLiteral y =
let (ls,rest) = span isLiteral xs
in fin $ LongLiteral (length ls + 2)
(foldr (\(Literal a) -> (a:)) [] (x:y:ls))
: rest
fin (LongLiteral l1 s1 : LongLiteral l2 s2 : xs) =
fin $ LongLiteral (l1+l2) (s1++s2) : xs
fin (LongLiteral l s : Literal c : xs) =
fin $ LongLiteral (l+1) (s++[c]) : xs
fin (x:xs) = x : fin xs
go [] = []
go (x@(CharRange _ _) : xs) =
case optimizeCharRange x of
x'@(CharRange _ _) -> x' : go xs
x' -> go (x':xs)
go (PathSeparator:ExtSeparator:xs@(PathSeparator:_)) = go xs
go (OpenRange (Just a) (Just b):xs)
| a == b = LongLiteral (length a) a : go xs
go (OpenRange (Just [a]) (Just [b]):xs)
| b > a = go $ CharRange True [Right (a,b)] : xs
go (x:xs) =
case find ($x) compressors of
Just c -> x : go (dropWhile c xs)
Nothing -> x : go xs
compressors = [isStar, isSlash, isStarSlash, isAnyNumber]
isLiteral (Literal _) = True
isLiteral _ = False
isStar AnyNonPathSeparator = True
isStar _ = False
isSlash PathSeparator = True
isSlash _ = False
isStarSlash AnyDirectory = True
isStarSlash _ = False
isAnyNumber (OpenRange Nothing Nothing) = True
isAnyNumber _ = False
optimizeCharRange :: Token -> Token
optimizeCharRange (CharRange b_ rs) = fin b_ . go . sortCharRange $ rs
where
fin True [Left c] | not (isPathSeparator c || isExtSeparator c) = Literal c
fin True [Right r] | r == (minBound,maxBound) = NonPathSeparator
fin b x = CharRange b x
go [] = []
go (x@(Left c) : xs) =
case xs of
[] -> [x]
y@(Left d) : ys
| c == d -> go$ Left c : ys
| d == succ c ->
let (ls,rest) = span isLeft xs
(catable,others) = increasingSeq (map fromLeft ls)
range = (c, head catable)
in
if null catable || null (tail catable)
then x : y : go ys
else go$ Right range : map Left others ++ rest
| otherwise -> x : go xs
Right r : ys ->
case addToRange r c of
Just r' -> go$ Right r' : ys
Nothing -> x : go xs
go (x@(Right r) : xs) =
case xs of
[] -> [x]
Left c : ys ->
case addToRange r c of
Just r' -> go$ Right r' : ys
Nothing -> x : go xs
Right r' : ys ->
case overlap r r' of
Just o -> go$ Right o : ys
Nothing -> x : go xs
optimizeCharRange _ = error "Glob.optimizeCharRange :: internal error"
sortCharRange :: [Either Char (Char,Char)] -> [Either Char (Char,Char)]
sortCharRange = sortBy cmp
where
cmp (Left a) (Left b) = compare a b
cmp (Left a) (Right (b,_)) = compare a b
cmp (Right (a,_)) (Left b) = compare a b
cmp (Right (a,_)) (Right (b,_)) = compare a b