Files
ww/lib/ascii/ascii.ww
Hojun-Cho 2f385cec00 ascii: graduate digitval to (i32 | void); selfhost tagged ABI follow-on
ascii.digitval returns (i32 | void) instead of an i32 -1 sentinel.
Two callers updated to match-on the result (lib/ww/lex/lex.ww escape
parse, selfhost/test/smoke.ww probe 6).

`!` would have been more idiomatic at both call sites — both have
verified isxdigit beforehand — but the selfhost parser doesn't yet
recognize postfix `!`/`?`, so using them in bootstrap-bound code
breaks the 993/995 byte-identity gates. Match is fine for now.

Selfhost cgen follow-on for the 8-byte-rounded tagged-union ABI
(landed in 1e2f55a for the C side):
- cgenutil.slotsize: tagged size = 8 (tag) + max(payload), padded to
  8-byte multiple. Was hardcoded 24.
- cgendecl prologue: spill size/8 arg registers, not always 3.
- cgenstmt cglet tagged-call path: spill the CX value-word only when
  the slot is >16 bytes.

All three were emitting 3-register patterns appropriate to (T | str)
sized unions and overflowing the new 16-byte (i32 | void) slots.
2026-05-12 02:10:10 +09:00

93 lines
2.3 KiB
Plaintext

// ascii — rune-class predicates and case folding for the ASCII range.
// Matches Hare's ascii::isdigit family (rune-taking signature). Runes
// outside 0..127 always answer `false`. The lexer hot path uses these
// inline; they are expected to inline to a couple of compares.
export fn isdigit(c: rune) bool = {
if (c < 48) { return false; };
if (c > 57) { return false; };
return true;
};
export fn isupper(c: rune) bool = {
if (c < 65) { return false; };
if (c > 90) { return false; };
return true;
};
export fn islower(c: rune) bool = {
if (c < 97) { return false; };
if (c > 122) { return false; };
return true;
};
export fn isalpha(c: rune) bool = {
if (isupper(c)) { return true; };
return islower(c);
};
export fn isalnum(c: rune) bool = {
if (isalpha(c)) { return true; };
return isdigit(c);
};
// isspace — the C/Hare set: space, tab, NL, VT, FF, CR.
export fn isspace(c: rune) bool = {
if (c == 32) { return true; }; // ' '
if (c == 9) { return true; }; // '\t'
if (c == 10) { return true; }; // '\n'
if (c == 11) { return true; }; // '\v'
if (c == 12) { return true; }; // '\f'
if (c == 13) { return true; }; // '\r'
return false;
};
export fn isxdigit(c: rune) bool = {
if (isdigit(c)) { return true; };
if (c >= 65) {
if (c <= 70) { return true; }; // 'A'..'F'
};
if (c >= 97) {
if (c <= 102) { return true; }; // 'a'..'f'
};
return false;
};
// digitval — value of `c` as a hex/decimal digit. void variant means
// `c` isn't a hex digit. Useful when scanning numeric literals.
export fn digitval(c: rune) (i32 | void) = {
if (isdigit(c)) { return (c - 48): i32; };
if (c >= 65) {
if (c <= 70) { return ((c - 65) + 10): i32; };
};
if (c >= 97) {
if (c <= 102) { return ((c - 97) + 10): i32; };
};
return;
};
// isidstart / isidpart — identifier classes used by the lexer.
// Alpha or '_' starts; alnum or '_' continues.
export fn isidstart(c: rune) bool = {
if (isalpha(c)) { return true; };
if (c == 95) { return true; }; // '_'
return false;
};
export fn isidpart(c: rune) bool = {
if (isalnum(c)) { return true; };
if (c == 95) { return true; };
return false;
};
// tolower / toupper — fold ASCII case. Non-letters pass through.
export fn tolower(c: rune) rune = {
if (isupper(c)) { return c + 32; };
return c;
};
export fn toupper(c: rune) rune = {
if (islower(c)) { return c - 32; };
return c;
};