lib/strconv: graduate to owned-str returns with Hare-shape base param
i64tos / u64tos / f64tos return a fresh owned str (caller frees via os.free) instead of writing into a caller-supplied [N]u8. Adds typed variants (i32tos / i16tos / i8tos and u32 / u16 / u8) and the missing base parameter on stoi64 / stou64 + typed parse wrappers. Base values are exported as plain-i32 `def`s (strconv.DEC, strconv.HEX_UPPER, ...) rather than a `base` enum: cross-module `strconv.base.DEC` chains miscompile in the cstage cgen — it emits a memory load through `base(SB)` rather than inlining the constant. The Sdef path resolves correctly, so callers say `strconv.DEC` and both cgens lower to an immediate. Also renames strings.byteindex / rbyteindex to strings.indexbyte / rindexbyte, matching bytes.indexbyte and reserving the Hare name `byteindex` for the future `(str | rune)`-needle shape. fmt drops printint / printlnint / fprintint — those were stand-ins for variadic `fmt::println(42)`; with the owned-str graduation the substitute is one call: `fmt.println(strconv.i64tos(42, strconv.DEC))`. strerror is sketched in a comment but not shipped — match arms over the wider `error = !(invalid | overflow)` union still expose a cstage-vs-wwstage spill divergence.
This commit is contained in:
@@ -336,117 +336,273 @@ export fn freearena(a: *arena) void = {
|
||||
};
|
||||
|
||||
// MODULE: strconv
|
||||
// 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).
|
||||
// strconv — number↔string conversions.
|
||||
//
|
||||
// Mirrors Hare's strconv:: surface. The *tos functions return a fresh
|
||||
// owned `str`; release via os.free(r.ptr, r.len: u64) when done.
|
||||
// Hare returns `const str` into a static buffer; ww allocates per
|
||||
// call because the wwstage cgen doesn't currently support mutating a
|
||||
// module-level `*u8` (so a lazy-init shared buffer isn't expressible
|
||||
// today). Graduate to the static-buffer shape once that lands.
|
||||
|
||||
use os;
|
||||
|
||||
// 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).
|
||||
// Payload is the byte index of the first offending position.
|
||||
// Mirrors Hare's strconv::invalid = !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.
|
||||
// overflow — input was valid but doesn't fit the target type.
|
||||
// Mirrors Hare's strconv::overflow = !void.
|
||||
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;
|
||||
// error — any error from a strconv call. Mirrors Hare's strconv::error.
|
||||
export type error = !(invalid | overflow);
|
||||
|
||||
// base — numeric base for parsing/formatting. Plain i32 (not a named
|
||||
// enum) because cross-module `strconv.base.DEC` chains miscompile in
|
||||
// the cstage cgen — it emits a memory load through `base(SB)` rather
|
||||
// than inlining the enum value. Hare names them as `strconv::base`
|
||||
// enum values; we expose them as module-level `def`s so callers say
|
||||
// `strconv.DEC` and the cgen inlines the immediate.
|
||||
//
|
||||
// HEX is HEX_UPPER; HEX_LOWER is a separate pseudo-base that produces
|
||||
// lowercase a-f digits.
|
||||
export def DEFAULT: i32 = 0;
|
||||
export def BIN: i32 = 2;
|
||||
export def OCT: i32 = 8;
|
||||
export def DEC: i32 = 10;
|
||||
export def HEX_UPPER: i32 = 16;
|
||||
export def HEX: i32 = 16;
|
||||
export def HEX_LOWER: i32 = 17;
|
||||
|
||||
fn basenum(b: i32) i64 = {
|
||||
if (b == BIN) { return 2; };
|
||||
if (b == OCT) { return 8; };
|
||||
if (b == HEX) { return 16; };
|
||||
if (b == HEX_UPPER) { return 16; };
|
||||
if (b == HEX_LOWER) { return 16; };
|
||||
return 10; // DEC and DEFAULT
|
||||
};
|
||||
|
||||
fn basedigit(d: i64, b: i32) u8 = {
|
||||
if (d < 10) { return (d + 48): u8; };
|
||||
let off: i64 = d - 10;
|
||||
if (b == HEX_LOWER) { return (off + 97): u8; };
|
||||
return (off + 65): u8;
|
||||
};
|
||||
|
||||
// u64tos — convert v to a base-b numeric string. Returns owned str;
|
||||
// release via os.free(r.ptr, r.len: u64). Mirrors Hare's
|
||||
// strconv::u64tos (Hare returns const str into a static buffer).
|
||||
export fn u64tos(v: u64, b: i32) str = {
|
||||
let nb: u64 = basenum(b): u64;
|
||||
let tmp: [65]u8;
|
||||
let i: i32 = 0;
|
||||
let n: u64 = v;
|
||||
if (n == 0u64) { tmp[0] = 48u8; i = 1; };
|
||||
for (n > 0u64) {
|
||||
tmp[i] = ((n % 10u64) + 48u64): u8;
|
||||
n = n / 10u64;
|
||||
let d: i64 = (n % nb): i64;
|
||||
tmp[i] = basedigit(d, b);
|
||||
n = n / nb;
|
||||
i += 1;
|
||||
};
|
||||
if (i == 0) {
|
||||
tmp[0] = 48u8;
|
||||
i = 1;
|
||||
};
|
||||
let buf: *u8 = os.alloc(i: u64): *u8;
|
||||
let out: i32 = 0;
|
||||
for (i > 0) {
|
||||
i -= 1;
|
||||
buf[out] = tmp[i];
|
||||
out += 1;
|
||||
};
|
||||
return out;
|
||||
let r: str;
|
||||
r.ptr = buf;
|
||||
r.len = out;
|
||||
return r;
|
||||
};
|
||||
|
||||
export fn i64tos(buf: []u8, v: i64) i32 = {
|
||||
// i64tos — convert v to a base-b numeric string. Returns owned str;
|
||||
// release via os.free. Mirrors Hare's strconv::i64tos.
|
||||
export fn i64tos(v: i64, b: i32) str = {
|
||||
let neg: bool = false;
|
||||
let n: i64 = v;
|
||||
if (n < 0) {
|
||||
neg = true;
|
||||
n = -n;
|
||||
};
|
||||
let tmp: [32]u8;
|
||||
if (n < 0) { neg = true; n = -n; };
|
||||
let nb: i64 = basenum(b);
|
||||
let tmp: [65]u8;
|
||||
let i: i32 = 0;
|
||||
if (n == 0) { tmp[0] = 48u8; i = 1; };
|
||||
for (n > 0) {
|
||||
tmp[i] = ((n % 10) + 48): u8;
|
||||
n = n / 10;
|
||||
let d: i64 = n % nb;
|
||||
tmp[i] = basedigit(d, b);
|
||||
n = n / nb;
|
||||
i += 1;
|
||||
};
|
||||
if (i == 0) {
|
||||
tmp[0] = 48u8;
|
||||
i = 1;
|
||||
};
|
||||
let extra: i32 = 0;
|
||||
if (neg) { extra = 1; };
|
||||
let total: i32 = i + extra;
|
||||
let buf: *u8 = os.alloc(total: u64): *u8;
|
||||
let out: i32 = 0;
|
||||
if (neg) {
|
||||
buf[out] = 45u8; // '-'
|
||||
out += 1;
|
||||
};
|
||||
if (neg) { buf[out] = 45u8; out += 1; }; // '-'
|
||||
for (i > 0) {
|
||||
i -= 1;
|
||||
buf[out] = tmp[i];
|
||||
out += 1;
|
||||
};
|
||||
return out;
|
||||
let r: str;
|
||||
r.ptr = buf;
|
||||
r.len = out;
|
||||
return r;
|
||||
};
|
||||
|
||||
// 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) = {
|
||||
export fn i32tos(v: i32, b: i32) str = { return i64tos(v: i64, b); };
|
||||
export fn i16tos(v: i16, b: i32) str = { return i64tos(v: i64, b); };
|
||||
export fn i8tos(v: i8, b: i32) str = { return i64tos(v: i64, b); };
|
||||
|
||||
export fn u32tos(v: u32, b: i32) str = { return u64tos(v: u64, b); };
|
||||
export fn u16tos(v: u16, b: i32) str = { return u64tos(v: u64, b); };
|
||||
export fn u8tos(v: u8, b: i32) 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: i32) i32 = {
|
||||
if (c >= 48u8) { if (c <= 57u8) { return (c - 48u8): i32; }; };
|
||||
if (b == HEX_LOWER) {
|
||||
if (c >= 97u8) { if (c <= 102u8) { return ((c - 97u8) + 10u8): i32; }; };
|
||||
return -1;
|
||||
};
|
||||
if (c >= 65u8) { if (c <= 70u8) { return ((c - 65u8) + 10u8): i32; }; };
|
||||
if (c >= 97u8) { if (c <= 102u8) { return ((c - 97u8) + 10u8): i32; }; };
|
||||
return -1;
|
||||
};
|
||||
|
||||
// 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: i32) (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];
|
||||
if (c < 48u8) { return i: invalid; };
|
||||
if (c > 57u8) { return i: invalid; };
|
||||
v = v * 10 + ((c: i64) - 48);
|
||||
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 — fallible unsigned decimal parser. No leading sign.
|
||||
export fn stou64(s: str) (u64 | invalid | overflow) = {
|
||||
// stou64 — parse unsigned base-b number. Mirrors Hare's strconv::stou64.
|
||||
export fn stou64(s: str, b: i32) (u64 | invalid | overflow) = {
|
||||
if (s.len == 0) { return 0: invalid; };
|
||||
let nb: u64 = basenum(b): u64;
|
||||
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);
|
||||
let d: i32 = digval(c, b);
|
||||
if (d < 0) { return i: invalid; };
|
||||
if ((d: u64) >= nb) { return i: invalid; };
|
||||
v = v * nb + (d: u64);
|
||||
i += 1;
|
||||
};
|
||||
return v;
|
||||
};
|
||||
|
||||
// f64tos — write `v` in decimal into `buf` and return the byte count.
|
||||
// Hare name; this is the buffer-in Plan 9 subset of Hare's
|
||||
// `f64tos(n) const str`. Today's surface:
|
||||
export fn stoi32(s: str, b: i32) (i32 | invalid | overflow) = {
|
||||
let r = stoi64(s, b);
|
||||
match (r) {
|
||||
case let v: i64 => {
|
||||
if (v > 2147483647i64) { return overflow{}; };
|
||||
if (v < -2147483648i64) { return overflow{}; };
|
||||
return v: i32;
|
||||
};
|
||||
case let e: invalid => return e;
|
||||
case let e: overflow => return e;
|
||||
};
|
||||
return 0: invalid; // unreachable; appeases the path-cov checker
|
||||
};
|
||||
|
||||
export fn stoi16(s: str, b: i32) (i16 | invalid | overflow) = {
|
||||
let r = stoi64(s, b);
|
||||
match (r) {
|
||||
case let v: i64 => {
|
||||
if (v > 32767i64) { return overflow{}; };
|
||||
if (v < -32768i64) { return overflow{}; };
|
||||
return v: i16;
|
||||
};
|
||||
case let e: invalid => return e;
|
||||
case let e: overflow => return e;
|
||||
};
|
||||
return 0: invalid;
|
||||
};
|
||||
|
||||
export fn stoi8(s: str, b: i32) (i8 | invalid | overflow) = {
|
||||
let r = stoi64(s, b);
|
||||
match (r) {
|
||||
case let v: i64 => {
|
||||
if (v > 127i64) { return overflow{}; };
|
||||
if (v < -128i64) { return overflow{}; };
|
||||
return v: i8;
|
||||
};
|
||||
case let e: invalid => return e;
|
||||
case let e: overflow => return e;
|
||||
};
|
||||
return 0: invalid;
|
||||
};
|
||||
|
||||
export fn stou32(s: str, b: i32) (u32 | invalid | overflow) = {
|
||||
let r = stou64(s, b);
|
||||
match (r) {
|
||||
case let v: u64 => {
|
||||
if (v > 4294967295u64) { return overflow{}; };
|
||||
return v: u32;
|
||||
};
|
||||
case let e: invalid => return e;
|
||||
case let e: overflow => return e;
|
||||
};
|
||||
return 0: invalid;
|
||||
};
|
||||
|
||||
export fn stou16(s: str, b: i32) (u16 | invalid | overflow) = {
|
||||
let r = stou64(s, b);
|
||||
match (r) {
|
||||
case let v: u64 => {
|
||||
if (v > 65535u64) { return overflow{}; };
|
||||
return v: u16;
|
||||
};
|
||||
case let e: invalid => return e;
|
||||
case let e: overflow => return e;
|
||||
};
|
||||
return 0: invalid;
|
||||
};
|
||||
|
||||
export fn stou8(s: str, b: i32) (u8 | invalid | overflow) = {
|
||||
let r = stou64(s, b);
|
||||
match (r) {
|
||||
case let v: u64 => {
|
||||
if (v > 255u64) { return overflow{}; };
|
||||
return v: u8;
|
||||
};
|
||||
case let e: invalid => return e;
|
||||
case let e: overflow => return e;
|
||||
};
|
||||
return 0: invalid;
|
||||
};
|
||||
|
||||
// f64tos — convert v to a decimal string. Returns owned str; release
|
||||
// via os.free. Mirrors Hare's strconv::f64tos (current ww impl is
|
||||
// fixed-point only, max 6 fractional digits, no NaN/Inf support —
|
||||
// see graduate-to-Ryū note below).
|
||||
//
|
||||
// Surface:
|
||||
//
|
||||
// - finite values only. NaN/±Inf detection needs an f64→u64 bit
|
||||
// reinterpret cast that the cgen doesn't expose yet.
|
||||
@@ -457,23 +613,20 @@ export fn stou64(s: str) (u64 | invalid | overflow) = {
|
||||
// in scientific notation via Ryū; we will graduate when the
|
||||
// compiler grows the bit-reinterpret cast.
|
||||
//
|
||||
// Round-trip is therefore lossy past 6 fractional digits; callers
|
||||
// that need bit-exact recovery should not use this until the
|
||||
// graduate-to-Ryū step lands. `f64tos(buf, 1.0)` writes "1" (no
|
||||
// decimal point), `f64tos(buf, 1.5)` writes "1.5", `f64tos(buf,
|
||||
// 0.1)` writes "0.1".
|
||||
// Round-trip is therefore lossy past 6 fractional digits.
|
||||
//
|
||||
// No float literals in the body — 990's wwdump diff requires this
|
||||
// file's TK_FLOAT count to match between C and ww front-ends, and
|
||||
// the ww-side wwdump currently skips TK_FLOAT.fval while the C side
|
||||
// %g-formats it. Same trick lib/ww/lex/lex.ww's parsef64 uses:
|
||||
// build f64 constants via int-to-f64 casts.
|
||||
export fn f64tos(buf: []u8, v: f64) i32 = {
|
||||
export fn f64tos(v: f64) str = {
|
||||
let tmp: [64]u8;
|
||||
let out: i32 = 0;
|
||||
let f: f64 = v;
|
||||
let zero: f64 = 0: f64;
|
||||
if (f < zero) {
|
||||
buf[out] = 45u8; // '-'
|
||||
tmp[out] = 45u8; // '-'
|
||||
out += 1;
|
||||
f = -f;
|
||||
};
|
||||
@@ -483,8 +636,14 @@ export fn f64tos(buf: []u8, v: f64) i32 = {
|
||||
if (f >= cap) {
|
||||
let s: str = "huge";
|
||||
let k: i32 = 0;
|
||||
for (k < s.len) { buf[out] = s[k]; out += 1; k += 1; };
|
||||
return out;
|
||||
for (k < s.len) { tmp[out] = s[k]; out += 1; k += 1; };
|
||||
let buf: *u8 = os.alloc(out: u64): *u8;
|
||||
let q: i32 = 0;
|
||||
for (q < out) { buf[q] = tmp[q]; q += 1; };
|
||||
let r: str;
|
||||
r.ptr = buf;
|
||||
r.len = out;
|
||||
return r;
|
||||
};
|
||||
let ip: i64 = f: i64;
|
||||
// Fractional part scaled to 6 decimal digits, with round-to-
|
||||
@@ -501,30 +660,42 @@ export fn f64tos(buf: []u8, v: f64) i32 = {
|
||||
ip += 1;
|
||||
fp = 0;
|
||||
};
|
||||
let itmp: [32]u8;
|
||||
let in: i32 = i64tos(itmp[0:32], ip);
|
||||
let intstr: str = i64tos(ip, DEC);
|
||||
let k: i32 = 0;
|
||||
for (k < in) { buf[out] = itmp[k]; out += 1; k += 1; };
|
||||
if (fp == 0) { return out; };
|
||||
buf[out] = 46u8; // '.'
|
||||
out += 1;
|
||||
let ftmp: [16]u8;
|
||||
let m: i32 = u64tos(ftmp[0:16], fp: u64);
|
||||
// Pad fractional to 6 digits with leading zeros (e.g. 0.05 →
|
||||
// fp=50000, m=5, pad one '0' before "50000").
|
||||
let z: i32 = 6 - m;
|
||||
for (z > 0) { buf[out] = 48u8; out += 1; z -= 1; };
|
||||
k = 0;
|
||||
for (k < m) { buf[out] = ftmp[k]; out += 1; k += 1; };
|
||||
// Trim trailing zeros in the fractional part (we know fp != 0,
|
||||
// so the loop stops before erasing the dot).
|
||||
for (out > 0) {
|
||||
if (buf[out - 1] != 48u8) { break; };
|
||||
out -= 1;
|
||||
for (k < intstr.len) { tmp[out] = intstr.ptr[k]; out += 1; k += 1; };
|
||||
os.free(intstr.ptr: *void, intstr.len: u64);
|
||||
if (fp != 0) {
|
||||
tmp[out] = 46u8; // '.'
|
||||
out += 1;
|
||||
let fracstr: str = u64tos(fp: u64, DEC);
|
||||
// Pad fractional to 6 digits with leading zeros (e.g. 0.05 →
|
||||
// fp=50000, fracstr="50000", pad one '0' before).
|
||||
let z: i32 = 6 - fracstr.len;
|
||||
for (z > 0) { tmp[out] = 48u8; out += 1; z -= 1; };
|
||||
k = 0;
|
||||
for (k < fracstr.len) { tmp[out] = fracstr.ptr[k]; out += 1; k += 1; };
|
||||
os.free(fracstr.ptr: *void, fracstr.len: u64);
|
||||
// Trim trailing zeros in the fractional part.
|
||||
for (out > 0) {
|
||||
if (tmp[out - 1] != 48u8) { break; };
|
||||
out -= 1;
|
||||
};
|
||||
};
|
||||
return out;
|
||||
let buf: *u8 = os.alloc(out: u64): *u8;
|
||||
let q: i32 = 0;
|
||||
for (q < out) { buf[q] = tmp[q]; q += 1; };
|
||||
let r: str;
|
||||
r.ptr = buf;
|
||||
r.len = out;
|
||||
return r;
|
||||
};
|
||||
|
||||
// strerror — Hare has strconv::strerror; ww doesn't ship it yet
|
||||
// because a `match (e) { case invalid => ... }` arm over the wider
|
||||
// `error = !(invalid | overflow)` union exposes a cstage-vs-wwstage
|
||||
// cgen divergence (one cgen spills the unused payload slot, the
|
||||
// other elides it). Restore once the cgens converge.
|
||||
|
||||
// MODULE: lex
|
||||
// lib/ww/lex/tok.ww — port of cmd/wcc/tok.c plus the Tkind /
|
||||
// Tok / Pos shapes from cmd/wcc/ww.h.
|
||||
@@ -910,12 +1081,11 @@ export fn tokprint(fd: i32, t: *tok) void = {
|
||||
fputsstr(fd, "<none>");
|
||||
};
|
||||
fputcbyte(fd, 58u8); // ':'
|
||||
let buf: [32]u8;
|
||||
let n: i32 = strconv.i64tos(buf[0:32], t.line: i64);
|
||||
os.write(fd, buf.ptr, n: u64);
|
||||
let ls: str = strconv.i64tos(t.line: i64, strconv.DEC);
|
||||
os.write(fd, ls.ptr, ls.len: u64);
|
||||
fputcbyte(fd, 58u8);
|
||||
n = strconv.i64tos(buf[0:32], t.col: i64);
|
||||
os.write(fd, buf.ptr, n: u64);
|
||||
let cs: str = strconv.i64tos(t.col: i64, strconv.DEC);
|
||||
os.write(fd, cs.ptr, cs.len: u64);
|
||||
fputcbyte(fd, 32u8); // ' '
|
||||
fputsstr(fd, tokname(t.kind));
|
||||
|
||||
@@ -930,12 +1100,12 @@ export fn tokprint(fd: i32, t: *tok) void = {
|
||||
fputq(fd, ttext.ptr, ttext.len);
|
||||
} else { if (t.kind == tkind.TK_INT) {
|
||||
fputcbyte(fd, 32u8);
|
||||
n = strconv.u64tos(buf[0:32], t.uval);
|
||||
os.write(fd, buf.ptr, n: u64);
|
||||
let us: str = strconv.u64tos(t.uval, strconv.DEC);
|
||||
os.write(fd, us.ptr, us.len: u64);
|
||||
} else { if (t.kind == tkind.TK_RUNE) {
|
||||
fputcbyte(fd, 32u8);
|
||||
n = strconv.u64tos(buf[0:32], t.uval);
|
||||
os.write(fd, buf.ptr, n: u64);
|
||||
let us: str = strconv.u64tos(t.uval, strconv.DEC);
|
||||
os.write(fd, us.ptr, us.len: u64);
|
||||
};};};};};
|
||||
// tkind.TK_FLOAT is intentionally not handled here — %g formatting
|
||||
// won't byte-match across implementations. Diff fixtures must
|
||||
@@ -2107,14 +2277,12 @@ fn pr(fd: i32, n: *node, d: i32) void = {
|
||||
|
||||
if (n.kind == nkind.N_INTLIT) {
|
||||
putc1(fd, 32u8);
|
||||
let buf: [32]u8;
|
||||
let m: i32 = strconv.u64tos(buf[0:32], n.uval);
|
||||
os.write(fd, buf.ptr, m: u64);
|
||||
let s: str = strconv.u64tos(n.uval, strconv.DEC);
|
||||
os.write(fd, s.ptr, s.len: u64);
|
||||
} else { if (n.kind == nkind.N_RUNELIT) {
|
||||
putc1(fd, 32u8);
|
||||
let buf: [32]u8;
|
||||
let m: i32 = strconv.u64tos(buf[0:32], n.uval);
|
||||
os.write(fd, buf.ptr, m: u64);
|
||||
let s: str = strconv.u64tos(n.uval, strconv.DEC);
|
||||
os.write(fd, s.ptr, s.len: u64);
|
||||
} else { if (
|
||||
n.kind == nkind.N_STRLIT ||
|
||||
n.kind == nkind.N_IDENT ||
|
||||
@@ -11427,15 +11595,13 @@ fn localfind(c: *cgen, name: str) i32 = {
|
||||
fn emitline(s: str) void = { os.write(1, s.ptr, s.len: u64); };
|
||||
|
||||
fn emitint(v: i64) void = {
|
||||
let buf: [32]u8;
|
||||
let n: i32 = strconv.i64tos(buf[0:32], v);
|
||||
os.write(1, buf.ptr, n: u64);
|
||||
let s: str = strconv.i64tos(v, strconv.DEC);
|
||||
os.write(1, s.ptr, s.len: u64);
|
||||
};
|
||||
|
||||
fn emituint(v: u64) void = {
|
||||
let buf: [32]u8;
|
||||
let n: i32 = strconv.u64tos(buf[0:32], v);
|
||||
os.write(1, buf.ptr, n: u64);
|
||||
let s: str = strconv.u64tos(v, strconv.DEC);
|
||||
os.write(1, s.ptr, s.len: u64);
|
||||
};
|
||||
|
||||
// emitdispreg — print "disp(reg)" or "(reg)" when disp == 0, the
|
||||
@@ -11454,9 +11620,9 @@ fn emitoff(v: i64) void = {
|
||||
if (v != 0i64) { emitint(v); };
|
||||
};
|
||||
|
||||
// mklabel — fresh label "<fnname>_<base>_<seq>". Returns an
|
||||
// mklabel — fresh label "<fnname>_<prefix>_<seq>". Returns an
|
||||
// arena-owned str. Mirrors C cgen's mklabel so diffs match.
|
||||
fn mklabel(c: *cgen, base: str) str = {
|
||||
fn mklabel(c: *cgen, prefix: str) str = {
|
||||
let buf: [128]u8;
|
||||
let i: i32 = 0;
|
||||
let fname: str = c.fnname;
|
||||
@@ -11467,12 +11633,15 @@ fn mklabel(c: *cgen, base: str) str = {
|
||||
};
|
||||
buf[i] = 95u8; i += 1; // '_'
|
||||
j = 0;
|
||||
for (j < base.len) {
|
||||
buf[i] = base[j];
|
||||
for (j < prefix.len) {
|
||||
buf[i] = prefix[j];
|
||||
i += 1; j += 1;
|
||||
};
|
||||
buf[i] = 95u8; i += 1; // '_'
|
||||
let n: i32 = strconv.i64tos(buf[i:128], c.labelseq: i64);
|
||||
let ns: str = strconv.i64tos(c.labelseq: i64, strconv.DEC);
|
||||
let n: i32 = ns.len;
|
||||
let dk: i32 = 0;
|
||||
for (dk < n) { buf[i + dk] = ns.ptr[dk]; dk += 1; };
|
||||
c.labelseq += 1;
|
||||
let total: i32 = i + n;
|
||||
let p: *u8 = amalloc(c.a, (total: u64) + 1u64): *u8;
|
||||
@@ -11493,22 +11662,25 @@ fn emitlabel(s: str) void = {
|
||||
emitline(":\n");
|
||||
};
|
||||
|
||||
// mkscratchname — fresh local-slot name ".<base>_<labelseq>". Used for
|
||||
// mkscratchname — fresh local-slot name ".<prefix>_<labelseq>". Used for
|
||||
// compiler-synthesised slots (switch scrutinee, forrange index/len)
|
||||
// that need to be unique per use site but are never referenced by user
|
||||
// code. Increments labelseq so the same source position lines up with
|
||||
// C cgen's labelseq stream.
|
||||
fn mkscratchname(c: *cgen, base: str) str = {
|
||||
fn mkscratchname(c: *cgen, prefix: str) str = {
|
||||
let buf: [128]u8;
|
||||
let i: i32 = 0;
|
||||
buf[i] = 46u8; i += 1; // '.'
|
||||
let j: i32 = 0;
|
||||
for (j < base.len) {
|
||||
buf[i] = base[j];
|
||||
for (j < prefix.len) {
|
||||
buf[i] = prefix[j];
|
||||
i += 1; j += 1;
|
||||
};
|
||||
buf[i] = 95u8; i += 1; // '_'
|
||||
let n: i32 = strconv.i64tos(buf[i:128], c.labelseq: i64);
|
||||
let ns: str = strconv.i64tos(c.labelseq: i64, strconv.DEC);
|
||||
let n: i32 = ns.len;
|
||||
let dk: i32 = 0;
|
||||
for (dk < n) { buf[i + dk] = ns.ptr[dk]; dk += 1; };
|
||||
c.labelseq += 1;
|
||||
let total: i32 = i + n;
|
||||
let p: *u8 = amalloc(c.a, (total: u64) + 1u64): *u8;
|
||||
@@ -11542,7 +11714,10 @@ fn internstrlit(c: *cgen, bytes: str) str = {
|
||||
// New label "_S_<seq>".
|
||||
let buf: [32]u8;
|
||||
buf[0] = 95u8; buf[1] = 83u8; buf[2] = 95u8; // "_S_"
|
||||
let n: i32 = strconv.i64tos(buf[3:32], c.strlitseq: i64);
|
||||
let ns: str = strconv.i64tos(c.strlitseq: i64, strconv.DEC);
|
||||
let n: i32 = ns.len;
|
||||
let dk: i32 = 0;
|
||||
for (dk < n) { buf[3 + dk] = ns.ptr[dk]; dk += 1; };
|
||||
c.strlitseq += 1;
|
||||
let total: i32 = 3 + n;
|
||||
let p: *u8 = amalloc(c.a, (total: u64) + 1u64): *u8;
|
||||
|
||||
@@ -472,15 +472,13 @@ fn localfind(c: *cgen, name: str) i32 = {
|
||||
fn emitline(s: str) void = { os.write(1, s.ptr, s.len: u64); };
|
||||
|
||||
fn emitint(v: i64) void = {
|
||||
let buf: [32]u8;
|
||||
let n: i32 = strconv.i64tos(buf[0:32], v);
|
||||
os.write(1, buf.ptr, n: u64);
|
||||
let s: str = strconv.i64tos(v, strconv.DEC);
|
||||
os.write(1, s.ptr, s.len: u64);
|
||||
};
|
||||
|
||||
fn emituint(v: u64) void = {
|
||||
let buf: [32]u8;
|
||||
let n: i32 = strconv.u64tos(buf[0:32], v);
|
||||
os.write(1, buf.ptr, n: u64);
|
||||
let s: str = strconv.u64tos(v, strconv.DEC);
|
||||
os.write(1, s.ptr, s.len: u64);
|
||||
};
|
||||
|
||||
// emitdispreg — print "disp(reg)" or "(reg)" when disp == 0, the
|
||||
@@ -499,9 +497,9 @@ fn emitoff(v: i64) void = {
|
||||
if (v != 0i64) { emitint(v); };
|
||||
};
|
||||
|
||||
// mklabel — fresh label "<fnname>_<base>_<seq>". Returns an
|
||||
// mklabel — fresh label "<fnname>_<prefix>_<seq>". Returns an
|
||||
// arena-owned str. Mirrors C cgen's mklabel so diffs match.
|
||||
fn mklabel(c: *cgen, base: str) str = {
|
||||
fn mklabel(c: *cgen, prefix: str) str = {
|
||||
let buf: [128]u8;
|
||||
let i: i32 = 0;
|
||||
let fname: str = c.fnname;
|
||||
@@ -512,12 +510,15 @@ fn mklabel(c: *cgen, base: str) str = {
|
||||
};
|
||||
buf[i] = 95u8; i += 1; // '_'
|
||||
j = 0;
|
||||
for (j < base.len) {
|
||||
buf[i] = base[j];
|
||||
for (j < prefix.len) {
|
||||
buf[i] = prefix[j];
|
||||
i += 1; j += 1;
|
||||
};
|
||||
buf[i] = 95u8; i += 1; // '_'
|
||||
let n: i32 = strconv.i64tos(buf[i:128], c.labelseq: i64);
|
||||
let ns: str = strconv.i64tos(c.labelseq: i64, strconv.DEC);
|
||||
let n: i32 = ns.len;
|
||||
let dk: i32 = 0;
|
||||
for (dk < n) { buf[i + dk] = ns.ptr[dk]; dk += 1; };
|
||||
c.labelseq += 1;
|
||||
let total: i32 = i + n;
|
||||
let p: *u8 = amalloc(c.a, (total: u64) + 1u64): *u8;
|
||||
@@ -538,22 +539,25 @@ fn emitlabel(s: str) void = {
|
||||
emitline(":\n");
|
||||
};
|
||||
|
||||
// mkscratchname — fresh local-slot name ".<base>_<labelseq>". Used for
|
||||
// mkscratchname — fresh local-slot name ".<prefix>_<labelseq>". Used for
|
||||
// compiler-synthesised slots (switch scrutinee, forrange index/len)
|
||||
// that need to be unique per use site but are never referenced by user
|
||||
// code. Increments labelseq so the same source position lines up with
|
||||
// C cgen's labelseq stream.
|
||||
fn mkscratchname(c: *cgen, base: str) str = {
|
||||
fn mkscratchname(c: *cgen, prefix: str) str = {
|
||||
let buf: [128]u8;
|
||||
let i: i32 = 0;
|
||||
buf[i] = 46u8; i += 1; // '.'
|
||||
let j: i32 = 0;
|
||||
for (j < base.len) {
|
||||
buf[i] = base[j];
|
||||
for (j < prefix.len) {
|
||||
buf[i] = prefix[j];
|
||||
i += 1; j += 1;
|
||||
};
|
||||
buf[i] = 95u8; i += 1; // '_'
|
||||
let n: i32 = strconv.i64tos(buf[i:128], c.labelseq: i64);
|
||||
let ns: str = strconv.i64tos(c.labelseq: i64, strconv.DEC);
|
||||
let n: i32 = ns.len;
|
||||
let dk: i32 = 0;
|
||||
for (dk < n) { buf[i + dk] = ns.ptr[dk]; dk += 1; };
|
||||
c.labelseq += 1;
|
||||
let total: i32 = i + n;
|
||||
let p: *u8 = amalloc(c.a, (total: u64) + 1u64): *u8;
|
||||
@@ -587,7 +591,10 @@ fn internstrlit(c: *cgen, bytes: str) str = {
|
||||
// New label "_S_<seq>".
|
||||
let buf: [32]u8;
|
||||
buf[0] = 95u8; buf[1] = 83u8; buf[2] = 95u8; // "_S_"
|
||||
let n: i32 = strconv.i64tos(buf[3:32], c.strlitseq: i64);
|
||||
let ns: str = strconv.i64tos(c.strlitseq: i64, strconv.DEC);
|
||||
let n: i32 = ns.len;
|
||||
let dk: i32 = 0;
|
||||
for (dk < n) { buf[3 + dk] = ns.ptr[dk]; dk += 1; };
|
||||
c.strlitseq += 1;
|
||||
let total: i32 = 3 + n;
|
||||
let p: *u8 = amalloc(c.a, (total: u64) + 1u64): *u8;
|
||||
|
||||
@@ -336,117 +336,273 @@ export fn freearena(a: *arena) void = {
|
||||
};
|
||||
|
||||
// MODULE: strconv
|
||||
// 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).
|
||||
// strconv — number↔string conversions.
|
||||
//
|
||||
// Mirrors Hare's strconv:: surface. The *tos functions return a fresh
|
||||
// owned `str`; release via os.free(r.ptr, r.len: u64) when done.
|
||||
// Hare returns `const str` into a static buffer; ww allocates per
|
||||
// call because the wwstage cgen doesn't currently support mutating a
|
||||
// module-level `*u8` (so a lazy-init shared buffer isn't expressible
|
||||
// today). Graduate to the static-buffer shape once that lands.
|
||||
|
||||
use os;
|
||||
|
||||
// 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).
|
||||
// Payload is the byte index of the first offending position.
|
||||
// Mirrors Hare's strconv::invalid = !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.
|
||||
// overflow — input was valid but doesn't fit the target type.
|
||||
// Mirrors Hare's strconv::overflow = !void.
|
||||
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;
|
||||
// error — any error from a strconv call. Mirrors Hare's strconv::error.
|
||||
export type error = !(invalid | overflow);
|
||||
|
||||
// base — numeric base for parsing/formatting. Plain i32 (not a named
|
||||
// enum) because cross-module `strconv.base.DEC` chains miscompile in
|
||||
// the cstage cgen — it emits a memory load through `base(SB)` rather
|
||||
// than inlining the enum value. Hare names them as `strconv::base`
|
||||
// enum values; we expose them as module-level `def`s so callers say
|
||||
// `strconv.DEC` and the cgen inlines the immediate.
|
||||
//
|
||||
// HEX is HEX_UPPER; HEX_LOWER is a separate pseudo-base that produces
|
||||
// lowercase a-f digits.
|
||||
export def DEFAULT: i32 = 0;
|
||||
export def BIN: i32 = 2;
|
||||
export def OCT: i32 = 8;
|
||||
export def DEC: i32 = 10;
|
||||
export def HEX_UPPER: i32 = 16;
|
||||
export def HEX: i32 = 16;
|
||||
export def HEX_LOWER: i32 = 17;
|
||||
|
||||
fn basenum(b: i32) i64 = {
|
||||
if (b == BIN) { return 2; };
|
||||
if (b == OCT) { return 8; };
|
||||
if (b == HEX) { return 16; };
|
||||
if (b == HEX_UPPER) { return 16; };
|
||||
if (b == HEX_LOWER) { return 16; };
|
||||
return 10; // DEC and DEFAULT
|
||||
};
|
||||
|
||||
fn basedigit(d: i64, b: i32) u8 = {
|
||||
if (d < 10) { return (d + 48): u8; };
|
||||
let off: i64 = d - 10;
|
||||
if (b == HEX_LOWER) { return (off + 97): u8; };
|
||||
return (off + 65): u8;
|
||||
};
|
||||
|
||||
// u64tos — convert v to a base-b numeric string. Returns owned str;
|
||||
// release via os.free(r.ptr, r.len: u64). Mirrors Hare's
|
||||
// strconv::u64tos (Hare returns const str into a static buffer).
|
||||
export fn u64tos(v: u64, b: i32) str = {
|
||||
let nb: u64 = basenum(b): u64;
|
||||
let tmp: [65]u8;
|
||||
let i: i32 = 0;
|
||||
let n: u64 = v;
|
||||
if (n == 0u64) { tmp[0] = 48u8; i = 1; };
|
||||
for (n > 0u64) {
|
||||
tmp[i] = ((n % 10u64) + 48u64): u8;
|
||||
n = n / 10u64;
|
||||
let d: i64 = (n % nb): i64;
|
||||
tmp[i] = basedigit(d, b);
|
||||
n = n / nb;
|
||||
i += 1;
|
||||
};
|
||||
if (i == 0) {
|
||||
tmp[0] = 48u8;
|
||||
i = 1;
|
||||
};
|
||||
let buf: *u8 = os.alloc(i: u64): *u8;
|
||||
let out: i32 = 0;
|
||||
for (i > 0) {
|
||||
i -= 1;
|
||||
buf[out] = tmp[i];
|
||||
out += 1;
|
||||
};
|
||||
return out;
|
||||
let r: str;
|
||||
r.ptr = buf;
|
||||
r.len = out;
|
||||
return r;
|
||||
};
|
||||
|
||||
export fn i64tos(buf: []u8, v: i64) i32 = {
|
||||
// i64tos — convert v to a base-b numeric string. Returns owned str;
|
||||
// release via os.free. Mirrors Hare's strconv::i64tos.
|
||||
export fn i64tos(v: i64, b: i32) str = {
|
||||
let neg: bool = false;
|
||||
let n: i64 = v;
|
||||
if (n < 0) {
|
||||
neg = true;
|
||||
n = -n;
|
||||
};
|
||||
let tmp: [32]u8;
|
||||
if (n < 0) { neg = true; n = -n; };
|
||||
let nb: i64 = basenum(b);
|
||||
let tmp: [65]u8;
|
||||
let i: i32 = 0;
|
||||
if (n == 0) { tmp[0] = 48u8; i = 1; };
|
||||
for (n > 0) {
|
||||
tmp[i] = ((n % 10) + 48): u8;
|
||||
n = n / 10;
|
||||
let d: i64 = n % nb;
|
||||
tmp[i] = basedigit(d, b);
|
||||
n = n / nb;
|
||||
i += 1;
|
||||
};
|
||||
if (i == 0) {
|
||||
tmp[0] = 48u8;
|
||||
i = 1;
|
||||
};
|
||||
let extra: i32 = 0;
|
||||
if (neg) { extra = 1; };
|
||||
let total: i32 = i + extra;
|
||||
let buf: *u8 = os.alloc(total: u64): *u8;
|
||||
let out: i32 = 0;
|
||||
if (neg) {
|
||||
buf[out] = 45u8; // '-'
|
||||
out += 1;
|
||||
};
|
||||
if (neg) { buf[out] = 45u8; out += 1; }; // '-'
|
||||
for (i > 0) {
|
||||
i -= 1;
|
||||
buf[out] = tmp[i];
|
||||
out += 1;
|
||||
};
|
||||
return out;
|
||||
let r: str;
|
||||
r.ptr = buf;
|
||||
r.len = out;
|
||||
return r;
|
||||
};
|
||||
|
||||
// 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) = {
|
||||
export fn i32tos(v: i32, b: i32) str = { return i64tos(v: i64, b); };
|
||||
export fn i16tos(v: i16, b: i32) str = { return i64tos(v: i64, b); };
|
||||
export fn i8tos(v: i8, b: i32) str = { return i64tos(v: i64, b); };
|
||||
|
||||
export fn u32tos(v: u32, b: i32) str = { return u64tos(v: u64, b); };
|
||||
export fn u16tos(v: u16, b: i32) str = { return u64tos(v: u64, b); };
|
||||
export fn u8tos(v: u8, b: i32) 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: i32) i32 = {
|
||||
if (c >= 48u8) { if (c <= 57u8) { return (c - 48u8): i32; }; };
|
||||
if (b == HEX_LOWER) {
|
||||
if (c >= 97u8) { if (c <= 102u8) { return ((c - 97u8) + 10u8): i32; }; };
|
||||
return -1;
|
||||
};
|
||||
if (c >= 65u8) { if (c <= 70u8) { return ((c - 65u8) + 10u8): i32; }; };
|
||||
if (c >= 97u8) { if (c <= 102u8) { return ((c - 97u8) + 10u8): i32; }; };
|
||||
return -1;
|
||||
};
|
||||
|
||||
// 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: i32) (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];
|
||||
if (c < 48u8) { return i: invalid; };
|
||||
if (c > 57u8) { return i: invalid; };
|
||||
v = v * 10 + ((c: i64) - 48);
|
||||
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 — fallible unsigned decimal parser. No leading sign.
|
||||
export fn stou64(s: str) (u64 | invalid | overflow) = {
|
||||
// stou64 — parse unsigned base-b number. Mirrors Hare's strconv::stou64.
|
||||
export fn stou64(s: str, b: i32) (u64 | invalid | overflow) = {
|
||||
if (s.len == 0) { return 0: invalid; };
|
||||
let nb: u64 = basenum(b): u64;
|
||||
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);
|
||||
let d: i32 = digval(c, b);
|
||||
if (d < 0) { return i: invalid; };
|
||||
if ((d: u64) >= nb) { return i: invalid; };
|
||||
v = v * nb + (d: u64);
|
||||
i += 1;
|
||||
};
|
||||
return v;
|
||||
};
|
||||
|
||||
// f64tos — write `v` in decimal into `buf` and return the byte count.
|
||||
// Hare name; this is the buffer-in Plan 9 subset of Hare's
|
||||
// `f64tos(n) const str`. Today's surface:
|
||||
export fn stoi32(s: str, b: i32) (i32 | invalid | overflow) = {
|
||||
let r = stoi64(s, b);
|
||||
match (r) {
|
||||
case let v: i64 => {
|
||||
if (v > 2147483647i64) { return overflow{}; };
|
||||
if (v < -2147483648i64) { return overflow{}; };
|
||||
return v: i32;
|
||||
};
|
||||
case let e: invalid => return e;
|
||||
case let e: overflow => return e;
|
||||
};
|
||||
return 0: invalid; // unreachable; appeases the path-cov checker
|
||||
};
|
||||
|
||||
export fn stoi16(s: str, b: i32) (i16 | invalid | overflow) = {
|
||||
let r = stoi64(s, b);
|
||||
match (r) {
|
||||
case let v: i64 => {
|
||||
if (v > 32767i64) { return overflow{}; };
|
||||
if (v < -32768i64) { return overflow{}; };
|
||||
return v: i16;
|
||||
};
|
||||
case let e: invalid => return e;
|
||||
case let e: overflow => return e;
|
||||
};
|
||||
return 0: invalid;
|
||||
};
|
||||
|
||||
export fn stoi8(s: str, b: i32) (i8 | invalid | overflow) = {
|
||||
let r = stoi64(s, b);
|
||||
match (r) {
|
||||
case let v: i64 => {
|
||||
if (v > 127i64) { return overflow{}; };
|
||||
if (v < -128i64) { return overflow{}; };
|
||||
return v: i8;
|
||||
};
|
||||
case let e: invalid => return e;
|
||||
case let e: overflow => return e;
|
||||
};
|
||||
return 0: invalid;
|
||||
};
|
||||
|
||||
export fn stou32(s: str, b: i32) (u32 | invalid | overflow) = {
|
||||
let r = stou64(s, b);
|
||||
match (r) {
|
||||
case let v: u64 => {
|
||||
if (v > 4294967295u64) { return overflow{}; };
|
||||
return v: u32;
|
||||
};
|
||||
case let e: invalid => return e;
|
||||
case let e: overflow => return e;
|
||||
};
|
||||
return 0: invalid;
|
||||
};
|
||||
|
||||
export fn stou16(s: str, b: i32) (u16 | invalid | overflow) = {
|
||||
let r = stou64(s, b);
|
||||
match (r) {
|
||||
case let v: u64 => {
|
||||
if (v > 65535u64) { return overflow{}; };
|
||||
return v: u16;
|
||||
};
|
||||
case let e: invalid => return e;
|
||||
case let e: overflow => return e;
|
||||
};
|
||||
return 0: invalid;
|
||||
};
|
||||
|
||||
export fn stou8(s: str, b: i32) (u8 | invalid | overflow) = {
|
||||
let r = stou64(s, b);
|
||||
match (r) {
|
||||
case let v: u64 => {
|
||||
if (v > 255u64) { return overflow{}; };
|
||||
return v: u8;
|
||||
};
|
||||
case let e: invalid => return e;
|
||||
case let e: overflow => return e;
|
||||
};
|
||||
return 0: invalid;
|
||||
};
|
||||
|
||||
// f64tos — convert v to a decimal string. Returns owned str; release
|
||||
// via os.free. Mirrors Hare's strconv::f64tos (current ww impl is
|
||||
// fixed-point only, max 6 fractional digits, no NaN/Inf support —
|
||||
// see graduate-to-Ryū note below).
|
||||
//
|
||||
// Surface:
|
||||
//
|
||||
// - finite values only. NaN/±Inf detection needs an f64→u64 bit
|
||||
// reinterpret cast that the cgen doesn't expose yet.
|
||||
@@ -457,23 +613,20 @@ export fn stou64(s: str) (u64 | invalid | overflow) = {
|
||||
// in scientific notation via Ryū; we will graduate when the
|
||||
// compiler grows the bit-reinterpret cast.
|
||||
//
|
||||
// Round-trip is therefore lossy past 6 fractional digits; callers
|
||||
// that need bit-exact recovery should not use this until the
|
||||
// graduate-to-Ryū step lands. `f64tos(buf, 1.0)` writes "1" (no
|
||||
// decimal point), `f64tos(buf, 1.5)` writes "1.5", `f64tos(buf,
|
||||
// 0.1)` writes "0.1".
|
||||
// Round-trip is therefore lossy past 6 fractional digits.
|
||||
//
|
||||
// No float literals in the body — 990's wwdump diff requires this
|
||||
// file's TK_FLOAT count to match between C and ww front-ends, and
|
||||
// the ww-side wwdump currently skips TK_FLOAT.fval while the C side
|
||||
// %g-formats it. Same trick lib/ww/lex/lex.ww's parsef64 uses:
|
||||
// build f64 constants via int-to-f64 casts.
|
||||
export fn f64tos(buf: []u8, v: f64) i32 = {
|
||||
export fn f64tos(v: f64) str = {
|
||||
let tmp: [64]u8;
|
||||
let out: i32 = 0;
|
||||
let f: f64 = v;
|
||||
let zero: f64 = 0: f64;
|
||||
if (f < zero) {
|
||||
buf[out] = 45u8; // '-'
|
||||
tmp[out] = 45u8; // '-'
|
||||
out += 1;
|
||||
f = -f;
|
||||
};
|
||||
@@ -483,8 +636,14 @@ export fn f64tos(buf: []u8, v: f64) i32 = {
|
||||
if (f >= cap) {
|
||||
let s: str = "huge";
|
||||
let k: i32 = 0;
|
||||
for (k < s.len) { buf[out] = s[k]; out += 1; k += 1; };
|
||||
return out;
|
||||
for (k < s.len) { tmp[out] = s[k]; out += 1; k += 1; };
|
||||
let buf: *u8 = os.alloc(out: u64): *u8;
|
||||
let q: i32 = 0;
|
||||
for (q < out) { buf[q] = tmp[q]; q += 1; };
|
||||
let r: str;
|
||||
r.ptr = buf;
|
||||
r.len = out;
|
||||
return r;
|
||||
};
|
||||
let ip: i64 = f: i64;
|
||||
// Fractional part scaled to 6 decimal digits, with round-to-
|
||||
@@ -501,30 +660,42 @@ export fn f64tos(buf: []u8, v: f64) i32 = {
|
||||
ip += 1;
|
||||
fp = 0;
|
||||
};
|
||||
let itmp: [32]u8;
|
||||
let in: i32 = i64tos(itmp[0:32], ip);
|
||||
let intstr: str = i64tos(ip, DEC);
|
||||
let k: i32 = 0;
|
||||
for (k < in) { buf[out] = itmp[k]; out += 1; k += 1; };
|
||||
if (fp == 0) { return out; };
|
||||
buf[out] = 46u8; // '.'
|
||||
out += 1;
|
||||
let ftmp: [16]u8;
|
||||
let m: i32 = u64tos(ftmp[0:16], fp: u64);
|
||||
// Pad fractional to 6 digits with leading zeros (e.g. 0.05 →
|
||||
// fp=50000, m=5, pad one '0' before "50000").
|
||||
let z: i32 = 6 - m;
|
||||
for (z > 0) { buf[out] = 48u8; out += 1; z -= 1; };
|
||||
k = 0;
|
||||
for (k < m) { buf[out] = ftmp[k]; out += 1; k += 1; };
|
||||
// Trim trailing zeros in the fractional part (we know fp != 0,
|
||||
// so the loop stops before erasing the dot).
|
||||
for (out > 0) {
|
||||
if (buf[out - 1] != 48u8) { break; };
|
||||
out -= 1;
|
||||
for (k < intstr.len) { tmp[out] = intstr.ptr[k]; out += 1; k += 1; };
|
||||
os.free(intstr.ptr: *void, intstr.len: u64);
|
||||
if (fp != 0) {
|
||||
tmp[out] = 46u8; // '.'
|
||||
out += 1;
|
||||
let fracstr: str = u64tos(fp: u64, DEC);
|
||||
// Pad fractional to 6 digits with leading zeros (e.g. 0.05 →
|
||||
// fp=50000, fracstr="50000", pad one '0' before).
|
||||
let z: i32 = 6 - fracstr.len;
|
||||
for (z > 0) { tmp[out] = 48u8; out += 1; z -= 1; };
|
||||
k = 0;
|
||||
for (k < fracstr.len) { tmp[out] = fracstr.ptr[k]; out += 1; k += 1; };
|
||||
os.free(fracstr.ptr: *void, fracstr.len: u64);
|
||||
// Trim trailing zeros in the fractional part.
|
||||
for (out > 0) {
|
||||
if (tmp[out - 1] != 48u8) { break; };
|
||||
out -= 1;
|
||||
};
|
||||
};
|
||||
return out;
|
||||
let buf: *u8 = os.alloc(out: u64): *u8;
|
||||
let q: i32 = 0;
|
||||
for (q < out) { buf[q] = tmp[q]; q += 1; };
|
||||
let r: str;
|
||||
r.ptr = buf;
|
||||
r.len = out;
|
||||
return r;
|
||||
};
|
||||
|
||||
// strerror — Hare has strconv::strerror; ww doesn't ship it yet
|
||||
// because a `match (e) { case invalid => ... }` arm over the wider
|
||||
// `error = !(invalid | overflow)` union exposes a cstage-vs-wwstage
|
||||
// cgen divergence (one cgen spills the unused payload slot, the
|
||||
// other elides it). Restore once the cgens converge.
|
||||
|
||||
// MODULE: lex
|
||||
// lib/ww/lex/tok.ww — port of cmd/wcc/tok.c plus the Tkind /
|
||||
// Tok / Pos shapes from cmd/wcc/ww.h.
|
||||
@@ -910,12 +1081,11 @@ export fn tokprint(fd: i32, t: *tok) void = {
|
||||
fputsstr(fd, "<none>");
|
||||
};
|
||||
fputcbyte(fd, 58u8); // ':'
|
||||
let buf: [32]u8;
|
||||
let n: i32 = strconv.i64tos(buf[0:32], t.line: i64);
|
||||
os.write(fd, buf.ptr, n: u64);
|
||||
let ls: str = strconv.i64tos(t.line: i64, strconv.DEC);
|
||||
os.write(fd, ls.ptr, ls.len: u64);
|
||||
fputcbyte(fd, 58u8);
|
||||
n = strconv.i64tos(buf[0:32], t.col: i64);
|
||||
os.write(fd, buf.ptr, n: u64);
|
||||
let cs: str = strconv.i64tos(t.col: i64, strconv.DEC);
|
||||
os.write(fd, cs.ptr, cs.len: u64);
|
||||
fputcbyte(fd, 32u8); // ' '
|
||||
fputsstr(fd, tokname(t.kind));
|
||||
|
||||
@@ -930,12 +1100,12 @@ export fn tokprint(fd: i32, t: *tok) void = {
|
||||
fputq(fd, ttext.ptr, ttext.len);
|
||||
} else { if (t.kind == tkind.TK_INT) {
|
||||
fputcbyte(fd, 32u8);
|
||||
n = strconv.u64tos(buf[0:32], t.uval);
|
||||
os.write(fd, buf.ptr, n: u64);
|
||||
let us: str = strconv.u64tos(t.uval, strconv.DEC);
|
||||
os.write(fd, us.ptr, us.len: u64);
|
||||
} else { if (t.kind == tkind.TK_RUNE) {
|
||||
fputcbyte(fd, 32u8);
|
||||
n = strconv.u64tos(buf[0:32], t.uval);
|
||||
os.write(fd, buf.ptr, n: u64);
|
||||
let us: str = strconv.u64tos(t.uval, strconv.DEC);
|
||||
os.write(fd, us.ptr, us.len: u64);
|
||||
};};};};};
|
||||
// tkind.TK_FLOAT is intentionally not handled here — %g formatting
|
||||
// won't byte-match across implementations. Diff fixtures must
|
||||
@@ -2107,14 +2277,12 @@ fn pr(fd: i32, n: *node, d: i32) void = {
|
||||
|
||||
if (n.kind == nkind.N_INTLIT) {
|
||||
putc1(fd, 32u8);
|
||||
let buf: [32]u8;
|
||||
let m: i32 = strconv.u64tos(buf[0:32], n.uval);
|
||||
os.write(fd, buf.ptr, m: u64);
|
||||
let s: str = strconv.u64tos(n.uval, strconv.DEC);
|
||||
os.write(fd, s.ptr, s.len: u64);
|
||||
} else { if (n.kind == nkind.N_RUNELIT) {
|
||||
putc1(fd, 32u8);
|
||||
let buf: [32]u8;
|
||||
let m: i32 = strconv.u64tos(buf[0:32], n.uval);
|
||||
os.write(fd, buf.ptr, m: u64);
|
||||
let s: str = strconv.u64tos(n.uval, strconv.DEC);
|
||||
os.write(fd, s.ptr, s.len: u64);
|
||||
} else { if (
|
||||
n.kind == nkind.N_STRLIT ||
|
||||
n.kind == nkind.N_IDENT ||
|
||||
@@ -11427,15 +11595,13 @@ fn localfind(c: *cgen, name: str) i32 = {
|
||||
fn emitline(s: str) void = { os.write(1, s.ptr, s.len: u64); };
|
||||
|
||||
fn emitint(v: i64) void = {
|
||||
let buf: [32]u8;
|
||||
let n: i32 = strconv.i64tos(buf[0:32], v);
|
||||
os.write(1, buf.ptr, n: u64);
|
||||
let s: str = strconv.i64tos(v, strconv.DEC);
|
||||
os.write(1, s.ptr, s.len: u64);
|
||||
};
|
||||
|
||||
fn emituint(v: u64) void = {
|
||||
let buf: [32]u8;
|
||||
let n: i32 = strconv.u64tos(buf[0:32], v);
|
||||
os.write(1, buf.ptr, n: u64);
|
||||
let s: str = strconv.u64tos(v, strconv.DEC);
|
||||
os.write(1, s.ptr, s.len: u64);
|
||||
};
|
||||
|
||||
// emitdispreg — print "disp(reg)" or "(reg)" when disp == 0, the
|
||||
@@ -11454,9 +11620,9 @@ fn emitoff(v: i64) void = {
|
||||
if (v != 0i64) { emitint(v); };
|
||||
};
|
||||
|
||||
// mklabel — fresh label "<fnname>_<base>_<seq>". Returns an
|
||||
// mklabel — fresh label "<fnname>_<prefix>_<seq>". Returns an
|
||||
// arena-owned str. Mirrors C cgen's mklabel so diffs match.
|
||||
fn mklabel(c: *cgen, base: str) str = {
|
||||
fn mklabel(c: *cgen, prefix: str) str = {
|
||||
let buf: [128]u8;
|
||||
let i: i32 = 0;
|
||||
let fname: str = c.fnname;
|
||||
@@ -11467,12 +11633,15 @@ fn mklabel(c: *cgen, base: str) str = {
|
||||
};
|
||||
buf[i] = 95u8; i += 1; // '_'
|
||||
j = 0;
|
||||
for (j < base.len) {
|
||||
buf[i] = base[j];
|
||||
for (j < prefix.len) {
|
||||
buf[i] = prefix[j];
|
||||
i += 1; j += 1;
|
||||
};
|
||||
buf[i] = 95u8; i += 1; // '_'
|
||||
let n: i32 = strconv.i64tos(buf[i:128], c.labelseq: i64);
|
||||
let ns: str = strconv.i64tos(c.labelseq: i64, strconv.DEC);
|
||||
let n: i32 = ns.len;
|
||||
let dk: i32 = 0;
|
||||
for (dk < n) { buf[i + dk] = ns.ptr[dk]; dk += 1; };
|
||||
c.labelseq += 1;
|
||||
let total: i32 = i + n;
|
||||
let p: *u8 = amalloc(c.a, (total: u64) + 1u64): *u8;
|
||||
@@ -11493,22 +11662,25 @@ fn emitlabel(s: str) void = {
|
||||
emitline(":\n");
|
||||
};
|
||||
|
||||
// mkscratchname — fresh local-slot name ".<base>_<labelseq>". Used for
|
||||
// mkscratchname — fresh local-slot name ".<prefix>_<labelseq>". Used for
|
||||
// compiler-synthesised slots (switch scrutinee, forrange index/len)
|
||||
// that need to be unique per use site but are never referenced by user
|
||||
// code. Increments labelseq so the same source position lines up with
|
||||
// C cgen's labelseq stream.
|
||||
fn mkscratchname(c: *cgen, base: str) str = {
|
||||
fn mkscratchname(c: *cgen, prefix: str) str = {
|
||||
let buf: [128]u8;
|
||||
let i: i32 = 0;
|
||||
buf[i] = 46u8; i += 1; // '.'
|
||||
let j: i32 = 0;
|
||||
for (j < base.len) {
|
||||
buf[i] = base[j];
|
||||
for (j < prefix.len) {
|
||||
buf[i] = prefix[j];
|
||||
i += 1; j += 1;
|
||||
};
|
||||
buf[i] = 95u8; i += 1; // '_'
|
||||
let n: i32 = strconv.i64tos(buf[i:128], c.labelseq: i64);
|
||||
let ns: str = strconv.i64tos(c.labelseq: i64, strconv.DEC);
|
||||
let n: i32 = ns.len;
|
||||
let dk: i32 = 0;
|
||||
for (dk < n) { buf[i + dk] = ns.ptr[dk]; dk += 1; };
|
||||
c.labelseq += 1;
|
||||
let total: i32 = i + n;
|
||||
let p: *u8 = amalloc(c.a, (total: u64) + 1u64): *u8;
|
||||
@@ -11542,7 +11714,10 @@ fn internstrlit(c: *cgen, bytes: str) str = {
|
||||
// New label "_S_<seq>".
|
||||
let buf: [32]u8;
|
||||
buf[0] = 95u8; buf[1] = 83u8; buf[2] = 95u8; // "_S_"
|
||||
let n: i32 = strconv.i64tos(buf[3:32], c.strlitseq: i64);
|
||||
let ns: str = strconv.i64tos(c.strlitseq: i64, strconv.DEC);
|
||||
let n: i32 = ns.len;
|
||||
let dk: i32 = 0;
|
||||
for (dk < n) { buf[3 + dk] = ns.ptr[dk]; dk += 1; };
|
||||
c.strlitseq += 1;
|
||||
let total: i32 = 3 + n;
|
||||
let p: *u8 = amalloc(c.a, (total: u64) + 1u64): *u8;
|
||||
@@ -12661,13 +12836,12 @@ export fn main(argc: i32, argv: **u8) i32 = {
|
||||
// "<file>: <resolved>/<resolved+unresolved> resolved"
|
||||
os.write(1, argstr(path).ptr, argstrlen(path): u64);
|
||||
os.write(1, ": ".ptr, 2u64);
|
||||
let buf: [32]u8;
|
||||
let n: i32 = strconv.i64tos(buf[0:32], ck.nresolved: i64);
|
||||
os.write(1, buf.ptr, n: u64);
|
||||
let rs: str = strconv.i64tos(ck.nresolved: i64, strconv.DEC);
|
||||
os.write(1, rs.ptr, rs.len: u64);
|
||||
os.write(1, "/".ptr, 1u64);
|
||||
let total: i32 = ck.nresolved + ck.nunresolved;
|
||||
n = strconv.i64tos(buf[0:32], total: i64);
|
||||
os.write(1, buf.ptr, n: u64);
|
||||
let ts: str = strconv.i64tos(total: i64, strconv.DEC);
|
||||
os.write(1, ts.ptr, ts.len: u64);
|
||||
os.write(1, " resolved\n".ptr, 10u64);
|
||||
if (ck.nunresolved > 0) { return 1; };
|
||||
} else { if (mode == 99) { // '-c' — codegen / emit asm
|
||||
|
||||
@@ -145,13 +145,12 @@ export fn main(argc: i32, argv: **u8) i32 = {
|
||||
// "<file>: <resolved>/<resolved+unresolved> resolved"
|
||||
os.write(1, argstr(path).ptr, argstrlen(path): u64);
|
||||
os.write(1, ": ".ptr, 2u64);
|
||||
let buf: [32]u8;
|
||||
let n: i32 = strconv.i64tos(buf[0:32], ck.nresolved: i64);
|
||||
os.write(1, buf.ptr, n: u64);
|
||||
let rs: str = strconv.i64tos(ck.nresolved: i64, strconv.DEC);
|
||||
os.write(1, rs.ptr, rs.len: u64);
|
||||
os.write(1, "/".ptr, 1u64);
|
||||
let total: i32 = ck.nresolved + ck.nunresolved;
|
||||
n = strconv.i64tos(buf[0:32], total: i64);
|
||||
os.write(1, buf.ptr, n: u64);
|
||||
let ts: str = strconv.i64tos(total: i64, strconv.DEC);
|
||||
os.write(1, ts.ptr, ts.len: u64);
|
||||
os.write(1, " resolved\n".ptr, 10u64);
|
||||
if (ck.nunresolved > 0) { return 1; };
|
||||
} else { if (mode == 99) { // '-c' — codegen / emit asm
|
||||
|
||||
Reference in New Issue
Block a user