Recently Written · git

CSV

Convert CSV (in a reader macro, or in a file) in to Lisp lists.

git clone https://github.com/equwal/CSV

Log | Files | Refs


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))))