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