Skip to content

Commit f28168a

Browse files
committed
Adding csv-writer support.
1 parent 8e67def commit f28168a

File tree

3 files changed

+97
-2
lines changed

3 files changed

+97
-2
lines changed

lib/chibi/csv-test.sld

+19
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@
99
(define string->csv
1010
(opt-lambda (str (reader (csv-read->list)))
1111
(reader (open-input-string str))))
12+
(define csv->string
13+
(opt-lambda (row (writer (csv-writer)))
14+
(let ((out (open-output-string)))
15+
(writer row out)
16+
(get-output-string out))))
1217
(define (run-tests)
1318
(test-begin "(chibi csv)")
1419
(test-assert (eof-object? (string->csv "")))
@@ -73,4 +78,18 @@ Paris,48°51′24″N,2°21′03″E"))
7378
(longitude "2°21′03″E")))
7479
((csv->sxml 'city '(name latitude longitude))
7580
(open-input-string city-csv))))
81+
(test "1997,Ford,E350\n"
82+
(csv->string '("1997" "Ford" "E350")))
83+
(test "1997,Ford,E350,\"Super, luxurious truck\"\n"
84+
(csv->string '("1997" "Ford" "E350" "Super, luxurious truck")))
85+
(test "1997,Ford,E350,\"Super, \"\"luxurious\"\" truck\"\n"
86+
(csv->string '("1997" "Ford" "E350" "Super, \"luxurious\" truck")))
87+
(test "1997,Ford,E350,\"Go get one now\nthey are going fast\"\n"
88+
(csv->string
89+
'("1997" "Ford" "E350" "Go get one now\nthey are going fast")))
90+
(test "1997,Ford,E350\n"
91+
(csv->string '(1997 "Ford" E350)))
92+
(test "1997,\"Ford\",\"E350\"\n"
93+
(csv->string '(1997 "Ford" E350)
94+
(csv-writer (csv-grammar '((quote-non-numeric? . #t))))))
7695
(test-end))))

lib/chibi/csv.scm

+75
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@
8484
(define default-tsv-grammar
8585
(csv-grammar '((separator-chars #\tab) (quote-char . #f))))
8686

87+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
88+
8789
;;> \section{CSV Parsers}
8890

8991
;;> Parsers are low-level utilities to perform operations on records a
@@ -375,3 +377,76 @@
375377
(opt-lambda ((in (current-input-port)))
376378
(cons '*TOP*
377379
(csv->list (csv-read->sxml row-name column-names parser) in)))))
380+
381+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
382+
383+
;;> \section{CSV Writers}
384+
385+
(define (write->string obj)
386+
(let ((out (open-output-string)))
387+
(write obj out)
388+
(get-output-string out)))
389+
390+
(define (csv-grammar-char-needs-quoting? grammar ch)
391+
(or (eqv? ch (csv-grammar-quote-char grammar))
392+
(eqv? ch (csv-grammar-escape-char grammar))
393+
(memv ch (csv-grammar-separator-chars grammar))
394+
(eqv? ch (csv-grammar-record-separator grammar))
395+
(memv ch '(#\newline #\return))))
396+
397+
(define (csv-write-quoted obj out grammar)
398+
(let ((in (open-input-string (if (string? obj) obj (write->string obj)))))
399+
(write-char (csv-grammar-quote-char grammar) out)
400+
(let lp ()
401+
(let ((ch (read-char in)))
402+
(cond
403+
((eof-object? ch))
404+
((or (eqv? ch (csv-grammar-quote-char grammar))
405+
(eqv? ch (csv-grammar-escape-char grammar)))
406+
(cond
407+
((and (csv-grammar-quote-doubling-escapes? grammar)
408+
(eqv? ch (csv-grammar-quote-char grammar)))
409+
(write-char ch out))
410+
((csv-grammar-escape-char grammar)
411+
=> (lambda (esc) (write-char esc out)))
412+
(else (error "no quote defined for" ch grammar)))
413+
(write-char ch out)
414+
(lp))
415+
(else
416+
(write-char ch out)
417+
(lp)))))
418+
(write-char (csv-grammar-quote-char grammar) out)))
419+
420+
(define csv-writer
421+
(opt-lambda ((grammar default-csv-grammar))
422+
(opt-lambda (row (out (current-output-port)))
423+
(let lp ((ls row) (first? #t))
424+
(when (pair? ls)
425+
(unless first?
426+
(write-char (car (csv-grammar-separator-chars grammar)) out))
427+
(if (or (and (csv-grammar-quote-non-numeric? grammar)
428+
(not (number? (car ls))))
429+
(and (string? (car ls))
430+
(string-any
431+
(lambda (ch) (csv-grammar-char-needs-quoting? grammar ch))
432+
(car ls)))
433+
(and (not (string? (car ls)))
434+
(not (number? (car ls)))
435+
(not (symbol? (car ls)))))
436+
(csv-write-quoted (car ls) out grammar)
437+
(display (car ls) out))
438+
(lp (cdr ls) #f)))
439+
(write-string
440+
(case (csv-grammar-record-separator grammar)
441+
((crlf) "\r\n")
442+
((lf lax) "\n")
443+
((cr) "\r")
444+
(else (string (csv-grammar-record-separator grammar))))
445+
out))))
446+
447+
(define csv-write
448+
(opt-lambda ((writer (csv-writer)))
449+
(opt-lambda (rows (out (current-output-port)))
450+
(for-each
451+
(lambda (row) (writer row out))
452+
rows))))

lib/chibi/csv.sld

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11

22
(define-library (chibi csv)
3-
(import (scheme base) (srfi 227))
3+
(import (scheme base) (scheme write) (srfi 130) (srfi 227))
44
(export csv-grammar csv-parser csv-grammar?
55
default-csv-grammar default-tsv-grammar
66
csv-read->list csv-read->vector csv-read->fixed-vector
77
csv-read->sxml
8-
csv-fold csv-map csv->list csv-for-each csv->sxml)
8+
csv-fold csv-map csv->list csv-for-each csv->sxml
9+
csv-writer csv-write)
910
(include "csv.scm"))

0 commit comments

Comments
 (0)