diff --git a/lib/std/core/dstring.c3 b/lib/std/core/dstring.c3 index 84b0e5f84..95b51bf99 100644 --- a/lib/std/core/dstring.c3 +++ b/lib/std/core/dstring.c3 @@ -375,7 +375,7 @@ fn void DString.reserve(DString* str, usz addition) *str = (DString)realloc(data, StringData.sizeof + new_capacity, .using = data.allocator); } -fn usz! DString.read_from_stream(DString* string, Stream* reader) +fn usz! DString.read_from_stream(DString* string, Stream reader) { if (reader.supports_available()) { diff --git a/lib/std/encoding/csv.c3 b/lib/std/encoding/csv.c3 new file mode 100644 index 000000000..75181a76a --- /dev/null +++ b/lib/std/encoding/csv.c3 @@ -0,0 +1,65 @@ +module std::encoding::csv; +import std::io; + +struct CsvReader +{ + Stream stream; + String separator; +} + +fn void CsvReader.init_file(CsvReader* csv, File* file, String separator = ",") +{ + csv.stream = file.as_stream(); + csv.separator = separator; +} + +fn void CsvReader.init(CsvReader* csv, Stream stream, String separator = ",") +{ + csv.stream = stream; + csv.separator = separator; +} + +fn String[]! CsvReader.read_row(CsvReader csv, Allocator* using = mem::heap()) +{ + @stack_mem(512; Allocator* mem) + { + return csv.stream.readline(mem).split(csv.separator, .using = using); + }; +} + +fn String[]! CsvReader.tread_row(CsvReader csv) +{ + return csv.read_row(mem::temp()) @inline; +} + +fn void! CsvReader.skip_row(CsvReader csv) @maydiscard +{ + @pool() + { + csv.stream.readline(mem::temp())!; + }; +} + +macro CsvReader.@each_row(CsvReader csv, int rows = int.max; @body(String[] row)) +{ + Stream stream = csv.stream; + String sep = csv.separator; + while (rows--) + { + @stack_mem(512; Allocator* mem) + { + String[] parts; + @pool() + { + String! s = stream.readline(mem::temp()); + if (catch err = s) + { + if (err == IoError.EOF) return; + return err?; + } + parts = s.split(sep, .using = mem); + }; + @body(parts); + }; + } +} \ No newline at end of file diff --git a/lib/std/io/io_stream.c3 b/lib/std/io/io_stream.c3 index f2c834795..7698fc271 100644 --- a/lib/std/io/io_stream.c3 +++ b/lib/std/io/io_stream.c3 @@ -1,18 +1,18 @@ module std::io; -def CloseStreamFn = fn void!(Stream*); -def FlushStreamFn = fn void!(Stream*); -def SeekStreamFn = fn usz!(Stream*, isz offset, Seek seek); -def LenStreamFn = fn usz(Stream*); -def AvailableStreamFn = fn usz(Stream*); -def ReadStreamFn = fn usz!(Stream*, char[] bytes); -def ReadFromStreamFn = fn usz!(Stream*, Stream*); -def ReadByteStreamFn = fn char!(Stream*); -def PushbackByteStreamFn = fn void!(Stream*); -def WriteStreamFn = fn usz!(Stream*, char[] bytes); -def WriteToStreamFn = fn usz!(Stream*, Stream* out); -def WriteByteStreamFn = fn void!(Stream*, char c); -def DestroyStreamFn = fn void!(Stream*); +def CloseStreamFn = fn void!(Stream); +def FlushStreamFn = fn void!(Stream); +def SeekStreamFn = fn usz!(Stream, isz offset, Seek seek); +def LenStreamFn = fn usz(Stream); +def AvailableStreamFn = fn usz(Stream); +def ReadStreamFn = fn usz!(Stream, char[] bytes); +def ReadFromStreamFn = fn usz!(Stream, Stream); +def ReadByteStreamFn = fn char!(Stream); +def PushbackByteStreamFn = fn void!(Stream); +def WriteStreamFn = fn usz!(Stream, char[] bytes); +def WriteToStreamFn = fn usz!(Stream, Stream out); +def WriteByteStreamFn = fn void!(Stream, char c); +def DestroyStreamFn = fn void!(Stream); struct StreamInterface { @@ -37,33 +37,33 @@ struct Stream void* data; } -fn bool Stream.supports_seek(Stream* s) @inline => (bool)s.fns.seek_fn; -fn bool Stream.supports_available(Stream* s) @inline => s.fns.available_fn || s.fns.seek_fn; -fn bool Stream.supports_len(Stream* s) @inline => s.fns.len_fn || s.fns.seek_fn; -fn bool Stream.supports_read(Stream* s) @inline => s.fns.read_fn || s.fns.read_byte_fn; -fn bool Stream.supports_read_from(Stream* s) @inline => (bool)s.fns.read_stream_fn; -fn bool Stream.supports_write_to(Stream* s) @inline => (bool)s.fns.write_stream_fn; -fn bool Stream.supports_pushback_byte(Stream* s) @inline => s.fns.pushback_byte_fn || s.fns.seek_fn; -fn bool Stream.supports_write(Stream* s) @inline => s.fns.write_fn || s.fns.write_byte_fn; +fn bool Stream.supports_seek(Stream s) @inline => (bool)s.fns.seek_fn; +fn bool Stream.supports_available(Stream s) @inline => s.fns.available_fn || s.fns.seek_fn; +fn bool Stream.supports_len(Stream s) @inline => s.fns.len_fn || s.fns.seek_fn; +fn bool Stream.supports_read(Stream s) @inline => s.fns.read_fn || s.fns.read_byte_fn; +fn bool Stream.supports_read_from(Stream s) @inline => (bool)s.fns.read_stream_fn; +fn bool Stream.supports_write_to(Stream s) @inline => (bool)s.fns.write_stream_fn; +fn bool Stream.supports_pushback_byte(Stream s) @inline => s.fns.pushback_byte_fn || s.fns.seek_fn; +fn bool Stream.supports_write(Stream s) @inline => s.fns.write_fn || s.fns.write_byte_fn; -fn void! Stream.destroy(Stream* s) @inline @maydiscard +fn void! Stream.destroy(Stream s) @inline @maydiscard { if (s.fns.destroy_fn) return s.fns.destroy_fn(s); return s.close(); } -fn void! Stream.close(Stream* s) @inline @maydiscard +fn void! Stream.close(Stream s) @inline @maydiscard { if (CloseStreamFn func = s.fns.close_fn) return func(s); } -fn usz! Stream.seek(Stream* s, isz offset, Seek seek) @inline +fn usz! Stream.seek(Stream s, isz offset, Seek seek) @inline { if (SeekStreamFn func = s.fns.seek_fn) return func(s, offset, seek); return IoError.NOT_SEEKABLE?; } -fn usz! Stream.available(Stream* s) @inline +fn usz! Stream.available(Stream s) @inline { if (AvailableStreamFn func = s.fns.available_fn) return func(s); if (SeekStreamFn func = s.fns.seek_fn) @@ -76,7 +76,7 @@ fn usz! Stream.available(Stream* s) @inline return IoError.NOT_SEEKABLE?; } -fn usz! Stream.read(Stream* s, char[] buffer) +fn usz! Stream.read(Stream s, char[] buffer) { if (ReadStreamFn func = s.fns.read_fn) return func(s, buffer); if (ReadByteStreamFn func = s.fns.read_byte_fn) @@ -97,13 +97,40 @@ fn usz! Stream.read(Stream* s, char[] buffer) return IoError.UNSUPPORTED_OPERATION?; } -fn char! Stream.read_byte(Stream* s) @inline +fn char! Stream.read_byte(Stream s) @inline { if (ReadByteStreamFn func = s.fns.read_byte_fn) return func(s); return IoError.UNSUPPORTED_OPERATION?; } -fn usz! Stream.write(Stream* s, char[] bytes) @inline +fn String! Stream.readline(Stream s, Allocator* using = mem::heap()) +{ + ReadByteStreamFn func; + if (func = s.fns.read_byte_fn, !func) return IoError.UNSUPPORTED_OPERATION?; + bool read = false; + char val = func(s)!; + if (val == '\n') return ""; + @stack_mem(256 + 64; Allocator* mem) + { + DString str = dstring::new_with_capacity(256, .using = mem); + if (val != '\r') str.append(val); + while (1) + { + char! c = func(s); + if (catch err = c) + { + if (err == IoError.EOF) break; + return err?; + } + if (c == '\r') continue; + if (c == '\n') break; + str.append_char(c); + } + return str.copy_str(using); + }; +} + +fn usz! Stream.write(Stream s, char[] bytes) @inline { if (WriteStreamFn func = s.fns.write_fn) return func(s, bytes); if (WriteByteStreamFn func = s.fns.write_byte_fn) @@ -114,31 +141,31 @@ fn usz! Stream.write(Stream* s, char[] bytes) @inline return IoError.UNSUPPORTED_OPERATION?; } -fn void! Stream.write_byte(Stream* s, char b) @inline +fn void! Stream.write_byte(Stream s, char b) @inline { if (WriteByteStreamFn func = s.fns.write_byte_fn) return func(s, b); return IoError.UNSUPPORTED_OPERATION?; } -fn usz! Stream.write_to(Stream* s, Stream* to) @inline +fn usz! Stream.write_to(Stream s, Stream to) @inline { if (WriteToStreamFn func = s.fns.write_stream_fn) return func(s, to); return IoError.UNSUPPORTED_OPERATION?; } -fn usz! Stream.read_from(Stream* s, Stream* from) @inline +fn usz! Stream.read_from(Stream s, Stream from) @inline { if (ReadFromStreamFn func = s.fns.read_stream_fn) return func(s, from); return IoError.UNSUPPORTED_OPERATION?; } -fn void! Stream.flush(Stream* s) @inline @maydiscard +fn void! Stream.flush(Stream s) @inline @maydiscard { if (FlushStreamFn func = s.fns.flush_fn) return func(s); return IoError.UNSUPPORTED_OPERATION?; } -fn usz! Stream.len(Stream* s) @inline +fn usz! Stream.len(Stream s) @inline { if (LenStreamFn func = s.fns.len_fn) return func(s); if (SeekStreamFn func = s.fns.seek_fn) @@ -151,7 +178,7 @@ fn usz! Stream.len(Stream* s) @inline return IoError.NOT_SEEKABLE?; } -fn void! Stream.pushback_byte(Stream* s) @inline +fn void! Stream.pushback_byte(Stream s) @inline { if (PushbackByteStreamFn func = s.fns.pushback_byte_fn) return func(s); if (SeekStreamFn func = s.fns.seek_fn) @@ -162,9 +189,9 @@ fn void! Stream.pushback_byte(Stream* s) @inline return IoError.UNSUPPORTED_OPERATION?; } -fn void! Stream.write_string(Stream* s, String str) @inline => (void)(s.write((char[])str)!); +fn void! Stream.write_string(Stream s, String str) @inline => (void)(s.write((char[])str)!); -fn usz! Stream.copy_to(Stream* s, Stream* dst, char[] buffer = {}) +fn usz! Stream.copy_to(Stream s, Stream dst, char[] buffer = {}) { if (buffer.len) return copy_through_buffer(s, dst, buffer); if (WriteToStreamFn func = s.fns.write_stream_fn) return func(s, dst); @@ -186,7 +213,7 @@ fn usz! Stream.copy_to(Stream* s, Stream* dst, char[] buffer = {}) $endswitch } -macro usz! copy_through_buffer(Stream* s, Stream* dst, char[] buffer) @local +macro usz! copy_through_buffer(Stream s, Stream dst, char[] buffer) @local { usz total_copied; while (true) diff --git a/lib/std/io/stream/bytereader.c3 b/lib/std/io/stream/bytereader.c3 index cfbfb3e78..dc819ab6c 100644 --- a/lib/std/io/stream/bytereader.c3 +++ b/lib/std/io/stream/bytereader.c3 @@ -53,7 +53,7 @@ fn usz! ByteReader.seek(ByteReader* reader, isz offset, Seek seek) return new_index; } -fn usz! ByteReader.write_stream(ByteReader* reader, Stream* writer) +fn usz! ByteReader.write_stream(ByteReader* reader, Stream writer) { if (reader.index >= reader.bytes.len) return 0; usz written = writer.write(reader.bytes[reader.index..])!; diff --git a/lib/std/io/stream/bytewriter.c3 b/lib/std/io/stream/bytewriter.c3 index c28afdcf4..d3f069c50 100644 --- a/lib/std/io/stream/bytewriter.c3 +++ b/lib/std/io/stream/bytewriter.c3 @@ -75,9 +75,9 @@ fn void! ByteWriter.write_byte(ByteWriter* writer, char c) /** * @param [&inout] writer - * @param [&inout] reader + * @param reader **/ -fn usz! ByteWriter.read_from(ByteWriter* writer, Stream* reader) +fn usz! ByteWriter.read_from(ByteWriter* writer, Stream reader) { usz start_index = writer.index; if (reader.supports_available()) diff --git a/lib/std/os/posix/process.c3 b/lib/std/os/posix/process.c3 index f1c145dcc..47a737ca7 100644 --- a/lib/std/os/posix/process.c3 +++ b/lib/std/os/posix/process.c3 @@ -32,6 +32,7 @@ def spawn = posix_spawn; extern fn CInt kill(Pid_t pid, CInt sig); extern fn Pid_t waitpid(Pid_t pid, CInt* stat_loc, int options); +extern fn CInt raise(CInt sig); macro CInt wEXITSTATUS(CInt status) => (status & 0xff00) >> 8; macro CInt wTERMSIG(CInt status) => status & 0x7f; diff --git a/releasenotes.md b/releasenotes.md index 853fddde7..aa794fc43 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -88,6 +88,7 @@ - `assert` may now take varargs for formatting. ### Stdlib changes +- `csv` package. - Updated posix/win32 stdlib namespacing - `process` stdlib - Stdlib updates to string. diff --git a/src/version.h b/src/version.h index d7820ef75..8f493c9e4 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define COMPILER_VERSION "0.4.543" \ No newline at end of file +#define COMPILER_VERSION "0.4.544" \ No newline at end of file diff --git a/test/unit/stdlib/io/bytestream.c3 b/test/unit/stdlib/io/bytestream.c3 index dfee49eca..ee26fd4ae 100644 --- a/test/unit/stdlib/io/bytestream.c3 +++ b/test/unit/stdlib/io/bytestream.c3 @@ -17,9 +17,9 @@ fn void! bytestream() ws.write("helloworld")!; assert(w.as_str() == "helloworld"); s.seek(0, SET)!; - ws.read_from(&s)!; + ws.read_from(s)!; s.seek(1, SET)!; - s.write_to(&ws)!; + s.write_to(ws)!; assert(w.as_str() == "helloworldabcbc"); } @@ -44,7 +44,7 @@ fn void! bytewriter_read_from() ByteWriter bw; bw.tinit(); - bw.read_from(&s)!; + bw.read_from(s)!; assert(bw.as_str() == data); } diff --git a/test/unit/stdlib/io/dstringstream.c3 b/test/unit/stdlib/io/dstringstream.c3 index b26bbd8c6..e42df587f 100644 --- a/test/unit/stdlib/io/dstringstream.c3 +++ b/test/unit/stdlib/io/dstringstream.c3 @@ -11,7 +11,7 @@ fn void! test_writing() ByteReader r; String test_str = "2134"; r.init(test_str); - s.read_from(&&r.as_stream())!; + s.read_from(r.as_stream())!; String o = foo.str(); assert(o == "hello-what?2134"); }