{-# LANGUAGE ViewPatterns, DeriveFunctor, BangPatterns, TupleSections, RecordWildCards #-}
module System.FilePattern.Step(
step, step_, Step(..), StepNext(..)
) where
import System.FilePattern.Core
import System.FilePattern.Tree
import System.FilePattern.Wildcard
import Control.Monad.Extra
import Data.List.Extra
import Data.Semigroup
import Data.Tuple.Extra
import Data.Functor
import Data.Either
import qualified Data.List.NonEmpty as NE
import Prelude
data StepNext
=
StepOnly [String]
|
StepEverything
|
StepUnknown
deriving (Eq,Ord,Show)
mergeStepNext :: [StepNext] -> StepNext
mergeStepNext = f id
where
f rest [] = StepOnly $ rest []
f rest (StepUnknown:xs) = if StepEverything `elem` xs then StepEverything else StepUnknown
f rest (StepEverything:xs) = StepEverything
f rest (StepOnly x:xs) = f (rest . (x ++)) xs
normaliseStepNext :: StepNext -> StepNext
normaliseStepNext (StepOnly xs) = StepOnly $ nubOrd xs
normaliseStepNext x = x
instance Semigroup StepNext where
a <> b = sconcat $ NE.fromList [a,b]
sconcat = normaliseStepNext . mergeStepNext . NE.toList
instance Monoid StepNext where
mempty = StepOnly []
mappend = (<>)
mconcat = maybe mempty sconcat . NE.nonEmpty
data Step a = Step
{stepDone :: [(a, [String])]
,stepNext :: StepNext
,stepApply :: String -> Step a
}
deriving Functor
mergeStep :: (StepNext -> StepNext) -> [Step a] -> Step a
mergeStep f [] = mempty
mergeStep f [x] = x
mergeStep f xs = Step
{stepDone = concatMap stepDone xs
,stepNext = f $ mergeStepNext $ map stepNext xs
,stepApply = \x -> mergeStep f $ map (`stepApply` x) xs
}
instance Semigroup (Step a) where
a <> b = sconcat $ NE.fromList [a,b]
sconcat (NE.toList -> ss)
| [s] <- ss = s
| otherwise = Step
{stepDone = concatMap stepDone ss
,stepNext = mconcat $ map stepNext ss
,stepApply = \x -> fastFoldMap (`stepApply` x) ss
}
instance Monoid (Step a) where
mempty = Step [] mempty $ const mempty
mappend = (<>)
mconcat = maybe mempty sconcat . NE.nonEmpty
fastFoldMap :: Monoid m => (a -> m) -> [a] -> m
fastFoldMap f = mconcat . map f
data Pat = Lits [Wildcard String]
| StarStar
| End
deriving (Show,Eq,Ord)
toPat :: Pattern -> [Pat]
toPat (Pattern (Literal xs)) = [Lits xs]
toPat (Pattern (Wildcard pre mid post)) = intercalate [StarStar] $ map lit $ pre : mid ++ [post]
where lit xs = [Lits xs | xs /= []]
step :: [(a, FilePattern)] -> Step a
step = restore . ($ id) . f [] . makeTree . map (second $ toPat . parsePattern)
where
f :: [Pat] -> Tree Pat a -> (Parts -> Step [a])
f seen (Tree ends nxts) = \parts -> mergeStep id $ map ($ parts) $ sEnds ++ sNxts
where
sEnds = case unroll ends (seen ++ [End]) of
_ | null ends -> []
Just ([], c) -> [c (error "step invariant violated (1)")]
_ -> error $ "step invariant violated (2), " ++ show seen
sNxts = flip map nxts $ \(p,ps) ->
let seen2 = seen ++ [p] in
case unroll (error "step invariant violated (3)") seen2 of
Nothing -> f seen2 ps
Just (nxt, c) -> c (f [] $ retree nxt ps)
retree [] t = t
retree (p:ps) t = Tree [] [(p, retree ps t)]
restore :: Step [a] -> Step a
restore Step{..} = Step
{stepDone = [(a, b) | (as,b) <- stepDone, a <- as]
,stepNext = normaliseStepNext stepNext
,stepApply = restore . stepApply
}
step_ :: [FilePattern] -> Step ()
step_ = step . map ((),)
match1 :: Wildcard String -> String -> Maybe [String]
match1 w x = rights <$> wildcardMatch equals w x
type Parts = [String] -> [String]
unroll :: a -> [Pat] -> Maybe ([Pat], (Parts -> Step a) -> Parts -> Step a)
unroll val [End] = Just ([], \_ parts -> mempty{stepDone = [(val, parts [])]})
unroll val [StarStar,StarStar] = Just ([StarStar], \cont parts -> cont (parts . ([]:)))
unroll val [Lits (l:ls)] = Just ([Lits ls | ls /= []], \cont parts -> Step
{stepDone = []
,stepNext = case l of Literal v -> StepOnly [v]; Wildcard{} -> StepUnknown
,stepApply = \s -> case match1 l s of
Just xs -> cont (parts . (xs++))
Nothing -> mempty
})
unroll val [StarStar,End] = Just ([], \_ parts -> g parts [])
where
g parts rseen = Step
{stepDone = [(val, parts [mkParts $ reverse rseen])]
,stepNext = StepEverything
,stepApply = \s -> g parts (s:rseen)
}
unroll val [StarStar,Lits (reverse &&& length -> (rls,nls)),End] = Just ([], \_ parts -> g parts 0 [])
where
g parts !nseen rseen = Step
{stepDone = case zipWithM match1 rls rseen of
_ | nseen < nls -> []
Just xss -> [(val, parts $ mkParts (reverse $ drop nls rseen) : concat (reverse xss))]
Nothing -> []
,stepNext = StepUnknown
,stepApply = \s -> g parts (nseen+1) (s:rseen)
}
unroll val [StarStar,Lits [l],StarStar] = Just ([StarStar], \cont parts -> g cont parts [])
where
g cont parts rseen = Step
{stepDone = []
,stepNext = StepUnknown
,stepApply = \s -> case match1 l s of
Just xs -> cont (parts . (++) (mkParts (reverse rseen) : xs))
Nothing -> g cont parts (s:rseen)
}
unroll val [StarStar,Lits (reverse &&& length -> (rls,nls)),StarStar] = Just ([StarStar], \cont parts -> g cont parts 0 [])
where
g cont parts !nseen rseen = Step
{stepDone = []
,stepNext = StepUnknown
,stepApply = \s -> case zipWithM match1 rls (s:rseen) of
_ | nseen+1 < nls -> g cont parts (nseen+1) (s:rseen)
Nothing -> g cont parts (nseen+1) (s:rseen)
Just xss -> cont (parts . (++) (mkParts (reverse $ drop nls $ s:rseen) : concat (reverse xss)))
}
unroll _ _ = Nothing