hare

[hare] The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

commit a986adec473af9dd117676d15acf351d386017ee
parent 678e4f644cfd39043150f015ac7339d0d7555800
Author: Armin Preiml <apreiml@strohwolke.at>
Date:   Thu, 28 Sep 2023 07:58:09 +0200

bufio::scanner: support unread

Diffstat:
Mbufio/scanner.ha | 9+++++++++
Mbufio/scanner_test+test.ha | 30++++++++++++++++++++++++++++++
Mbufio/stream.ha | 13++++++++++---
3 files changed, 49 insertions(+), 3 deletions(-)

diff --git a/bufio/scanner.ha b/bufio/scanner.ha @@ -271,6 +271,15 @@ export fn scan_buffer(scan: *scanner) []u8 = { return scan.buffer[..scan.pending]; }; +fn scan_unread(scan: *scanner, buf: []u8) void = { + if (len(buf) == 0) { + return; + }; + assert(len(buf) <= scan.readout); + scan.buffer[scan.readout - len(buf)..scan.readout] = buf; + scan.readout -= len(buf); +}; + // Reads a single byte from an [[io::handle]]. export fn read_byte(file: io::handle) (u8 | io::EOF | io::error) = { let buf: [1]u8 = [0...]; diff --git a/bufio/scanner_test+test.ha b/bufio/scanner_test+test.ha @@ -119,7 +119,37 @@ use strings; let in = memio::fixed(expected); let scanner = newscanner(&in, 2); + defer finish(&scanner); let result = io::drain(&scanner)!; defer free(result); assert(bytes::equal(expected, result)); }; + +@test fn scan_unread() void = { + const expected: str = " I will not repeat \nDone!\n"; + let in = memio::fixed(strings::toutf8(expected)); + + let scanner = newscanner(&in, 32); + defer finish(&scanner); + let l = scan_line(&scanner)! as const str; + assert(l == " I will not repeat "); + + unread(&scanner, strings::toutf8("\n")); + unread(&scanner, strings::toutf8(l)); + let l = scan_line(&scanner)! as const str; + assert(l == " I will not repeat "); + + unread(&scanner, strings::toutf8("\n")); + unread(&scanner, strings::toutf8(strings::trim(l))); + let l = scan_line(&scanner)! as const str; + assert(l == "I will not repeat"); + + unread(&scanner, strings::toutf8("See?\n")); + let l = scan_line(&scanner)! as const str; + assert(l == "See?"); + + let l = scan_line(&scanner)! as const str; + assert(l == "Done!"); + + assert(scan_line(&scanner) is io::EOF); +}; diff --git a/bufio/stream.ha b/bufio/stream.ha @@ -128,15 +128,22 @@ export fn setflush(s: io::handle, b: []u8) void = { // buffered stream. Attempting to unread more data than can fit into the read // buffer will abort the program. export fn unread(s: io::handle, buf: []u8) void = { - let s = match (s) { + match (s) { case let st: *io::stream => - if (st.reader != &read) { + switch (st.reader) { + case &read => + stream_unread(s: *stream, buf); + case &scan_read => + scan_unread(s: *scanner, buf); + case => abort("Attempted unread on unbuffered stream"); }; - yield st: *stream; case => abort("Attempted unread on unbuffered stream"); }; +}; + +fn stream_unread(s: *stream, buf: []u8) void = { assert(s.rpos >= len(buf), "Attempted to unread more data than buffer has available"); s.rbuffer[s.rpos - len(buf)..s.rpos] = buf;