Files
ww/lib/io/stream.ww
Hojun-Cho e5379848de lib: split io empty stream into empty.ww per ref/hare/io/empty.ha
Pure file-boundary move of lib/io/stream.ww's single 'empty stream'
banner section (_empty_read, _empty_write, _empty_vt, empty) into
lib/io/empty.ww, mirroring ref/hare/io/empty.ha. No code, name,
signature, or behavior change. Comment-only adjustments: the banner
line is deleted (the file name carries it); the moved block's
'Lives in stream.ww' self-reference now says empty.ww; stream.ww's
head drops its empty bullet (content duplicated by empty.ww's own
comments); io.ww's ownership map gains the empty.ww line; types.ww's
'three files' count becomes four. empty.ww needs no imports (package
io types only).

Makefile: lib/io/empty.ww added beside every lib/io/stream.ww
occurrence (WWFIXTURE_SRC line 75; w6c_ww/wwdump_ww bootstrap prereq
lists at lines 184/204). lib/io has no _test.ww, so no LIBRARY_TESTS
or byteid-roster change; byteid coverage rides the bootstrap gates.

Validation: out/bin/w6c lib/io/io.ww (standalone contract) exit 0;
importer tests green: bufio 26, memio 11, log 11, getopt 12 (calls
io.empty), fmt 49.
2026-08-08 17:17:42 +09:00

