lib/strconv: parseint sign+overflow core; stoi64/stou64 fidelity (strconv-int fold-1 C1)
Port ref/hare/strconv/stou.ha:8-65 (rune_to_integer + parseint) and the stoi64/stou64 fidelity rewrite (stoi.ha:9-17, stou.ha:70-76) over the old digval loop. parseint is the shared sign + per-digit + multiply-overflow core returning ((bool, u64) | invalid | overflow); stoi64/stou64 destructure its `(sign, u)` tuple-in-union result — the shape unblocked by #242/#241. Wins over the prior ad-hoc parse: leading '+' accepted, '-' on stou64 is overflow (not silently dropped), wraparound overflow detection (n < old), and the invalid payload carries the offending byte index per Hare. Tests: lib/strconv/test/inttest.ww (run via test/wcc/922_strconv_int_run.c), inline per-case checks mirroring Hare's assert sequences stoi.ha:56-86 / stou.ha:116-138 (Hare's strconv int tests are flat sequences, not row tables; feedback_test_match_hare_source). Covers valid dec/hex/oct/bin, +/- sign, invalid+index, overflow, and U64_MAX / I64_MAX / I64_MIN boundaries. The I64_MIN expectation is spelled -I64_MAX-1 (Hare's own two's-complement identity) to isolate the test from #245 (wwstage mis-lexes the literal 9223372036854775808 -> 0); the parse INPUT is unaffected and yields the correct value on both stages. combined.ww regen: strconv is compiler-imported (via fmt), so w6c + wwdump main.combined.ww are regenerated.
This commit is contained in:
@@ -8,6 +8,7 @@
|
||||
|
||||
package strconv;
|
||||
|
||||
import ascii;
|
||||
import os;
|
||||
import strings;
|
||||
|
||||
@@ -125,59 +126,110 @@ export fn u32tos(v: u32, b: base) str = { return u64tos(v: u64, b); };
|
||||
export fn u16tos(v: u16, b: base) str = { return u64tos(v: u64, b); };
|
||||
export fn u8tos(v: u8, b: base) str = { return u64tos(v: u64, b); };
|
||||
|
||||
// digval — value of digit byte `c` under base `b`, or -1 if not a
|
||||
// valid digit. Letters are accepted case-insensitively under HEX /
|
||||
// HEX_UPPER; only lowercase under HEX_LOWER.
|
||||
fn digval(c: u8, b: base) i32 = {
|
||||
if (c >= 48u8) { if (c <= 57u8) { return (c - 48u8): i32; }; };
|
||||
if (b == base.HEX_LOWER) {
|
||||
if (c >= 97u8) { if (c <= 102u8) { return ((c - 97u8) + 10u8): i32; }; };
|
||||
return -1;
|
||||
// rune_to_integer — digit value of r (0-9 → 0-9; a-z/A-Z → 10-35),
|
||||
// or void if r is not alphanumeric. Verbatim port of
|
||||
// ref/hare/strconv/stou.ha:8-15 (ww yields the void variant with a
|
||||
// bare `return;`, per lib/bytes/bytes.ww:65).
|
||||
fn rune_to_integer(r: rune) (u64 | void) = {
|
||||
if (ascii.isdigit(r)) {
|
||||
return (r: u32 - '0'): u64;
|
||||
} else if (ascii.isalpha(r) && ascii.islower(r)) {
|
||||
return (r: u32 - 'a'): u64 + 10;
|
||||
} else if (ascii.isalpha(r) && ascii.isupper(r)) {
|
||||
return (r: u32 - 'A'): u64 + 10;
|
||||
};
|
||||
if (c >= 65u8) { if (c <= 70u8) { return ((c - 65u8) + 10u8): i32; }; };
|
||||
if (c >= 97u8) { if (c <= 102u8) { return ((c - 97u8) + 10u8): i32; }; };
|
||||
return -1;
|
||||
return;
|
||||
};
|
||||
|
||||
// stoi64 — parse signed base-b number. Mirrors Hare's strconv::stoi64.
|
||||
// No locale, no whitespace, no underscores: optional leading '-' then
|
||||
// digits. Returns invalid with the offending index or overflow on
|
||||
// out-of-range.
|
||||
export fn stoi64(s: str, b: base) (i64 | invalid | overflow) = {
|
||||
if (s.len == 0) { return 0: invalid; };
|
||||
let i: i32 = 0;
|
||||
let neg: bool = false;
|
||||
if (s[0] == 45u8) { neg = true; i = 1; };
|
||||
if (i >= s.len) { return i: invalid; };
|
||||
let nb: i32 = basenum(b): i32;
|
||||
let v: i64 = 0;
|
||||
for (i < s.len) {
|
||||
let c: u8 = s[i];
|
||||
let d: i32 = digval(c, b);
|
||||
if (d < 0) { return i: invalid; };
|
||||
if (d >= nb) { return i: invalid; };
|
||||
v = v * (nb: i64) + (d: i64);
|
||||
i += 1;
|
||||
};
|
||||
if (neg) { v = -v; };
|
||||
return v;
|
||||
};
|
||||
|
||||
// stou64 — parse unsigned base-b number. Mirrors Hare's strconv::stou64.
|
||||
export fn stou64(s: str, b: base) (u64 | invalid | overflow) = {
|
||||
if (s.len == 0) { return 0: invalid; };
|
||||
// parseint — shared sign+digit+overflow core for stoi64/stou64.
|
||||
// Verbatim port of ref/hare/strconv/stou.ha:17-65. Divergences:
|
||||
// - param `base` → `b` (ww: avoid the type/value name collision; the
|
||||
// file already names the enum arg `b`).
|
||||
// - Hare's DEFAULT→DEC / HEX_LOWER→HEX base reassignment + the
|
||||
// base-validity assert collapse into basenum(b), which already maps
|
||||
// every base to its numeric value {2,8,10,16} (default 10). HEX_LOWER
|
||||
// thus parses case-insensitively, matching Hare's normalize-then-parse.
|
||||
// - str is byte-indexable, so Hare's `buf = strings::toutf8(s)` is
|
||||
// elided (existing file convention, cf. the old stoi64/stou64).
|
||||
// - n *= base / n += digit spelled as plain assignment (sibling-fn
|
||||
// convention).
|
||||
fn parseint(s: str, b: base) ((bool, u64) | invalid | overflow) = {
|
||||
let nb: u64 = basenum(b): u64;
|
||||
let v: u64 = 0u64;
|
||||
|
||||
if (s.len == 0) {
|
||||
return 0: invalid;
|
||||
};
|
||||
|
||||
let i: i32 = 0;
|
||||
for (i < s.len) {
|
||||
let c: u8 = s[i];
|
||||
let d: i32 = digval(c, b);
|
||||
if (d < 0) { return i: invalid; };
|
||||
if ((d: u64) >= nb) { return i: invalid; };
|
||||
v = v * nb + (d: u64);
|
||||
|
||||
let sign: bool = s[i] == '-';
|
||||
if (sign || s[i] == '+') {
|
||||
i += 1;
|
||||
};
|
||||
return v;
|
||||
|
||||
// Require at least one digit.
|
||||
if (i == s.len) {
|
||||
return i: invalid;
|
||||
};
|
||||
|
||||
let n: u64 = 0u64;
|
||||
// Hare's `for (i < len(buf); i += 1)` (stou.ha:43) → condition-only
|
||||
// for + tail increment (ww has no 2-clause for; sort.ww:25). Early
|
||||
// returns exit before the increment, so it's never skipped.
|
||||
for (i < s.len) {
|
||||
let digit: u64 = match (rune_to_integer(s[i]: rune)) {
|
||||
case void => return i: invalid;
|
||||
case let d: u64 => yield d;
|
||||
};
|
||||
|
||||
if (digit >= nb) {
|
||||
return i: invalid;
|
||||
};
|
||||
|
||||
let old: u64 = n;
|
||||
|
||||
n = n * nb;
|
||||
n = n + digit;
|
||||
|
||||
if (n < old) {
|
||||
return overflow{};
|
||||
};
|
||||
|
||||
i += 1;
|
||||
};
|
||||
return (sign, n);
|
||||
};
|
||||
|
||||
// stoi64 — parse signed base-b number. Verbatim port of
|
||||
// ref/hare/strconv/stoi.ha:9-17. types.I64_MAX is inlined (the const is
|
||||
// package-private — see the mulshift32 note in ftos.ww).
|
||||
export fn stoi64(s: str, b: base) (i64 | invalid | overflow) = {
|
||||
let (sign, u) = parseint(s, b)?;
|
||||
// Two's complement: I64_MIN = -I64_MAX - 1. Hare's two if-expressions
|
||||
// (stoi.ha:12,16) are lowered to statement-if — ww has no
|
||||
// if-expression (standing divergence, see the note in stof.ww:31).
|
||||
let max: u64 = 9223372036854775807u64;
|
||||
if (sign) {
|
||||
max = max + 1u64;
|
||||
};
|
||||
if (u > max) {
|
||||
return overflow{};
|
||||
};
|
||||
let r: i64 = u: i64;
|
||||
if (sign) {
|
||||
r = -r;
|
||||
};
|
||||
return r;
|
||||
};
|
||||
|
||||
// stou64 — parse unsigned base-b number. Verbatim port of
|
||||
// ref/hare/strconv/stou.ha:70-76.
|
||||
export fn stou64(s: str, b: base) (u64 | invalid | overflow) = {
|
||||
let (sign, u) = parseint(s, b)?;
|
||||
if (sign) {
|
||||
return overflow{};
|
||||
};
|
||||
return u;
|
||||
};
|
||||
|
||||
export fn stoi32(s: str, b: base) (i32 | invalid | overflow) = {
|
||||
|
||||
Reference in New Issue
Block a user