Source File csv/csv.ils
1 2
3 4
5 6 7
8 9 10 11 12 13 14 15
16 17
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
37 38
39 40 41 42 (defclass VedaCsvParser ()
43 ((filename @initarg filename)
44 (line @initform 1)
45 (cell @initform nil)
46 (row @initform nil)
47 (rows @initform nil)))
48
49 50
51 (define VedaCsvParseLines nil)
52 (define VedaCsvParsePort nil)
53 (define VedaCsvParseFile nil)
54
55 56 (let ()
57 58
59 (define asciiQuotationMark
60 (getchar "\"" 1))
61
62 (define asciiComma
63 (getchar "," 1))
64
65 (define asciiLineFeed
66 (intToChar 0xa))
67
68 (define asciiCarriageReturn
69 (intToChar 0x0d))
70
71 (define endOfRow
72 (list nil asciiLineFeed))
73
74 75 (defun MakeParser (filename)
76 (makeInstance 'VedaCsvParser ?filename filename))
77
78 79
80 (defun FinishCell (p)
81 (letseq ((str (buildString (reverse p->cell) ""))
82 (newrow (cons str p->row)))
83 (p->row = newrow)
84 (p->cell = nil)))
85
86 (defun FinishRow (p)
87 (let ((newrows (cons (reverse p->row) p->rows)))
88 (p->rows = newrows)
89 (p->row = nil)
90 (p->cell = nil)))
91
92 (defun Rows (p)
93 (reverse p->rows))
94
95 (defun PushToCell (p c "gs")
96 (let ((newcell (cons c p->cell)))
97 (p->cell = newcell)))
98
99 (defun Error (p msg "gt")
100 (let (pieces)
101 (push (sprintf nil "%L" p->line) pieces)
102 (push (or p->filename "<input>") pieces)
103 (let ((prefix (buildString pieces ":")))
104 (error "%s: %s" prefix msg))))
105
106 107
108 109 110 (defun NewRowState (p c)
111 (unless (null c)
112 (p->row = nil)
113 (p->cell = nil)
114 (CellState p c)))
115
116 117 (defun CellState (p c)
118 (cond
119 ((eq c asciiComma)
120 (FinishCell p)
121 CellState)
122 ((eq c asciiQuotationMark)
123 QuotedCellState)
124 ((memq c endOfRow)
125 (FinishCell p)
126 (FinishRow p)
127 (when c
128 NewRowState))
129 ((eq c asciiCarriageReturn)
130 131 CellState)
132 (t
133 (PushToCell p c)
134 CellState)))
135
136 137 138 (defun QuotedCellState (p c)
139 (cond
140 ((eq c asciiQuotationMark)
141 QuotedCellStateSeenQuote)
142 ((null c)
143 (Error p "Unexpected EOF within quoted CSV cell."))
144 (t
145 (PushToCell p c)
146 QuotedCellState)))
147
148 149 150 151 (defun QuotedCellStateSeenQuote (p c)
152 (cond
153 ((eq c asciiQuotationMark)
154 (PushToCell p c)
155 QuotedCellState)
156 (t
157 (CellState p c))))
158
159 160
161 162 163 164 165 166 (defun ParseCharStream (cs @optional filename)
167 (let ((p (MakeParser filename))
168 (state NewRowState))
169 (while state
170 (let ((c (funcall cs)))
171 (when (eq c asciiLineFeed)
172 (p->line = (p->line + 1)))
173 (setq state (funcall state p c))))
174 (Rows p)))
175
176 177 178 (defun MakeLinesCharStream (lines)
179 (cond
180 ((null lines)
181 (lambda () nil))
182 (t
183 (let ((index 1)
184 (end (strlen (car lines)))
185 (line (pop lines)))
186 (lambda ()
187 (cond
188 ((index > end)
189 (when lines
190 (setq line (pop lines))
191 (setq index 1)
192 (setq end (strlen line))
193 asciiLineFeed))
194 (t
195 (prog1
196 (getchar line index)
197 (setq index (index + 1))))))))))
198
199 200 201 202 203 204 205 206 207 208 209 (defun ParseLines (lines "l")
210 (ParseCharStream (MakeLinesCharStream lines)))
211
212 (setq VedaCsvParseLines ParseLines)
213
214 215 216 217 218 (defun ParsePort (port "p")
219 (ParseCharStream (lambda () (getc port))))
220
221 (setq VedaCsvParsePort ParsePort)
222
223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 (defun ParseFile (filename "t")
246 (let ((port (or (infile filename)
247 (error "Unable to open %L for reading" filename))))
248 249 (prog1
250 (ParseCharStream (lambda () (getc port)) filename)
251 (close port))))
252
253 (setq VedaCsvParseFile ParseFile))
254
255