186 lines
6.0 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// stream — Hare-shaped vtable surface. Project #94 fold-eFinal.
//
// The single io stream surface (the fold-eFinal collapse retired the
// pre-vtable `stream` struct + `closed` tag). Exports:
//
// vtable a struct of optional fn-pointer slots — reader/writer/
// closer per ref/hare/io/stream.ha:36-42. Hare spells the
// nullable as `nullable *T`; ww parser rejects that
// spelling (#192), so each slot is a `(*T | void)` tagged
// union (Plan-9-lean per the Nullable kept ruling; opt-in
// at optionality boundaries only).
// stream `*vtable` alias matching Hare's `stream = *vtable`
// (ref/hare/io/stream.ha:33).
// read /
// write /
// close the three dispatchers per ref/hare/io/stream.ha:44-68.
// Each `match`es the matching vtable slot, returns
// `errors.unsupported` (widened twice) when the slot is
// void, otherwise calls through the fn pointer. Hare keeps
// these private (`st_read` …) behind a `handle`-typed
// public `read`/`write`/`close`; ww has no `handle` yet
// (io fold-2, #5), so the dispatchers ARE the public
// surface and grow the `handle` match when #5 lands.
//
// Deferrals (drew-signed): `seeker` lands with io fold-2 (#5) once `off`
// + `whence` plug into the signature. Hare's `?`-propagating `close`
// body (`c(s)?;`) collapses to a direct `return (*c)(s);` here because
// the dispatcher's surface stays Hare-shaped; only the internal
// try-prop is omitted (#173 history, now closed — kept direct for the
// void-arm fall-through shape).
package io;
// Q2 layering (ratified): the file-arm of the handle dispatchers routes
// to os.{read,write,close,lseek}; ww `os` plays Hare's `sys` role, so
// `lib/io import os` is the correct direction (os is the import floor).
import os;
// ref/hare/io/stream.ha:36-42. Nullable spelling per #192 (ww parser
// rejects `nullable *T`); each slot is `(*T | void)`. The `seeker` slot
// (4th) lands with io fold-2 (#5); `copier` stays deferred.
export type vtable = struct {
reader: (*reader | void),
writer: (*writer | void),
closer: (*closer | void),
seeker: (*seeker | void),
};
// ref/hare/io/stream.ha:33.
export type stream = *vtable;
// ref/hare/io/stream.ha:44-51. Private stream-arm body behind the
// handle-typed [[read]] (#5). The void-arm `return e;` chains two
// direct widens: `errors.unsupported → error` via #199 α, then
// `error → (size | eof | error)` via #205. The call-arm spells the
// fn-ptr call explicitly per #193 (ww requires `(*r)(args)` rather
// than implicit `r(args)`).
fn st_read(s: stream, buf: []u8) (size | eof | error) = {
match (s.reader) {
case void => {
let u: errors.unsupported;
let e: error = u;
return e;
};
case let r: *reader => return (*r)(s, buf);
};
};
// ref/hare/io/stream.ha:53-60.
fn st_write(s: stream, buf: []u8) (size | error) = {
match (s.writer) {
case void => {
let u: errors.unsupported;
let e: error = u;
return e;
};
case let w: *writer => return (*w)(s, buf);
};
};
// ref/hare/io/stream.ha:62-68. Hare propagates the close error via
// `c(s)?;` then falls through to the void arm; ww collapses to a
// direct return for the void-arm fall-through shape. The surface
// (void | error) matches.
fn st_close(s: stream) (void | error) = {
match (s.closer) {
case void => return void;
case let c: *closer => return (*c)(s);
};
};
// ref/hare/io/stream.ha:70-77. Clone of st_read's slot-dispatch shape
// over the new `seeker` vtable slot.
fn st_seek(s: stream, off: off, w: whence) (off | error) = {
match (s.seeker) {
case void => {
let u: errors.unsupported;
let e: error = u;
return e;
};
case let sk: *seeker => return (*sk)(s, off, w);
};
};
// ref/hare/io/handle.ha:16-23. The handle-typed public read: the
// file-arm routes to os.read (Q2 layering), the stream-arm delegates to
// the unchanged st_read vtable dispatch above.
export fn read(h: handle, buf: []u8) (size | eof | error) = {
match (h) {
case let fd: file => {
let r: i64 = os.read(fd, buf.ptr, buf.len: u64);
if (r < 0) {
// #199b: faithful errno→io.error needs the ...errors.error spread; see lib/io/types.ww:40
let u: errors.unsupported;
let e: error = u;
return e;
};
if (r == 0) {
let z: eof;
return z;
};
return r: size;
};
case let s: stream => return st_read(s, buf);
};
};
// ref/hare/io/handle.ha:27-36.
export fn write(h: handle, buf: []u8) (size | error) = {
match (h) {
case let fd: file => {
let r: i64 = os.write(fd, buf.ptr, buf.len: u64);
if (r < 0) {
// #199b: faithful errno→io.error needs the ...errors.error spread; see lib/io/types.ww:40
let u: errors.unsupported;
let e: error = u;
return e;
};
return r: size;
};
case let s: stream => return st_write(s, buf);
};
};
// ref/hare/io/handle.ha:44-51. Hare propagates each arm's error via `?`
// then falls through; ww returns directly (matching st_close's shape).
export fn close(h: handle) (void | error) = {
match (h) {
case let fd: file => {
let r: i32 = os.close(fd);
if (r < 0) {
// #199b: faithful errno→io.error needs the ...errors.error spread; see lib/io/types.ww:40
let u: errors.unsupported;
let e: error = u;
return e;
};
return void;
};
case let s: stream => return st_close(s);
};
};
// ref/hare/io/handle.ha:55-62. The io.whence → os.whence cast bridges
// the two enum copies (same SET/CUR/END values); os.lseek owns the
// syscall.
export fn seek(h: handle, off: off, w: whence) (off | error) = {
match (h) {
case let fd: file => {
let r: i64 = os.lseek(fd, off, w: os.whence);
if (r < 0) {
// #199b: faithful errno→io.error needs the ...errors.error spread; see lib/io/types.ww:40
let u: errors.unsupported;
let e: error = u;
return e;
};
return r: off;
};
case let s: stream => return st_seek(s, off, w);
};
};
// ref/hare/io/handle.ha:65-67.
export fn tell(h: handle) (off | error) = {
return seek(h, 0, whence.CUR);
};