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.

Pages: 1 2

2 Responses to “Grep”

  1. […] today’s Programming Praxis exercise we’re supposed to implement grep based on the regex matching […]

  2. Remco Niemeijer said

    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
    

Leave a comment