-
Notifications
You must be signed in to change notification settings - Fork 0
/
csv.zp
69 lines (64 loc) · 2.47 KB
/
csv.zp
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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
(module "csv"
(export
(list "read" read)
(list "write" write))
(default #{:separator #\,
:quote #\"
:port #f
:quote? #t})
(write (lambda (l . opt)
"translates a list of lists into the CSV format.
The optional config object <par>opt</par> supports
the following options (listed here with default values):
<zepto>
#{:separator #\,
:quote #\"
:port #f
:quote? #t})
</zepto>
params:
- l: the list that should be parsed
- opt: an optional argument that holds the configuration
complexity: O(n)
returns: nil if it was written to a port, or the CSV value if no port was specified"
(let ((conf (if (null? opt) default (make-hash default (car opt)))))
(define (writel line)
(string:join
(map
(lambda (x)
(let ((qu (conf :quote)))
(if (and (conf :quote?) (in? x (conf :separator)))
(++ (string qu) x (string qu))
x)))
line)
(conf :separator)))
(let ((parsed (string:join (map writel s) #\newline)))
(if (conf :port)
(write parsed (conf :port))
parsed)))))
(read (lambda (s . opt)
"translates CSV values (provided in <par>s</par> as a string or port)
to a list of lists (where the outer list is the line and the inner list
are the values. The supported options are the same as in <fun>csv:write</fun>.
params:
- s: the string or port
- opt: an optional argument that holds the configuration
complexity: O(n)
returns: the list of lists"
(let ((str (if (string? s) s (read-contents s)))
(conf (if (null? opt) default (make-hash default (car opt)))))
(define (readl line parsed)
(cond
((string:empty? line) parsed)
((eq? (string:head line) (conf :quote))
(let ((sep (add1 (string:find (string:tail line) (conf :quote)))))
(if (eq? sep 0)
:err-quote-mismatch
(readl (substring line (+ sep 2) (length line)) (++ parsed (substring line 1 sep))))))
(else
(let ((sep (string:find line (conf :separator))))
(if (eq? sep -1)
(++ parsed line)
(readl (substring line (add1 sep) (length line)) (++ parsed (substring line 0 sep))))))))
(map (lambda (line) (readl line []))
(string:split str #\newline))))))