(defpackage streamcsv)
(in-package streamcsv)

(defun read-headings (in) "
(read-headings in)
(Values (list <first line READs>))
"
(let ((line (read-line in)))
 (labels ((get-heading (stream list)
           (let ((heading (read stream nil nil)))
            (if heading
             (get-heading stream (push heading list))
             (nreverse list)))))
  (with-input-from-string (str line)
   (get-heading str (list))))))

(defvar *assoclosures* (list))

(defun rw-each (headings in out) "
(rw-each headings in out)
Like (mapl (lambda (x) (apply (cdar x) (list in out))))

Read (describe 'mapl)
"
(when headings
 (apply (cdr (assoc (car headings) *assoclosures*
                   :test 'string=))
       (list in out))
 (rw-each (cdr headings) in out)))

(defun rw-rows (headings in out) "
(rw-rows headings in out)
peeks for eof before doing #'rw-each
"
(let ((peek1 (peek-char t in nil nil))
      (peek2 (peek-char nil in nil nil)))
 (when peek2
  (rw-each (copy-list headings) in out)
  (rw-rows headings in out))))

;;;An alist like
;;;(("string" . (lambda (&optional in out) 'foo)))
(setq *assoclosures*
(list
 (cons "this"
  (let ((last-val nil))
   (lambda (&optional in out)
    (if (and in out)
     (format out "\"~a:~a\" " "this"
            (setf last-val (read in)))
     last-val))))
 (cons "that"
  (let ((last-val nil))
   (lambda (&optional in out)
    (if (and in out)
     (format out "\"~a:~a\" " "that"
            (setf last-val (read in)))
     last-val))))
 (cons "the"
  (let ((last-val nil))
   (lambda (&optional in out)
    (if (and in out)
     (format out "\"~a:~a\" " "the"
            (setf last-val (read in)))
     last-val))))
 (cons "other"
  (let ((last-val nil))
   (lambda (&optional in out)
    (if (and in out)
     (format out "\"~a:~a\" " "other"
            (setf last-val (read in nil nil)))
     last-val))))
 (cons "rowsum"
  (let ((last-val nil))
   (lambda (&optional in out)
    (if (and in out)
     (format out "\"~a:~a\"~%" "rowsum"
      (setf last-val
           (reduce '+
            (mapcan (lambda (x)
                     (let ((n (funcall (cdr x))))
                      (and n (list n))))
             *assoclosures*))))
    last-val))))))

;;;Test space delimited READable "csv" input.
(defvar *test-input* "\"this\" \"that\" \"the\" \"other\"
1 2 3 4
5 6 7 8
9 10 11 12
")

;;;Test.
(with-input-from-string (in *test-input*)
(let ((headings (append (read-headings in) (list "rowsum"))))
 (terpri)
 (rw-rows headings in t)))

(terpri)
(ext:quit)