Replace magic ASCII decimals with char literals in ascii/fnmatch/shlex predicates (e.g. `c < 48` → `c < '0'`). Byte-id-neutral: ascii params are rune, so rune<rune emission is unchanged; fnmatch/shlex compare u8 against value-preserving (<=126) rune constants. Range bounds (0/31/127), the ±32 case offset, the 128 high-bit mask, and fnmatch 0u8 sentinels stay decimal. Regenerate the three combined.ww that embed ascii (w6c, wwdump, smoke). Add functional rows pinning predicates reachable only via fnmatch ctype classes / shlex split: [[:space:]]/[[:print:]]/[[:graph:]] + the '\t' arm of [[:blank:]] (fnmatchtest), '\t'/'\n' split separators + issafe's special-char set (shlextest) — so a wrong substitution would be caught.
204 lines
5.0 KiB
Plaintext
204 lines
5.0 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.
|
|
|
|
package ascii;
|
|
|
|
import strings;
|
|
|
|
export fn isdigit(c: rune) bool = {
|
|
if (c < '0') { return false; };
|
|
if (c > '9') { return false; };
|
|
return true;
|
|
};
|
|
|
|
export fn isupper(c: rune) bool = {
|
|
if (c < 'A') { return false; };
|
|
if (c > 'Z') { return false; };
|
|
return true;
|
|
};
|
|
|
|
export fn islower(c: rune) bool = {
|
|
if (c < 'a') { return false; };
|
|
if (c > 'z') { 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 == ' ') { return true; };
|
|
if (c == '\t') { return true; };
|
|
if (c == '\n') { return true; };
|
|
if (c == '\v') { return true; };
|
|
if (c == '\f') { return true; };
|
|
if (c == '\r') { return true; };
|
|
return false;
|
|
};
|
|
|
|
export fn isxdigit(c: rune) bool = {
|
|
if (isdigit(c)) { return true; };
|
|
if (c >= 'A') {
|
|
if (c <= 'F') { return true; };
|
|
};
|
|
if (c >= 'a') {
|
|
if (c <= 'f') { return true; };
|
|
};
|
|
return false;
|
|
};
|
|
|
|
// valid — `c` is in the 0..127 ASCII range.
|
|
export fn valid(c: rune) bool = {
|
|
if (c < 0) { return false; };
|
|
if (c > 127) { return false; };
|
|
return true;
|
|
};
|
|
|
|
// validstr — every byte in `s` is ASCII (0..127).
|
|
export fn validstr(s: str) bool = {
|
|
let i: i32 = 0;
|
|
for (i < s.len) {
|
|
// High-bit test rather than `> 127u8`; both cgens lower
|
|
// the bitwise form identically. The `> u8` form picks
|
|
// JA vs JG depending on signed/unsigned dispatch.
|
|
if ((s[i] & 128u8) != 0u8) { return false; };
|
|
i += 1;
|
|
};
|
|
return true;
|
|
};
|
|
|
|
// iscntrl — control chars: 0..31 and 127.
|
|
export fn iscntrl(c: rune) bool = {
|
|
if (c >= 0) { if (c <= 31) { return true; }; };
|
|
if (c == 127) { return true; };
|
|
return false;
|
|
};
|
|
|
|
// isblank — space and tab.
|
|
export fn isblank(c: rune) bool = {
|
|
if (c == ' ') { return true; };
|
|
if (c == '\t') { return true; };
|
|
return false;
|
|
};
|
|
|
|
// isprint — printable: space through '~'.
|
|
export fn isprint(c: rune) bool = {
|
|
if (c < ' ') { return false; };
|
|
if (c > '~') { return false; };
|
|
return true;
|
|
};
|
|
|
|
// isgraph — printable, non-space.
|
|
export fn isgraph(c: rune) bool = {
|
|
if (c < '!') { return false; };
|
|
if (c > '~') { return false; };
|
|
return true;
|
|
};
|
|
|
|
// ispunct — printable, non-alnum, non-space.
|
|
export fn ispunct(c: rune) bool = {
|
|
if (!isgraph(c)) { return false; };
|
|
if (isalnum(c)) { return false; };
|
|
return true;
|
|
};
|
|
|
|
// 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;
|
|
};
|
|
|
|
// strcasecmp — three-way ASCII case-insensitive compare.
|
|
export fn strcasecmp(a: str, b: str) i32 = {
|
|
let n: i32 = a.len;
|
|
if (b.len < n) { n = b.len; };
|
|
let i: i32 = 0;
|
|
for (i < n) {
|
|
let ca: rune = tolower(a[i]: rune);
|
|
let cb: rune = tolower(b[i]: rune);
|
|
if (ca != cb) { return (ca - cb): i32; };
|
|
i += 1;
|
|
};
|
|
return a.len - b.len;
|
|
};
|
|
|
|
// strlower — ASCII-lowercased copy of s, newly allocated.
|
|
// ref/hare/ascii/string.ha:11.
|
|
export fn strlower(s: str) (str | nomem) = {
|
|
// empty bypass: ww alloc([],0) routes through nomem; Hare allocs 0
|
|
// and zero-loops (ref/hare/ascii/string.ha:12).
|
|
if (s.len == 0) {
|
|
let r: str;
|
|
r.ptr = nil;
|
|
r.len = 0;
|
|
return r;
|
|
};
|
|
let buf: []u8 = alloc([], s.len: u64)?;
|
|
return strlower_buf(s, buf);
|
|
};
|
|
|
|
// strlower_buf — ASCII-lowercase s into buf (overwrites). nomem if buf
|
|
// too small. ref/hare/ascii/string.ha:21.
|
|
// Byte-wise fold: ASCII case-fold only touches bytes <0x80; UTF-8
|
|
// multibyte bytes are >=0x80 and pass through unchanged, so byte-wise
|
|
// equals Hare's rune fold and is length-preserving.
|
|
// ww uses an explicit `buf.cap < s.len` check + `let nm: nomem` value
|
|
// because it has no static-append builtin; Hare reaches the same
|
|
// nomem-on-too-small via `static append(buf, ...)?` (string.ha:25).
|
|
export fn strlower_buf(s: str, buf: []u8) (str | nomem) = {
|
|
if (buf.cap < s.len) {
|
|
let nm: nomem;
|
|
return nm;
|
|
};
|
|
let i: i32 = 0;
|
|
for (i < s.len) {
|
|
buf.ptr[i] = tolower(s[i]: rune): u8;
|
|
i += 1;
|
|
};
|
|
buf.len = s.len;
|
|
return strings.frombytes(buf);
|
|
};
|
|
|
|
// strupper — ASCII-uppercased copy of s, newly allocated.
|
|
// ref/hare/ascii/string.ha:33.
|
|
export fn strupper(s: str) (str | nomem) = {
|
|
if (s.len == 0) {
|
|
let r: str;
|
|
r.ptr = nil;
|
|
r.len = 0;
|
|
return r;
|
|
};
|
|
let buf: []u8 = alloc([], s.len: u64)?;
|
|
return strupper_buf(s, buf);
|
|
};
|
|
|
|
// strupper_buf — see strlower_buf. ref/hare/ascii/string.ha:43.
|
|
export fn strupper_buf(s: str, buf: []u8) (str | nomem) = {
|
|
if (buf.cap < s.len) {
|
|
let nm: nomem;
|
|
return nm;
|
|
};
|
|
let i: i32 = 0;
|
|
for (i < s.len) {
|
|
buf.ptr[i] = toupper(s[i]: rune): u8;
|
|
i += 1;
|
|
};
|
|
buf.len = s.len;
|
|
return strings.frombytes(buf);
|
|
};
|