eddie 0.1 → 0.2
raw patch · 3 files changed
+53/−30 lines, 3 files
Files
- eddie.cabal +1/−1
- eddie.hs +50/−28
- test.sh +2/−1
eddie.cabal view
@@ -1,5 +1,5 @@ Name: eddie-Version: 0.1+Version: 0.2 Synopsis: Command line file filtering with haskell Description: A tool to let you use short haskell expressions to filter files at the command line.
eddie.hs view
@@ -3,24 +3,32 @@ -- import Data.ByteString.Lazy (readFile) import Language.Haskell.Interpreter (setImportsQ, interpret, runInterpreter, as, MonadInterpreter)-import Data.List (intercalate) import Data.IORef (newIORef, readIORef, writeIORef) import System.Environment (getArgs, getProgName)-import System.IO (openFile, hClose, stdin, IOMode (ReadMode), hIsEOF, hGetChar, withFile)+import System.IO (openFile, openBinaryFile, hClose, stdin, IOMode (ReadMode), + hIsEOF, hGetChar, Handle, hSetBinaryMode, stdout) import System.IO.Unsafe (unsafeInterleaveIO) import System.Console.CmdArgs.Implicit (cmdArgs, (&=), Data, Typeable, help, explicit, name, args, summary, program, details) import Control.Exception (finally, evaluate)+import Control.Monad (when) main = do- myname <- getProgName- opts <- cmdArgs (eddie &= program myname)- let opts' = parseOpts opts- fun <- runInterpreter $ makeFun opts'- case fun of- Left e -> putStrLn $ myname ++ ": Error: " ++ show e- Right f -> withFiles (files opts') (putStrLn . f)+ myName <- getProgName+ opts <- cmdArgs (eddie &= program myName)+ case parseOpts opts of+ Left e -> putStrLn $ "usage: " ++ myName ++ " " ++ e+ Right o -> runIt myName o+ where runIt name opts = do+ fun <- runInterpreter $ makeFun opts+ let opener = if binary opts then openBinaryFile else openFile+ let outputFunction = if binary opts then putStr else putStrLn+ when (binary opts) $+ hSetBinaryMode stdout True+ case fun of+ Left e -> putStrLn $ name ++ ": Error: " ++ show e+ Right f -> withFiles (files opts) opener (outputFunction . f) makeFun :: MonadInterpreter m => Eddie -> m (String->String) makeFun opts = do@@ -30,13 +38,15 @@ -- an even lazier version of withFile (courtesy of Heinrich Apfelmus) -- Tweaked by mwm so withFiles uses stdin if [FilePath] is an empty list-withFile' :: Maybe FilePath -> (String -> IO a) -> IO a-withFile' name f = do+-- Further tweaked by mwm to allow caller to specify opening function.+withFile' :: Maybe FilePath -> (FilePath -> IOMode -> IO Handle) ->+ (String -> IO a) -> IO a+withFile' name opener f = do fin <- newIORef (return ()) let close = readIORef fin >>= id open = do- h <- maybe (return stdin) (flip openFile ReadMode) name+ h <- maybe (return stdin) (flip opener ReadMode) name writeIORef fin (hClose h) lazyRead h finally (unsafeInterleaveIO open >>= f >>= evaluate) close@@ -51,33 +61,45 @@ return (c:cs) -withFiles :: [FilePath] -> (String -> IO a) -> IO a-withFiles [] f = withFile' Nothing f-withFiles [x] f = withFile' (Just x) f-withFiles (x:xs) f = withFile' (Just x) $ \s ->- let f' t = f (s ++ t) in withFiles xs f'+withFiles :: [FilePath] -> (FilePath -> IOMode -> IO Handle) -> + (String -> IO a) -> IO a+withFiles [] o f = withFile' Nothing o f+withFiles [x] o f = withFile' (Just x) o f+withFiles (x:xs) o f = withFile' (Just x) o $ \s ->+ let f' t = f (s ++ t) in withFiles xs o f' -- argument processing data Eddie = Eddie { line :: Bool,+ binary :: Bool, expr :: [String], files :: [String], modules :: [String], asModules :: [(String, Maybe String)] } deriving (Show, Data, Typeable) -parseOpts :: Eddie -> Eddie-parseOpts opts = opts {expr = [e], asModules = mods, files = fs' }- where es = expr opts- fs = files opts- e:fs' = if null es then fs else intercalate "\n" es:fs- mods = zip (modules opts) (repeat Nothing) ++ asModules opts+parseOpts :: Eddie -> Either String Eddie+parseOpts opts = + let es = expr opts+ fs = files opts+ e:fs' = case (es, fs) of+ ([], []) -> [""]+ ([], _) -> fs+ (_, _) -> unlines es:fs+ mods = zip (modules opts) (repeat Nothing) ++ asModules opts+ in+ if e == "" then Left $ unlines ["[options] (-e expr | expr) [files ...]",+ "--help for options"]+ else Right $ opts {expr = [e], asModules = mods, files = fs' }+ -eddie = Eddie {line = False &= help "Process line at a time.",- expr = [] &= help "Line of expression to evaluate.",+eddie = Eddie {line = False &= help "Process one line at a time",+ binary = False &= help "Process a binary file",+ expr = [] &= help "Line of expression to evaluate", modules = ["Prelude", "Data.List", "Data.Char"]- &= help "Modules to import for expr.",- asModules = [] &= help "Modules to import qualified." &= explicit+ &= help "Modules to import for expr",+ asModules = [] &= help "Modules to import qualified" &= explicit &= name "M" &= name "Modules", files = [] &= args} - &= summary "eddie 0.1" &= details ["Haskell for shell scripts."]+ &= summary "eddie 0.2" &= details ["Haskell for shell scripts."]+
test.sh view
@@ -22,12 +22,13 @@ } FAILED="no"+test "" "echo 'usage: eddie.hs [options] (-e expr | expr) [files ...]'; echo '--help for options'" "No arguments test" test "show.length" "wc -c | xargs echo" "simple char count" test "show.length.lines" "wc -l | xargs echo" "line count" test "show.length.words" "wc -w | xargs echo " "word count" test "-e show.length" "wc -c | xargs echo" "test -e option" test "-e 'let taker = take 10 in' -e unlines.taker.lines" head "double -e option test"-test "-l reverse /etc/motd" "rev eddie.hs" "test -l option" X+test "-l reverse" "rev eddie.hs" "test -l option" X test "-m Text.Printf 'unlines . (zipWith (printf \"%6d\t%s\") [(1::Int) ..]) . lines'" 'cat -n' 'test -m option' X test "-M Text.Printf,P 'unlines . (zipWith (P.printf \"%6d\t%s\") [(1::Int) ..]) . lines'" 'cat -n' 'test -M option' X echo -e