Every // ---- section banner dies (132 -> 0): names carry the WHAT. Narration deleted (filename restatements, run-with lines, what-the- next-line-does); every ref/hare cite, task cite, divergence, ABI/ layout contract, and ownership qualifier kept (borrowed-view lines restored where the sweep over-cut). Comment-only proven: all 442 walk-workdir .s and 32 import-probe .s byte-identical before/after; libbyteid 56-roster all-ID.
240 lines
8.3 KiB
Plaintext
240 lines
8.3 KiB
Plaintext
// log — process logger over an [[io.stream]] (= `*io.vtable`) sink.
|
|
// Subset of Hare's lib/log (ref/hare/log/{logger,funcs,global,silent}.ha).
|
|
// Project #94 fold-eFinal.
|
|
//
|
|
// VALUE-RETURN (Hare ref/hare/log/logger.ha:20): [[new]] builds a
|
|
// stdlogger in a local and `return r;` (proven field-by-field sret).
|
|
// stdlogger is 24B (two fn-ptrs + sink), returned via the SysV memory
|
|
// class; the caller owns the returned logger (stack ownership, no-GC)
|
|
// and passes `&sl.logger` to the dispatch fns.
|
|
//
|
|
// Divergences from Hare:
|
|
//
|
|
// • silent / default / _default initialised via a lazy [[ensureinit]]
|
|
// (lib/temp's `rnginit==0` pattern) rather than module-scope struct-
|
|
// literal lets: cstage `emit_lets` only constexprs primitive /
|
|
// str-literal / array-literal lets. The public *logger globals are
|
|
// zero initially and become non-nil after the first lib/log call;
|
|
// callers that read globals directly must call some lib/log fn first
|
|
// so init runs. Graduates if static-init lands.
|
|
//
|
|
// • Param name divergence: the format-string parameter is `format`
|
|
// (Hare's is `fmt`). Permanent — #19 refuses any let/param shadowing
|
|
// an imported module name; `use fmt; fn x(fmt: T)` is structurally
|
|
// ambiguous under ww's `.`-for-both rule.
|
|
//
|
|
// Sink today is an [[io.stream]] only. io fold-2 (#5) landed
|
|
// [[io.handle]] = (io.file | io.stream) and graduated lib/fmt onto it,
|
|
// but lib/log's sink graduation is a separate follow-up; until then the
|
|
// default logger writes to stderr via a private fd-backed stream shim
|
|
// ([[stderrsink_ctx]]) whose write callback forwards to [[os.write]] on
|
|
// os.STDERR_FILENO.
|
|
//
|
|
// Cast workaround per #206-payoff (ken: KEEP the explicit casts; they
|
|
// are cgen-neutral and sidestep the #214 over-acceptance surface). The
|
|
// `(&fn_name): *io.<role>` cast at the stderrsink_ctx_g vt wire-up; the
|
|
// #206 cast-drop is gated on #214. stderrwrite constructs nomem and
|
|
// widens to io.error explicitly rather than `os.trywrite(...)?` for the
|
|
// no-handle interim.
|
|
//
|
|
// let sl: log.stdlogger = log.new(&s.vt);
|
|
// log.lprintln(&sl.logger, "hello", 42i64);
|
|
// log.setlogger(&sl.logger);
|
|
// log.println("through global");
|
|
// log.setlogger(log.silent);
|
|
// log.println("dropped");
|
|
|
|
package log;
|
|
|
|
import fmt;
|
|
import io;
|
|
import os;
|
|
|
|
// logger — interface for log dispatch. Two vtable slots: bare-args
|
|
// `println` (formattable-variadic) and `printfln` (format-string +
|
|
// field-variadic). Mirrors ref/hare/log/logger.ha:9.
|
|
export type logger = struct {
|
|
println: fn(l: *logger, args: fmt.formattable...) void,
|
|
printfln: fn(l: *logger, format: str, fields: fmt.field...) void,
|
|
};
|
|
|
|
// stdlogger — concrete logger forwarding to an io.stream sink.
|
|
// First-field embed: `&sl.logger` yields a `*logger` (intrusive
|
|
// outer→inner cast); the vtable callback recovers the outer via
|
|
// `*stdlogger` cast. Mirrors ref/hare/log/logger.ha:18.
|
|
export type stdlogger = struct {
|
|
logger: logger,
|
|
sink: io.stream,
|
|
};
|
|
|
|
// stderrsink_ctx — module-static state for the default logger's stderr
|
|
// sink. vt FIRST field for the intrusive io.stream→*stderrsink_ctx cast
|
|
// in stderrwrite (the same vtable-first-field shape lib/fmt used before
|
|
// #5 graduated it onto io.handle); only scalars/ptrs beyond vt.
|
|
type stderrsink_ctx = struct {
|
|
vt: io.vtable,
|
|
fd: i32,
|
|
};
|
|
|
|
// Zero-init at link time per #129 A.2/A.3 SSoT; ensureinit wires
|
|
// vt.reader / vt.writer + fd lazily on first dispatch.
|
|
let stderrsink_ctx_g: stderrsink_ctx;
|
|
|
|
// _silent / _default — backing storage for the silent / default
|
|
// *logger globals. Zero-init at link time; ensureinit fills the
|
|
// println / printfln slots.
|
|
let _silent: logger;
|
|
let _default: stdlogger;
|
|
|
|
// silent / default / global — Hare-style *logger globals. Zero-init at
|
|
// link time; lazily wired by [[ensureinit]] on the first call to any
|
|
// exported fn. See the file header for the divergence rationale.
|
|
export let silent: *logger;
|
|
export let default: *logger;
|
|
export let global: *logger;
|
|
|
|
let initdone: i32 = 0;
|
|
|
|
fn ensureinit() void = {
|
|
if (initdone != 0) { return; };
|
|
stderrsink_ctx_g.fd = os.STDERR_FILENO;
|
|
stderrsink_ctx_g.vt.reader = (&stderrread): *io.reader;
|
|
stderrsink_ctx_g.vt.writer = (&stderrwrite): *io.writer;
|
|
|
|
_silent.println = silentprintln;
|
|
_silent.printfln = silentprintfln;
|
|
|
|
_default.logger.println = stdprintln;
|
|
_default.logger.printfln = stdprintfln;
|
|
_default.sink = &stderrsink_ctx_g.vt;
|
|
|
|
silent = &_silent;
|
|
default = &_default.logger;
|
|
global = default;
|
|
initdone = 1;
|
|
};
|
|
|
|
// stderrread / stderrwrite — vtable callbacks for the private stderr
|
|
// sink. Read is eof-only — lib/log writes; never reads. -errno collapses
|
|
// to a nomem-widened io.error.
|
|
fn stderrread(s: io.stream, buf: []u8) (size | io.eof | io.error) = {
|
|
let e: io.eof;
|
|
return e;
|
|
};
|
|
|
|
fn stderrwrite(s: io.stream, buf: []u8) (size | io.error) = {
|
|
let c: *stderrsink_ctx = s: *stderrsink_ctx;
|
|
let r: i64 = os.write(c.fd, buf.ptr, buf.len: u64);
|
|
if (r < 0) {
|
|
let nm: nomem;
|
|
let e: io.error = nm;
|
|
return e;
|
|
};
|
|
return r: size;
|
|
};
|
|
|
|
// stdprintln / stdprintfln — vtable callbacks for stdlogger. Forwards
|
|
// to fmt.fprint / fprintf on the sink + appends the trailing newline
|
|
// via io.write. Hare's fmt.fprintln emits the newline inside one
|
|
// wrapper (ref/hare/fmt/wrappers.ha:60-68); we inline that split at the
|
|
// log site because fmt.fprint is the bare (no-newline) primitive. The
|
|
// (size | io.error) results are dropped — lib/log's surface is `void`.
|
|
fn stdprintln(l: *logger, args: fmt.formattable...) void = {
|
|
let sl: *stdlogger = l: *stdlogger;
|
|
fmt.fprint(sl.sink, args...);
|
|
let nl: [1]u8;
|
|
nl[0] = 10u8;
|
|
let v: []u8 = nl[0:1];
|
|
io.write(sl.sink, v);
|
|
};
|
|
|
|
fn stdprintfln(l: *logger, format: str, fields: fmt.field...) void = {
|
|
let sl: *stdlogger = l: *stdlogger;
|
|
fmt.fprintf(sl.sink, format, fields...);
|
|
let nl: [1]u8;
|
|
nl[0] = 10u8;
|
|
let v: []u8 = nl[0:1];
|
|
io.write(sl.sink, v);
|
|
};
|
|
|
|
// silentprintln / silentprintfln — vtable callbacks for the silent
|
|
// logger. Discard every record without dispatching through fmt; keeps
|
|
// silent truly silent if fmt ever gets stateful. Mirror
|
|
// ref/hare/log/silent.ha:15.
|
|
fn silentprintln(l: *logger, args: fmt.formattable...) void = { };
|
|
fn silentprintfln(l: *logger, format: str, fields: fmt.field...) void = { };
|
|
|
|
// new — build a stdlogger over `sink`, returned BY VALUE. Mirrors
|
|
// ref/hare/log/logger.ha:20.
|
|
export fn new(sink: io.stream) stdlogger = {
|
|
ensureinit();
|
|
let r: stdlogger;
|
|
r.logger.println = stdprintln;
|
|
r.logger.printfln = stdprintfln;
|
|
r.sink = sink;
|
|
return r;
|
|
};
|
|
|
|
// lprintln — dispatch `args` through `log`. Mirrors ref/hare/log/funcs.ha:8.
|
|
export fn lprintln(log: *logger, args: fmt.formattable...) void = {
|
|
ensureinit();
|
|
log.println(log, args...);
|
|
};
|
|
|
|
// println — dispatch through the [[global]] logger. Mirrors
|
|
// ref/hare/log/funcs.ha:18.
|
|
export fn println(args: fmt.formattable...) void = {
|
|
ensureinit();
|
|
lprintln(global, args...);
|
|
};
|
|
|
|
// lfatal — lprintln to `log` then exit(255). `never` return marks the
|
|
// bottom type so flow-control checks treat callers as terminated.
|
|
// Mirrors ref/hare/log/funcs.ha:28.
|
|
export fn lfatal(log: *logger, args: fmt.formattable...) never = {
|
|
lprintln(log, args...);
|
|
os.exit(255);
|
|
};
|
|
|
|
// fatal — lprintln to [[global]] then exit(255). Mirrors
|
|
// ref/hare/log/funcs.ha:45.
|
|
export fn fatal(args: fmt.formattable...) never = {
|
|
println(args...);
|
|
os.exit(255);
|
|
};
|
|
|
|
// setlogger — install `log` as the [[global]] logger. Mirrors
|
|
// ref/hare/log/global.ha:20.
|
|
export fn setlogger(log: *logger) void = {
|
|
ensureinit();
|
|
global = log;
|
|
};
|
|
|
|
// lprintfln — dispatch a format-string record through `log`. Mirrors
|
|
// ref/hare/log/funcs.ha:13.
|
|
export fn lprintfln(log: *logger, format: str, fields: fmt.field...) void = {
|
|
ensureinit();
|
|
log.printfln(log, format, fields...);
|
|
};
|
|
|
|
// printfln — dispatch through the [[global]] logger. Mirrors
|
|
// ref/hare/log/funcs.ha:23.
|
|
export fn printfln(format: str, fields: fmt.field...) void = {
|
|
ensureinit();
|
|
lprintfln(global, format, fields...);
|
|
};
|
|
|
|
// lfatalf — lprintfln to `log` then exit(255). Mirrors
|
|
// ref/hare/log/funcs.ha:35.
|
|
export fn lfatalf(log: *logger, format: str, fields: fmt.field...) never = {
|
|
lprintfln(log, format, fields...);
|
|
os.exit(255);
|
|
};
|
|
|
|
// fatalf — lprintfln to [[global]] then exit(255). Mirrors
|
|
// ref/hare/log/funcs.ha:52.
|
|
export fn fatalf(format: str, fields: fmt.field...) never = {
|
|
printfln(format, fields...);
|
|
os.exit(255);
|
|
};
|