csv.lisp (2466 bytes)
1 (in-package :csv) 2 (defun make-escapep (prev curr) 3 (char= #\" prev curr)) 4 (defun end-tokenp (prev curr) 5 (declare (ignore curr)) 6 (char= #\" prev)) 7 (defun openp (prev curr) 8 (declare (ignore curr)) 9 (char= #\" prev)) 10 (defun str->list (str) 11 (labels ((inner (str acc len) 12 (if (= 0 len) 13 (nreverse acc) 14 (inner (subseq str 1) (cons (elt str 0) acc) (1- len))))) 15 (inner str nil (length str)))) 16 (let ((in nil) 17 (escape nil)) 18 (setf (fdefinition 'switches) 19 (lambda (prev curr str) 20 (let ((str (if (array-has-fill-pointer-p str) 21 str 22 (make-array (length str) 23 :fill-pointer 0 24 :adjustable t 25 :element-type 'character 26 :initial-contents (str->list str))))) 27 (if (null curr) 28 (progn (setf in nil escape nil) str) 29 (if in 30 (if escape 31 (progn (toggle escape) (push-on prev str)) 32 (if (make-escapep prev curr) 33 (progn (toggle escape) str) 34 (if (end-tokenp prev curr) 35 (progn (toggle in) (push-on #\Nul str)) 36 (push-on prev str)))) 37 (if (openp prev curr) 38 (progn (toggle in) str) 39 str))))))) 40 (defun splice (str) 41 (labels ((inner (str acc!) 42 (if (string= "" str) 43 (nreverse acc!) 44 (let ((res (search (string #\Nul) str))) 45 (inner (subseq str (1+ res)) (push (subseq str 0 res) acc!)))))) 46 (inner str nil))) 47 (make-reader my-csv (stream #\# #\!) 48 (macrolet ((once (&body body) ``',,@body)) 49 (do ((prev #1= (read-char stream nil nil) curr) 50 (curr #1# #1#) 51 (str (make-array 0 :adjustable t :element-type 'character) 52 (switches prev curr str))) 53 ((and (char= curr #\#) 54 (char= prev #\!)) (once (splice (switches nil nil str))))))) 55 (defun read-to-string (stream) 56 (with-output-to-string (s) 57 (dolines (line stream) 58 (princ line s) 59 (format s "~%")))) 60 (defun slurp-csv (path) 61 "Read in a CSV file to string. Resource intensive." 62 (with-open-file (s path) 63 (concatenate 'string "#!" (read-to-string s) "!#"))) 64 (defun dequote (list) 65 (if (eql 'quote (car list)) 66 (cadr list) 67 list)) 68 (defun slurp (path) 69 "Read in a CSV file to list. Resource intensive." 70 (dequote (read-from-string (slurp-csv path))))