diff --git a/lib/strconv/strconv.ww b/lib/strconv/strconv.ww index ef9c6895..225ff029 100644 --- a/lib/strconv/strconv.ww +++ b/lib/strconv/strconv.ww @@ -274,6 +274,23 @@ export fn stoi8(s: str, b: base) (i8 | invalid | overflow) = { return 0: invalid; }; +// stoi — parse signed base-b number into an int. Mirrors Hare's +// strconv::stoi (ref/hare/strconv/stoi.ha:53), which clamps to +// types::INT_MIN/INT_MAX via stoiminmax. ww's int is a machine word +// (8B → i64-width, so INT_MIN/INT_MAX == I64_MIN/I64_MAX per +// lib/types/types.ww:30-31), so stoi64's result always fits and the +// clamp is a no-op — omitted, not inlined (the bound consts are +// package-private; see the inline note at mulshift32 in ftos.ww). +export fn stoi(s: str, b: base) (int | invalid | overflow) = { + let r = stoi64(s, b); + match (r) { + case let v: i64 => return v: int; + case let e: invalid => return e; + case let e: overflow => return e; + }; + return 0: invalid; +}; + export fn stou32(s: str, b: base) (u32 | invalid | overflow) = { let r = stou64(s, b); match (r) { @@ -313,6 +330,34 @@ export fn stou8(s: str, b: base) (u8 | invalid | overflow) = { return 0: invalid; }; +// stou — parse unsigned base-b number into a uint. Mirrors Hare's +// strconv::stou (ref/hare/strconv/stou.ha:107), which clamps to +// types::UINT_MAX via stoumax. ww's uint is a machine word (8B → +// u64-width, so UINT_MAX == U64_MAX per lib/types/types.ww:33), so +// stou64's result always fits and the clamp is a no-op. +export fn stou(s: str, b: base) (uint | invalid | overflow) = { + let r = stou64(s, b); + match (r) { + case let v: u64 => return v: uint; + case let e: invalid => return e; + case let e: overflow => return e; + }; + return 0: invalid; +}; + +// stoz — parse unsigned base-b number into a size. Mirrors Hare's +// strconv::stoz (ref/hare/strconv/stou.ha:113). ww's size is u64-width +// (SIZE_MAX == U64_MAX per lib/types/types.ww:37), so the clamp is a no-op. +export fn stoz(s: str, b: base) (size | invalid | overflow) = { + let r = stou64(s, b); + match (r) { + case let v: u64 => return v: size; + case let e: invalid => return e; + case let e: overflow => return e; + }; + return 0: invalid; +}; + // f64tos — graduated to the Ryū shortest-round-trippable implementation // in ftos.ww (strconv #106 fold-5). The old lossy fixed-point version // (6 fractional digits, "huge" fallback ≥9e18, no NaN/Inf) was deleted diff --git a/lib/strconv/test/inttest.ww b/lib/strconv/test/inttest.ww index ee6bbee5..a85c1942 100644 --- a/lib/strconv/test/inttest.ww +++ b/lib/strconv/test/inttest.ww @@ -99,6 +99,51 @@ fn cki32(id: i32, s: str, b: base, want: i32) void = { }; }; +fn ck_int(id: i32, s: str, b: base, want: int) void = { + signalled = id; + match (stoi(s, b)) { + case let v: int => if (v != want) { fail(); }; + case let e: invalid => fail(); + case let e: overflow => fail(); + }; +}; + +fn ck_uint(id: i32, s: str, b: base, want: uint) void = { + signalled = id; + match (stou(s, b)) { + case let v: uint => if (v != want) { fail(); }; + case let e: invalid => fail(); + case let e: overflow => fail(); + }; +}; + +fn ck_size(id: i32, s: str, b: base, want: size) void = { + signalled = id; + match (stoz(s, b)) { + case let v: size => if (v != want) { fail(); }; + case let e: invalid => fail(); + case let e: overflow => fail(); + }; +}; + +fn ck_int_ovf(id: i32, s: str, b: base) void = { + signalled = id; + match (stoi(s, b)) { + case let v: int => fail(); + case let e: invalid => fail(); + case let e: overflow => { }; + }; +}; + +fn ck_uint_ovf(id: i32, s: str, b: base) void = { + signalled = id; + match (stou(s, b)) { + case let v: uint => fail(); + case let e: invalid => fail(); + case let e: overflow => { }; + }; +}; + // ref/hare/strconv/stoi.ha:56-79. @test fn test_stoi64() void = { cki_inv(1, "", base.DEC, 0); @@ -164,10 +209,35 @@ fn cki32(id: i32, s: str, b: base, want: i32) void = { cku(45, "110101", base.BIN, 53u64); // 0b110101 }; +// stoi / stou / stoz — int/uint/size machine-word wrappers +// (ref/hare/strconv/stoi.ha:53, stou.ha:107,113). ww's int/uint/size are +// 8B, so the iN/uN clamp is a no-op: the full i64/u64 range parses with +// no spurious overflow. That no-clamp fidelity is what these check. +@test fn test_stoi_stou_stoz() void = { + ck_int(50, "0", base.DEC, 0); + ck_int(51, "-1", base.DEC, -1); + ck_int(52, "9223372036854775807", base.DEC, 9223372036854775807i64: int); // I64_MAX fits int + ck_int_ovf(53, "9223372036854775808", base.DEC); + ck_int_ovf(54, "-9223372036854775809", base.DEC); + + ck_uint(55, "0", base.DEC, 0u64: uint); + ck_uint(56, "18446744073709551615", base.DEC, 18446744073709551615u64: uint); // U64_MAX fits uint + ck_uint_ovf(57, "18446744073709551616", base.DEC); + ck_uint_ovf(58, "-1", base.DEC); + + ck_size(59, "0", base.DEC, 0u64: size); + ck_size(60, "18446744073709551615", base.DEC, 18446744073709551615u64: size); // U64_MAX fits size + + // bases route through the same parseint core. + ck_int(61, "-7f", base.HEX, -127i64: int); // -0x7f + ck_uint(62, "110101", base.BIN, 53u64: uint); // 0b110101 +}; + export fn main() i32 = { test_stoi64(); test_stoi64_bases(); test_stou64(); test_stou64_bases(); + test_stoi_stou_stoz(); return 0; }; diff --git a/selfhost/cmd/w6c/main.combined.ww b/selfhost/cmd/w6c/main.combined.ww index 5c9bbb4a..0db42cbf 100644 --- a/selfhost/cmd/w6c/main.combined.ww +++ b/selfhost/cmd/w6c/main.combined.ww @@ -6234,6 +6234,23 @@ export fn stoi8(s: str, b: base) (i8 | invalid | overflow) = { return 0: invalid; }; +// stoi — parse signed base-b number into an int. Mirrors Hare's +// strconv::stoi (ref/hare/strconv/stoi.ha:53), which clamps to +// types::INT_MIN/INT_MAX via stoiminmax. ww's int is a machine word +// (8B → i64-width, so INT_MIN/INT_MAX == I64_MIN/I64_MAX per +// lib/types/types.ww:30-31), so stoi64's result always fits and the +// clamp is a no-op — omitted, not inlined (the bound consts are +// package-private; see the inline note at mulshift32 in ftos.ww). +export fn stoi(s: str, b: base) (int | invalid | overflow) = { + let r = stoi64(s, b); + match (r) { + case let v: i64 => return v: int; + case let e: invalid => return e; + case let e: overflow => return e; + }; + return 0: invalid; +}; + export fn stou32(s: str, b: base) (u32 | invalid | overflow) = { let r = stou64(s, b); match (r) { @@ -6273,6 +6290,34 @@ export fn stou8(s: str, b: base) (u8 | invalid | overflow) = { return 0: invalid; }; +// stou — parse unsigned base-b number into a uint. Mirrors Hare's +// strconv::stou (ref/hare/strconv/stou.ha:107), which clamps to +// types::UINT_MAX via stoumax. ww's uint is a machine word (8B → +// u64-width, so UINT_MAX == U64_MAX per lib/types/types.ww:33), so +// stou64's result always fits and the clamp is a no-op. +export fn stou(s: str, b: base) (uint | invalid | overflow) = { + let r = stou64(s, b); + match (r) { + case let v: u64 => return v: uint; + case let e: invalid => return e; + case let e: overflow => return e; + }; + return 0: invalid; +}; + +// stoz — parse unsigned base-b number into a size. Mirrors Hare's +// strconv::stoz (ref/hare/strconv/stou.ha:113). ww's size is u64-width +// (SIZE_MAX == U64_MAX per lib/types/types.ww:37), so the clamp is a no-op. +export fn stoz(s: str, b: base) (size | invalid | overflow) = { + let r = stou64(s, b); + match (r) { + case let v: u64 => return v: size; + case let e: invalid => return e; + case let e: overflow => return e; + }; + return 0: invalid; +}; + // f64tos — graduated to the Ryū shortest-round-trippable implementation // in ftos.ww (strconv #106 fold-5). The old lossy fixed-point version // (6 fractional digits, "huge" fallback ≥9e18, no NaN/Inf) was deleted diff --git a/selfhost/cmd/wwdump/main.combined.ww b/selfhost/cmd/wwdump/main.combined.ww index 7320184f..c6e53624 100644 --- a/selfhost/cmd/wwdump/main.combined.ww +++ b/selfhost/cmd/wwdump/main.combined.ww @@ -6234,6 +6234,23 @@ export fn stoi8(s: str, b: base) (i8 | invalid | overflow) = { return 0: invalid; }; +// stoi — parse signed base-b number into an int. Mirrors Hare's +// strconv::stoi (ref/hare/strconv/stoi.ha:53), which clamps to +// types::INT_MIN/INT_MAX via stoiminmax. ww's int is a machine word +// (8B → i64-width, so INT_MIN/INT_MAX == I64_MIN/I64_MAX per +// lib/types/types.ww:30-31), so stoi64's result always fits and the +// clamp is a no-op — omitted, not inlined (the bound consts are +// package-private; see the inline note at mulshift32 in ftos.ww). +export fn stoi(s: str, b: base) (int | invalid | overflow) = { + let r = stoi64(s, b); + match (r) { + case let v: i64 => return v: int; + case let e: invalid => return e; + case let e: overflow => return e; + }; + return 0: invalid; +}; + export fn stou32(s: str, b: base) (u32 | invalid | overflow) = { let r = stou64(s, b); match (r) { @@ -6273,6 +6290,34 @@ export fn stou8(s: str, b: base) (u8 | invalid | overflow) = { return 0: invalid; }; +// stou — parse unsigned base-b number into a uint. Mirrors Hare's +// strconv::stou (ref/hare/strconv/stou.ha:107), which clamps to +// types::UINT_MAX via stoumax. ww's uint is a machine word (8B → +// u64-width, so UINT_MAX == U64_MAX per lib/types/types.ww:33), so +// stou64's result always fits and the clamp is a no-op. +export fn stou(s: str, b: base) (uint | invalid | overflow) = { + let r = stou64(s, b); + match (r) { + case let v: u64 => return v: uint; + case let e: invalid => return e; + case let e: overflow => return e; + }; + return 0: invalid; +}; + +// stoz — parse unsigned base-b number into a size. Mirrors Hare's +// strconv::stoz (ref/hare/strconv/stou.ha:113). ww's size is u64-width +// (SIZE_MAX == U64_MAX per lib/types/types.ww:37), so the clamp is a no-op. +export fn stoz(s: str, b: base) (size | invalid | overflow) = { + let r = stou64(s, b); + match (r) { + case let v: u64 => return v: size; + case let e: invalid => return e; + case let e: overflow => return e; + }; + return 0: invalid; +}; + // f64tos — graduated to the Ryū shortest-round-trippable implementation // in ftos.ww (strconv #106 fold-5). The old lossy fixed-point version // (6 fractional digits, "huge" fallback ≥9e18, no NaN/Inf) was deleted diff --git a/selfhost/test/smoke.combined.ww b/selfhost/test/smoke.combined.ww index 92dd25d1..09c01862 100644 --- a/selfhost/test/smoke.combined.ww +++ b/selfhost/test/smoke.combined.ww @@ -6234,6 +6234,23 @@ export fn stoi8(s: str, b: base) (i8 | invalid | overflow) = { return 0: invalid; }; +// stoi — parse signed base-b number into an int. Mirrors Hare's +// strconv::stoi (ref/hare/strconv/stoi.ha:53), which clamps to +// types::INT_MIN/INT_MAX via stoiminmax. ww's int is a machine word +// (8B → i64-width, so INT_MIN/INT_MAX == I64_MIN/I64_MAX per +// lib/types/types.ww:30-31), so stoi64's result always fits and the +// clamp is a no-op — omitted, not inlined (the bound consts are +// package-private; see the inline note at mulshift32 in ftos.ww). +export fn stoi(s: str, b: base) (int | invalid | overflow) = { + let r = stoi64(s, b); + match (r) { + case let v: i64 => return v: int; + case let e: invalid => return e; + case let e: overflow => return e; + }; + return 0: invalid; +}; + export fn stou32(s: str, b: base) (u32 | invalid | overflow) = { let r = stou64(s, b); match (r) { @@ -6273,6 +6290,34 @@ export fn stou8(s: str, b: base) (u8 | invalid | overflow) = { return 0: invalid; }; +// stou — parse unsigned base-b number into a uint. Mirrors Hare's +// strconv::stou (ref/hare/strconv/stou.ha:107), which clamps to +// types::UINT_MAX via stoumax. ww's uint is a machine word (8B → +// u64-width, so UINT_MAX == U64_MAX per lib/types/types.ww:33), so +// stou64's result always fits and the clamp is a no-op. +export fn stou(s: str, b: base) (uint | invalid | overflow) = { + let r = stou64(s, b); + match (r) { + case let v: u64 => return v: uint; + case let e: invalid => return e; + case let e: overflow => return e; + }; + return 0: invalid; +}; + +// stoz — parse unsigned base-b number into a size. Mirrors Hare's +// strconv::stoz (ref/hare/strconv/stou.ha:113). ww's size is u64-width +// (SIZE_MAX == U64_MAX per lib/types/types.ww:37), so the clamp is a no-op. +export fn stoz(s: str, b: base) (size | invalid | overflow) = { + let r = stou64(s, b); + match (r) { + case let v: u64 => return v: size; + case let e: invalid => return e; + case let e: overflow => return e; + }; + return 0: invalid; +}; + // f64tos — graduated to the Ryū shortest-round-trippable implementation // in ftos.ww (strconv #106 fold-5). The old lossy fixed-point version // (6 fractional digits, "huge" fallback ≥9e18, no NaN/Inf) was deleted