summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKaz Kylheku <kaz@kylheku.com>2025-01-21 19:41:00 -0800
committerKaz Kylheku <kaz@kylheku.com>2025-01-21 19:41:00 -0800
commit9ee82d9d744de52916817412318f43f26960d719 (patch)
tree5dec34c18ec021c580441953a73ac53979cf2a8c
parent35d1c7f01f6bf2004c048ebecf12132e48454d87 (diff)
downloadtxr-9ee82d9d744de52916817412318f43f26960d719.tar.gz
txr-9ee82d9d744de52916817412318f43f26960d719.tar.bz2
txr-9ee82d9d744de52916817412318f43f26960d719.zip
get-csv: rewrite in C.
* autload.c (csv_set_entries, csv_instantiate): Functions removed. (autoload_init): Autoload registration for stdlib/csv removed. * stdlib/csv.tl: File removed. * stream.c (get_csv): New function. (stream_init): Register get-csv intrinsic. * stream.h (get_csv): Declared.
-rw-r--r--autoload.c17
-rw-r--r--stdlib/csv.tl74
-rw-r--r--stream.c78
-rw-r--r--stream.h1
4 files changed, 79 insertions, 91 deletions
diff --git a/autoload.c b/autoload.c
index 251707aa..dd1ef410 100644
--- a/autoload.c
+++ b/autoload.c
@@ -1020,22 +1020,6 @@ static val enum_instantiate(void)
return nil;
}
-static val csv_set_entries(val fun)
-{
- val name[] = {
- lit("get-csv"),
- nil
- };
- autoload_set(al_fun, name, fun);
- return nil;
-}
-
-static val csv_instantiate(void)
-{
- load(scat2(stdlib_path, lit("csv")));
- return nil;
-}
-
val autoload_reg(val (*instantiate)(void),
val (*set_entries)(val))
{
@@ -1108,7 +1092,6 @@ void autoload_init(void)
autoload_reg(csort_instantiate, csort_set_entries);
autoload_reg(glob_instantiate, glob_set_entries);
autoload_reg(enum_instantiate, enum_set_entries);
- autoload_reg(csv_instantiate, csv_set_entries);
reg_fun(intern(lit("autoload-try-fun"), system_package), func_n1(autoload_try_fun));
}
diff --git a/stdlib/csv.tl b/stdlib/csv.tl
deleted file mode 100644
index f3b4f190..00000000
--- a/stdlib/csv.tl
+++ /dev/null
@@ -1,74 +0,0 @@
-;; Copyright 2025
-;; Kaz Kylheku <kaz@kylheku.com>
-;; Vancouver, Canada
-;; All rights reserved.
-;;
-;; Redistribution and use in source and binary forms, with or without
-;; modification, are permitted provided that the following conditions are met:
-;;
-;; 1. Redistributions of source code must retain the above copyright notice,
-;; this list of conditions and the following disclaimer.
-;;
-;; 2. Redistributions in binary form must reproduce the above copyright notice,
-;; this list of conditions and the following disclaimer in the documentation
-;; and/or other materials provided with the distribution.
-;;
-;; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-;; AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-;; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-;; ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-;; LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-;; CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-;; SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-;; CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-;; ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-;; POSSIBILITY OF SUCH DAMAGE.
-
-(defun get-csv (: (stream *stdin*))
- (if (stringp stream)
- (upd stream make-string-input-stream))
- (let ((record (vec))
- (field (str 0))
- (state :rfield)
- (done nil))
- (while (not done)
- (let ((ch (get-char stream)))
- (when (eq ch #\return)
- (let ((ch2 (get-char stream)))
- (if (eq ch2 #\newline)
- (set ch ch2)
- (if ch2
- (unget-char ch2 stream)))))
- (caseq state
- (:rfield (caseql* ch
- (#\newline (vec-push record field)
- (set done t))
- (#\, (vec-push record field)
- (set field (str 0)))
- (#\" (cond
- ((empty field)
- (set state :qfield))
- (t
- (string-extend field ch))))
- (nil (vec-push record field)
- (set done t))
- (t (string-extend field ch))))
- (:qfield (caseql* ch
- (#\" (set state :quot))
- (nil (vec-push record field)
- (set done t))
- (t (string-extend field ch))))
- (:quot (caseql* ch
- (#\, (vec-push record field)
- (set field (str 0)
- state :rfield))
- (#\" (string-extend field ch)
- (set state :qfield))
- (#\newline (vec-push record field)
- (set done t))
- (nil (vec-push record field)
- (set done t))
- (t (string-extend field ch)
- (set state :rfield)))))))
- record))
diff --git a/stream.c b/stream.c
index 51b8c4d9..8b9b2f60 100644
--- a/stream.c
+++ b/stream.c
@@ -5422,6 +5422,83 @@ val make_byte_input_stream(val obj)
}
}
+val get_csv(val source_opt)
+{
+ val source = if3(missingp(source_opt),
+ std_input,
+ if3(stringp(source_opt),
+ make_string_input_stream(source_opt),
+ source_opt));
+ val record = vector(zero, nil);
+ val field = mkstring(zero, chr(' '));
+ enum { rfield, qfield, quot } state = rfield;
+ int done = 0;
+
+ while (!done) {
+ val ch = get_char(source);
+
+ if (ch == chr('\r')) {
+ val ch2 = get_char(source);
+ if (ch2 == chr('\n'))
+ ch = ch2;
+ else if (ch2)
+ unget_char(ch2, source);
+ }
+
+ switch (state) {
+ case rfield:
+ if (ch == chr('\n')) {
+ vec_push(record, field);
+ done = 1;
+ } else if (ch == chr(',')) {
+ vec_push(record, field);
+ field = mkstring(zero, chr(' '));
+ } else if (ch == chr('"')) {
+ if (empty(field))
+ state = qfield;
+ else
+ string_extend(field, ch, nil);
+ } else if (ch == nil) {
+ vec_push(record, field);
+ done = 1;
+ } else {
+ string_extend(field, ch, nil);
+ }
+ break;
+ case qfield:
+ if (ch == chr('"')) {
+ state = quot;
+ } else if (ch == nil) {
+ vec_push(record, field);
+ done = 1;
+ } else {
+ string_extend(field, ch, nil);
+ }
+ break;
+ case quot:
+ if (ch == chr(',')) {
+ vec_push(record, field);
+ field = mkstring(zero, chr(' '));
+ state = rfield;
+ } else if (ch == chr('"')) {
+ string_extend(field, ch, nil);
+ state = qfield;
+ } else if (ch == chr('\n')) {
+ vec_push(record, field);
+ done = 1;
+ } else if (ch == nil) {
+ vec_push(record, field);
+ done = 1;
+ } else {
+ string_extend(field, ch, nil);
+ state = rfield;
+ }
+ }
+ }
+
+ return record;
+}
+
val tmpfile_wrap(void)
{
val self = lit("tmpfile");
@@ -5697,6 +5774,7 @@ void stream_init(void)
reg_varl(intern(lit("indent-data"), user_package), num_fast(indent_data));
reg_varl(intern(lit("indent-code"), user_package), num_fast(indent_code));
reg_varl(intern(lit("indent-foff"), user_package), num_fast(indent_foff));
+ reg_fun(intern(lit("get-csv"), user_package), func_n1o(get_csv, 0));
reg_fun(intern(lit("tmpfile"), user_package), func_n0(tmpfile_wrap));
#if HAVE_MKDTEMP
reg_fun(intern(lit("mkdtemp"), user_package), func_n1(mkdtemp_wrap));
diff --git a/stream.h b/stream.h
index a0042933..23ca546d 100644
--- a/stream.h
+++ b/stream.h
@@ -285,6 +285,7 @@ val trim_path_seps(val name);
val path_cat(val dir_name, val base_name);
val add_suffix(val name, val suffix);
val make_byte_input_stream(val obj);
+val get_csv(val source_opt);
val iobuf_get(void);
void iobuf_put(val buf);
void iobuf_list_empty(void);