// Verbatim port of ref/hare/strconv/stoi.ha:56-86 (stoi/stoi_bases) and // stou.ha:116-138 (stou/stou_bases). Hare's strconv integer tests are // flat assert SEQUENCES, not row-array tables — mirrored here as inline // per-case checks (feedback_test_match_hare_source: inline @test-fn for // verbatim ports). The leading id arg to each check helper is retained // to keep the rows 1:1 but is now inert (signalled plumbing removed, task #5). // // Hex/oct/bin expected values are written in decimal (ww has no 0x/0o/0b // literal form for the expectation side); the original Hare radix form // is noted inline. package strconv_test; import strconv; fn cki(id: i32, s: str, b: strconv.base, want: i64) void = { match (strconv.stoi64(s, b)) { case let v: i64 => assert(!(v != want)); case let e: strconv.invalid => abort(); case let e: strconv.overflow => abort(); }; }; fn cki_inv(id: i32, s: str, b: strconv.base, idx: i32) void = { match (strconv.stoi64(s, b)) { case let v: i64 => abort(); case let e: strconv.invalid => assert(!(e: i32 != idx)); case let e: strconv.overflow => abort(); }; }; fn cki_ovf(id: i32, s: str, b: strconv.base) void = { match (strconv.stoi64(s, b)) { case let v: i64 => abort(); case let e: strconv.invalid => abort(); case let e: strconv.overflow => { }; }; }; fn cku(id: i32, s: str, b: strconv.base, want: u64) void = { match (strconv.stou64(s, b)) { case let v: u64 => assert(!(v != want)); case let e: strconv.invalid => abort(); case let e: strconv.overflow => abort(); }; }; fn cku_inv(id: i32, s: str, b: strconv.base, idx: i32) void = { match (strconv.stou64(s, b)) { case let v: u64 => abort(); case let e: strconv.invalid => assert(!(e: i32 != idx)); case let e: strconv.overflow => abort(); }; }; fn cku_ovf(id: i32, s: str, b: strconv.base) void = { match (strconv.stou64(s, b)) { case let v: u64 => abort(); case let e: strconv.invalid => abort(); case let e: strconv.overflow => { }; }; }; fn cki32_ovf(id: i32, s: str, b: strconv.base) void = { match (strconv.stoi32(s, b)) { case let v: i32 => abort(); case let e: strconv.invalid => abort(); case let e: strconv.overflow => { }; }; }; fn cki32(id: i32, s: str, b: strconv.base, want: i32) void = { match (strconv.stoi32(s, b)) { case let v: i32 => assert(!(v != want)); case let e: strconv.invalid => abort(); case let e: strconv.overflow => abort(); }; }; fn ck_int(id: i32, s: str, b: strconv.base, want: int) void = { match (strconv.stoi(s, b)) { case let v: int => assert(!(v != want)); case let e: strconv.invalid => abort(); case let e: strconv.overflow => abort(); }; }; fn ck_uint(id: i32, s: str, b: strconv.base, want: uint) void = { match (strconv.stou(s, b)) { case let v: uint => assert(!(v != want)); case let e: strconv.invalid => abort(); case let e: strconv.overflow => abort(); }; }; fn ck_size(id: i32, s: str, b: strconv.base, want: size) void = { match (strconv.stoz(s, b)) { case let v: size => assert(!(v != want)); case let e: strconv.invalid => abort(); case let e: strconv.overflow => abort(); }; }; fn ck_int_ovf(id: i32, s: str, b: strconv.base) void = { match (strconv.stoi(s, b)) { case let v: int => abort(); case let e: strconv.invalid => abort(); case let e: strconv.overflow => { }; }; }; fn ck_uint_ovf(id: i32, s: str, b: strconv.base) void = { match (strconv.stou(s, b)) { case let v: uint => abort(); case let e: strconv.invalid => abort(); case let e: strconv.overflow => { }; }; }; // ref/hare/strconv/stoi.ha:56-79. @test fn test_stoi64() void = { cki_inv(1, "", strconv.base.DEC, 0); cki_inv(2, "abc", strconv.base.DEC, 0); cki_inv(3, "1a", strconv.base.DEC, 1); cki_inv(4, "+", strconv.base.DEC, 1); cki_inv(5, "-+", strconv.base.DEC, 1); cki_inv(6, "-z", strconv.base.DEC, 1); cki_ovf(7, "9223372036854775808", strconv.base.DEC); cki_ovf(8, "-9223372036854775809", strconv.base.DEC); // The last multiply wraps to a value larger than its prefix, so a // post-arithmetic `n < old` check misses this overflow. cki_ovf(19, "21000000000000000000", strconv.base.DEC); cki(9, "0", strconv.base.DEC, 0); cki(10, "1", strconv.base.DEC, 1); cki(11, "+1", strconv.base.DEC, 1); cki(12, "-1", strconv.base.DEC, -1); cki(13, "9223372036854775807", strconv.base.DEC, 9223372036854775807i64); // I64_MIN. Spelled -I64_MAX-1 (Hare's own two's-complement identity, // stoi64 comment in stoi.ha:11) because the wwstage mis-lexes the // direct literal -9223372036854775808 (and types.I64_MIN) to 0 — // proj #245. The INPUT string is unaffected; stoi64 parses it to the // correct value on both stages. This isolates the parse test from #245. cki(14, "-9223372036854775808", strconv.base.DEC, -9223372036854775807i64 - 1i64); // width wrapper boundaries (ref/hare/strconv/stoi.ha:74-78). cki32_ovf(15, "2147483648", strconv.base.DEC); cki32_ovf(16, "-2147483649", strconv.base.DEC); cki32(17, "2147483647", strconv.base.DEC, 2147483647i32); cki32(18, "-2147483648", strconv.base.DEC, -2147483648i32); }; // ref/hare/strconv/stoi.ha:81-86. @test fn test_stoi64_bases() void = { cki(20, "-7f", strconv.base.HEX, -127i64); // -0x7f cki(21, "7F", strconv.base.HEX, 127i64); // 0x7f cki(22, "37", strconv.base.OCT, 31i64); // 0o37 cki(23, "-110101", strconv.base.BIN, -53i64); // -0b110101 }; // ref/hare/strconv/stou.ha:116-130. @test fn test_stou64() void = { cku_inv(30, "", strconv.base.DEC, 0); cku_inv(31, "+", strconv.base.DEC, 1); cku_inv(32, "+a", strconv.base.DEC, 1); cku_inv(33, "abc", strconv.base.DEC, 0); cku_inv(34, "1a", strconv.base.DEC, 1); cku_ovf(35, "18446744073709551616", strconv.base.DEC); cku_ovf(36, "184467440737095516150", strconv.base.DEC); cku_ovf(37, "-1", strconv.base.DEC); cku_ovf(46, "21000000000000000000", strconv.base.DEC); cku(38, "0", strconv.base.DEC, 0u64); cku(39, "1", strconv.base.DEC, 1u64); cku(40, "18446744073709551615", strconv.base.DEC, 18446744073709551615u64); }; // ref/hare/strconv/stou.ha:132-138. @test fn test_stou64_bases() void = { cku(41, "f", strconv.base.HEX_LOWER, 15u64); // 0xf cku(42, "7f", strconv.base.HEX, 127u64); // 0x7f cku(43, "7F", strconv.base.HEX, 127u64); // 0x7f cku(44, "37", strconv.base.OCT, 31u64); // 0o37 cku(45, "110101", strconv.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", strconv.base.DEC, 0); ck_int(51, "-1", strconv.base.DEC, -1); ck_int(52, "9223372036854775807", strconv.base.DEC, 9223372036854775807i64: int); // I64_MAX fits int ck_int_ovf(53, "9223372036854775808", strconv.base.DEC); ck_int_ovf(54, "-9223372036854775809", strconv.base.DEC); ck_uint(55, "0", strconv.base.DEC, 0u64: uint); ck_uint(56, "18446744073709551615", strconv.base.DEC, 18446744073709551615u64: uint); // U64_MAX fits uint ck_uint_ovf(57, "18446744073709551616", strconv.base.DEC); ck_uint_ovf(58, "-1", strconv.base.DEC); ck_size(59, "0", strconv.base.DEC, 0u64: size); ck_size(60, "18446744073709551615", strconv.base.DEC, 18446744073709551615u64: size); // U64_MAX fits size // bases route through the same parseint core. ck_int(61, "-7f", strconv.base.HEX, -127i64: int); // -0x7f ck_uint(62, "110101", strconv.base.BIN, 53u64: uint); // 0b110101 }; @test fn test_narrow_parse_boundaries() void = { match (strconv.stoi16("32767", strconv.base.DEC)) { case let v: i16 => assert(v == 32767i16); case let e: strconv.invalid => abort(); case let e: strconv.overflow => abort(); }; match (strconv.stoi16("-32768", strconv.base.DEC)) { case let v: i16 => assert(v == -32768i16); case let e: strconv.invalid => abort(); case let e: strconv.overflow => abort(); }; match (strconv.stoi16("32768", strconv.base.DEC)) { case let v: i16 => abort(); case let e: strconv.invalid => abort(); case let e: strconv.overflow => { }; }; match (strconv.stoi16("-32769", strconv.base.DEC)) { case let v: i16 => abort(); case let e: strconv.invalid => abort(); case let e: strconv.overflow => { }; }; match (strconv.stoi8("127", strconv.base.DEC)) { case let v: i8 => assert(v == 127i8); case let e: strconv.invalid => abort(); case let e: strconv.overflow => abort(); }; match (strconv.stoi8("-128", strconv.base.DEC)) { case let v: i8 => assert(v == -128i8); case let e: strconv.invalid => abort(); case let e: strconv.overflow => abort(); }; match (strconv.stoi8("128", strconv.base.DEC)) { case let v: i8 => abort(); case let e: strconv.invalid => abort(); case let e: strconv.overflow => { }; }; match (strconv.stoi8("-129", strconv.base.DEC)) { case let v: i8 => abort(); case let e: strconv.invalid => abort(); case let e: strconv.overflow => { }; }; match (strconv.stou16("65535", strconv.base.DEC)) { case let v: u16 => assert(v == 65535u16); case let e: strconv.invalid => abort(); case let e: strconv.overflow => abort(); }; match (strconv.stou16("65536", strconv.base.DEC)) { case let v: u16 => abort(); case let e: strconv.invalid => abort(); case let e: strconv.overflow => { }; }; match (strconv.stou8("255", strconv.base.DEC)) { case let v: u8 => assert(v == 255u8); case let e: strconv.invalid => abort(); case let e: strconv.overflow => abort(); }; match (strconv.stou8("256", strconv.base.DEC)) { case let v: u8 => abort(); case let e: strconv.invalid => abort(); case let e: strconv.overflow => { }; }; }; @test fn test_strerror_static() void = { let inv: strconv.invalid = 0: strconv.invalid; let ie: strconv.error = inv; let ia: str = strconv.strerror(ie); let ib: str = strconv.strerror(ie); assert(ia.ptr == ib.ptr); assert(streq(ia, "input is not a valid number")); let ov: strconv.overflow; let oe: strconv.error = ov; let oa: str = strconv.strerror(oe); let ob: str = strconv.strerror(oe); assert(oa.ptr == ob.ptr); assert(streq(oa, "input number doesn't fit target type")); }; // Verbatim ports of ref/hare/strconv/utos.ha:74-103 (utos/utos_bases) and // itos.ha:54-87 (itos/itos_bases) — Hare's format tests are flat assert // sequences too. Radix-literal inputs are written in DECIMAL (ww value // side has no 0b/0o/0x form): 0b11010=26, 0o1234567=342391, // 0x123456789ABCDEF=81985529216486895; the original radix is noted inline. fn streq(a: str, b: str) bool = { if (a.len != b.len) { return false; }; let k: i32 = 0; for (k < a.len) { if (a[k] != b[k]) { return false; }; k += 1; }; return true; }; fn cks(id: i32, got: str, want: str) void = { assert(!(!streq(got, want))); }; // ref/hare/strconv/utos.ha:74-83. @test fn test_u64tos_bases() void = { cks(70, strconv.u64tos(26u64, strconv.base.BIN), "11010"); // 0b11010 cks(71, strconv.u64tos(342391u64, strconv.base.OCT), "1234567"); // 0o1234567 cks(72, strconv.u64tos(123456789u64, strconv.base.DEC), "123456789"); cks(73, strconv.u64tos(81985529216486895u64, strconv.base.HEX), "123456789ABCDEF"); // 0x123456789ABCDEF cks(74, strconv.u64tos(81985529216486895u64, strconv.base.HEX_UPPER), "123456789ABCDEF"); cks(75, strconv.u64tos(81985529216486895u64, strconv.base.HEX_LOWER), "123456789abcdef"); cks(76, strconv.u64tos(18446744073709551615u64, strconv.base.BIN), // U64_MAX "1111111111111111111111111111111111111111111111111111111111111111"); }; // ref/hare/strconv/utos.ha:85-103. @test fn test_u64tos() void = { cks(77, strconv.u64tos(1234u64, strconv.base.DEC), "1234"); cks(78, strconv.u64tos(4321u64, strconv.base.DEC), "4321"); cks(79, strconv.u64tos(0u64, strconv.base.DEC), "0"); // U64_MIN cks(80, strconv.u64tos(18446744073709551615u64, strconv.base.DEC), "18446744073709551615"); // U64_MAX }; // ref/hare/strconv/itos.ha:54-63. @test fn test_i64tos_bases() void = { cks(81, strconv.i64tos(26i64, strconv.base.BIN), "11010"); cks(82, strconv.i64tos(342391i64, strconv.base.OCT), "1234567"); cks(83, strconv.i64tos(123456789i64, strconv.base.DEC), "123456789"); cks(84, strconv.i64tos(81985529216486895i64, strconv.base.HEX), "123456789ABCDEF"); cks(85, strconv.i64tos(81985529216486895i64, strconv.base.HEX_UPPER), "123456789ABCDEF"); cks(86, strconv.i64tos(81985529216486895i64, strconv.base.HEX_LOWER), "123456789abcdef"); // I64_MIN binary = '1' + 63 zeros, prefixed '-'. Spelled -I64_MAX-1 // (proj #245: wwstage mis-lexes the direct 2^63 / types.I64_MIN literal). cks(87, strconv.i64tos(-9223372036854775807i64 - 1i64, strconv.base.BIN), "-1000000000000000000000000000000000000000000000000000000000000000"); }; // ref/hare/strconv/itos.ha:65-87. @test fn test_i64tos() void = { cks(88, strconv.i64tos(1234i64, strconv.base.DEC), "1234"); cks(89, strconv.i64tos(4321i64, strconv.base.DEC), "4321"); cks(90, strconv.i64tos(-1337i64, strconv.base.DEC), "-1337"); cks(91, strconv.i64tos(0i64, strconv.base.DEC), "0"); cks(92, strconv.i64tos(9223372036854775807i64, strconv.base.DEC), "9223372036854775807"); // I64_MAX // drew probe-1: NEG-of-I64_MIN + i64→u64 reinterpret preserves the // two's-complement bit pattern. I64_MIN spelled -I64_MAX-1 (proj #245). cks(93, strconv.i64tos(-9223372036854775807i64 - 1i64, strconv.base.DEC), "-9223372036854775808"); }; // machine-word wrappers + width wrappers. ww int/uint/size/uintptr are // all 8B, so the full i64/u64 range renders untruncated. ww-authored // (Hare's wrappers are trivial aliases): itos.ha:37-52, utos.ha:44-72. @test fn test_word_wrappers() void = { cks(94, strconv.itos(-1337, strconv.base.DEC), "-1337"); cks(95, strconv.itos(9223372036854775807i64: int, strconv.base.DEC), "9223372036854775807"); cks(96, strconv.utos(18446744073709551615u64: uint, strconv.base.DEC), "18446744073709551615"); cks(97, strconv.ztos(255u64: size, strconv.base.HEX), "FF"); cks(98, strconv.uptrtos(4096u64: uintptr, strconv.base.DEC), "4096"); cks(99, strconv.i32tos(-2147483648i32, strconv.base.DEC), "-2147483648"); cks(100, strconv.u8tos(255u8, strconv.base.HEX_LOWER), "ff"); };