Files
ww/lib/io/stream.ww
Hojun-Cho 06c00e0e1b lib: io.handle union + seeker + handle-typed read/write/close/seek dispatch (#5)
Ports ref/hare/io handle.ha: a handle is (file | *stream); ww stream is already *vtable (#94 collapse) so the payload is (file | stream). file=i32 (Hare int is 32-bit, ww int is 8B word -- width-faithful, USER-ruled). read/write/close/seek/tell match on the handle: file-arm to os.read/write/close/lseek (os plays Hare sys role), stream-arm to the unchanged st_* vtable bodies; seeker is the 4th vtable slot (copier deferred). On a file-arm syscall error the stub returns errors.unsupported with a #199b marker -- faithful errno to io.error needs io.error to spread ...errors.error (#204/#199b-blocked); only the error value is lossy until then, the type stays faithful. Regenerates w6c/wwdump combined.ww.
2026-05-31 17:30:55 +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). Three 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);
};