lib/fmt: #6 formattable += int|uint — bare-int print, the types::numeric widen (both stages)
ww had narrowed Hare's formattable (types::numeric) to a lone i64 arm, so a bare int/uint was not printable: cstage CORRECTLY rejected it, wwstage leniently accepted via the #128 size-keying. Append int|uint LAST (existing tags 0-4 frozen, zero byte-id churn) so BOTH stages accept by membership — closing the #128 int-path leniency by construction. int renders via the signed i64 path; uint via strconv.u64tos unsigned (+ a rawlenu64 width twin) — i64dec would render a high-bit uint negative. Staged cut: narrower widths + full types::numeric graduate per-caller (mirrors the f32-arm precedent). fmt is NOT embedded in the selfhost compiler tools (grep-verified: 0 fmt fn-defs in w6c/wwdump combined.ww) — no combined.ww regen, compiler binaries unchanged. Pin: table-driven test/wcc/815_fmt_int_run (bare int, high-bit uint 2^63+1 positive, i64/str regression guards, byte-id).
This commit is contained in:
127
lib/fmt/fmt.ww
127
lib/fmt/fmt.ww
@@ -73,7 +73,18 @@ fn i64dec(v: i64) str = {
|
||||
// caller. Callers with an `f32` cast at the call site (`myf: f64`),
|
||||
// mirroring how `i64` covers every int width today. Ship the `f32`
|
||||
// arm when the first in-tree caller needs it.
|
||||
export type formattable = (i64 | str | bool | rune | f64);
|
||||
//
|
||||
// `int`/`uint` appended LAST: variant tags follow declaration order
|
||||
// (cstage cg_tag_for_variant / wwstage flatvariantidxt), so the
|
||||
// pre-existing tags i64=0,str=1,bool=2,rune=3,f64=4 stay frozen and
|
||||
// int=5,uint=6 — zero byte-id churn for existing callers (#6). This is a
|
||||
// STAGED step toward Hare's full `types::numeric` (ref/hare/types/
|
||||
// classes.ha:5-17 → ref/hare/fmt/iter.ha:14): int/uint are the machine-
|
||||
// word types apps actually print; narrower widths (i8/i16/i32, u8/u16/
|
||||
// u32, size) graduate when a caller lands. Making them real members (not
|
||||
// a size/name-keyed coercion) closes the #128 int-path leniency by
|
||||
// construction — both stages now accept bare int by MEMBERSHIP.
|
||||
export type formattable = (i64 | str | bool | rune | f64 | int | uint);
|
||||
|
||||
// ---- internal stream formatters --------------------------------------
|
||||
|
||||
@@ -111,6 +122,15 @@ fn writeone(s: io.handle, a: formattable) (size | io.error) = {
|
||||
let v2: str = strconv.f64tos(v);
|
||||
return putbytes(s, v2.ptr, v2.len);
|
||||
};
|
||||
case let n: int => {
|
||||
let v: str = i64dec(n: i64);
|
||||
return putbytes(s, v.ptr, v.len);
|
||||
};
|
||||
case let n: uint => {
|
||||
// unsigned path: i64dec would render a high-bit value negative.
|
||||
let v: str = strconv.u64tos(n: u64, strconv.base.DEC);
|
||||
return putbytes(s, v.ptr, v.len);
|
||||
};
|
||||
};
|
||||
return 0: size;
|
||||
};
|
||||
@@ -281,6 +301,19 @@ fn rawleni64(v: i64, m: *mods) i32 = {
|
||||
return signlen + inner;
|
||||
};
|
||||
|
||||
// rawlenu64 — bytes the raw render of unsigned `v` under `m` would
|
||||
// emit. uint twin of [[rawleni64]]: no value-derived sign (uint is
|
||||
// never negative), so neg_flag is fixed false — only an explicit
|
||||
// PLUS/SPACE mod adds a sign byte. Mirror print.ha.
|
||||
fn rawlenu64(v: u64, m: *mods) i32 = {
|
||||
let signlen: i32 = 0;
|
||||
if (signof(false, m) != 0u8) { signlen = 1; };
|
||||
let dlen: i32 = digitsu64(v, basenum(m.base));
|
||||
let inner: i32 = dlen;
|
||||
if (m.prec > signlen + dlen) { inner = m.prec - signlen; };
|
||||
return signlen + inner;
|
||||
};
|
||||
|
||||
// rawlenstr — bytes the raw render of `s` would emit (after `prec`
|
||||
// truncation, per Hare print.ha:86).
|
||||
fn rawlenstr(s: str, m: *mods) i32 = {
|
||||
@@ -314,6 +347,8 @@ fn rawlen(arg: formattable, m: *mods) i32 = {
|
||||
case let b: bool => { if (b) { return 4; }; return 5; };
|
||||
case let r: rune => return 1;
|
||||
case let v: f64 => return rawlenf64(v, m);
|
||||
case let v: int => return rawleni64(v: i64, m);
|
||||
case let v: uint => return rawlenu64(v: u64, m);
|
||||
};
|
||||
return 0; // unreachable — match is exhaustive
|
||||
};
|
||||
@@ -408,6 +443,88 @@ fn formatraw(s: io.handle, arg: formattable, m: *mods) (size | io.error) = {
|
||||
};
|
||||
return total;
|
||||
};
|
||||
case let vi: int => {
|
||||
// int is the signed machine word; widen to i64 and emit the
|
||||
// signed render — identical to the i64 arm above.
|
||||
let v: i64 = vi: i64;
|
||||
let neg_flag: bool = v < 0;
|
||||
let u: u64 = v: u64;
|
||||
if (neg_flag) { u = (-v): u64; };
|
||||
let sb: u8 = signof(neg_flag, m);
|
||||
let total: size = 0;
|
||||
if (sb != 0u8) {
|
||||
let buf: [1]u8;
|
||||
buf[0] = sb;
|
||||
let r: (size | io.error) = putbytes(s, &buf[0], 1);
|
||||
match (r) {
|
||||
case let n: size => { total += n; };
|
||||
case let e: io.error => return e;
|
||||
};
|
||||
};
|
||||
let dlen: i32 = digitsu64(u, basenum(m.base));
|
||||
let signlen: i32 = 0;
|
||||
if (sb != 0u8) { signlen = 1; };
|
||||
let pad0: i32 = 0;
|
||||
if (m.prec > signlen + dlen) { pad0 = m.prec - signlen - dlen; };
|
||||
let pi: i32 = 0;
|
||||
for (pi < pad0) {
|
||||
let buf: [1]u8;
|
||||
buf[0] = '0';
|
||||
let r: (size | io.error) = putbytes(s, &buf[0], 1);
|
||||
match (r) {
|
||||
case let n: size => { total += n; };
|
||||
case let e: io.error => return e;
|
||||
};
|
||||
pi += 1;
|
||||
};
|
||||
let view: str = strconv.u64tos(u, m.base);
|
||||
let r: (size | io.error) = putbytes(s, view.ptr, view.len);
|
||||
match (r) {
|
||||
case let n: size => { total += n; };
|
||||
case let e: io.error => return e;
|
||||
};
|
||||
return total;
|
||||
};
|
||||
case let vu: uint => {
|
||||
// unsigned path: no value-derived sign (uint never negative), so
|
||||
// neg_flag is fixed false — a high-bit value renders as its true
|
||||
// unsigned decimal, not negative. strconv.u64tos, not i64dec.
|
||||
let u: u64 = vu: u64;
|
||||
let sb: u8 = signof(false, m);
|
||||
let total: size = 0;
|
||||
if (sb != 0u8) {
|
||||
let buf: [1]u8;
|
||||
buf[0] = sb;
|
||||
let r: (size | io.error) = putbytes(s, &buf[0], 1);
|
||||
match (r) {
|
||||
case let n: size => { total += n; };
|
||||
case let e: io.error => return e;
|
||||
};
|
||||
};
|
||||
let dlen: i32 = digitsu64(u, basenum(m.base));
|
||||
let signlen: i32 = 0;
|
||||
if (sb != 0u8) { signlen = 1; };
|
||||
let pad0: i32 = 0;
|
||||
if (m.prec > signlen + dlen) { pad0 = m.prec - signlen - dlen; };
|
||||
let pi: i32 = 0;
|
||||
for (pi < pad0) {
|
||||
let buf: [1]u8;
|
||||
buf[0] = '0';
|
||||
let r: (size | io.error) = putbytes(s, &buf[0], 1);
|
||||
match (r) {
|
||||
case let n: size => { total += n; };
|
||||
case let e: io.error => return e;
|
||||
};
|
||||
pi += 1;
|
||||
};
|
||||
let view: str = strconv.u64tos(u, m.base);
|
||||
let r: (size | io.error) = putbytes(s, view.ptr, view.len);
|
||||
match (r) {
|
||||
case let n: size => { total += n; };
|
||||
case let e: io.error => return e;
|
||||
};
|
||||
return total;
|
||||
};
|
||||
};
|
||||
let z: size = 0; return z; // unreachable — match is exhaustive
|
||||
};
|
||||
@@ -485,6 +602,14 @@ fn formatfield(s: io.handle, f: field, m: *mods) (size | io.error) = {
|
||||
let a: formattable = v;
|
||||
return formatone(s, a, m);
|
||||
};
|
||||
case let v: int => {
|
||||
let a: formattable = v;
|
||||
return formatone(s, a, m);
|
||||
};
|
||||
case let v: uint => {
|
||||
let a: formattable = v;
|
||||
return formatone(s, a, m);
|
||||
};
|
||||
case let p: *mods => { fmtabort(); let z: size = 0; return z; };
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user