nemesis 2013.6.13 → 2013.6.16
raw patch · 15 files changed
+123/−1611 lines, 15 filesdep +Globdep −data-defaultdep −filepathdep −old-timedep ~airPVP ok
version bump matches the API change (PVP)
Dependencies added: Glob
Dependencies removed: data-default, filepath, old-time, transformers
Dependency ranges changed: air
API changes (from Hackage documentation)
Files
- Nemesis +1/−0
- changelog.md +7/−0
- nemesis.cabal +26/−25
- readme.md +33/−62
- src/System/FilePath/Glob.hs +0/−74
- src/System/FilePath/Glob/Base.hs +0/−629
- src/System/FilePath/Glob/Directory.hs +0/−299
- src/System/FilePath/Glob/Match.hs +0/−157
- src/System/FilePath/Glob/Primitive.hs +0/−57
- src/System/FilePath/Glob/Simplify.hs +0/−42
- src/System/FilePath/Glob/Utils.hs +0/−165
- src/System/Nemesis/Runner.hs +0/−101
- test/N1.hs +19/−0
- test/N2.hs +22/−0
- test/N3.hs +15/−0
Nemesis view
@@ -12,6 +12,7 @@ , "manifest" , "main" , "nemesis-tmp.*"+ , "dist" ]
changelog.md view
@@ -1,3 +1,10 @@+2013.6.16+---------++* add Glob dependency+* remove binary version as it's not 'simple'++ 2009.10.7 ---------
nemesis.cabal view
@@ -1,25 +1,41 @@ Name: nemesis-Version: 2013.6.13+Version: 2013.6.16 Build-type: Simple-Synopsis: a Rake like task management tool-Description: smart per project code snippets+Synopsis: a task management tool for Haskell+Description: Organize common shell tasks into a meaningful tree like structure with dependency tracking License: BSD3 License-file: LICENSE Author: Jinjing Wang Maintainer: Jinjing Wang <nfjinjing@gmail.com> Build-Depends: base-Cabal-version: >= 1.2+Cabal-version: >= 1.6 category: Web homepage: http://github.com/nfjinjing/nemesis-data-files: readme.md, changelog.md, known-issues.md, Nemesis +data-files: readme.md+ , changelog.md+ , known-issues.md+ , Nemesis+ , test/N1.hs+ , test/N2.hs+ , test/N3.hs+ +source-repository head+ type: git+ location: https://github.com/nfjinjing/nemesis.git+ library ghc-options: -Wall- build-depends: base >= 4 && < 100, old-time, time, mtl, process, containers, air >= 2011.6.19- , dlist- , filepath- , transformers+ build-depends: base >= 4 && < 100+ , time+ , mtl+ , process+ , containers+ , air+ , dlist+ , Glob+ , directory hs-source-dirs: src/ exposed-modules: @@ -29,20 +45,5 @@ other-modules: System.Nemesis.Util - System.FilePath.Glob- System.FilePath.Glob.Primitive- System.FilePath.Glob.Base- System.FilePath.Glob.Directory- System.FilePath.Glob.Match- System.FilePath.Glob.Simplify- System.FilePath.Glob.Utils- + -Executable nemesis- build-depends: base >= 4 && < 100, mtl, process, containers, data-default, directory, air >= 2011.6.19- - hs-source-dirs: src/- main-is: System/Nemesis/Runner.hs- other-modules: System.Nemesis.Util- System.Nemesis.DSL-
readme.md view
@@ -1,25 +1,27 @@-Nemesis: a rake like task management tool for haskell+Nemesis: a task management tool for Haskell ===================================================== Demo ---- - nemesis = do+ import System.Nemesis.Env + main = run $ do+ clean [ "**/*.hi" , "**/*.o" , "manifest" ]- - task "dist" - do++ task "dist" $ do sh "cabal clean" sh "cabal configure" sh "cabal sdist" task "i" (sh "ghci -isrc src/System/Nemesis.hs") - task "manifest" - do+ task "manifest" $ do sh "find . | grep 'hs$' > manifest" Tutorial@@ -32,14 +34,17 @@ ### DSL -Put the following code into a file named `Nemesis`+Create a file named `Nemesis` - nemesis = do+ import System.Nemesis.Env+ import Air.Env ((-))+ import Prelude hiding ((-)) - -- desc is optional, it gives some description to the task that follows+ main = run - do+ + -- desc is optional, it gives some description to the task+ -- task syntax: task "keyword: space seperated dependencies" io-action desc "Hunter attack macro"-- -- syntax: task "keyword: dependencies" io-action task "attack: pet-attack auto-attack" (putStrLn "attack macro done!") desc "Pet attack"@@ -56,14 +61,14 @@ ### Run -run `nemesis`+`runghc Nemesis` attack Hunter attack macro auto-attack Auto attack mark Hunter's mark pet-attack Pet attack -run `nemesis attack`+`runghc Nemesis attack` casting hunter's mark pet attack@@ -73,73 +78,39 @@ ### Namespace -Suppose you have the following tasks+Create namespaces for tasks with the keyword `namespace` - nemesis = do+ import System.Nemesis.Env+ import Air.Env ((-))+ import Prelude hiding ((-))++ main = run - do namespace "eat" - do task "bread: salad" - putStrLn "eating bread"- task "salad: /drink/coke" - putStrLn "nice salad"+ task "salad: /drink/coke" - putStrLn "eating salad" namespace "drink" - do task "coke" - putStrLn "drinking coke" -then+Namespaces are referenced as path components. - nemesis bread =>- .nemesis: bread does not exist!+`runghc Nemesis bread`++ Nemesis: bread does not exist! - nemesis eat/bread =>+`runghc Nemesis eat/bread`+ drinking coke- nice salad+ eating salad eating bread + Hints ----- -* Please add `.nemesis` to `.gitignore` or equivalents.-* alias `nemesis` to something sweeter, e.g. `n`--Advance usage----------------### Use LANGUAGE pragma--Put a `-- Nem` line after the `Langauge` pragma-- {-# LANGUAGE QuasiQuotes #-}-- -- Nem-- nemesis = do- task "i" (sh "ghci -isrc src/System/Nemesis.hs")--currently the separator `-- Nem` is hard coded--### Bypass preprocessing, i.e. run as EDSL--Define `main`, i.e. add `main = run nemesis` in the code. The preprocessor looks for the function `main`, if it's defined, preprocessing is skipped.--This turns `Nemesis` into an EDSL in Haskell, runnable by `runghc`.--For example:-- import System.Nemesis.Env- import Air.Env ((-))- import Prelude hiding ((-))-- main = run nemesis- - nemesis = do- task "hello" - do- sh "echo 'hello world'"- --Try:-- runghc Nemesis hello-+* alias `runghc Nemesis` to something sweeter, e.g. `n`
− src/System/FilePath/Glob.hs
@@ -1,74 +0,0 @@--- File created: 2008-10-10 13:37:42---- | A library for globbing: matching patterns against file paths akin to the--- POSIX @glob()@ function.------ Pattern syntax is documented by 'compile'. To toggle features at compile--- time, look into 'CompOptions'. To modify matching behaviour, look into--- 'MatchOptions'.------ Basic usage examples:------ Matching a 'String' pattern against a 'FilePath':------ @--- 'match' ('compile' pattern) filepath--- @------ Matching a 'String' pattern against all paths in the current working--- directory:------ @--- 'glob' pattern--- @------ Matching a 'String' pattern against all paths in a given directory (a--- 'FilePath'):------ @--- 'globDir1' ('compile' pattern) directorypath--- @------ Matching a list of 'String' patterns against all paths in a given directory,--- returning the matches for each pattern as well as the paths not matched by--- any of the patterns:------ @--- 'globDir' (map 'compile' patterns) directorypath--- @-module System.FilePath.Glob- ( -- * Data type- Pattern- -- * Functions- -- ** Compilation- , compile, decompile, simplify- -- *** Options- , CompOptions(..)- , compileWith, tryCompileWith- -- **** Predefined option sets- , compDefault, compPosix- -- ** Matching- , match- , globDir, globDir1, glob- -- *** Options- , MatchOptions(..)- , matchWith- , globDirWith- -- **** Predefined option sets- , matchDefault, matchPosix- -- ** Miscellaneous- , commonDirectory- ) where--import System.FilePath.Glob.Base ( Pattern- , CompOptions(..), MatchOptions(..)- , compDefault, compPosix- , matchDefault, matchPosix- , compile, compileWith, tryCompileWith- , decompile- )-import System.FilePath.Glob.Directory ( globDir, globDirWith, globDir1, glob- , commonDirectory- )-import System.FilePath.Glob.Match (match, matchWith)-import System.FilePath.Glob.Simplify (simplify)
− src/System/FilePath/Glob/Base.hs
@@ -1,629 +0,0 @@--- File created: 2008-10-10 13:29:26--{-# LANGUAGE CPP #-}--module System.FilePath.Glob.Base- ( Token(..), Pattern(..)-- , CompOptions(..), MatchOptions(..)- , compDefault, compPosix, matchDefault, matchPosix-- , decompile-- , compile- , compileWith, tryCompileWith- , tokenize -- for tests-- , optimize-- , liftP, tokToLower- ) where--import Control.Arrow (first)-import Control.Monad.Trans.Class (lift)-import Control.Monad.Trans.Error (ErrorT, runErrorT, throwError)-import Control.Monad.Trans.Writer.Strict (Writer, runWriter, tell)-import Control.Exception (assert)-import Data.Char (isDigit, isAlpha, toLower)-import Data.List (find, sortBy)-import Data.Maybe (fromMaybe)-import Data.Monoid (Monoid, mappend, mempty, mconcat)-import System.FilePath ( pathSeparator, extSeparator- , isExtSeparator, isPathSeparator- )--import System.FilePath.Glob.Utils ( dropLeadingZeroes- , isLeft, fromLeft- , increasingSeq- , addToRange, overlap- )--#if __GLASGOW_HASKELL__-import Text.Read (readPrec, lexP, parens, prec, Lexeme(Ident))-#endif--data Token- -- primitives- = Literal !Char- | ExtSeparator -- .- | PathSeparator -- /- | NonPathSeparator -- ?- | CharRange !Bool [Either Char (Char,Char)] -- []- | OpenRange (Maybe String) (Maybe String) -- <>- | AnyNonPathSeparator -- *- | AnyDirectory -- **/-- -- after optimization only- | LongLiteral !Int String- deriving (Eq)---- Note: CharRanges aren't converted, because this is tricky in general.--- Consider for instance [@-[], which includes the range A-Z. This would need--- to become [@[a-z]: so essentially we'd need to either:------ 1) Have a list of ranges of uppercase Unicode. Check if our range--- overlaps with any of them and if it does, take the non-overlapping--- part and combine it with the toLower of the overlapping part.------ 2) Simply expand the entire range to a list and map toLower over it.------ In either case we'd need to re-optimize the CharRange—we can't assume that--- if the uppercase characters are consecutive, so are the lowercase.------ 1) might be feasible if someone bothered to get the latest data.------ 2) obviously isn't since you might have 'Right (minBound, maxBound)' in--- there somewhere.------ The current solution is to just check both the toUpper of the character and--- the toLower.-tokToLower :: Token -> Token-tokToLower (Literal c) = Literal (toLower c)-tokToLower (LongLiteral n s) = LongLiteral n (map toLower s)-tokToLower tok = tok---- |An abstract data type representing a compiled pattern.------ Note that the 'Eq' instance cannot tell you whether two patterns behave in--- the same way; only whether they compile to the same 'Pattern'. For instance,--- @'compile' \"x\"@ and @'compile' \"[x]\"@ may or may not compare equal,--- though a @'match'@ will behave the exact same way no matter which 'Pattern'--- is used.-newtype Pattern = Pattern { unPattern :: [Token] } deriving (Eq)--liftP :: ([Token] -> [Token]) -> Pattern -> Pattern-liftP f (Pattern pat) = Pattern (f pat)--instance Show Token where- show (Literal c)- | c `elem` "*?[<" || isExtSeparator c- = ['[',c,']']- | otherwise = assert (not $ isPathSeparator c) [c]- show ExtSeparator = [ extSeparator]- show PathSeparator = [pathSeparator]- show NonPathSeparator = "?"- show AnyNonPathSeparator = "*"- show AnyDirectory = "**/"- show (LongLiteral _ s) = concatMap (show . Literal) s- show (OpenRange a b) =- '<' : fromMaybe "" a ++ "-" ++- fromMaybe "" b ++ ">"-- -- We have to be careful here with ^ and ! lest [a!b] become [!ab]. So we- -- just put them at the end.- --- -- Also, [^x-] was sorted and should not become [^-x].- show (CharRange b r) =- let f = either (:[]) (\(x,y) -> [x,'-',y])- (caret,exclamation,fs) =- foldr (\c (ca,ex,ss) ->- case c of- Left '^' -> ("^",ex,ss)- Left '!' -> (ca,"!",ss)- _ -> (ca, ex,(f c ++) . ss)- )- ("", "", id)- r- (beg,rest) = let s' = fs []- (x,y) = splitAt 1 s'- in if not b && x == "-"- then (y,x)- else (s',"")- in concat [ "["- , if b then "" else "^"- , beg, caret, exclamation, rest- , "]"- ]--instance Show Pattern where- showsPrec d p = showParen (d > 10) $- showString "compile " . showsPrec (d+1) (decompile p)--instance Read Pattern where-#if __GLASGOW_HASKELL__- readPrec = parens . prec 10 $ do- Ident "compile" <- lexP- fmap compile readPrec-#else- readsPrec d = readParen (d > 10) $ \r -> do- ("compile",string) <- lex r- (xs,rest) <- readsPrec (d+1) string- [(compile xs, rest)]-#endif--instance Monoid Pattern where- mempty = Pattern []- mappend (Pattern a) (Pattern b) = optimize . Pattern $ (a ++ b)- mconcat = optimize . Pattern . concatMap unPattern---- |Options which can be passed to the 'tryCompileWith' or 'compileWith'--- functions: with these you can selectively toggle certain features at compile--- time.------ Note that some of these options depend on each other: classes can never--- occur if ranges aren't allowed, for instance.---- We could presumably put locale information in here, too.-data CompOptions = CompOptions- { characterClasses :: Bool -- ^Allow character classes, @[[:...:]]@.- , characterRanges :: Bool -- ^Allow character ranges, @[...]@.- , numberRanges :: Bool -- ^Allow open ranges, @\<...>@.- , wildcards :: Bool -- ^Allow wildcards, @*@ and @?@.- , recursiveWildcards :: Bool -- ^Allow recursive wildcards, @**/@.-- , pathSepInRanges :: Bool- -- ^Allow path separators in character ranges.- --- -- If true, @a[/]b@ never matches anything (since character ranges can't- -- match path separators); if false and 'errorRecovery' is enabled,- -- @a[/]b@ matches itself, i.e. a file named @]b@ in the subdirectory- -- @a[@.-- , errorRecovery :: Bool- -- ^If the input is invalid, recover by turning any invalid part into- -- literals. For instance, with 'characterRanges' enabled, @[abc@ is an- -- error by default (unclosed character range); with 'errorRecovery', the- -- @[@ is turned into a literal match, as though 'characterRanges' were- -- disabled.- } deriving (Show,Read,Eq)---- |The default set of compilation options: closest to the behaviour of the--- @zsh@ shell, with 'errorRecovery' enabled.------ All options are enabled.-compDefault :: CompOptions-compDefault = CompOptions- { characterClasses = True- , characterRanges = True- , numberRanges = True- , wildcards = True- , recursiveWildcards = True- , pathSepInRanges = True- , errorRecovery = True- }---- |Options for POSIX-compliance, as described in @man 7 glob@.------ 'numberRanges', 'recursiveWildcards', and 'pathSepInRanges' are disabled.-compPosix :: CompOptions-compPosix = CompOptions- { characterClasses = True- , characterRanges = True- , numberRanges = False- , wildcards = True- , recursiveWildcards = False- , pathSepInRanges = False- , errorRecovery = True- }---- |Options which can be passed to the 'matchWith' or 'globDirWith' functions:--- with these you can selectively toggle certain features at matching time.-data MatchOptions = MatchOptions- { matchDotsImplicitly :: Bool- -- ^Allow @*@, @?@, and @**/@ to match @.@ at the beginning of paths.-- , ignoreCase :: Bool- -- ^Case-independent matching.-- , ignoreDotSlash :: Bool- -- ^Treat @./@ as a no-op in both paths and patterns.- --- -- (Of course e.g. @../@ means something different and will not be- -- ignored.)- }---- |The default set of execution options: closest to the behaviour of the @zsh@--- shell.------ Currently identical to 'matchPosix'.-matchDefault :: MatchOptions-matchDefault = matchPosix---- |Options for POSIX-compliance, as described in @man 7 glob@.------ 'ignoreDotSlash' is enabled, the rest are disabled.-matchPosix :: MatchOptions-matchPosix = MatchOptions- { matchDotsImplicitly = False- , ignoreCase = False- , ignoreDotSlash = True- }---- |Decompiles a 'Pattern' object into its textual representation: essentially--- the inverse of 'compile'.------ Note, however, that due to internal optimization, @decompile . compile@ is--- not the identity function. Instead, @compile . decompile@ is.------ Be careful with 'CompOptions': 'decompile' always produces a 'String' which--- can be passed to 'compile' to get back the same 'Pattern'. @compileWith--- options . decompile@ is /not/ the identity function unless @options@ is--- 'compDefault'.-decompile :: Pattern -> String-decompile = concatMap show . unPattern----------------------------------------------- COMPILATION------------------------------------------------ |Compiles a glob pattern from its textual representation into a 'Pattern'--- object.------ For the most part, a character matches itself. Recognized operators are as--- follows:------ [@?@] Matches any character except path separators.------ [@*@] Matches any number of characters except path separators,--- including the empty string.------ [@[..\]@] Matches any of the enclosed characters. Ranges of characters can--- be specified by separating the endpoints with a @\'-'@. @\'-'@ or--- @']'@ can be matched by including them as the first character(s)--- in the list. Never matches path separators: @[\/]@ matches--- nothing at all. Named character classes can also be matched:--- @[:x:]@ within @[]@ specifies the class named @x@, which matches--- certain predefined characters. See below for a full list.------ [@[^..\]@ or @[!..\]@] Like @[..]@, but matches any character /not/ listed.--- Note that @[^-x]@ is not the inverse of @[-x]@, but--- the range @[^-x]@.------ [@\<m-n>@] Matches any integer in the range m to n, inclusive. The range may--- be open-ended by leaving out either number: @\"\<->\"@, for--- instance, matches any integer.------ [@**/@] Matches any number of characters, including path separators,--- excluding the empty string.------ Supported character classes:------ [@[:alnum:\]@] Equivalent to @\"0-9A-Za-z\"@.------ [@[:alpha:\]@] Equivalent to @\"A-Za-z\"@.------ [@[:blank:\]@] Equivalent to @\"\\t \"@.------ [@[:cntrl:\]@] Equivalent to @\"\\0-\\x1f\\x7f\"@.------ [@[:digit:\]@] Equivalent to @\"0-9\"@.------ [@[:graph:\]@] Equivalent to @\"!-~\"@.------ [@[:lower:\]@] Equivalent to @\"a-z\"@.------ [@[:print:\]@] Equivalent to @\" -~\"@.------ [@[:punct:\]@] Equivalent to @\"!-\/:-\@[-`{-~\"@.------ [@[:space:\]@] Equivalent to @\"\\t-\\r \"@.------ [@[:upper:\]@] Equivalent to @\"A-Z\"@.------ [@[:xdigit:\]@] Equivalent to @\"0-9A-Fa-f\"@.------ Note that path separators (typically @\'/\'@) have to be matched explicitly--- or using the @**/@ pattern. In addition, extension separators (typically--- @\'.\'@) have to be matched explicitly at the beginning of the pattern or--- after any path separator.------ If a system supports multiple path separators, any one of them will match--- any of them. For instance, on Windows, @\'/\'@ will match itself as well as--- @\'\\\'@.------ Error recovery will be performed: erroneous operators will not be considered--- operators, but matched as literal strings. Such operators include:------ * An empty @[]@ or @[^]@ or @[!]@------ * A @[@ or @\<@ without a matching @]@ or @>@------ * A malformed @\<>@: e.g. nonnumeric characters or no hyphen------ So, e.g. @[]@ will match the string @\"[]\"@.-compile :: String -> Pattern-compile = compileWith compDefault---- |Like 'compile', but recognizes operators according to the given--- 'CompOptions' instead of the defaults.------ If an error occurs and 'errorRecovery' is disabled, 'error' will be called.-compileWith :: CompOptions -> String -> Pattern-compileWith opts = either error id . tryCompileWith opts---- |A safe version of 'compileWith'.------ If an error occurs and 'errorRecovery' is disabled, the error message will--- be returned in a 'Left'.-tryCompileWith :: CompOptions -> String -> Either String Pattern-tryCompileWith opts = fmap optimize . tokenize opts--tokenize :: CompOptions -> String -> Either String Pattern-tokenize opts = fmap Pattern . sequence . go- where- err _ c cs | errorRecovery opts = Right (Literal c) : go cs- err s _ _ = [Left s]-- go :: String -> [Either String Token]- go [] = []- go ('?':cs) | wcs = Right NonPathSeparator : go cs- go ('*':cs) | wcs =- case cs of- '*':p:xs | rwcs && isPathSeparator p- -> Right AnyDirectory : go xs- _ -> Right AnyNonPathSeparator : go cs-- go ('[':cs) | crs = let (range,rest) = charRange opts cs- in case range of- Left s -> err s '[' cs- r -> r : go rest-- go ('<':cs) | ors =- let (range, rest) = break (=='>') cs- in if null rest- then err "compile :: unclosed <> in pattern" '<' cs- else case openRange range of- Left s -> err s '<' cs- r -> r : go (tail rest)- go (c:cs)- | isPathSeparator c = Right PathSeparator : go cs- | isExtSeparator c = Right ExtSeparator : go cs- | otherwise = Right (Literal c) : go cs-- wcs = wildcards opts- rwcs = recursiveWildcards opts- crs = characterRanges opts- ors = numberRanges opts---- <a-b> where a > b can never match anything; this is not considered an error-openRange :: String -> Either String Token-openRange ['-'] = Right $ OpenRange Nothing Nothing-openRange ('-':s) =- case span isDigit s of- (b,"") -> Right $ OpenRange Nothing (openRangeNum b)- _ -> Left $ "compile :: bad <>, expected number, got " ++ s-openRange s =- case span isDigit s of- (a,"-") -> Right $ OpenRange (openRangeNum a) Nothing- (a,'-':s') ->- case span isDigit s' of- (b,"") -> Right $ OpenRange (openRangeNum a) (openRangeNum b)- _ -> Left $ "compile :: bad <>, expected number, got " ++ s'- _ -> Left $ "compile :: bad <>, expected number followed by - in " ++ s--openRangeNum :: String -> Maybe String-openRangeNum = Just . dropLeadingZeroes--type CharRange = [Either Char (Char,Char)]--charRange :: CompOptions -> String -> (Either String Token, String)-charRange opts zs =- case zs of- y:ys | y `elem` "^!" ->- case ys of- -- [!-#] is not the inverse of [-#], it is the range ! through- -- #- '-':']':xs -> (Right (CharRange False [Left '-']), xs)- '-' :_ -> first (fmap (CharRange True )) (start zs)- xs -> first (fmap (CharRange False)) (start xs)- _ -> first (fmap (CharRange True )) (start zs)- where- start :: String -> (Either String CharRange, String)- start (']':xs) = run $ char ']' xs- start ('-':xs) = run $ char '-' xs- start xs = run $ go xs-- run :: ErrorT String (Writer CharRange) String- -> (Either String CharRange, String)- run m = case runWriter.runErrorT $ m of- (Left err, _) -> (Left err, [])- (Right rest, cs) -> (Right cs, rest)-- go :: String -> ErrorT String (Writer CharRange) String- go ('[':':':xs) | characterClasses opts = readClass xs- go ( ']':xs) = return xs- go ( c:xs) =- if not (pathSepInRanges opts) && isPathSeparator c- then throwError "compile :: path separator within []"- else char c xs- go [] = throwError "compile :: unclosed [] in pattern"-- char :: Char -> String -> ErrorT String (Writer CharRange) String- char c ('-':x:xs) =- if x == ']'- then ltell [Left c, Left '-'] >> return xs- else ltell [Right (c,x)] >> go xs-- char c xs = ltell [Left c] >> go xs-- readClass :: String -> ErrorT String (Writer CharRange) String- readClass xs = let (name,end) = span isAlpha xs- in case end of- ':':']':rest -> charClass name >> go rest- _ -> ltell [Left '[',Left ':'] >> go xs-- charClass :: String -> ErrorT String (Writer CharRange) ()- charClass name =- -- The POSIX classes- --- -- TODO: this is ASCII-only, not sure how this should be extended- -- Unicode, or with a locale as input, or something else?- case name of- "alnum" -> ltell [digit,upper,lower]- "alpha" -> ltell [upper,lower]- "blank" -> ltell blanks- "cntrl" -> ltell [Right ('\0','\x1f'), Left '\x7f']- "digit" -> ltell [digit]- "graph" -> ltell [Right ('!','~')]- "lower" -> ltell [lower]- "print" -> ltell [Right (' ','~')]- "punct" -> ltell punct- "space" -> ltell spaces- "upper" -> ltell [upper]- "xdigit" -> ltell [digit, Right ('A','F'), Right ('a','f')]- _ ->- throwError ("compile :: unknown character class '" ++name++ "'")-- digit = Right ('0','9')- upper = Right ('A','Z')- lower = Right ('a','z')- punct = map Right [('!','/'), (':','@'), ('[','`'), ('{','~')]- blanks = [Left '\t', Left ' ']- spaces = [Right ('\t','\r'), Left ' ']-- ltell = lift . tell------------------------------------------------ OPTIMIZATION----------------------------------------------optimize :: Pattern -> Pattern-optimize = liftP (fin . go)- where- fin [] = []-- -- Literals to LongLiteral- -- Has to be done here: we can't backtrack in go, but some cases might- -- result in consecutive Literals being generated.- -- E.g. "a[b]".- 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-- -- concatenate LongLiterals- -- Has to be done here because LongLiterals are generated above.- --- -- So one could say that we have one pass (go) which flattens everything as- -- much as it can and one pass (fin) which concatenates what it can.- 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 (LongLiteral 1 s : xs) = Literal (head s) : fin xs-- fin (Literal c : LongLiteral l s : xs) =- fin $ LongLiteral (l+1) (c:s) : 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)-- -- <a-a> -> a- go (OpenRange (Just a) (Just b):xs)- | a == b = LongLiteral (length a) a : go xs-- -- <a-b> -> [a-b]- -- a and b are guaranteed non-null- 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 -> let (compressed,ys) = span c xs- in if null compressed- then x : go ys- else go (x : ys)- Nothing -> x : go xs-- compressors = [isStar, isStarSlash, isAnyNumber]-- isLiteral (Literal _) = True- isLiteral _ = False- isStar AnyNonPathSeparator = True- isStar _ = 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- -- [/] is interesting, it actually matches nothing at all- -- [.] can be Literalized though, just don't make it into an ExtSeparator so- -- that it doesn't match a leading dot- fin True [Left c] | not (isPathSeparator 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- -- [aaaaa] -> [a]- | c == d -> go$ Left c : ys- | d == succ c ->- let (ls,rest) = span isLeft xs -- start from y- (catable,others) = increasingSeq (map fromLeft ls)- range = (c, head catable)-- in -- three (or more) Lefts make a Right- if null catable || null (tail catable)- then x : y : go ys- -- [abcd] -> [a-d]- else go$ Right range : map Left others ++ rest-- | otherwise -> x : go xs-- Right r : ys ->- case addToRange r c of- -- [da-c] -> [a-d]- 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- -- [a-cd] -> [a-d]- Just r' -> go$ Right r' : ys- Nothing -> x : go xs-- Right r' : ys ->- case overlap r r' of- -- [a-cb-d] -> [a-d]- 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
− src/System/FilePath/Glob/Directory.hs
@@ -1,299 +0,0 @@--- File created: 2008-10-16 12:12:50--module System.FilePath.Glob.Directory- ( globDir, globDirWith, globDir1, glob- , commonDirectory- ) where--import Control.Arrow (first, second)-import Control.Monad (forM)-import qualified Data.DList as DL-import Data.DList (DList)-import Data.List ((\\))-import System.Directory ( doesDirectoryExist, getDirectoryContents- , getCurrentDirectory- )-import System.FilePath ( (</>), takeDrive, splitDrive- , extSeparator, isExtSeparator- , pathSeparator, isPathSeparator- )--import System.FilePath.Glob.Base ( Pattern(..), Token(..)- , MatchOptions, matchDefault- , compile- )-import System.FilePath.Glob.Match (matchWith)-import System.FilePath.Glob.Utils ( getRecursiveContents- , nubOrd- , pathParts- , partitionDL- )--import System.IO.Error (catchIOError)---- The Patterns in TypedPattern don't contain PathSeparator or AnyDirectory------ We store the number of PathSeparators that Dir and AnyDir were followed by--- so that "foo////*" can match "foo/bar" but return "foo////bar". It's the--- exact number for convenience: (</>) doesn't add a path separator if one is--- already there. This way, '\(Dir n _) -> replicate n pathSeparator </> "bar"'--- results in the correct amount of slashes.-data TypedPattern- = Any Pattern -- pattern- | Dir Int Pattern -- pattern/- | AnyDir Int Pattern -- pattern**/- deriving Show---- |Matches each given 'Pattern' against the contents of the given 'FilePath',--- recursively. The result pair\'s first component contains the matched paths,--- grouped for each given 'Pattern', and the second contains all paths which--- were not matched by any 'Pattern'. The results are not in any defined order.------ The given directory is prepended to all the matches: the returned paths are--- all valid from the point of view of the current working directory.------ If multiple 'Pattern's match a single 'FilePath', that path will be included--- in multiple groups.------ Two 'FilePath's which can be canonicalized to the same file (e.g. @\"foo\"@--- and @\"./foo\"@) may appear separately if explicit matching on paths--- beginning with @\".\"@ is done. Looking for @\".*/*\"@, for instance, will--- cause @\"./foo\"@ to return as a match but @\"foo\"@ to not be matched.------ This function is different from a simple 'filter' over all the contents of--- the directory: the matching is performed relative to the directory, so that--- for instance the following is true:------ > fmap (head.fst) (globDir [compile "*"] dir) == getDirectoryContents dir------ (With the exception that that glob won't match anything beginning with @.@.)------ If the given 'FilePath' is @[]@, 'getCurrentDirectory' will be used.------ If the given 'Pattern' starts with a drive (as defined by--- 'System.FilePath'), it is not relative to the given directory and the--- 'FilePath' parameter is completely ignored! Similarly, if the given--- 'Pattern' starts with a path separator, only the drive part of the--- 'FilePath' is used. On Posix systems these behaviours are equivalent:--- 'Pattern's starting with @\/@ work relative to @\/@. On Windows, 'Pattern's--- starting with @\/@ or @\\@ work relative only to the drive part of the--- 'FilePath' and 'Pattern's starting with absolute paths ignore the--- 'FilePath'.------ Note that in some cases results outside the given directory may be returned:--- for instance the @.*@ pattern matches the @..@ directory.------ Any results deeper than in the given directory are enumerated lazily, using--- 'unsafeInterleaveIO'.------ Directories without read permissions are returned as entries but their--- contents, of course, are not.-globDir :: [Pattern] -> FilePath -> IO ([[FilePath]], [FilePath])-globDir = globDirWith matchDefault---- |Like 'globDir', but applies the given 'MatchOptions' instead of the--- defaults when matching.-globDirWith :: MatchOptions -> [Pattern] -> FilePath- -> IO ([[FilePath]], [FilePath])-globDirWith _ [] dir = do- dir' <- if null dir then getCurrentDirectory else return dir- c <- getRecursiveContents dir'- return ([], DL.toList c)--globDirWith opts pats@(_:_) dir = do- results <- mapM (\p -> globDir'0 opts p dir) pats-- let (matches, others) = unzip results- allMatches = DL.toList . DL.concat $ matches- allOthers = DL.toList . DL.concat $ others-- return ( map DL.toList matches- , nubOrd allOthers \\ allMatches- )---- |A convenience wrapper on top of 'globDir', for when you only have one--- 'Pattern' you care about. Returns only the matched paths.-globDir1 :: Pattern -> FilePath -> IO [FilePath]-globDir1 p = fmap (head . fst) . globDir [p]---- |The simplest IO function. Finds matches to the given pattern in the current--- working directory. Takes a 'String' instead of a 'Pattern' to avoid the need--- for a call to 'compile', simplifying usage further.------ Can also be seen as a convenience wrapper on top of 'globDir1', for when you--- want to work in the current directory or have a pattern referring to an--- absolute path.-glob :: String -> IO [FilePath]-glob = flip globDir1 "" . compile--globDir'0 :: MatchOptions -> Pattern -> FilePath- -> IO (DList FilePath, DList FilePath)-globDir'0 opts pat dir = do- let (pat', drive) = driveSplit pat- dir' <- case drive of- Just "" -> fmap takeDrive getCurrentDirectory- Just d -> return d- Nothing -> if null dir then getCurrentDirectory else return dir- globDir' opts (separate pat') dir'--globDir' :: MatchOptions -> [TypedPattern] -> FilePath- -> IO (DList FilePath, DList FilePath)-globDir' opts pats@(_:_) dir = do- entries <- getDirectoryContents dir `catchIOError` const (return [])-- results <- forM entries $ \e -> matchTypedAndGo opts pats e (dir </> e)-- let (matches, others) = unzip results-- return (DL.concat matches, DL.concat others)--globDir' _ [] dir =- -- We can only get here from matchTypedAndGo getting a [Dir _]: it means the- -- original pattern had a trailing PathSeparator. Reproduce it here.- return (DL.singleton (dir ++ [pathSeparator]), DL.empty)--matchTypedAndGo :: MatchOptions- -> [TypedPattern]- -> FilePath -> FilePath- -> IO (DList FilePath, DList FilePath)---- (Any p) is always the last element-matchTypedAndGo opts [Any p] path absPath =- if matchWith opts p path- then return (DL.singleton absPath, DL.empty)- else doesDirectoryExist absPath >>= didn'tMatch path absPath--matchTypedAndGo opts (Dir n p:ps) path absPath = do- isDir <- doesDirectoryExist absPath- if isDir && matchWith opts p path- then globDir' opts ps (absPath ++ replicate n pathSeparator)- else didn'tMatch path absPath isDir--matchTypedAndGo opts (AnyDir n p:ps) path absPath = do- if path `elem` [".",".."]- then didn'tMatch path absPath True- else do- isDir <- doesDirectoryExist absPath- let m = matchWith opts (unseparate ps)- unconditionalMatch =- null (unPattern p) && not (isExtSeparator $ head path)- p' = Pattern (unPattern p ++ [AnyNonPathSeparator])-- case unconditionalMatch || matchWith opts p' path of- True | isDir -> do- contents <- getRecursiveContents- (absPath ++ replicate n pathSeparator)- return $- -- foo**/ should match foo/ and nothing below it- -- relies on head contents == absPath- if null ps- then (DL.singleton $ DL.head contents, DL.tail contents)- else partitionDL (any m . pathParts) contents-- True | m path -> return (DL.singleton absPath, DL.empty)- _ -> didn'tMatch path absPath isDir--matchTypedAndGo _ _ _ _ = error "Glob.matchTypedAndGo :: internal error"---- To be called when a pattern didn't match a path: given the path and whether--- it was a directory, return all paths which didn't match (i.e. for a file,--- just the file, and for a directory, everything inside it).-didn'tMatch :: FilePath -> FilePath -> Bool- -> IO (DList FilePath, DList FilePath)-didn'tMatch path absPath isDir = (fmap $ (,) DL.empty) $- if isDir- then if path `elem` [".",".."]- then return DL.empty- else getRecursiveContents absPath- else return$ DL.singleton absPath--separate :: Pattern -> [TypedPattern]-separate = go DL.empty . unPattern- where- go gr [] | null (DL.toList gr) = []- go gr [] = [Any (pat gr)]- go gr (PathSeparator:ps) = slash gr Dir ps- go gr ( AnyDirectory:ps) = slash gr AnyDir ps- go gr ( p:ps) = go (gr `DL.snoc` p) ps-- pat = Pattern . DL.toList-- slash gr f ps = let (n,ps') = first length . span isSlash $ ps- in f (n+1) (pat gr) : go DL.empty ps'-- isSlash PathSeparator = True- isSlash _ = False--unseparate :: [TypedPattern] -> Pattern-unseparate = Pattern . foldr f []- where- f (AnyDir n p) ts = u p ++ AnyDirectory : replicate n PathSeparator ++ ts- f ( Dir n p) ts = u p ++ PathSeparator : replicate n PathSeparator ++ ts- f (Any p) ts = u p ++ ts-- u = unPattern---- Note that we consider "/foo" to specify a drive on Windows, even though it's--- relative to the current drive.------ Returns the [TypedPattern] of the Pattern (with the drive dropped if--- appropriate) and, if the Pattern specified a drive, a Maybe representing the--- drive to use. If it's a Just "", use the drive of the current working--- directory.-driveSplit :: Pattern -> (Pattern, Maybe FilePath)-driveSplit = check . split . unPattern- where- -- We can't just use something like commonDirectory because of Windows- -- drives being possibly longer than one "directory", like "//?/foo/bar/".- -- So just take as much as possible.- split (LongLiteral _ l : xs) = first (l++) (split xs)- split ( Literal l : xs) = first (l:) (split xs)- split (PathSeparator : xs) = first (pathSeparator:) (split xs)- split ( ExtSeparator : xs) = first ( extSeparator:) (split xs)- split xs = ([],xs)-- -- The isPathSeparator check is interesting in two ways:- --- -- 1. It's correct to return simply Just "" because there can't be more than- -- one path separator if splitDrive gave a null drive: "//x" is a shared- -- "drive" in Windows and starts with the root "drive" in Posix.- --- -- 2. The 'head' is safe because we have not (null d) && null drive.- check (d,ps)- | null d = (Pattern ps, Nothing)- | not (null drive) = (dirify rest ps, Just drive)- | isPathSeparator (head rest) = (Pattern ps, Just "")- | otherwise = (dirify d ps, Nothing)- where- (drive, rest) = splitDrive d-- dirify path = Pattern . (comp path++)-- comp s = let (p,l) = foldr f ([],[]) s in if null l then p else ll l p- where- f c (p,l) | isExtSeparator c = (ExtSeparator : ll l p, [])- | isPathSeparator c = (PathSeparator : ll l p, [])- | otherwise = (p, c:l)-- ll l p = if null l then p else LongLiteral (length l) l : p---- |Factors out the directory component of a 'Pattern'. Useful in conjunction--- with 'globDir'.------ Preserves the number of path separators: @commonDirectory (compile--- \"foo\/\/\/bar\")@ becomes @(\"foo\/\/\/\", compile \"bar\")@.-commonDirectory :: Pattern -> (FilePath, Pattern)-commonDirectory = second unseparate . splitP . separate- where- splitP pt@(Dir n p:ps) =- case fromConst DL.empty (unPattern p) of- Just d -> first ((d ++ replicate n pathSeparator) </>) (splitP ps)- Nothing -> ("", pt)-- splitP pt = ("", pt)-- fromConst d [] = Just (DL.toList d)- fromConst d (Literal c :xs) = fromConst (d `DL.snoc` c) xs- fromConst d (ExtSeparator :xs) = fromConst (d `DL.snoc` extSeparator) xs- fromConst d (LongLiteral _ s:xs) = fromConst (d `DL.append`DL.fromList s) xs- fromConst _ _ = Nothing
− src/System/FilePath/Glob/Match.hs
@@ -1,157 +0,0 @@--- File created: 2008-10-10 13:29:03--module System.FilePath.Glob.Match (match, matchWith) where--import Control.Exception (assert)-import Data.Char (isDigit, toLower, toUpper)-import Data.Monoid (mappend)-import System.FilePath (isPathSeparator, isExtSeparator)--import System.FilePath.Glob.Base ( Pattern(..), Token(..)- , MatchOptions(..), matchDefault- , tokToLower- )-import System.FilePath.Glob.Utils (dropLeadingZeroes, inRange, pathParts)---- |Matches the given 'Pattern' against the given 'FilePath', returning 'True'--- if the pattern matches and 'False' otherwise.-match :: Pattern -> FilePath -> Bool-match = matchWith matchDefault---- |Like 'match', but applies the given 'MatchOptions' instead of the defaults.-matchWith :: MatchOptions -> Pattern -> FilePath -> Bool-matchWith opts p f = begMatch opts (lcPat $ unPattern p) (lcPath f)- where- lcPath = if ignoreCase opts then map toLower else id- lcPat = if ignoreCase opts then map tokToLower else id---- begMatch takes care of some things at the beginning of a pattern or after /:--- - . needs to be matched explicitly--- - ./foo is equivalent to foo (for any number of /)------ .*/foo still needs to match ./foo though, and it won't match plain foo;--- special case that one------ and .**/foo should /not/ match ../foo; more special casing------ (All of the above is modulo options, of course)-begMatch, match' :: MatchOptions -> [Token] -> FilePath -> Bool-begMatch _ (ExtSeparator:AnyDirectory:_) (x:y:_)- | isExtSeparator x && isExtSeparator y = False--begMatch opts (ExtSeparator:PathSeparator:pat) s | ignoreDotSlash opts =- begMatch opts (dropWhile isSlash pat) s- where- isSlash PathSeparator = True- isSlash _ = False--begMatch opts pat (x:y:s)- | dotSlash && dotStarSlash = match' opts pat' s- | ignoreDotSlash opts && dotSlash = begMatch opts pat s- where- dotSlash = isExtSeparator x && isPathSeparator y- (dotStarSlash, pat') =- case pat of- ExtSeparator:AnyNonPathSeparator:PathSeparator:rest -> (True, rest)- _ -> (False, pat)--begMatch opts pat s =- if not (null s) && isExtSeparator (head s) && not (matchDotsImplicitly opts)- then case pat of- ExtSeparator:pat' -> match' opts pat' (tail s)- _ -> False- else match' opts pat s--match' _ [] s = null s-match' _ (AnyNonPathSeparator:s) "" = null s-match' _ _ "" = False-match' o (Literal l :xs) (c:cs) = l == c && match' o xs cs-match' o ( ExtSeparator :xs) (c:cs) = isExtSeparator c && match' o xs cs-match' o (NonPathSeparator:xs) (c:cs) =- not (isPathSeparator c) && match' o xs cs--match' o (PathSeparator :xs) (c:cs) =- isPathSeparator c && begMatch o xs (dropWhile isPathSeparator cs)--match' o (CharRange b rng :xs) (c:cs) =- let rangeMatch r =- either (== c) (`inRange` c) r ||- -- See comment near Base.tokToLower for an explanation of why we- -- do this- if ignoreCase o- then either (== toUpper c) (`inRange` toUpper c) r- else False- in not (isPathSeparator c) &&- any rangeMatch rng == b &&- match' o xs cs--match' o (OpenRange lo hi :xs) path =- let- (lzNum,cs) = span isDigit path- num = dropLeadingZeroes lzNum- numChoices =- tail . takeWhile (not.null.snd) . map (flip splitAt num) $ [0..]- in if null lzNum- then False -- no digits- else- -- So, given the path "123foo" what we've got is:- -- cs = "foo"- -- num = "123"- -- numChoices = [("1","23"),("12","3")]- --- -- We want to try matching x against each of 123, 12, and 1.- -- 12 and 1 are in numChoices already, but we need to add (num,"")- -- manually.- any (\(n,rest) -> inOpenRange lo hi n && match' o xs (rest ++ cs))- ((num,"") : numChoices)--match' o again@(AnyNonPathSeparator:xs) path@(c:cs) =- match' o xs path || (if isPathSeparator c then False else match' o again cs)--match' o again@(AnyDirectory:xs) path =- let parts = pathParts (dropWhile isPathSeparator path)- matches = any (match' o xs) parts || any (match' o again) (tail parts)- in if null xs && not (matchDotsImplicitly o)- -- **/ shouldn't match foo/.bar, so check that remaining bits don't- -- start with .- then all (not.isExtSeparator.head) (init parts) && matches- else matches--match' o (LongLiteral len s:xs) path =- let (pre,cs) = splitAt len path- in pre == s && match' o xs cs---- Does the actual open range matching: finds whether the third parameter--- is between the first two or not.------ It does this by keeping track of the Ordering so far (e.g. having--- looked at "12" and "34" the Ordering of the two would be LT: 12 < 34)--- and aborting if a String "runs out": a longer string is automatically--- greater.------ Assumes that the input strings contain only digits, and no leading zeroes.-inOpenRange :: Maybe String -> Maybe String -> String -> Bool-inOpenRange l_ h_ s_ = assert (all isDigit s_) $ go l_ h_ s_ EQ EQ- where- go Nothing Nothing _ _ _ = True -- no bounds- go (Just []) _ [] LT _ = False -- lesser than lower bound- go _ (Just []) _ _ GT = False -- greater than upper bound- go _ (Just []) (_:_) _ _ = False -- longer than upper bound- go (Just (_:_)) _ [] _ _ = False -- shorter than lower bound- go _ _ [] _ _ = True-- go (Just (l:ls)) (Just (h:hs)) (c:cs) ordl ordh =- let ordl' = ordl `mappend` compare c l- ordh' = ordh `mappend` compare c h- in go (Just ls) (Just hs) cs ordl' ordh'-- go Nothing (Just (h:hs)) (c:cs) _ ordh =- let ordh' = ordh `mappend` compare c h- in go Nothing (Just hs) cs GT ordh'-- go (Just (l:ls)) Nothing (c:cs) ordl _ =- let ordl' = ordl `mappend` compare c l- in go (Just ls) Nothing cs ordl' LT-- -- lower bound is shorter: s is greater- go (Just []) hi s _ ordh = go Nothing hi s GT ordh
− src/System/FilePath/Glob/Primitive.hs
@@ -1,57 +0,0 @@--- File created: 2009-01-17---- |A number of primitives from which complete 'Pattern's may be constructed.------ Using this together with the functions provided by the 'Monoid' instance of--- 'Pattern' allows for direct manipulation of 'Pattern's beyond what can be--- done with just the 'compile' family of functions. And of course you don't--- have to go via 'String's if you use these.-module System.FilePath.Glob.Primitive- ( literal- , singleWildcard, wildcard, recursiveWildcard- , charRange, numberRange- ) where--import System.FilePath (isPathSeparator, isExtSeparator)--import System.FilePath.Glob.Base (Pattern(..), Token(..), optimize)---- |A 'Pattern' which matches the given 'String' literally.------ Handles any embedded path and extension separators.-literal :: String -> Pattern-literal = optimize . Pattern . map f- where- f c | isPathSeparator c = PathSeparator- | isExtSeparator c = ExtSeparator- | otherwise = Literal c---- |Matches any single character except a path separator: corresponds to the--- @?@ operator.-singleWildcard :: Pattern-singleWildcard = Pattern [NonPathSeparator]---- |Matches any number of characters up to a path separator: corresponds to the--- @*@ operator.-wildcard :: Pattern-wildcard = Pattern [AnyNonPathSeparator]---- |Matches any number of characters including path separators: corresponds to--- the @**/@ operator.-recursiveWildcard :: Pattern-recursiveWildcard = Pattern [AnyDirectory]---- |Matches a single character if it is within the (inclusive) range in any--- 'Right' or if it is equal to the character in any 'Left'. Corresponds to the--- @[]@, @[^]@ and @[!]@ operators.------ If the given 'Bool' is 'False', the result of the match is inverted: the--- match succeeds if the character does /not/ match according to the above--- rules.-charRange :: Bool -> [Either Char (Char,Char)] -> Pattern-charRange b rs = optimize $ Pattern [CharRange b rs]---- |Matches a number in the given range, which may be open, half-open, or--- closed. Corresponds to the @\<\>@ operator.-numberRange :: Maybe Integer -> Maybe Integer -> Pattern-numberRange a b = Pattern [OpenRange (fmap show a) (fmap show b)]
− src/System/FilePath/Glob/Simplify.hs
@@ -1,42 +0,0 @@--- File created: 2009-01-30 14:54:14--module System.FilePath.Glob.Simplify (simplify) where--import System.FilePath.Glob.Base (Pattern(..), Token(..), liftP)---- |Simplifies a 'Pattern' object: removes redundant @\"./\"@, for instance.--- The resulting 'Pattern' matches the exact same input as the original one,--- with some differences:------ * The output of 'globDir' will differ: for example, globbing for @\"./\*\"@--- gives @\"./foo\"@, but after simplification this'll be only @\"foo\"@.------ * Decompiling the simplified 'Pattern' will obviously not give the original.------ * The simplified 'Pattern' is a bit faster to match with and uses less--- memory, since some redundant data is removed.------ For the last of the above reasons, if you're performance-conscious and not--- using 'globDir', you should always 'simplify' after calling 'compile'.-simplify :: Pattern -> Pattern-simplify = liftP (go . pre)- where- -- ./ at beginning -> nothing (any number of /'s)- pre (ExtSeparator:PathSeparator:xs) = pre (dropWhile isSlash xs)- pre xs = xs-- go [] = []-- -- /./ -> /- go (PathSeparator:ExtSeparator:xs@(PathSeparator:_)) = go xs-- go (x:xs) =- if isSlash x- then let (compressed,ys) = span isSlash xs- in if null compressed- then x : go ys- else go (x : ys)- else x : go xs-- isSlash PathSeparator = True- isSlash _ = False
− src/System/FilePath/Glob/Utils.hs
@@ -1,165 +0,0 @@-{-# LANGUAGE CPP #-}--- File created: 2008-10-10 13:40:35--module System.FilePath.Glob.Utils- ( isLeft, fromLeft- , increasingSeq- , addToRange, inRange, overlap- , dropLeadingZeroes- , pathParts- , nubOrd- , partitionDL- , getRecursiveContents- ) where--import Control.Monad (foldM)-import Data.List ((\\))-import qualified Data.DList as DL-import Data.DList (DList)-import qualified Data.Set as Set-import System.Directory (getDirectoryContents)-import System.FilePath ((</>), isPathSeparator, dropDrive)-import System.IO.Unsafe (unsafeInterleaveIO)--#if mingw32_HOST_OS-import Data.Bits ((.&.))-import System.Win32.Types (withTString)-import System.Win32.File (c_GetFileAttributes, fILE_ATTRIBUTE_DIRECTORY)-#else-import Foreign.C.String (withCString)-import Foreign.Marshal.Alloc (allocaBytes)-import System.FilePath- (isDrive, dropTrailingPathSeparator, addTrailingPathSeparator)-import System.Posix.Internals (sizeof_stat, lstat, s_isdir, st_mode)-#endif---import System.IO.Error (catchIOError)--inRange :: Ord a => (a,a) -> a -> Bool-inRange (a,b) c = c >= a && c <= b---- returns Just (a range which covers both given ranges) or Nothing if they are--- disjoint.------ Assumes that the ranges are in the correct order, i.e. (fst x < snd x).-overlap :: Ord a => (a,a) -> (a,a) -> Maybe (a,a)-overlap (a,b) (c,d) =- if b >= c- then if b >= d- then if a <= c- then Just (a,b)- else Just (c,b)- else if a <= c- then Just (a,d)- else Just (c,d)- else Nothing--addToRange :: (Ord a, Enum a) => (a,a) -> a -> Maybe (a,a)-addToRange (a,b) c- | inRange (a,b) c = Just (a,b)- | c == pred a = Just (c,b)- | c == succ b = Just (a,c)- | otherwise = Nothing---- fst of result is in reverse order so that:------ If x = fst (increasingSeq (a:xs)), then--- x == reverse [a .. head x]-increasingSeq :: (Eq a, Enum a) => [a] -> ([a],[a])-increasingSeq [] = ([],[])-increasingSeq (x:xs) = go [x] xs- where- go is [] = (is,[])- go is@(i:_) (y:ys) =- if y == succ i- then go (y:is) ys- else (is, y:ys)- go _ _ = error "Glob.increasingSeq :: internal error"--isLeft :: Either a b -> Bool-isLeft (Left _) = True-isLeft _ = False--fromLeft :: Either a b -> a-fromLeft (Left x) = x-fromLeft _ = error "fromLeft :: Right"--dropLeadingZeroes :: String -> String-dropLeadingZeroes s =- let x = dropWhile (=='0') s- in if null x then "0" else x---- foo/bar/baz -> [foo/bar/baz,bar/baz,baz]-pathParts :: FilePath -> [FilePath]-pathParts p = p : let d = dropDrive p- in if null d || d == p- then f d- else d : f d- where- f [] = []- f (x:xs@(y:_)) | isPathSeparator x && isPathSeparator y = f xs- f (x:xs) =- if isPathSeparator x- then xs : f xs- else f xs---- Significantly speedier than System.Directory.doesDirectoryExist.-doesDirectoryExist :: FilePath -> IO Bool-#if mingw32_HOST_OS--- This one allocates more memory since it has to do a UTF-16 conversion, but--- that can't really be helped: the below version is locale-dependent.-doesDirectoryExist = flip withTString $ \s -> do- a <- c_GetFileAttributes s- return (a /= 0xffffffff && a.&.fILE_ATTRIBUTE_DIRECTORY /= 0)-#else-doesDirectoryExist s =- allocaBytes sizeof_stat $ \p ->- withCString- (if isDrive s- then addTrailingPathSeparator s- else dropTrailingPathSeparator s)- $ \c -> do- st <- lstat c p- if st == 0- then fmap s_isdir (st_mode p)- else return False-#endif--getRecursiveContents :: FilePath -> IO (DList FilePath)-getRecursiveContents dir =- flip catchIOError (\_ -> return $ DL.singleton dir) $ do-- raw <- getDirectoryContents dir-- let entries = map (dir </>) (raw \\ [".",".."])- (dirs,files) <- partitionM doesDirectoryExist entries-- subs <- unsafeInterleaveIO . mapM getRecursiveContents $ dirs-- return$ DL.cons dir (DL.fromList files `DL.append` DL.concat subs)--partitionM :: (Monad m) => (a -> m Bool) -> [a] -> m ([a], [a])-partitionM p_ = foldM (f p_) ([],[])- where- f p (ts,fs) x = p x >>= \b ->- if b- then return (x:ts, fs)- else return (ts, x:fs)--partitionDL :: (a -> Bool) -> DList a -> (DList a, DList a)-partitionDL p_ = DL.foldr (f p_) (DL.empty,DL.empty)- where- f p x (ts,fs) =- if p x- then (DL.cons x ts, fs)- else (ts, DL.cons x fs)--nubOrd :: Ord a => [a] -> [a]-nubOrd = go Set.empty- where- go _ [] = []- go set (x:xs) =- if Set.member x set- then go set xs- else x : go (Set.insert x set) xs
− src/System/Nemesis/Runner.hs
@@ -1,101 +0,0 @@-import Control.Monad hiding (join)-import Data.List (find)-import Data.Time.Clock.POSIX-import Prelude ()-import System.Environment-import System.Cmd-import System.Directory-import System.Nemesis.Util-import System.Time-import System.Nemesis.DSL-import qualified Prelude as P-import Data.Maybe-import System.Exit--start, end :: String-start = start_nemesis + start_nemesis_dsl + init_air + init_prelude- where - start_nemesis = "import System.Nemesis (run)\n"- start_nemesis_dsl = "import System.Nemesis.DSL\n"- init_air = "import Air.Light ((-))\n"- init_prelude = "import Prelude hiding ((-))\n"-end = "\nmain = run nemesis\n"--main :: IO ()-main = do- recompile <- should_recompile- when recompile compile- - args <- getArgs- exit_code <- system - "./.nemesis " + args.join " "- - exitWith exit_code- - where- bin = ".nemesis"- should_recompile = do- bin_exists <- doesFileExist bin- if not bin_exists- then return True- else do- bin_stamp <- bin.getModificationTime- src_stamp <- get_src_name >>= getModificationTime- return - bin_stamp P.< src_stamp---get_src_name :: IO String-get_src_name = do- dir <- ls "."- return - dir.filter (belongs_to possible_source) .get_name- where- possible_source = ["Nemesis", "nemesis", "Nemesis.hs", "nemesis.hs"]- get_name [] = error "Nemesis does not exist!"- get_name xs = xs.first--compile :: IO ()-compile = do- src_name <- get_src_name- let src_o = src_base_name src_name + ".o"- src_hi = src_base_name src_name + ".hi"- src <- readFile src_name- - let maybe_patch_end = yield_string_if_no_line_starts_with main_src_prefix src end- maybe_patch_start = yield_string_if_no_line_starts_with main_src_prefix src start- - __nem_seperated_header = src.lines.takeWhile (lower > starts_with sep > not) .unlines- __nem_seperated_footer = src.lines.dropWhile (lower > starts_with sep > not) .unlines-- if (maybe_patch_end.isNothing && maybe_patch_start.isNothing && src_name.ends_with ".hs")- then do- sh - "ghc --make -O1 " + src_name + " -o " + bin- rm src_o- rm src_hi- else do- if __nem_seperated_footer.null- then output_tmp - maybe_patch_start.fromMaybe "" + __nem_seperated_header + maybe_patch_end.fromMaybe ""- else output_tmp - __nem_seperated_header + maybe_patch_start.fromMaybe "" + "\n" + __nem_seperated_footer + maybe_patch_end.fromMaybe ""- - sh - "ghc --make -O1 " + tmp_name + " -o " + bin- rm tmp_name- rm tmp_o- rm tmp_hi- - where-- main_src_prefix = "main ="- - sep = "-- nem"- output_tmp = writeFile tmp_name- tmp_name = "nemesis-tmp.hs"- tmp_o = "nemesis-tmp.o"- tmp_hi = "nemesis-tmp.hi"- bin = ".nemesis"- src_base_name s = if s.ends_with ".hs" - then s.reverse.drop 3.reverse- else s- - yield_string_if_no_line_starts_with prefix str yield = case str.lines.find (starts_with prefix) of- Nothing -> Just yield- Just _ -> Nothing- -
+ test/N1.hs view
@@ -0,0 +1,19 @@+import System.Nemesis.Env++main = run $ do++ clean+ [ "**/*.hi"+ , "**/*.o"+ , "manifest"+ ]+ + task "dist" $ do+ sh "cabal clean"+ sh "cabal configure"+ sh "cabal sdist"++ task "i" (sh "ghci -isrc src/System/Nemesis.hs")++ task "manifest" $ do+ sh "find . | grep 'hs$' > manifest"
+ test/N2.hs view
@@ -0,0 +1,22 @@+import System.Nemesis.Env+import Air.Env ((-))+import Prelude hiding ((-))++main = run - do++ -- desc is optional, it gives some description to the task+ -- task syntax: task "keyword: space seperated dependencies" io-action+ desc "Hunter attack macro"+ task "attack: pet-attack auto-attack" (putStrLn "attack macro done!")++ desc "Pet attack"+ task "pet-attack: mark" - do+ sh "echo 'pet attack'"++ desc "Hunter's mark"+ task "mark" - do+ sh "echo \"casting hunter's mark\""++ desc "Auto attack"+ task "auto-attack" - do+ sh "echo 'auto shoot'"
+ test/N3.hs view
@@ -0,0 +1,15 @@+import System.Nemesis.Env+import Air.Env ((-))+import Prelude hiding ((-))++main = run - do++ namespace "eat" - do++ task "bread: salad" - putStrLn "eating bread"+ task "salad: /drink/coke" - putStrLn "eating salad"+++ namespace "drink" - do++ task "coke" - putStrLn "drinking coke"