eddie 0.2 → 0.5
raw patch · 12 files changed
+307/−115 lines, 12 files
Files
- eddie.1 +108/−34
- eddie.cabal +2/−2
- eddie.hs +68/−37
- test.sh +0/−42
- tests/counters.test +23/−0
- tests/error.test +3/−0
- tests/expr.test +27/−0
- tests/files.test +9/−0
- tests/imports.test +17/−0
- tests/lines.test +27/−0
- tests/test.data +12/−0
- tests/usage.test +11/−0
eddie.1 view
@@ -2,13 +2,9 @@ .SH NAME eddie \- run haskell filters from the command line .SH SYNOPSYS-.B eddie -[-.I options-]-[-.I expr-]+.B eddie+.RI [ options ]+.RI [ expr ] .I file ... .SH DESCRIPTION .B eddie@@ -19,44 +15,53 @@ arguments concatenated together, or standard input if no file arguments are present. .PP-If the-.B \-l-option is used, the +The+.BR \-\-line ,+.B \-\-file+and+.B \-\-list+options can be used to cause .I expression-is used to process each line instead of the entire contents.+to be used to process each line, each file,+or a list of files or lines respectively. .PP The prelude, Data.List and Data.Char modules are available for building expressions. Other modules may be added with the-.B \-M+.B \-\-Modules and-.B \-m+.B \-\-modules options. .SH OPTIONS-.IP \-e+.IP \-\-expr The-.BI \-e\ expr-(long form-.BI \-\-expr\ expr)+.B \-\-expr+.I expr+(short form+.B \-e+.IR expr ) option concatenates it's value to the haskell expression being evaluated with a newline separator. Multiple occurrences can be used to build up a multi-line expression.-.IP ""+.RS+.PP If no-.BI \-e\ expr+.B \-e+.I expr option is present, the first non-flag argument will be used for the haskell expression.-.IP \-l+.RE 1+.IP \-\-line The-.B \-l-(long form .B \-\-line-)+(short form+.BR \-l ) option causes .B eddie to process input line at a time. The .I expr-will be run on each line and the results concatenated together.+will be run on each line and the results concatenated together+(using unlines). .RS .PP The command@@ -71,32 +76,101 @@ .PP .B eddie "unlines . map+.IR expr .+lines"+.I file ...+.+.RE 1+.IP \-\-file+The+.B \-\-file+(short form+.BR \-f )+causes each file to be processed individually.+.RS+.PP+Note that the+.B \-l+and+.B \-f+options cannot be used together.+.RE 1+.IP \-\-list+The+.B \-\-list+(short form+.BR \-L )+option causes+.B eddie+to pass the expression a list of lines or files.+This requires one of the+.B \-l+or+.B \-f+options be used.+.RS+.PP+The command+.RS+.PP+.B eddie \-lL+.I expr file ...+.RE+.PP+is equivalent to the command+.RS+.PP+.B eddie+"unlines . .I expr \&. lines" .I file ... . .RE 1-.IP \-m+.IP \-\-binary The-.BI \-m name-(long form-.BI \-\-module name-)+.B \-\-binary+(short form+.BR \-b )+option switches the output mode to binary.+It also disables the maybe newline processing.+Normally +.B eddie+will output a final newline if the last character+of the input is not a newline to place the prompt properly.+If the+.B \-b+option is used, the final newline will not be output.+.RE 1+.IP \-\-modules+The+.B \-\-modules+.I name+(short form+.B \-m +.IR name ) option is used to import module .I name into haskell before evaluating the expression.-.IP \-M+.IP \-\-Modules The-.BI \-M name,as-(long form-.BI \-\-Modules name,as-)+.B \-\-Modules+.I name,as+(short form+.B \-M+.IR name,as ) option imports the .I name module using a qualified import with an as clause, with .I as being the value for the as clause.+.SH DIAGNOSTICS+If the command runs with no problems, eddie will exit with a status of 0.+If there are problems with the options, such that the expression is never+evaluated, then eddie will exit with a status of 1.+If haskell returns an error - probably a compilation problem - then+eddie will exit with the status of 2. .SH SEE ALSO The wiki at <http://code.google.com/p/eddie/w/list> for examples. .SH BUGS
eddie.cabal view
@@ -1,5 +1,5 @@ Name: eddie-Version: 0.2+Version: 0.5 Synopsis: Command line file filtering with haskell Description: A tool to let you use short haskell expressions to filter files at the command line.@@ -12,7 +12,7 @@ Build-type: Simple Homepage: http://eddie.googlecode.com/ Cabal-version: >=1.8-Extra-source-files: test.sh eddie.1+Extra-source-files: eddie.1 tests/*.test tests/*.data Executable eddie Main-is: eddie.hs
eddie.hs view
@@ -1,76 +1,103 @@+#!/usr/bin/env runghc {-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE GADTs #-} -- import Data.ByteString.Lazy (readFile) import Language.Haskell.Interpreter (setImportsQ, interpret, runInterpreter, as, MonadInterpreter) import Data.IORef (newIORef, readIORef, writeIORef) import System.Environment (getArgs, getProgName)-import System.IO (openFile, openBinaryFile, hClose, stdin, IOMode (ReadMode), - hIsEOF, hGetChar, Handle, hSetBinaryMode, stdout)+import System.IO+ (openFile, openBinaryFile, hClose, stdin, IOMode (ReadMode), hIsEOF,+ hGetChar, Handle, hSetBinaryMode, stdout, stderr, hPutStrLn) import System.IO.Unsafe (unsafeInterleaveIO) import System.Console.CmdArgs.Implicit (cmdArgs, (&=), Data, Typeable, help, explicit, name, args, summary, program, details)+import System.Exit (exitWith, ExitCode (..)) import Control.Exception (finally, evaluate)-import Control.Monad (when)+import Control.Monad (when, liftM, join) main = do myName <- getProgName opts <- cmdArgs (eddie &= program myName) case parseOpts opts of- Left e -> putStrLn $ "usage: " ++ myName ++ " " ++ e+ Left e -> do hPutStrLn stderr $ "usage: " ++ myName ++ " " ++ e+ exitWith (ExitFailure 1) 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+ let outputFunc = putStrMaybeLn (binary opts)+ when (binary opts) $ hSetBinaryMode stdout True+ fun <- runInterpreter $ makeFun opts case fun of- Left e -> putStrLn $ name ++ ": Error: " ++ show e- Right f -> withFiles (files opts) opener (outputFunction . f)+ Left e -> do hPutStrLn stderr $ name ++ ": Error: " ++ show e+ exitWith (ExitFailure 2)+ Right f -> withFiles (files opts) opener (outputFunc . f) -makeFun :: MonadInterpreter m => Eddie -> m (String->String)+putStrMaybeLn :: Bool -> String -> IO ()+putStrMaybeLn binary val = + (if binary || last val == '\n' then putStr else putStrLn) val+++makeFun :: MonadInterpreter m => Eddie -> m ([String] -> String) makeFun opts = do setImportsQ (asModules opts)- fun <- interpret (head (expr opts)) (as :: String -> String)- return $ if line opts then unlines . map fun . lines else fun+ eval (head (expr opts)) mode+ where mode | line opts && list opts = Mode (as :: [String] -> [String]) id+ (lines . concat) unlines+ | line opts = Mode (as :: String -> String) map + (lines . concat) unlines+ | file opts && list opts = Mode (as :: [String] -> [String]) id + id concat+ | file opts = Mode (as :: String -> String) map id concat+ | otherwise = Mode (as :: String -> String) id concat id ++data Mode where+ Mode :: (Typeable a, Typeable b)+ => (a -> b) -- witness to `interpret`+ -> ((a -> b) -> c -> d) -- application of interpreted function+ -> ([String] -> c) -- preprocess+ -> (d -> String) -- postprocess+ -> Mode++eval :: MonadInterpreter m => String -> Mode -> m ([String] -> String)+eval s (Mode f t bra ket) = liftM ((ket .) . (. bra) . t) $ interpret s f+ -- an even lazier version of withFile (courtesy of Heinrich Apfelmus)--- Tweaked by mwm so withFiles uses stdin if [FilePath] is an empty list--- 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+-- Tweaked by mwm to accept a handle instead of a file so caller can+-- use favorite opening function or pass in stdin.+withFile :: IO Handle -> (String -> IO a) -> IO a+withFile ih f = do fin <- newIORef (return ())- let- close = readIORef fin >>= id+ h <- ih+ let close = join (readIORef fin) open = do- h <- maybe (return stdin) (flip opener ReadMode) name writeIORef fin (hClose h) lazyRead h finally (unsafeInterleaveIO open >>= f >>= evaluate) close - where- lazyRead h = hIsEOF h >>= \b ->- if b+ where lazyRead h = hIsEOF h >>= \b ->+ if b then do hClose h; return []- else do- c <- hGetChar h- cs <- unsafeInterleaveIO $ lazyRead h- return (c:cs)+ else do c <- hGetChar h+ cs <- unsafeInterleaveIO $ lazyRead h+ return (c:cs) 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'+ ([String] -> IO a) -> IO a+withFiles [] o f = withFile (return stdin) (f . (:[]))+withFiles [x] o f = withFile (o x ReadMode) (f . (:[]))+withFiles (x:xs) o f = withFile (o x ReadMode) $ \s ->+ let f' t = f (s:t) in withFiles xs o f' -- argument processing data Eddie = Eddie { line :: Bool,+ file :: Bool,+ list :: Bool, binary :: Bool, expr :: [String], files :: [String],@@ -88,18 +115,22 @@ (_, _) -> unlines es:fs mods = zip (modules opts) (repeat Nothing) ++ asModules opts in- if e == "" then Left $ unlines ["[options] (-e expr | expr) [files ...]",+ if e == "" || (file opts && null fs') || (file opts && line opts)+ || (list opts && not (file opts || line opts))+ 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 one line at a time",+eddie = Eddie {line = False &= help "Process one line at a time (conflicts with --file)",+ file = False &= help "Process files individually (requires at least one file name)",+ list = False &= help "Process the list of files/lines (requires --line or --file)" &= name "L", binary = False &= help "Process a binary file",- expr = [] &= help "Line of expression to evaluate",+ expr = [] &= help "Line of expression to evaluate" &= name "e", modules = ["Prelude", "Data.List", "Data.Char"] &= help "Modules to import for expr", asModules = [] &= help "Modules to import qualified" &= explicit &= name "M" &= name "Modules", files = [] &= args} - &= summary "eddie 0.2" &= details ["Haskell for shell scripts."]+ &= summary "eddie 0.5" &= details ["Haskell for shell scripts."]
− test.sh
@@ -1,42 +0,0 @@-#!/bin/sh--# $1 is args to use with eddie.hs to process a file, $2 is a shell command to-# run the same process. Run them both on our test file.-# If the outputs differ, report failure of $3, including output unless $4-# is present.-test () {- TEST=$(eval runghc eddie.hs $1 < eddie.hs)- EXPECTED=$(eval "cat eddie.hs | $2")- if [ "$EXPECTED" != "$TEST" ]- then- FAILED="yes"- echo -e- if [ -n "$4" ]- then- echo $3 failed "(output supressed)"- else- echo $3 failed: expected $EXPECTED, got $TEST- fi- fi- echo -n .-} --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" "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--if [ $FAILED = "yes" ]-then- exit 1-else- exit 0-fi-
+ tests/counters.test view
@@ -0,0 +1,23 @@+eddie show.length+<<<+asdf+>>>+5+>>>= 0++eddie show.length.lines+<<<+1+2+3+4+>>>+4+>>>= 0++eddie show.length.words+<<<+now is the time for all good programmers to come to haskell+>>>+12+>>>= 0
+ tests/error.test view
@@ -0,0 +1,3 @@+eddie the.lines+>>>2 /Error: /+>>>= 2
+ tests/expr.test view
@@ -0,0 +1,27 @@+eddie -e show.length.words+<<<+now is the time for all good programmers to come to haskell+>>>+12+>>>= 0++eddie -e "let taker = take 4 in" -e unlines.taker.lines+<<<+now+is+the+time+for+all+good+programmers+to+come+to+haskell.+>>>+now+is+the+time+>>>= 0
+ tests/files.test view
@@ -0,0 +1,9 @@+eddie -fL "(:[]).show.length" tests/test.data tests/test.data+>>>+2+>>>= 0++eddie -f "show.length" tests/test.data tests/test.data+>>>+6161+>>>= 0
+ tests/imports.test view
@@ -0,0 +1,17 @@+eddie -m GHC.Exts the.lines+<<<+aaaa+aaaa+aaaa+>>>+aaaa+>>>= 0++eddie -M GHC.Exts,E E.the.lines+<<<+aaaa+aaaa+aaaa+>>>+aaaa+>>>= 0
+ tests/lines.test view
@@ -0,0 +1,27 @@+eddie -l reverse+<<<+now+is+the+time+>>>+won+si+eht+emit+>>>= 0++eddie -lL "take 4"+<<<+now+is+the+time+for+all+>>>+now+is+the+time+>>>= 0
+ tests/test.data view
@@ -0,0 +1,12 @@+now+is+the+time+for+all+good+programmers+to+come+to+haskell.
+ tests/usage.test view
@@ -0,0 +1,11 @@+eddie+>>>2 /usage:/+>>>= 1++eddie -lf reverse+>>>2 /usage:/+>>>= 1++eddie -L reverse+>>>2 /usage:/+>>>= 1