aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd.ha2
-rw-r--r--format/tsv/README6
-rw-r--r--format/tsv/reader.ha52
-rw-r--r--format/tsv/writer.ha63
-rw-r--r--tsv/tsv.ha22
5 files changed, 122 insertions, 23 deletions
diff --git a/cmd.ha b/cmd.ha
index 3335209..290ff72 100644
--- a/cmd.ha
+++ b/cmd.ha
@@ -7,7 +7,7 @@ use path;
use os::exec;
use strconv;
use ascii;
-use tsv;
+use format::tsv;
type error = !(!str | io::error | path::error | exec::error | strconv::error);
diff --git a/format/tsv/README b/format/tsv/README
new file mode 100644
index 0000000..67d9d71
--- /dev/null
+++ b/format/tsv/README
@@ -0,0 +1,6 @@
+format::tsv supports writing and reading text in TSV (Tab Separated Values) format:
+
+ name\tdescription
+ Hare\tThe Hare programming language
+
+TSV doesn't allow tabs to be used as anything other than a separator. Due to this, any existing tabs are removed when writing.
diff --git a/format/tsv/reader.ha b/format/tsv/reader.ha
new file mode 100644
index 0000000..df2ba35
--- /dev/null
+++ b/format/tsv/reader.ha
@@ -0,0 +1,52 @@
+use bufio;
+use io;
+use encoding::utf8;
+use strings;
+use memio;
+use fmt;
+
+// Reads records from an io::handle and returns them.
+export fn readrecords(r: io::handle) ([][]str | io::error | utf8::invalid) = {
+ const sc = bufio::newscanner(r);
+ defer bufio::finish(&sc);
+
+ let records: [][]str = [];
+ for (const line: str => bufio::scan_line(&sc)?) {
+ append(records, strings::dupall(strings::split(line, "\t")));
+ };
+ return records;
+};
+
+// Frees all the records.
+export fn freerecords(records: [][]str) void = {
+ for (const record .. records) {
+ strings::freeall(record);
+ };
+ free(records);
+};
+
+@test fn readnormal() void = {
+ const b = strings::toutf8("col1\tcol2\tcol3
+1\t2\t3
+4\t5\t6\n"
+);
+ const st = memio::fixed(b);
+
+ const expected = [
+ ["col1", "col2", "col3"],
+ ["1", "2", "3"],
+ ["4", "5", "6"],
+ ];
+
+ const actual = readrecords(&st)!;
+ defer freerecords(actual);
+ for (let i = 0z; i < len(expected); i += 1) {
+ for (let j = 0z; j < len(actual); j += 1) {
+ const exp = expected[i][j];
+ const act = actual[i][j];
+ fmt::errorfln("exp: {}", exp)!;
+ fmt::errorfln("act: {}", act)!;
+ assert(exp == act);
+ };
+ };
+};
diff --git a/format/tsv/writer.ha b/format/tsv/writer.ha
new file mode 100644
index 0000000..f1f045b
--- /dev/null
+++ b/format/tsv/writer.ha
@@ -0,0 +1,63 @@
+use io;
+use strings;
+use fmt;
+use memio;
+
+// Writes a slice strings to a handle in TSV format. Existing tabs in the record
+// are removed.
+export fn writerecord(w: io::handle, record: []str) (void | io::error) = {
+ let sep = "";
+ for (const field .. record) {
+ const pfield = strings::replace(field, "\t", "");
+ defer free(pfield);
+ fmt::fprintf(w, "{}{}", sep, pfield)!;
+ sep = "\t";
+ };
+ fmt::fprintln(w)!;
+};
+
+// Writes a slice of string slices to a handle in TSV format. Existing tabs in
+// the records are removed.
+export fn writerecords(w: io::handle, records: [][]str) (void | io::error) = {
+ for (const record .. records) {
+ writerecord(w, record)?;
+ };
+};
+
+@test fn writenormal() void = {
+ const expected = "col1\tcol2\tcol3
+1\t2\t3
+4\t5\t6\n";
+ const input: [][]str = [
+ ["col1", "col2", "col3"],
+ ["1", "2", "3"],
+ ["4", "5", "6"],
+ ];
+ const st = memio::dynamic();
+ defer io::close(&st)!;
+ writerecords(&st, input)!;
+ const actual = memio::string(&st)!;
+ fmt::errorfln("expected: {}EOF", expected)!;
+ fmt::errorln()!;
+ fmt::errorfln("actual: {}EOF", actual)!;
+ assert(actual == expected);
+};
+
+@test fn writetabs() void = {
+ const expected = "col1\tcol2\tcol3
+1\t2\t3
+4\t5\t6\n";
+ const input: [][]str = [
+ ["col1\t", "co\tl2", "col3"],
+ ["1", "2", "\t3"],
+ ["4\t", "\t\t5\t", "6"],
+ ];
+ const st = memio::dynamic();
+ defer io::close(&st)!;
+ writerecords(&st, input)!;
+ const actual = memio::string(&st)!;
+ fmt::errorfln("expected: {}EOF", expected)!;
+ fmt::errorln()!;
+ fmt::errorfln("actual: {}EOF", actual)!;
+ assert(actual == expected);
+};
diff --git a/tsv/tsv.ha b/tsv/tsv.ha
deleted file mode 100644
index 85015a1..0000000
--- a/tsv/tsv.ha
+++ /dev/null
@@ -1,22 +0,0 @@
-use io;
-use strings;
-use fmt;
-
-export type error = !(str | io::error);
-
-export fn writerecord(w: io::handle, record: []str) (void | error) = {
- let sep = "";
- for (const field .. record) {
- const pfield = strings::replace(field, "\t", "");
- defer free(pfield);
- fmt::fprintf(w, "{}{}", sep, pfield)!;
- sep = "\t";
- };
- fmt::fprintln(w)!;
-};
-
-export fn writerecords(w: io::handle, records: [][]str) (void | error) = {
- for (const record .. records) {
- writerecord(w, record)?;
- };
-};