A type prefixed with `!` is flagged as an error variant. When any variant in a tagged union carries the flag, `?` propagation uses those (and only those) as the error subset; the unflagged variant is the success type. The legacy "first variant = success" rule still applies when no `!`-flag is present, so existing code keeps working. - TK_NOT in parsetype → N_TBANG wrapper (lhs = inner type expr). Appended to Nkind tail for wwdump-diff byte stability. - resolve_type N_TBANG: wraps primitives in a fresh Type copy so the iserror bit doesn't taint shared globals like ty_str/ty_i32; flips the bit in place on NAMED (already unique per alias decl). - Type.iserror; type_named and typedecl inherit it from under. - New check.c helpers: tagged_has_errflag, tagged_is_error_variant, tagged_success_type. N_TRYPROP uses them to find the error subset and verify each error variant is propagatable to the enclosing return. - cgen mirrors with cg_tagged_success_tag + cg_variant_is_error. `?` compares AX against the success tag (no longer always 0) and remaps each error variant's tag for the enclosing fn. `!` aborts on any non-success tag. strconv.invalid and strconv.overflow now use `!`-flagged shape (`!i32` and `!void`) — visible signal in the API surface that they are error types, matching Hare. The (i64 | invalid | overflow) return shape and behavior are unchanged for callers; their match arms still bind the same way. Selfhost: lib/ww/parse/parse.ww recognises `!T` and emits N_TBANG. The selfhost typechecker and cgen ignore the flag — none of the selfhost sources use `!`, so byte-identity gates are unaffected. The selfhost mirror catches up when there's a source using it.
108 lines
2.7 KiB
Plaintext
108 lines
2.7 KiB
Plaintext
// strconv — number↔string conversions. Decimal i64 to/from a fixed
|
|
// buffer. Error shapes mirror Hare's strconv types: (T | invalid |
|
|
// overflow) where each error is a named alias over a payload type
|
|
// (Hare uses !size / !void; ww uses i32 / void without the `!` mark).
|
|
|
|
// invalid — input wasn't a valid number in the requested format.
|
|
// Payload is the byte index of the first offending position. Mirrors
|
|
// Hare's strconv::invalid = !size (we use i32 instead of size).
|
|
export type invalid = !i32;
|
|
|
|
// overflow — input was valid but doesn't fit the target type. No
|
|
// payload (a single yes/no signal). Mirrors Hare's !void shape.
|
|
export type overflow = !void;
|
|
|
|
// u64tos — write `v` in decimal into `buf` and return the byte count.
|
|
// Hare name; the buffer-in shape is the sanctioned Plan 9 subset of
|
|
// Hare's `u64tos(u, base) const str`. Unsigned-only so callers don't
|
|
// have to think about wraparound when printing a u64 with the high
|
|
// bit set.
|
|
export fn u64tos(buf: []u8, v: u64) i32 = {
|
|
let tmp: [32]u8;
|
|
let i: i32 = 0;
|
|
let n: u64 = v;
|
|
for (n > 0u64) {
|
|
tmp[i] = ((n % 10u64) + 48u64): u8;
|
|
n = n / 10u64;
|
|
i += 1;
|
|
};
|
|
if (i == 0) {
|
|
tmp[0] = 48u8;
|
|
i = 1;
|
|
};
|
|
let out: i32 = 0;
|
|
for (i > 0) {
|
|
i -= 1;
|
|
buf[out] = tmp[i];
|
|
out += 1;
|
|
};
|
|
return out;
|
|
};
|
|
|
|
export fn i64tos(buf: []u8, v: i64) i32 = {
|
|
let neg: bool = false;
|
|
let n: i64 = v;
|
|
if (n < 0) {
|
|
neg = true;
|
|
n = -n;
|
|
};
|
|
let tmp: [32]u8;
|
|
let i: i32 = 0;
|
|
for (n > 0) {
|
|
tmp[i] = ((n % 10) + 48): u8;
|
|
n = n / 10;
|
|
i += 1;
|
|
};
|
|
if (i == 0) {
|
|
tmp[0] = 48u8;
|
|
i = 1;
|
|
};
|
|
let out: i32 = 0;
|
|
if (neg) {
|
|
buf[out] = 45u8; // '-'
|
|
out += 1;
|
|
};
|
|
for (i > 0) {
|
|
i -= 1;
|
|
buf[out] = tmp[i];
|
|
out += 1;
|
|
};
|
|
return out;
|
|
};
|
|
|
|
// stoi64 — Hare-style fallible signed decimal parser. No locale, no
|
|
// whitespace, no underscores: a leading '-' is the only non-digit
|
|
// accepted, and only at position 0.
|
|
export fn stoi64(s: str) (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 v: i64 = 0;
|
|
for (i < s.len) {
|
|
let c: u8 = s[i];
|
|
if (c < 48u8) { return i: invalid; };
|
|
if (c > 57u8) { return i: invalid; };
|
|
v = v * 10 + ((c: i64) - 48);
|
|
i += 1;
|
|
};
|
|
if (neg) { v = -v; };
|
|
return v;
|
|
};
|
|
|
|
// stou64 — fallible unsigned decimal parser. No leading sign.
|
|
export fn stou64(s: str) (u64 | invalid | overflow) = {
|
|
if (s.len == 0) { return 0: invalid; };
|
|
let v: u64 = 0u64;
|
|
let i: i32 = 0;
|
|
for (i < s.len) {
|
|
let c: u8 = s[i];
|
|
if (c < 48u8) { return i: invalid; };
|
|
if (c > 57u8) { return i: invalid; };
|
|
v = v * 10u64 + ((c: u64) - 48u64);
|
|
i += 1;
|
|
};
|
|
return v;
|
|
};
|