Grep
September 25, 2009
We write the program in stages: main interprets command-line arguments and initializes the matcher, do-args processes the optional filename arguments, and do-input searches a single file. Here is the complete program:
#! /usr/bin/scheme --script
; grep [-v] [file ...]
. . . cut and paste from standard prelude and regular-expression library . . .
(define (do-input match? filename)
(do ((line (read-line) (read-line))) ((eof-object? line))
(when (match? line)
(when filename (display filename) (display ": "))
(display line) (newline))))
(define (do-args match? args)
(cond ((null? args) (do-input match? #f))
((null? (cdr args))
(with-input-from-file (car args)
(lambda () (do-input match? #f))))
(else (do ((args args (cdr args))) ((null? args))
(with-input-from-file (car args)
(lambda () (do-input match? (car args))))))))
(define (main args)
(if (string=? (car args) "-v")
(do-args
(complement (lambda (str) (rx-match? (make-rx (cadr args)) str)))
(cddr args))
(do-args (lambda (str) (rx-match? (make-rx (car args)) str)) (cdr args))))
(main (cdr (command-line)))
The header line and the command-line function are specific to Chez Scheme, and may have to change for other Scheme systems. Main builds a matcher on the fly, storing the compiled regular expression in its closure; the -v option is handled by complement, which takes a function that returns a boolean and returns a new function that returns the opposite.
You can see the program with all of its declarations at http://programmingpraxis.codepad.org/qkOnYNIC. When, complement and read-line are from the Standard Prelude; the remaining functions come from the regular expression library of the previous exercises.
[…] today’s Programming Praxis exercise we’re supposed to implement grep based on the regex matching […]
My Haskell solution (see http://bonsaicode.wordpress.com/2009/09/25/programming-praxis-grep/ for a version with comments):
import System.Environment import Text.Printf --Code from the previous two exercises goes here grepString :: Bool -> Maybe String -> [Chunk] -> String -> IO () grepString b p cs = mapM_ (printf "%s%s\n" $ maybe "" (++ ": ") p) . filter ((== b) . match cs) . lines grep :: Bool -> [String] -> IO () grep _ [] = error "Not enough arguments provided" grep b (r:ps) = either print (f ps) $ parseRegex r where f [] cs = grepString b Nothing cs =<< getLine f _ cs = mapM_ (\p -> grepString b (Just p) cs =<< readFile p) ps main :: IO () main = f =<< getArgs where f ("-v":args) = grep False args f args = grep True args