; -*- Scheme -*-
;
; $Id: bawk01.scm,v 1.1 1998/03/16 07:57:37 foner Exp $
;
;-------------
;
; BAWK - Bloated AWK
;
;-------------

(require 'read-line)
(require 'bawk:table)
(require 'char-set:whitespace)
(require 'string:split)

;------------

(define bawk::NF #f)
(define bawk::split-line #f)
(define bawk::line #f)

; In an attempt to avoid unnecessary work, a line is not automatically
; split as it is read.  Instead, splitting is delayed until an operation
; that requires the split line is applied.

(define bawk::split
  (lambda ()
    (let ((sl (string:split bawk::line bawk:FS)))
      (set! bawk::split-line (list->vector sl))
      (set! bawk::NF (vector-length bawk::split-line)))))


;+doc
; variable:  bawk:NR
; signature: int
;
; Contains the current line number in the file being processed.  This
; is 1 based.  This is equivalent to NR in AWK.
;-doc

(define bawk:NR 1)


;+doc
; procedure: bawk:NF
; signature: () -> int
;
; Returns the number of fields in the current line.
; This is equivalent to NF in AWK, but note that this is a procedure
; _not_ a variable.
;-doc

(define bawk:NF
  (lambda ()
    (if (not (vector? bawk::split-line)) (bawk::split))
    bawk::NF))


;+doc
; variable:  bawk:FS
; signature: (char|string|char-set)
;
; Contains the current field separator to be used to split a line.
; This is equivalent to FS in AWK.  The default is whitespace.
;-doc

(define bawk:FS char-set:whitespace)


;+doc
; procedure: bawk:$
; arguments: pos
; signature: int -> string
;
; Returns the nth string in the current line.
; If POS is zero, the whole line is returned.
; If POS is greater than the number of strings, "" is returned.
; This procedure is a substitute for the "$" variables in AWK.
;-doc

(define bawk:$
  (lambda (n)
    (if (zero? n)
	bawk::line
	(begin
	  (if (not (vector? bawk::split-line)) (bawk::split))
	  (if (<= n (vector-length bawk::split-line))
	      (vector-ref bawk::split-line (- n 1))
	      "")))))

;+doc
; procedure: bawk:awk
; arguments: action 
; signature: (() -> unspecified) -> unspecified
; arguments: action port
; signature: (() -> unspecified) x port -> unspecified
; pre:       (input-port? port)
;
; Applies ACTION to each line read from PORT or from (CURRENT-INPUT-PORT)
; if PORT is not defined.
;
; > (bawk:bawk 
; >   (lambda () 
; >     (display bawk:NR) (display " ") (display (bawk:$ 0)) (newline)))
;
; This prints out the (CURENT-INPUT-PORT) line by line with the line
; number at the start of the line.  This is, therefore a naive version
; of BSD "cat -n" or SYS V "nl" 
;-doc

(define bawk:awk
  (lambda (a . opt)
    (let ((ip (if (null? opt) (current-input-port) (car opt))))
      (let loop ((l (read-line ip)) (ln 0))
	(if (eof-object? l)
	    l				; arbitrary
	    (begin 
	      (set! bawk:NR ln)
	      (set! bawk::line l)
	      (set! bawk::split-line #f)
	      (a)
	      (loop (read-line ip) (+ ln 1))))))))

;+doc
; procedure: bawk:file
; arguments: file action
; signature: string x (() -> unspecified) -> unspecified
;
; Opens FILE as an input port and applies ACTION to each line read
; from PORT (which is open on FILE).
;-doc

(define bawk:file
  (lambda (f a)
    (let ((ip (open-input-file f)))
      (bawk:awk a ip)
      (close-input-port ip))))

; eof
