mirror of
https://github.com/ashinn/chibi-scheme.git
synced 2025-05-19 05:39:18 +02:00
Merge branch 'master' into r6rs
This commit is contained in:
commit
c1b017aaa7
16 changed files with 931 additions and 31 deletions
|
@ -1403,7 +1403,7 @@ namespace.
|
||||||
|
|
||||||
\item{\hyperlink["lib/chibi/shell.html"]{(chibi shell) - Process combinators with high-level pipeline syntax in the spirit of SCSH.}}
|
\item{\hyperlink["lib/chibi/shell.html"]{(chibi shell) - Process combinators with high-level pipeline syntax in the spirit of SCSH.}}
|
||||||
|
|
||||||
\item{\hyperlink["lib/chibi/show.html"]{(chibi show) - Monadic formattinga.}}
|
\item{\hyperlink["lib/chibi/show.html"]{(chibi show) - Monadic formatting.}}
|
||||||
|
|
||||||
\item{\hyperlink["lib/chibi/scribble.html"]{(chibi scribble) - A parser for the scribble syntax used to write this manual}}
|
\item{\hyperlink["lib/chibi/scribble.html"]{(chibi scribble) - A parser for the scribble syntax used to write this manual}}
|
||||||
|
|
||||||
|
|
72
lib/chibi/csv-test.sld
Normal file
72
lib/chibi/csv-test.sld
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
|
||||||
|
(define-library (chibi csv-test)
|
||||||
|
(import (scheme base)
|
||||||
|
(srfi 227)
|
||||||
|
(chibi csv)
|
||||||
|
(chibi test))
|
||||||
|
(export run-tests)
|
||||||
|
(begin
|
||||||
|
(define string->csv
|
||||||
|
(opt-lambda (str (reader (csv-read->list)))
|
||||||
|
(reader (open-input-string str))))
|
||||||
|
(define (run-tests)
|
||||||
|
(test-begin "(chibi csv)")
|
||||||
|
(test-assert (eof-object? (string->csv "")))
|
||||||
|
(test '("1997" "Ford" "E350")
|
||||||
|
(string->csv "1997,Ford,E350"))
|
||||||
|
(test '("1997" "Ford" "E350")
|
||||||
|
(string->csv "\n1997,Ford,E350"))
|
||||||
|
(test '(" ")
|
||||||
|
(string->csv " \n1997,Ford,E350"))
|
||||||
|
(test '("" "")
|
||||||
|
(string->csv ",\n1997,Ford,E350"))
|
||||||
|
(test '("1997" "Ford" "E350")
|
||||||
|
(string->csv "\"1997\",\"Ford\",\"E350\""))
|
||||||
|
(test '("1997" "Ford" "E350" "Super, luxurious truck")
|
||||||
|
(string->csv "1997,Ford,E350,\"Super, luxurious truck\""))
|
||||||
|
(test '("1997" "Ford" "E350" "Super, \"luxurious\" truck")
|
||||||
|
(string->csv "1997,Ford,E350,\"Super, \"\"luxurious\"\" truck\""))
|
||||||
|
(test '("1997" "Ford" "E350" "Go get one now\nthey are going fast")
|
||||||
|
(string->csv "1997,Ford,E350,\"Go get one now
|
||||||
|
they are going fast\""))
|
||||||
|
(test '("1997" "Ford" "E350")
|
||||||
|
(string->csv
|
||||||
|
"# this is a comment\n1997,Ford,E350"
|
||||||
|
(csv-read->list
|
||||||
|
(csv-parser (csv-grammar '((comment-chars #\#)))))))
|
||||||
|
(test '("1997" "Fo\"rd" "E3\"50")
|
||||||
|
(string->csv "1997\tFo\"rd\tE3\"50"
|
||||||
|
(csv-read->list (csv-parser default-tsv-grammar))))
|
||||||
|
(test '#("1997" "Ford" "E350")
|
||||||
|
(string->csv "1997,Ford,E350" (csv-read->vector)))
|
||||||
|
(test '#("1997" "Ford" "E350")
|
||||||
|
(string->csv "1997,Ford,E350" (csv-read->fixed-vector 3)))
|
||||||
|
(test-error
|
||||||
|
(string->csv "1997,Ford,E350" (csv-read->fixed-vector 2)))
|
||||||
|
(let ((city-csv "Los Angeles,34°03′N,118°15′W
|
||||||
|
New York City,40°42′46″N,74°00′21″W
|
||||||
|
Paris,48°51′24″N,2°21′03″E"))
|
||||||
|
(test '(*TOP*
|
||||||
|
(row (col-0 "Los Angeles")
|
||||||
|
(col-1 "34°03′N")
|
||||||
|
(col-2 "118°15′W"))
|
||||||
|
(row (col-0 "New York City")
|
||||||
|
(col-1 "40°42′46″N")
|
||||||
|
(col-2 "74°00′21″W"))
|
||||||
|
(row (col-0 "Paris")
|
||||||
|
(col-1 "48°51′24″N")
|
||||||
|
(col-2 "2°21′03″E")))
|
||||||
|
((csv->sxml) (open-input-string city-csv)))
|
||||||
|
(test '(*TOP*
|
||||||
|
(city (name "Los Angeles")
|
||||||
|
(latitude "34°03′N")
|
||||||
|
(longitude "118°15′W"))
|
||||||
|
(city (name "New York City")
|
||||||
|
(latitude "40°42′46″N")
|
||||||
|
(longitude "74°00′21″W"))
|
||||||
|
(city (name "Paris")
|
||||||
|
(latitude "48°51′24″N")
|
||||||
|
(longitude "2°21′03″E")))
|
||||||
|
((csv->sxml 'city '(name latitude longitude))
|
||||||
|
(open-input-string city-csv))))
|
||||||
|
(test-end))))
|
362
lib/chibi/csv.scm
Normal file
362
lib/chibi/csv.scm
Normal file
|
@ -0,0 +1,362 @@
|
||||||
|
|
||||||
|
;;> \section{CSV Grammars}
|
||||||
|
|
||||||
|
;;> CSV is a simple and compact format for tabular data, which has
|
||||||
|
;;> made it popular for a variety of tasks since the early days of
|
||||||
|
;;> computing. Unfortunately, there are many incompatible dialects
|
||||||
|
;;> requiring a grammar to specify all of the different options.
|
||||||
|
|
||||||
|
(define-record-type Csv-Grammar
|
||||||
|
(make-csv-grammar separator-chars quote-char escape-char record-separator comment-chars)
|
||||||
|
csv-grammar?
|
||||||
|
(separator-chars csv-grammar-separator-chars csv-grammar-separator-chars-set!)
|
||||||
|
(quote-char csv-grammar-quote-char csv-grammar-quote-char-set!)
|
||||||
|
(escape-char csv-grammar-escape-char csv-grammar-escape-char-set!)
|
||||||
|
(record-separator csv-grammar-record-separator csv-grammar-record-separator-set!)
|
||||||
|
(comment-chars csv-grammar-comment-chars csv-grammar-comment-chars-set!))
|
||||||
|
|
||||||
|
;; TODO: Consider some minimal low-level parsing options. In general
|
||||||
|
;; this is intended to be performed by the parser, but if we can skip
|
||||||
|
;; intermediate string generation (e.g. parsing numbers directly) it
|
||||||
|
;; can save a considerable amount of garbage when parsing large files.
|
||||||
|
|
||||||
|
;;> Creates a new CSV grammar from the given spec, an alist of symbols
|
||||||
|
;;> to values. The following options are supported:
|
||||||
|
;;>
|
||||||
|
;;> \itemlist[
|
||||||
|
;;> \item{\scheme{'separator-chars} - A non-empty list of characters used to delimit fields, by default \scheme{'(#\\,)} (comma-separated).}
|
||||||
|
;;> \item{\scheme{'quote-char} - A single character used to quote fields containing special characters, or \scheme{#f} to disable quoting, by default \scheme{#\\"} (a double-quote).}
|
||||||
|
;;> \item{\scheme{'escape-char} - A single character used to escape characters within quoted fields, or \scheme{#f} to disable escapes, by default \scheme{#\\"} (a double-quote). If this is the same character as the \scheme{quote-char}, then the quote char can be doubled to escape, but no other characters can be escaped.}
|
||||||
|
;;> \item{\scheme{'record-separator} - A single character used to delimit the record (row), or one of the symbols \scheme{'cr}, \scheme{'crlf}, \scheme{'lf} or \scheme{'lax}. These correspond to sequences of carriage return and line feed, or in the case of \scheme{'lax} any of the other three sequences. Defaults to \scheme{'lax}.}
|
||||||
|
;;> \item{\scheme{'comment-chars} - A list of characters which if found at the start of a record indicate it is a comment, discarding all characters through to the next record-separator. Defaults to the empty list (no comments).}
|
||||||
|
;;> ]
|
||||||
|
;;>
|
||||||
|
;;> Example Gecos grammar:
|
||||||
|
;;>
|
||||||
|
;;> \example{
|
||||||
|
;;> (csv-grammar
|
||||||
|
;;> '((separator-chars #\\:)
|
||||||
|
;;> (quote-char . #f)
|
||||||
|
;;> (escape-char . #f)))
|
||||||
|
;;> }
|
||||||
|
(define (csv-grammar spec)
|
||||||
|
(let ((grammar (make-csv-grammar '(#\,) #\" #\" 'lax '())))
|
||||||
|
(for-each
|
||||||
|
(lambda (x)
|
||||||
|
(case (car x)
|
||||||
|
((separator-chars delimiter)
|
||||||
|
(csv-grammar-separator-chars-set! grammar (cdr x)))
|
||||||
|
((quote-char)
|
||||||
|
(csv-grammar-quote-char-set! grammar (cdr x)))
|
||||||
|
((escape-char)
|
||||||
|
(csv-grammar-escape-char-set! grammar (cdr x)))
|
||||||
|
((record-separator newline-type)
|
||||||
|
(let ((rec-sep
|
||||||
|
(case (cdr x)
|
||||||
|
((crlf lax) (cdr x))
|
||||||
|
((cr) #\return)
|
||||||
|
((lf) #\newline)
|
||||||
|
(else
|
||||||
|
(if (char? (cdr x))
|
||||||
|
(cdr x)
|
||||||
|
(error "invalid record-separator, expected a char or one of 'lax or 'crlf" (cdr x)))))))
|
||||||
|
(csv-grammar-escape-char-set! grammar (cdr x))))
|
||||||
|
((comment-chars)
|
||||||
|
(csv-grammar-comment-chars-set! grammar (cdr x)))
|
||||||
|
(else
|
||||||
|
(error "unknown csv-grammar spec" x))))
|
||||||
|
spec)
|
||||||
|
grammar))
|
||||||
|
|
||||||
|
;;> The default CSV grammar for convenience, with all of the defaults
|
||||||
|
;;> from \scheme{csv-grammar}, i.e. comma-delimited with \scheme{#\"}
|
||||||
|
;;> for quoting, doubled to escape.
|
||||||
|
(define default-csv-grammar
|
||||||
|
(csv-grammar '()))
|
||||||
|
|
||||||
|
;;> The default TSV grammar for convenience, splitting fields only on
|
||||||
|
;;> tabs, with no quoting or escaping.
|
||||||
|
(define default-tsv-grammar
|
||||||
|
(csv-grammar '((separator-chars #\tab) (quote-char . #f) (escape-char . #f))))
|
||||||
|
|
||||||
|
;;> \section{CSV Parsers}
|
||||||
|
|
||||||
|
;;> Parsers are low-level utilities to perform operations on records a
|
||||||
|
;;> field at a time. You generally want to work with readers, which
|
||||||
|
;;> build on this to build records into familiar data structures.
|
||||||
|
|
||||||
|
;;> Parsers follow the rules of a grammar to parse a single CSV
|
||||||
|
;;> record, possible comprised of multiple fields. A parser is a
|
||||||
|
;;> procedure of three arguments which performs a fold operation over
|
||||||
|
;;> the fields of the record. The parser signature is:
|
||||||
|
;;> \scheme{(parser kons knil in)}, where \scheme{kons} itself is
|
||||||
|
;;> a procedure of three arguments: \scheme{(proc acc index field)}.
|
||||||
|
;;> \scheme{proc} is called on each field of the record, in order,
|
||||||
|
;;> along with its zero-based \scheme{index} and the accumulated
|
||||||
|
;;> result of the last call, starting with \scheme{knil}.
|
||||||
|
|
||||||
|
;;> Returns a new CSV parser for the given \var{grammar}. The parser
|
||||||
|
;;> by itself can be used to parse a record at a time.
|
||||||
|
;;>
|
||||||
|
;;> \example{
|
||||||
|
;;> (let ((parse (csv-parser)))
|
||||||
|
;;> (parse (lambda (vec i field) (vector-set! vec i (string->number field)) vec)
|
||||||
|
;;> (make-vector 3)
|
||||||
|
;;> (open-input-string "1,2,3")))
|
||||||
|
;;> }
|
||||||
|
(define csv-parser
|
||||||
|
(opt-lambda ((grammar default-csv-grammar))
|
||||||
|
(lambda (kons knil in)
|
||||||
|
(when (pair? (csv-grammar-comment-chars grammar))
|
||||||
|
(let lp ()
|
||||||
|
(when (memv (peek-char in) (csv-grammar-comment-chars grammar))
|
||||||
|
(csv-skip-line in grammar)
|
||||||
|
(lp))))
|
||||||
|
(let lp ((acc knil)
|
||||||
|
(index 0)
|
||||||
|
(out (open-output-string)))
|
||||||
|
(define (finish-row)
|
||||||
|
(let ((field (get-output-string out)))
|
||||||
|
(if (and (zero? index) (equal? field ""))
|
||||||
|
;; empty row, read again
|
||||||
|
(lp acc index out)
|
||||||
|
(kons acc index field))))
|
||||||
|
(let ((ch (read-char in)))
|
||||||
|
(cond
|
||||||
|
((eof-object? ch)
|
||||||
|
(let ((field (get-output-string out)))
|
||||||
|
(if (and (zero? index) (equal? field ""))
|
||||||
|
;; no data
|
||||||
|
ch
|
||||||
|
(kons acc index field))))
|
||||||
|
((memv ch (csv-grammar-separator-chars grammar))
|
||||||
|
(lp (kons acc index (get-output-string out))
|
||||||
|
(+ index 1)
|
||||||
|
(open-output-string)))
|
||||||
|
((eqv? ch (csv-grammar-quote-char grammar))
|
||||||
|
;; TODO: Consider a strict mode to enforce no text
|
||||||
|
;; before/after the quoted text.
|
||||||
|
(csv-read-quoted in out grammar)
|
||||||
|
(lp acc index out))
|
||||||
|
((eqv? ch (csv-grammar-record-separator grammar))
|
||||||
|
(finish-row))
|
||||||
|
((and (eqv? ch #\return)
|
||||||
|
(memq (csv-grammar-record-separator grammar) '(crlf lax)))
|
||||||
|
(cond
|
||||||
|
((eqv? (peek-char in) #\newline)
|
||||||
|
(read-char in)
|
||||||
|
(finish-row))
|
||||||
|
((eq? (csv-grammar-record-separator grammar) 'lax)
|
||||||
|
(finish-row))
|
||||||
|
(else
|
||||||
|
(write-char ch out)
|
||||||
|
(lp acc (+ index 1) out))))
|
||||||
|
((and (eqv? ch #\newline)
|
||||||
|
(eq? (csv-grammar-record-separator grammar) 'lax))
|
||||||
|
(finish-row))
|
||||||
|
(else
|
||||||
|
(write-char ch out)
|
||||||
|
(lp acc index out))))))))
|
||||||
|
|
||||||
|
(define (csv-skip-line in grammar)
|
||||||
|
(let lp ()
|
||||||
|
(let ((ch (read-char in)))
|
||||||
|
(cond
|
||||||
|
((eof-object? ch))
|
||||||
|
((eqv? ch (csv-grammar-record-separator grammar)))
|
||||||
|
((and (eqv? ch #\newline)
|
||||||
|
(eq? (csv-grammar-record-separator grammar) 'lax)))
|
||||||
|
((and (eqv? ch #\return)
|
||||||
|
(memq (csv-grammar-record-separator grammar) '(crlf lax)))
|
||||||
|
(cond
|
||||||
|
((eqv? (peek-char in) #\newline) (read-char in))
|
||||||
|
((eq? (csv-grammar-record-separator grammar) 'lax))
|
||||||
|
(else (lp))))
|
||||||
|
(else (lp))))))
|
||||||
|
|
||||||
|
(define (csv-read-quoted in out grammar)
|
||||||
|
(let lp ()
|
||||||
|
(let ((ch (read-char in)))
|
||||||
|
(cond
|
||||||
|
((eof-object? ch)
|
||||||
|
(error "unterminated csv quote" (get-output-string out)))
|
||||||
|
((eqv? ch (csv-grammar-quote-char grammar))
|
||||||
|
(when (and (eqv? ch (csv-grammar-escape-char grammar))
|
||||||
|
(eqv? ch (peek-char in)))
|
||||||
|
(write-char (read-char in) out)
|
||||||
|
(lp)))
|
||||||
|
((eqv? ch (csv-grammar-escape-char grammar))
|
||||||
|
(write-char (read-char in) out)
|
||||||
|
(lp))
|
||||||
|
(else
|
||||||
|
;; TODO: Consider an option to disable newlines in quotes.
|
||||||
|
(write-char ch out)
|
||||||
|
(lp))))))
|
||||||
|
|
||||||
|
;;> \section{CSV Readers}
|
||||||
|
|
||||||
|
;;> A CSV reader reads a single record, returning some representation
|
||||||
|
;;> of it. You can either loop manually with these or pass them to
|
||||||
|
;;> one of the high-level utilities to operate on a whole CSV file at
|
||||||
|
;;> a time.
|
||||||
|
|
||||||
|
;;> The simplest reader, simply returns the field string values in
|
||||||
|
;;> order as a list.
|
||||||
|
;;>
|
||||||
|
;;> \example{
|
||||||
|
;;> ((csv-read->list) (open-input-string "foo,bar,baz"))
|
||||||
|
;;> }
|
||||||
|
(define csv-read->list
|
||||||
|
(opt-lambda ((parser (csv-parser)))
|
||||||
|
(opt-lambda ((in (current-input-port)))
|
||||||
|
(let ((res (parser (lambda (ls i field) (cons field ls)) '() in)))
|
||||||
|
(if (pair? res)
|
||||||
|
(reverse res)
|
||||||
|
res)))))
|
||||||
|
|
||||||
|
;;> The equivalent of \scheme{csv-read->list} but returns a vector.
|
||||||
|
;;>
|
||||||
|
;;> \example{
|
||||||
|
;;> ((csv-read->vector) (open-input-string "foo,bar,baz"))
|
||||||
|
;;> }
|
||||||
|
(define csv-read->vector
|
||||||
|
(opt-lambda ((parser (csv-parser)))
|
||||||
|
(let ((reader (csv-read->list parser)))
|
||||||
|
(opt-lambda ((in (current-input-port)))
|
||||||
|
(let ((res (reader in)))
|
||||||
|
(if (pair? res)
|
||||||
|
(list->vector res)
|
||||||
|
res))))))
|
||||||
|
|
||||||
|
;;> The same as \scheme{csv-read->vector} but requires the vector to
|
||||||
|
;;> be of a fixed size, and may be more efficient.
|
||||||
|
;;>
|
||||||
|
;;> \example{
|
||||||
|
;;> ((csv-read->fixed-vector 3) (open-input-string "foo,bar,baz"))
|
||||||
|
;;> }
|
||||||
|
(define csv-read->fixed-vector
|
||||||
|
(opt-lambda (size (parser (csv-parser)))
|
||||||
|
(opt-lambda ((in (current-input-port)))
|
||||||
|
(let ((res (make-vector size)))
|
||||||
|
(let ((len (parser (lambda (prev-i i field) (vector-set! res i field) i)
|
||||||
|
0
|
||||||
|
in)))
|
||||||
|
(if (zero? len)
|
||||||
|
eof-object
|
||||||
|
res))))))
|
||||||
|
|
||||||
|
;;> Returns an SXML representation of the record, as a row with
|
||||||
|
;;> multiple named columns.
|
||||||
|
;;>
|
||||||
|
;;> \example{
|
||||||
|
;;> ((csv-read->sxml 'city '(name latitude longitude))
|
||||||
|
;;> (open-input-string "Tokyo,35°41′23″N,139°41′32″E"))
|
||||||
|
;;> }
|
||||||
|
(define csv-read->sxml
|
||||||
|
(opt-lambda ((row-name 'row)
|
||||||
|
(column-names
|
||||||
|
(lambda (i)
|
||||||
|
(string->symbol (string-append "col-" (number->string i)))))
|
||||||
|
(parser (csv-parser)))
|
||||||
|
(define (get-column-name i)
|
||||||
|
(if (procedure? column-names)
|
||||||
|
(column-names i)
|
||||||
|
(list-ref column-names i)))
|
||||||
|
(opt-lambda ((in (current-input-port)))
|
||||||
|
(let ((res (parser (lambda (ls i field)
|
||||||
|
`((,(get-column-name i) ,field) ,@ls))
|
||||||
|
(list row-name)
|
||||||
|
in)))
|
||||||
|
(if (pair? res)
|
||||||
|
(reverse res)
|
||||||
|
res)))))
|
||||||
|
|
||||||
|
;;> \section{CSV Utilities}
|
||||||
|
|
||||||
|
;;> A folding operation on records. \var{proc} is called successively
|
||||||
|
;;> on each row and the accumulated result.
|
||||||
|
;;>
|
||||||
|
;;> \example{
|
||||||
|
;;> (csv-fold
|
||||||
|
;;> (lambda (row acc) (cons (cadr (assq 'name (cdr row))) acc))
|
||||||
|
;;> '()
|
||||||
|
;;> (csv-read->sxml 'city '(name latitude longitude))
|
||||||
|
;;> (open-input-string
|
||||||
|
;;> "Tokyo,35°41′23″N,139°41′32″E
|
||||||
|
;;> Paris,48°51′24″N,2°21′03″E"))
|
||||||
|
;;> }
|
||||||
|
(define csv-fold
|
||||||
|
(opt-lambda (proc
|
||||||
|
knil
|
||||||
|
(reader (csv-read->list))
|
||||||
|
(in (current-input-port)))
|
||||||
|
(let lp ((acc knil))
|
||||||
|
(let ((row (reader in)))
|
||||||
|
(cond
|
||||||
|
((eof-object? row) acc)
|
||||||
|
(else (lp (proc row acc))))))))
|
||||||
|
|
||||||
|
;;> An iterator which simply calls \var{proc} on each record in the
|
||||||
|
;;> input in order.
|
||||||
|
;;>
|
||||||
|
;;> \example{
|
||||||
|
;;> (let ((count 0))
|
||||||
|
;;> (csv-for-each
|
||||||
|
;;> (lambda (row) (if (string->number (car row)) (set! count (+ 1 count))))
|
||||||
|
;;> (csv-read->list)
|
||||||
|
;;> (open-input-string
|
||||||
|
;;> "1,foo\\n2,bar\\nthree,baz\\n4,qux"))
|
||||||
|
;;> count)
|
||||||
|
;;> }
|
||||||
|
(define csv-for-each
|
||||||
|
(opt-lambda (proc
|
||||||
|
(reader (csv-read->list))
|
||||||
|
(in (current-input-port)))
|
||||||
|
(csv-fold (lambda (row acc) (proc row)) #f reader in)))
|
||||||
|
|
||||||
|
;;> Returns a list containing the result of calling \var{proc} on each
|
||||||
|
;;> element in the input.
|
||||||
|
;;>
|
||||||
|
;;> \example{
|
||||||
|
;;> (csv-map
|
||||||
|
;;> (lambda (row) (string->symbol (cadr row)))
|
||||||
|
;;> (csv-read->list)
|
||||||
|
;;> (open-input-string
|
||||||
|
;;> "1,foo\\n2,bar\\nthree,baz\\n4,qux"))
|
||||||
|
;;> }
|
||||||
|
(define csv-map
|
||||||
|
(opt-lambda (proc
|
||||||
|
(reader (csv-read->list))
|
||||||
|
(in (current-input-port)))
|
||||||
|
(reverse (csv-fold (lambda (row acc) (cons (proc row) acc)) '() reader in))))
|
||||||
|
|
||||||
|
;;> Returns a list of all of the read records in the input.
|
||||||
|
;;>
|
||||||
|
;;> \example{
|
||||||
|
;;> (csv->list
|
||||||
|
;;> (csv-read->list)
|
||||||
|
;;> (open-input-string
|
||||||
|
;;> "1,foo\\n2,bar\\nthree,baz\\n4,qux"))
|
||||||
|
;;> }
|
||||||
|
(define csv->list
|
||||||
|
(opt-lambda ((reader (csv-read->list))
|
||||||
|
(in (current-input-port)))
|
||||||
|
(csv-map (lambda (row) row) reader in)))
|
||||||
|
|
||||||
|
;;> Returns an SXML representation of the CSV.
|
||||||
|
;;>
|
||||||
|
;;> \example{
|
||||||
|
;;> ((csv->sxml 'city '(name latitude longitude))
|
||||||
|
;;> (open-input-string
|
||||||
|
;;> "Tokyo,35°41′23″N,139°41′32″E
|
||||||
|
;;> Paris,48°51′24″N,2°21′03″E"))
|
||||||
|
;;> }
|
||||||
|
(define csv->sxml
|
||||||
|
(opt-lambda ((row-name 'row)
|
||||||
|
(column-names
|
||||||
|
(lambda (i)
|
||||||
|
(string->symbol (string-append "col-" (number->string i)))))
|
||||||
|
(parser (csv-parser)))
|
||||||
|
(opt-lambda ((in (current-input-port)))
|
||||||
|
(cons '*TOP*
|
||||||
|
(csv->list (csv-read->sxml row-name column-names parser) in)))))
|
9
lib/chibi/csv.sld
Normal file
9
lib/chibi/csv.sld
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
|
||||||
|
(define-library (chibi csv)
|
||||||
|
(import (scheme base) (srfi 227))
|
||||||
|
(export csv-grammar csv-parser csv-grammar?
|
||||||
|
default-csv-grammar default-tsv-grammar
|
||||||
|
csv-read->list csv-read->vector csv-read->fixed-vector
|
||||||
|
csv-read->sxml
|
||||||
|
csv-fold csv-map csv->list csv-for-each csv->sxml)
|
||||||
|
(include "csv.scm"))
|
|
@ -32,6 +32,11 @@
|
||||||
|
|
||||||
;;> If no patterns match an error is signalled.
|
;;> If no patterns match an error is signalled.
|
||||||
|
|
||||||
|
;;> Note there is no \scheme{else} clause. \scheme{else} is sometimes
|
||||||
|
;;> used descriptively for the last pattern, since an identifier used
|
||||||
|
;;> only once matches anything, but it's preferred to use \scheme{_}
|
||||||
|
;;> described below.
|
||||||
|
|
||||||
;;> Identifiers will match anything, and make the corresponding
|
;;> Identifiers will match anything, and make the corresponding
|
||||||
;;> binding available in the body.
|
;;> binding available in the body.
|
||||||
|
|
||||||
|
@ -128,7 +133,7 @@
|
||||||
;;> are bound if the \scheme{or} operator matches, but the binding is
|
;;> are bound if the \scheme{or} operator matches, but the binding is
|
||||||
;;> only defined for identifiers from the subpattern which matched.
|
;;> only defined for identifiers from the subpattern which matched.
|
||||||
|
|
||||||
;;> \example{(match 1 ((or) #t) (else #f))}
|
;;> \example{(match 1 ((or) #t) (_ #f))}
|
||||||
;;> \example{(match 1 ((or x) x))}
|
;;> \example{(match 1 ((or x) x))}
|
||||||
;;> \example{(match 1 ((or x 2) x))}
|
;;> \example{(match 1 ((or x 2) x))}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
;; repl.scm - friendlier repl with line editing and signal handling
|
;; repl.scm - friendlier repl with line editing and signal handling
|
||||||
;; Copyright (c) 2012-2013 Alex Shinn. All rights reserved.
|
;; Copyright (c) 2012-2024 Alex Shinn. All rights reserved.
|
||||||
;; BSD-style license: http://synthcode.com/license.txt
|
;; BSD-style license: http://synthcode.com/license.txt
|
||||||
|
|
||||||
;;> A user-friendly REPL with line editing and signal handling. The
|
;;> A user-friendly REPL with line editing and signal handling. The
|
||||||
|
@ -296,6 +296,8 @@
|
||||||
(pair? (exception-irritants exn)))
|
(pair? (exception-irritants exn)))
|
||||||
(let ((name (car (exception-irritants exn))))
|
(let ((name (car (exception-irritants exn))))
|
||||||
(cond
|
(cond
|
||||||
|
((and (identifier? name) (not (env-parent (current-environment))))
|
||||||
|
(display "Did you forget to import a language? e.g. (import (scheme base))\n" out))
|
||||||
((identifier? name)
|
((identifier? name)
|
||||||
(display "Searching for modules exporting " out)
|
(display "Searching for modules exporting " out)
|
||||||
(display name out)
|
(display name out)
|
||||||
|
@ -400,6 +402,16 @@
|
||||||
((= (length value) 1) (push-history-value! (car value)))
|
((= (length value) 1) (push-history-value! (car value)))
|
||||||
(else (push-history-value! value))))
|
(else (push-history-value! value))))
|
||||||
|
|
||||||
|
(define-generic repl-print)
|
||||||
|
|
||||||
|
(define-method (repl-print obj (out output-port?))
|
||||||
|
(write/ss obj out))
|
||||||
|
|
||||||
|
(define-generic repl-print-exception)
|
||||||
|
|
||||||
|
(define-method (repl-print-exception obj (out output-port?))
|
||||||
|
(print-exception obj out))
|
||||||
|
|
||||||
(define (repl/eval rp expr-list)
|
(define (repl/eval rp expr-list)
|
||||||
(let ((thread (current-thread))
|
(let ((thread (current-thread))
|
||||||
(out (repl-out rp)))
|
(out (repl-out rp)))
|
||||||
|
@ -409,7 +421,7 @@
|
||||||
(lambda ()
|
(lambda ()
|
||||||
(protect (exn
|
(protect (exn
|
||||||
(else
|
(else
|
||||||
(print-exception exn out)
|
(repl-print-exception exn out)
|
||||||
(repl-advise-exception exn (current-error-port))))
|
(repl-advise-exception exn (current-error-port))))
|
||||||
(for-each
|
(for-each
|
||||||
(lambda (expr)
|
(lambda (expr)
|
||||||
|
@ -420,17 +432,17 @@
|
||||||
(null? expr))
|
(null? expr))
|
||||||
(eval expr (repl-env rp))
|
(eval expr (repl-env rp))
|
||||||
expr))
|
expr))
|
||||||
(lambda res-list
|
(lambda res-values
|
||||||
(cond
|
(cond
|
||||||
((not (or (null? res-list)
|
((not (or (null? res-values)
|
||||||
(equal? res-list (list (if #f #f)))))
|
(equal? res-values (list undefined-value))))
|
||||||
(push-history-value-maybe! res-list)
|
(push-history-value-maybe! res-values)
|
||||||
(write/ss (car res-list) out)
|
(repl-print (car res-values) out)
|
||||||
(for-each
|
(for-each
|
||||||
(lambda (res)
|
(lambda (res)
|
||||||
(write-char #\space out)
|
(write-char #\space out)
|
||||||
(write/ss res out))
|
(repl-print res out))
|
||||||
(cdr res-list))
|
(cdr res-values))
|
||||||
(newline out))))))
|
(newline out))))))
|
||||||
expr-list))))))
|
expr-list))))))
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
|
|
||||||
(define-library (chibi repl)
|
(define-library (chibi repl)
|
||||||
(export repl $0 $1 $2 $3 $4 $5 $6 $7 $8 $9)
|
(export repl repl-print repl-print-exception
|
||||||
|
$0 $1 $2 $3 $4 $5 $6 $7 $8 $9)
|
||||||
(import (chibi) (only (meta) load-module module-name->file)
|
(import (chibi) (only (meta) load-module module-name->file)
|
||||||
(chibi ast) (chibi modules) (chibi doc)
|
(chibi ast) (chibi modules) (chibi doc) (chibi generic)
|
||||||
(chibi string) (chibi io) (chibi optional)
|
(chibi string) (chibi io) (chibi optional)
|
||||||
(chibi process) (chibi term edit-line)
|
(chibi process) (chibi term edit-line)
|
||||||
(srfi 1)
|
(srfi 1)
|
||||||
|
|
|
@ -526,6 +526,7 @@
|
||||||
(not (assq-ref info 'line-number)))
|
(not (assq-ref info 'line-number)))
|
||||||
`((file-name . ,(car (pair-source expr)))
|
`((file-name . ,(car (pair-source expr)))
|
||||||
(line-number . ,(cdr (pair-source expr)))
|
(line-number . ,(cdr (pair-source expr)))
|
||||||
|
(format . ,(current-test-value-formatter))
|
||||||
,@info)
|
,@info)
|
||||||
info)))
|
info)))
|
||||||
|
|
||||||
|
@ -584,14 +585,20 @@
|
||||||
((SKIP) "-")
|
((SKIP) "-")
|
||||||
(else "."))))
|
(else "."))))
|
||||||
|
|
||||||
(define (display-expected/actual expected actual)
|
(define (display-expected/actual expected actual format)
|
||||||
(let* ((e-str (write-to-string expected))
|
(let ((e-str (format expected))
|
||||||
(a-str (write-to-string actual))
|
(a-str (format actual)))
|
||||||
(diff (diff e-str a-str read-char)))
|
(if (and (equal? e-str a-str)
|
||||||
|
(not (eqv? format write-to-string)))
|
||||||
|
;; If the formatter can't display any difference, fall back to
|
||||||
|
;; write-to-string.
|
||||||
|
(display-expected/actual expected actual write-to-string)
|
||||||
|
(let ((diff (diff e-str a-str read-char)))
|
||||||
(write-string "expected ")
|
(write-string "expected ")
|
||||||
(write-string (edits->string/color (car diff) (car (cddr diff)) 1))
|
(write-string (edits->string/color (car diff) (car (cddr diff)) 1))
|
||||||
(write-string " but got ")
|
(write-string " but got ")
|
||||||
(write-string (edits->string/color (cadr diff) (car (cddr diff)) 2))))
|
(write-string (edits->string/color (cadr diff) (car (cddr diff)) 2))
|
||||||
|
))))
|
||||||
|
|
||||||
(define (test-print-explanation indent status info)
|
(define (test-print-explanation indent status info)
|
||||||
(cond
|
(cond
|
||||||
|
@ -617,8 +624,9 @@
|
||||||
(write (assq-ref info 'result)))))
|
(write (assq-ref info 'result)))))
|
||||||
((eq? status 'FAIL)
|
((eq? status 'FAIL)
|
||||||
(display indent)
|
(display indent)
|
||||||
(display-expected/actual
|
(display-expected/actual (assq-ref info 'expected)
|
||||||
(assq-ref info 'expected) (assq-ref info 'result))))
|
(assq-ref info 'result)
|
||||||
|
(or (assq-ref info 'format) write-to-string))))
|
||||||
;; print variables
|
;; print variables
|
||||||
(cond
|
(cond
|
||||||
((and (memq status '(FAIL ERROR)) (assq-ref info 'var-names))
|
((and (memq status '(FAIL ERROR)) (assq-ref info 'var-names))
|
||||||
|
@ -863,6 +871,11 @@
|
||||||
|
|
||||||
;;> \section{Parameters}
|
;;> \section{Parameters}
|
||||||
|
|
||||||
|
;;> If specified, takes a single object as input (the expected or
|
||||||
|
;;> actual value of a test) and returns the string representation
|
||||||
|
;;> (default \scheme{write-to-string}).
|
||||||
|
(define current-test-value-formatter (make-parameter #f))
|
||||||
|
|
||||||
;;> The current test group as started by \scheme{test-group} or
|
;;> The current test group as started by \scheme{test-group} or
|
||||||
;;> \scheme{test-begin}.
|
;;> \scheme{test-begin}.
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
test-get-name! test-group-name test-group-ref
|
test-get-name! test-group-name test-group-ref
|
||||||
test-group-set! test-group-inc! test-group-push!
|
test-group-set! test-group-inc! test-group-push!
|
||||||
;; parameters
|
;; parameters
|
||||||
current-test-verbosity
|
current-test-value-formatter current-test-verbosity
|
||||||
current-test-applier current-test-skipper current-test-reporter
|
current-test-applier current-test-skipper current-test-reporter
|
||||||
current-test-group-reporter test-failure-count
|
current-test-group-reporter test-failure-count
|
||||||
current-test-epsilon current-test-comparator
|
current-test-epsilon current-test-comparator
|
||||||
|
|
24
lib/srfi/35.sld
Normal file
24
lib/srfi/35.sld
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
(define-library (srfi 35)
|
||||||
|
(import (srfi 35 internal))
|
||||||
|
(export make-condition-type
|
||||||
|
condition-type?
|
||||||
|
make-condition
|
||||||
|
condition?
|
||||||
|
condition-has-type?
|
||||||
|
condition-ref
|
||||||
|
make-compound-condition
|
||||||
|
extract-condition
|
||||||
|
define-condition-type
|
||||||
|
condition
|
||||||
|
|
||||||
|
&condition
|
||||||
|
|
||||||
|
&message
|
||||||
|
message-condition?
|
||||||
|
condition-message
|
||||||
|
|
||||||
|
&serious
|
||||||
|
serious-condition?
|
||||||
|
|
||||||
|
&error
|
||||||
|
error?))
|
249
lib/srfi/35/internal.scm
Normal file
249
lib/srfi/35/internal.scm
Normal file
|
@ -0,0 +1,249 @@
|
||||||
|
(define-record-type Simple-Condition
|
||||||
|
(make-simple-condition)
|
||||||
|
simple-condition?)
|
||||||
|
|
||||||
|
(define-record-type Compound-Condition
|
||||||
|
(%make-compound-condition components)
|
||||||
|
compound-condition?
|
||||||
|
(components compound-condition-components))
|
||||||
|
|
||||||
|
(define (make-condition-type id parent field-names)
|
||||||
|
(make-rtd id
|
||||||
|
(list->vector
|
||||||
|
(map
|
||||||
|
(lambda (field-name)
|
||||||
|
(list 'immutable field-name))
|
||||||
|
field-names))
|
||||||
|
parent))
|
||||||
|
|
||||||
|
(define (condition? obj)
|
||||||
|
(or (simple-condition? obj)
|
||||||
|
(compound-condition? obj)))
|
||||||
|
|
||||||
|
(define (condition-type? obj)
|
||||||
|
(condition-subtype? obj Simple-Condition))
|
||||||
|
|
||||||
|
(define (condition-subtype? maybe-child-ct maybe-parent-ct)
|
||||||
|
(and (rtd? maybe-child-ct)
|
||||||
|
(or (eqv? maybe-child-ct maybe-parent-ct)
|
||||||
|
(condition-subtype? (rtd-parent maybe-child-ct)
|
||||||
|
maybe-parent-ct))))
|
||||||
|
|
||||||
|
(define (condition-type-ancestors ct)
|
||||||
|
(unfold (lambda (a) (not (condition-type? a)))
|
||||||
|
(lambda (a) a)
|
||||||
|
(lambda (a) (rtd-parent a))
|
||||||
|
ct))
|
||||||
|
|
||||||
|
(define (condition-type-common-ancestor ct_1 ct_2)
|
||||||
|
(let ((ct_1-as (condition-type-ancestors ct_1))
|
||||||
|
(ct_2-as (condition-type-ancestors ct_2)))
|
||||||
|
(find (lambda (a)
|
||||||
|
(memv a ct_2-as))
|
||||||
|
ct_1-as)))
|
||||||
|
|
||||||
|
(define (make-condition ct . plist)
|
||||||
|
(define *undef* (cons '*undef* '()))
|
||||||
|
(let* ((field-names (rtd-all-field-names ct))
|
||||||
|
(field-values (make-vector (vector-length field-names) *undef*)))
|
||||||
|
(let loop ((property plist))
|
||||||
|
(if (null? property)
|
||||||
|
(cond ((vector-any (lambda (name value)
|
||||||
|
(and (eq? value *undef*) name))
|
||||||
|
field-names
|
||||||
|
field-values)
|
||||||
|
=> (lambda (undef-field-name)
|
||||||
|
(error "make-condition: value not given for field"
|
||||||
|
undef-field-name
|
||||||
|
ct)))
|
||||||
|
(else
|
||||||
|
(apply (rtd-constructor ct) (vector->list field-values))))
|
||||||
|
(let ((idx (vector-index (lambda (x) (eq? x (car property)))
|
||||||
|
field-names)))
|
||||||
|
(if idx
|
||||||
|
(begin
|
||||||
|
(vector-set! field-values idx (cadr property))
|
||||||
|
(loop (cddr property)))
|
||||||
|
(error "make-condition: unknown field" (car property))))))))
|
||||||
|
|
||||||
|
(define (make-compound-condition . cs)
|
||||||
|
(if (= (length cs) 1)
|
||||||
|
(car cs)
|
||||||
|
;; SRFI 35 requires at least one component, but R6RS doesn’t;
|
||||||
|
;; defer to R6RS’s less strict error checking (!)
|
||||||
|
(%make-compound-condition
|
||||||
|
(append-map
|
||||||
|
(lambda (c)
|
||||||
|
(if (simple-condition? c)
|
||||||
|
(list c)
|
||||||
|
(compound-condition-components c)))
|
||||||
|
cs))))
|
||||||
|
|
||||||
|
(define (condition-has-type? c ct)
|
||||||
|
(if (simple-condition? c)
|
||||||
|
(is-a? c ct)
|
||||||
|
(any
|
||||||
|
(lambda (comp) (condition-has-type? comp ct))
|
||||||
|
(compound-condition-components c))))
|
||||||
|
|
||||||
|
(define (condition-ref c field-name)
|
||||||
|
(if (simple-condition? c)
|
||||||
|
((rtd-accessor (record-rtd c) field-name) c)
|
||||||
|
(condition-ref
|
||||||
|
(find
|
||||||
|
(lambda (comp)
|
||||||
|
(find field-name
|
||||||
|
(vector->list
|
||||||
|
(rtd-all-field-names (record-rtd c)))))
|
||||||
|
(compound-condition-components c))
|
||||||
|
field-name)))
|
||||||
|
|
||||||
|
(define (simple-conditions c)
|
||||||
|
(if (simple-condition? c)
|
||||||
|
(list c)
|
||||||
|
(compound-condition-components c)))
|
||||||
|
|
||||||
|
(define (extract-condition c ct)
|
||||||
|
(if (and (simple-condition? c)
|
||||||
|
(condition-has-type? c ct))
|
||||||
|
c
|
||||||
|
(find
|
||||||
|
(lambda (comp)
|
||||||
|
(condition-has-type? comp ct))
|
||||||
|
(compound-condition-components ct))))
|
||||||
|
|
||||||
|
(define (condition-predicate ct)
|
||||||
|
(lambda (obj)
|
||||||
|
(and (condition? obj)
|
||||||
|
(condition-has-type? obj ct))))
|
||||||
|
(define (condition-accessor ct proc)
|
||||||
|
(lambda (c)
|
||||||
|
(cond ((and (simple-condition? c)
|
||||||
|
(condition-has-type? c ct))
|
||||||
|
(proc c))
|
||||||
|
((find (lambda (comp) (condition-has-type? comp ct))
|
||||||
|
(compound-condition-components c))
|
||||||
|
=> (lambda (comp)
|
||||||
|
(proc comp)))
|
||||||
|
(else (error "condition-accessor: condition does not have the right type"
|
||||||
|
c ct)))))
|
||||||
|
|
||||||
|
(define-syntax define-condition-type/constructor
|
||||||
|
(syntax-rules ()
|
||||||
|
((_ name parent constructor predicate
|
||||||
|
(field-name field-accessor) ...)
|
||||||
|
(begin
|
||||||
|
(define ct (make-condition-type 'name
|
||||||
|
parent
|
||||||
|
'(field-name ...)))
|
||||||
|
(define name ct)
|
||||||
|
(define constructor (rtd-constructor ct))
|
||||||
|
(define predicate (condition-predicate ct))
|
||||||
|
(define field-accessor
|
||||||
|
(condition-accessor ct
|
||||||
|
(rtd-accessor ct 'field-name))) ...))))
|
||||||
|
|
||||||
|
(define-syntax define-condition-type
|
||||||
|
(syntax-rules ()
|
||||||
|
((_ name parent predicate (field-name field-accessor) ...)
|
||||||
|
(define-condition-type/constructor
|
||||||
|
name parent blah-ignored predicate
|
||||||
|
(field-name field-accessor) ...))))
|
||||||
|
|
||||||
|
(define (%condition . specs)
|
||||||
|
(define (find-common-field-spec ct name)
|
||||||
|
(let loop ((more-specs specs))
|
||||||
|
(if (null? more-specs)
|
||||||
|
#f
|
||||||
|
(let* ((other-ct (caar more-specs))
|
||||||
|
(field-specs (cdar more-specs))
|
||||||
|
(a (condition-type-common-ancestor ct other-ct)))
|
||||||
|
(cond ((and (vector-index
|
||||||
|
(lambda (n)
|
||||||
|
(eq? n name))
|
||||||
|
(rtd-all-field-names a))
|
||||||
|
(assq name field-specs)))
|
||||||
|
(else (loop (cdr more-specs))))))))
|
||||||
|
(let loop ((more-specs specs)
|
||||||
|
(components '()))
|
||||||
|
(if (null? more-specs)
|
||||||
|
(apply make-compound-condition (reverse components))
|
||||||
|
(let* ((this-spec (car more-specs))
|
||||||
|
(ct (car this-spec))
|
||||||
|
(field-specs (cdr this-spec))
|
||||||
|
(field-names (rtd-all-field-names ct))
|
||||||
|
(field-values
|
||||||
|
(vector-map
|
||||||
|
(lambda (field-name)
|
||||||
|
(cond ((assq field-name field-specs) => cdr)
|
||||||
|
((find-common-field-spec ct field-name) => cdr)
|
||||||
|
(else
|
||||||
|
(error "condition: value not given for field"
|
||||||
|
field-name
|
||||||
|
ct))))
|
||||||
|
field-names)))
|
||||||
|
(loop
|
||||||
|
(cdr more-specs)
|
||||||
|
(cons
|
||||||
|
(apply (rtd-constructor ct) (vector->list field-values))
|
||||||
|
components))))))
|
||||||
|
(define-syntax condition
|
||||||
|
(syntax-rules ()
|
||||||
|
((_ (ct (field-name field-value) ...) ...)
|
||||||
|
(%condition (list ct (cons 'field-name field-value) ...) ...))))
|
||||||
|
|
||||||
|
(define &condition Simple-Condition)
|
||||||
|
|
||||||
|
(define-condition-type/constructor &message &condition
|
||||||
|
make-message-condition message-condition?
|
||||||
|
(message condition-message))
|
||||||
|
|
||||||
|
(define-condition-type/constructor &serious &condition
|
||||||
|
make-serious-condition serious-condition?)
|
||||||
|
|
||||||
|
(define-condition-type/constructor &error &serious
|
||||||
|
make-error error?)
|
||||||
|
|
||||||
|
;; (chibi repl) support
|
||||||
|
(define-method (repl-print-exception (exn condition?) (out output-port?))
|
||||||
|
(define components (simple-conditions exn))
|
||||||
|
(define n-components (length components))
|
||||||
|
(display "CONDITION: " out)
|
||||||
|
(display n-components out)
|
||||||
|
(display " component" out)
|
||||||
|
(if (not (= n-components 1)) (display "s" out))
|
||||||
|
(display "\n" out)
|
||||||
|
(for-each
|
||||||
|
(lambda (component idx)
|
||||||
|
(define component-type (record-rtd component))
|
||||||
|
(display " " out)
|
||||||
|
(display idx out)
|
||||||
|
(display ". " out)
|
||||||
|
(display (rtd-name component-type) out)
|
||||||
|
(display "\n" out)
|
||||||
|
(let loop ((as (reverse
|
||||||
|
(condition-type-ancestors component-type)))
|
||||||
|
(idx 0))
|
||||||
|
(if (not (null? as))
|
||||||
|
(let ((a (car as)))
|
||||||
|
(let a-loop ((fields (vector->list (rtd-field-names a)))
|
||||||
|
(idx idx))
|
||||||
|
(if (null? fields)
|
||||||
|
(loop (cdr as) idx)
|
||||||
|
(begin
|
||||||
|
(display " " out)
|
||||||
|
(display (if (pair? (car fields))
|
||||||
|
(car (cdar fields))
|
||||||
|
(car fields))
|
||||||
|
out)
|
||||||
|
(if (not (eqv? a component-type))
|
||||||
|
(begin
|
||||||
|
(display " (" out)
|
||||||
|
(display (rtd-name a) out)
|
||||||
|
(display ")" out)))
|
||||||
|
(display ": " out)
|
||||||
|
(write (slot-ref component-type component idx) out)
|
||||||
|
(display "\n" out)
|
||||||
|
(a-loop (cdr fields) (+ idx 1)))))))))
|
||||||
|
components
|
||||||
|
(iota n-components 1)))
|
48
lib/srfi/35/internal.sld
Normal file
48
lib/srfi/35/internal.sld
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
(define-library (srfi 35 internal)
|
||||||
|
(import (except (scheme base)
|
||||||
|
define-record-type
|
||||||
|
;; exclude (srfi 1 immutable) duplicate imports:
|
||||||
|
map cons list append reverse)
|
||||||
|
(scheme write)
|
||||||
|
(only (chibi)
|
||||||
|
slot-ref
|
||||||
|
is-a?)
|
||||||
|
(only (chibi repl) repl-print-exception)
|
||||||
|
(only (chibi generic) define-method)
|
||||||
|
;; don’t let people go messing with a compound condition
|
||||||
|
;; components list:
|
||||||
|
(srfi 1 immutable)
|
||||||
|
(srfi 99)
|
||||||
|
(srfi 133))
|
||||||
|
(export make-condition-type
|
||||||
|
condition?
|
||||||
|
condition-type?
|
||||||
|
condition-subtype?
|
||||||
|
make-condition
|
||||||
|
make-compound-condition
|
||||||
|
condition-has-type?
|
||||||
|
condition-ref
|
||||||
|
simple-conditions
|
||||||
|
extract-condition
|
||||||
|
condition-predicate
|
||||||
|
condition-accessor
|
||||||
|
define-condition-type/constructor
|
||||||
|
define-condition-type
|
||||||
|
condition
|
||||||
|
|
||||||
|
&condition
|
||||||
|
|
||||||
|
&message
|
||||||
|
make-message-condition
|
||||||
|
message-condition?
|
||||||
|
condition-message
|
||||||
|
|
||||||
|
&serious
|
||||||
|
make-serious-condition
|
||||||
|
serious-condition?
|
||||||
|
|
||||||
|
&error
|
||||||
|
make-error
|
||||||
|
error?)
|
||||||
|
|
||||||
|
(include "internal.scm"))
|
94
lib/srfi/35/test.sld
Normal file
94
lib/srfi/35/test.sld
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
(define-library (srfi 35 test)
|
||||||
|
(import (scheme base)
|
||||||
|
(srfi 35 internal)
|
||||||
|
(chibi test))
|
||||||
|
(export run-tests)
|
||||||
|
(begin
|
||||||
|
(define (run-tests)
|
||||||
|
(test-begin "srfi-35: condition types")
|
||||||
|
(test-group "Adapted from the SRFI 35 examples"
|
||||||
|
(define-condition-type &c &condition
|
||||||
|
c?
|
||||||
|
(x c-x))
|
||||||
|
|
||||||
|
(define-condition-type &c1 &c
|
||||||
|
c1?
|
||||||
|
(a c1-a))
|
||||||
|
|
||||||
|
(define-condition-type &c2 &c
|
||||||
|
c2?
|
||||||
|
(b c2-b))
|
||||||
|
(define v1 (make-condition &c1 'x "V1" 'a "a1"))
|
||||||
|
(define v2 (condition (&c2
|
||||||
|
(x "V2")
|
||||||
|
(b "b2"))))
|
||||||
|
(define v3 (condition (&c1
|
||||||
|
(x "V3/1")
|
||||||
|
(a "a3"))
|
||||||
|
(&c2
|
||||||
|
(b "b3"))))
|
||||||
|
(define v4 (make-compound-condition v1 v2))
|
||||||
|
(define v5 (make-compound-condition v2 v3))
|
||||||
|
|
||||||
|
(test #t (c? v1))
|
||||||
|
(test #t (c1? v1))
|
||||||
|
(test #f (c2? v1))
|
||||||
|
(test "V1" (c-x v1))
|
||||||
|
(test "a1" (c1-a v1))
|
||||||
|
|
||||||
|
(test #t (c? v2))
|
||||||
|
(test #f (c1? v2))
|
||||||
|
(test #t (c2? v2))
|
||||||
|
(test "V2" (c-x v2))
|
||||||
|
(test "b2" (c2-b v2))
|
||||||
|
|
||||||
|
(test #t (c? v3))
|
||||||
|
(test #t (c1? v3))
|
||||||
|
(test #t (c2? v3))
|
||||||
|
(test "V3/1" (c-x v3))
|
||||||
|
(test "a3" (c1-a v3))
|
||||||
|
(test "b3" (c2-b v3))
|
||||||
|
|
||||||
|
(test #t (c? v4))
|
||||||
|
(test #t (c1? v4))
|
||||||
|
(test #t (c2? v4))
|
||||||
|
(test "V1" (c-x v4))
|
||||||
|
(test "a1" (c1-a v4))
|
||||||
|
(test "b2" (c2-b v4))
|
||||||
|
|
||||||
|
(test #t (c? v5))
|
||||||
|
(test #t (c1? v5))
|
||||||
|
(test #t (c2? v5))
|
||||||
|
(test "V2" (c-x v5))
|
||||||
|
(test "a3" (c1-a v5))
|
||||||
|
(test "b2" (c2-b v5)))
|
||||||
|
|
||||||
|
(test-group "Standard condition hierarchy"
|
||||||
|
(let ((mc (make-message-condition "foo!")))
|
||||||
|
(test #t (message-condition? mc))
|
||||||
|
(test "foo!" (condition-message mc))
|
||||||
|
|
||||||
|
(let ((ec (make-error)))
|
||||||
|
(test #t (error? ec))
|
||||||
|
(test #t (serious-condition? ec))
|
||||||
|
|
||||||
|
(let ((cc (make-compound-condition ec mc)))
|
||||||
|
(test #t (error? cc))
|
||||||
|
(test #t (serious-condition? cc))
|
||||||
|
(test #t (message-condition? cc))
|
||||||
|
(test "foo!" (condition-message mc))))))
|
||||||
|
|
||||||
|
(test-group "R6RS extension: shadowing field names"
|
||||||
|
(define-condition-type/constructor &a &condition
|
||||||
|
make-a a?
|
||||||
|
(val a-val))
|
||||||
|
(define-condition-type/constructor &b &a
|
||||||
|
make-b b?
|
||||||
|
(val b-val))
|
||||||
|
|
||||||
|
(define c (make-b 'a 'b))
|
||||||
|
|
||||||
|
(test 'a (a-val c))
|
||||||
|
(test 'b (b-val c)))
|
||||||
|
|
||||||
|
(test-end))))
|
|
@ -9,7 +9,13 @@
|
||||||
(type? x))
|
(type? x))
|
||||||
|
|
||||||
(define (rtd-constructor rtd . o)
|
(define (rtd-constructor rtd . o)
|
||||||
(let ((fields (vector->list (if (pair? o) (car o) (rtd-all-field-names rtd))))
|
(let ((fields
|
||||||
|
(if (pair? o)
|
||||||
|
(map
|
||||||
|
(lambda (field)
|
||||||
|
(rtd-field-offset rtd field))
|
||||||
|
(vector->list (car o)))
|
||||||
|
(iota (vector-length (rtd-all-field-names rtd)))))
|
||||||
(make (make-constructor (type-name rtd) rtd)))
|
(make (make-constructor (type-name rtd) rtd)))
|
||||||
(lambda args
|
(lambda args
|
||||||
(let ((res (make)))
|
(let ((res (make)))
|
||||||
|
@ -18,7 +24,7 @@
|
||||||
((null? a) (if (null? p) res (error "not enough args" p)))
|
((null? a) (if (null? p) res (error "not enough args" p)))
|
||||||
((null? p) (error "too many args" a))
|
((null? p) (error "too many args" a))
|
||||||
(else
|
(else
|
||||||
(slot-set! rtd res (rtd-field-offset rtd (car p)) (car a))
|
(slot-set! rtd res (car p) (car a))
|
||||||
(lp (cdr a) (cdr p)))))))))
|
(lp (cdr a) (cdr p)))))))))
|
||||||
|
|
||||||
(define (rtd-predicate rtd)
|
(define (rtd-predicate rtd)
|
||||||
|
@ -35,13 +41,13 @@
|
||||||
|
|
||||||
(define (rtd-field-offset rtd field)
|
(define (rtd-field-offset rtd field)
|
||||||
(let ((p (type-parent rtd)))
|
(let ((p (type-parent rtd)))
|
||||||
(or (and (type? p)
|
(or (let ((i (field-index-of (type-slots rtd) field)))
|
||||||
(rtd-field-offset p field))
|
|
||||||
(let ((i (field-index-of (type-slots rtd) field)))
|
|
||||||
(and i
|
(and i
|
||||||
(if (type? p)
|
(if (type? p)
|
||||||
(+ i (vector-length (rtd-all-field-names p)))
|
(+ i (vector-length (rtd-all-field-names p)))
|
||||||
i))))))
|
i)))
|
||||||
|
(and (type? p)
|
||||||
|
(rtd-field-offset p field)))))
|
||||||
|
|
||||||
(define (rtd-accessor rtd field)
|
(define (rtd-accessor rtd field)
|
||||||
(make-getter (type-name rtd) rtd (rtd-field-offset rtd field)))
|
(make-getter (type-name rtd) rtd (rtd-field-offset rtd field)))
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
|
|
||||||
(define-library (srfi 99 records procedural)
|
(define-library (srfi 99 records procedural)
|
||||||
(export make-rtd rtd? rtd-constructor rtd-predicate rtd-accessor rtd-mutator)
|
(export make-rtd rtd? rtd-constructor rtd-predicate rtd-accessor rtd-mutator)
|
||||||
(import (chibi) (chibi ast) (srfi 99 records inspection))
|
(import (chibi)
|
||||||
|
(chibi ast)
|
||||||
|
(only (srfi 1) iota)
|
||||||
|
(srfi 99 records inspection))
|
||||||
(include "procedural.scm"))
|
(include "procedural.scm"))
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
(rename (srfi 18 test) (run-tests run-srfi-18-tests))
|
(rename (srfi 18 test) (run-tests run-srfi-18-tests))
|
||||||
(rename (srfi 26 test) (run-tests run-srfi-26-tests))
|
(rename (srfi 26 test) (run-tests run-srfi-26-tests))
|
||||||
(rename (srfi 27 test) (run-tests run-srfi-27-tests))
|
(rename (srfi 27 test) (run-tests run-srfi-27-tests))
|
||||||
|
(rename (srfi 35 test) (run-tests run-srfi-35-tests))
|
||||||
(rename (srfi 38 test) (run-tests run-srfi-38-tests))
|
(rename (srfi 38 test) (run-tests run-srfi-38-tests))
|
||||||
(rename (srfi 41 test) (run-tests run-srfi-41-tests))
|
(rename (srfi 41 test) (run-tests run-srfi-41-tests))
|
||||||
(rename (srfi 69 test) (run-tests run-srfi-69-tests))
|
(rename (srfi 69 test) (run-tests run-srfi-69-tests))
|
||||||
|
@ -83,6 +84,7 @@
|
||||||
(run-srfi-18-tests)
|
(run-srfi-18-tests)
|
||||||
(run-srfi-26-tests)
|
(run-srfi-26-tests)
|
||||||
(run-srfi-27-tests)
|
(run-srfi-27-tests)
|
||||||
|
(run-srfi-35-tests)
|
||||||
(run-srfi-38-tests)
|
(run-srfi-38-tests)
|
||||||
(run-srfi-41-tests)
|
(run-srfi-41-tests)
|
||||||
(run-srfi-69-tests)
|
(run-srfi-69-tests)
|
||||||
|
|
Loading…
Add table
Reference in a new issue