lib: char-literals for ascii/fnmatch/shlex magic decimals (Wave-1)

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.
This commit is contained in:
2026-06-02 19:24:40 +09:00
parent 90479fed68
commit d1310a03ad
8 changed files with 150 additions and 136 deletions

View File

@@ -181,7 +181,7 @@ fn scan_backslash(out: io.stream, in: str, pos: *i32) (void | syntaxerr) = {
if (*pos >= in.len) { let e: syntaxerr; return e; };
let r: u8 = in[*pos];
*pos += 1;
if (r == 10u8) { return; }; // '\n' deleted
if (r == '\n') { return; }; // deleted per POSIX
let _w = writebyte(out, r);
return;
};
@@ -195,8 +195,8 @@ fn scan_double(out: io.stream, in: str, pos: *i32) (void | syntaxerr) = {
if (*pos >= in.len) { let e: syntaxerr; return e; };
let r: u8 = in[*pos];
*pos += 1;
if (r == 34u8) { loop = false; } // '"'
else { if (r == 92u8) { // '\\'
if (r == '"') { loop = false; }
else { if (r == '\\') {
let er = scan_backslash(out, in, pos);
match (er) {
case let e: syntaxerr => return e;
@@ -218,7 +218,7 @@ fn scan_single(out: io.stream, in: str, pos: *i32) (void | syntaxerr) = {
if (*pos >= in.len) { let e: syntaxerr; return e; };
let r: u8 = in[*pos];
*pos += 1;
if (r == 39u8) { loop = false; } // "'"
if (r == '\'') { loop = false; }
else { let _w = writebyte(out, r); };
};
return;
@@ -254,14 +254,14 @@ export fn split(in: str) ([]str | syntaxerr) = {
pos += 1;
dirty = true;
if (r == 32u8 || r == 9u8 || r == 10u8) {
if (r == ' ' || r == '\t' || r == '\n') {
// Collapse a run of whitespace.
let inner: bool = true;
for (inner) {
if (pos >= in.len) { inner = false; }
else {
let r2: u8 = in[pos];
if (r2 != 32u8 && r2 != 9u8 && r2 != 10u8) {
if (r2 != ' ' && r2 != '\t' && r2 != '\n') {
inner = false;
} else {
pos += 1;
@@ -275,7 +275,7 @@ export fn split(in: str) ([]str | syntaxerr) = {
memio.reset(&st);
};
dirty = false;
} else { if (r == 92u8) { // '\\'
} else { if (r == '\\') {
let er = scan_backslash(snk, in, &pos);
match (er) {
case let e: syntaxerr => {
@@ -285,7 +285,7 @@ export fn split(in: str) ([]str | syntaxerr) = {
};
case void => {};
};
} else { if (r == 34u8) { // '"'
} else { if (r == '"') {
let er = scan_double(snk, in, &pos);
match (er) {
case let e: syntaxerr => {
@@ -295,7 +295,7 @@ export fn split(in: str) ([]str | syntaxerr) = {
};
case void => {};
};
} else { if (r == 39u8) { // "'"
} else { if (r == '\'') {
let er = scan_single(snk, in, &pos);
match (er) {
case let e: syntaxerr => {
@@ -332,15 +332,15 @@ fn issafe(s: str) bool = {
for (i < s.len) {
let c: u8 = s[i];
// Hare's switch list: '@', '%', '+', '=', ':', ',', '.', '/', '-'.
if (c == 64u8 || c == 37u8 || c == 43u8 || c == 61u8
|| c == 58u8 || c == 44u8 || c == 46u8 || c == 47u8
|| c == 45u8) {
if (c == '@' || c == '%' || c == '+' || c == '='
|| c == ':' || c == ',' || c == '.' || c == '/'
|| c == '-') {
i += 1;
} else {
let alnum: bool = false;
if (c >= 48u8 && c <= 57u8) { alnum = true; }; // 0-9
if (c >= 65u8 && c <= 90u8) { alnum = true; }; // A-Z
if (c >= 97u8 && c <= 122u8) { alnum = true; }; // a-z
if (c >= '0' && c <= '9') { alnum = true; };
if (c >= 'A' && c <= 'Z') { alnum = true; };
if (c >= 'a' && c <= 'z') { alnum = true; };
if (!alnum) { return false; };
i += 1;
};
@@ -361,7 +361,7 @@ fn issafe(s: str) bool = {
export fn quote(sink: io.stream, s: str) (size | io.error) = {
if (s.len == 0) {
let buf: [2]u8;
buf[0] = 39u8; buf[1] = 39u8;
buf[0] = '\''; buf[1] = '\'';
let r = io.write(sink, buf[0:2]);
match (r) {
case let n: size => return n;
@@ -380,7 +380,7 @@ export fn quote(sink: io.stream, s: str) (size | io.error) = {
};
let total: size = 0;
let r1 = writebyte(sink, 39u8); // "'"
let r1 = writebyte(sink, '\'');
match (r1) {
case let n: size => { total += n; };
case let e: io.error => return e;
@@ -388,10 +388,10 @@ export fn quote(sink: io.stream, s: str) (size | io.error) = {
let i: i32 = 0;
for (i < s.len) {
let c: u8 = s[i];
if (c == 39u8) { // "'" → '"'"'
if (c == '\'') { // "'" → '"'"'
let pat: [5]u8;
pat[0] = 39u8; pat[1] = 34u8; pat[2] = 39u8;
pat[3] = 34u8; pat[4] = 39u8;
pat[0] = '\''; pat[1] = '"'; pat[2] = '\'';
pat[3] = '"'; pat[4] = '\'';
let rr = io.write(sink, pat[0:5]);
match (rr) {
case let n: size => { total += n; };
@@ -406,7 +406,7 @@ export fn quote(sink: io.stream, s: str) (size | io.error) = {
};
i += 1;
};
let r2 = writebyte(sink, 39u8); // "'"
let r2 = writebyte(sink, '\'');
match (r2) {
case let n: size => { total += n; };
case let e: io.error => return e;