lex,wwi: \u/\U unicode escapes + wide-rune .wwi round-trip, both stages (#50)
Hare-faithful \u (4 hex) / \U (8 hex) escapes; \x/\u/\U share one codepoint path (ref/hare/hare/lex/lex.ha lex_unicode); string literals UTF-8-encode multi-byte codepoints (cstage inline utf8enc, wwstage utf8.encoderune). The .wwi producer rune serializer now emits \u/\U so exported wide-rune defs round-trip (was a fatal >0xFF). Reject >0x10FFFF and surrogates with Hare- verbatim error strings. Closes the int-cast spelling divergence (#48 RUNE_MAX). Both stages byte-identical; 446 tests pass.
This commit is contained in:
@@ -210,6 +210,68 @@ parseint(const char *s, u64 n, int base, int *ok)
|
||||
return v;
|
||||
}
|
||||
|
||||
/* shared escape decoder for \xHH (n=2), \uHHHH (n=4), \UHHHHHHHH (n=8).
|
||||
* All three yield a codepoint, not a raw byte — mirrors
|
||||
* ref/hare/hare/lex/lex.ha:347 fn lex_unicode. Error strings copied
|
||||
* verbatim from that reference for diagnostic fidelity (#50). */
|
||||
static int
|
||||
lexunicode(Lex *l, int n, int *out)
|
||||
{
|
||||
u32 u = 0;
|
||||
for (int i = 0; i < n; i++) {
|
||||
int c = lget(l);
|
||||
if (c < 0) {
|
||||
Pos p = lpos(l);
|
||||
errorf(p, "unexpected EOF scanning for escape");
|
||||
l->errs++;
|
||||
return -1;
|
||||
}
|
||||
if (!ishex(c)) {
|
||||
Pos p = lpos(l);
|
||||
errorf(p, "unexpected rune scanning for escape");
|
||||
l->errs++;
|
||||
return -1;
|
||||
}
|
||||
int d = (c <= '9' ? c - '0' : (c | 0x20) - 'a' + 10);
|
||||
u = (u << 4) | (u32)d;
|
||||
}
|
||||
if (u > 0x10FFFF || (u >= 0xD800 && u < 0xE000)) {
|
||||
Pos p = lpos(l);
|
||||
errorf(p, "invalid unicode codepoint in escape");
|
||||
l->errs++;
|
||||
return -1;
|
||||
}
|
||||
*out = (int)u;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* utf8enc — encode codepoint cp (already validated <= 0x10FFFF and
|
||||
* non-surrogate by lexunicode) into out (>= 4 bytes), return the byte
|
||||
* count. The C bootstrap has no stdlib; this mirrors lib/encoding/utf8
|
||||
* encoderune (the ww side calls that directly). */
|
||||
static int
|
||||
utf8enc(u32 cp, char *out)
|
||||
{
|
||||
if (cp < 0x80) {
|
||||
out[0] = (char)cp;
|
||||
return 1;
|
||||
} else if (cp < 0x800) {
|
||||
out[0] = (char)(0xC0 | (cp >> 6));
|
||||
out[1] = (char)(0x80 | (cp & 0x3F));
|
||||
return 2;
|
||||
} else if (cp < 0x10000) {
|
||||
out[0] = (char)(0xE0 | (cp >> 12));
|
||||
out[1] = (char)(0x80 | ((cp >> 6) & 0x3F));
|
||||
out[2] = (char)(0x80 | (cp & 0x3F));
|
||||
return 3;
|
||||
}
|
||||
out[0] = (char)(0xF0 | (cp >> 18));
|
||||
out[1] = (char)(0x80 | ((cp >> 12) & 0x3F));
|
||||
out[2] = (char)(0x80 | ((cp >> 6) & 0x3F));
|
||||
out[3] = (char)(0x80 | (cp & 0x3F));
|
||||
return 4;
|
||||
}
|
||||
|
||||
static int
|
||||
escape(Lex *l, int *out)
|
||||
{
|
||||
@@ -227,19 +289,9 @@ escape(Lex *l, int *out)
|
||||
case 'b': *out = '\b'; return 0;
|
||||
case 'f': *out = '\f'; return 0;
|
||||
case 'v': *out = '\v'; return 0;
|
||||
case 'x': {
|
||||
int hi = lget(l), lo = lget(l);
|
||||
if (!ishex(hi) || !ishex(lo)) {
|
||||
Pos p = lpos(l);
|
||||
errorf(p, "bad \\x escape");
|
||||
l->errs++;
|
||||
return -1;
|
||||
}
|
||||
int h = (hi <= '9' ? hi - '0' : (hi | 0x20) - 'a' + 10);
|
||||
int o = (lo <= '9' ? lo - '0' : (lo | 0x20) - 'a' + 10);
|
||||
*out = (h << 4) | o;
|
||||
return 0;
|
||||
}
|
||||
case 'x': return lexunicode(l, 2, out);
|
||||
case 'u': return lexunicode(l, 4, out);
|
||||
case 'U': return lexunicode(l, 8, out);
|
||||
}
|
||||
{ Pos p = lpos(l); errorf(p, "bad escape \\%c", c); l->errs++; }
|
||||
return -1;
|
||||
@@ -389,22 +441,34 @@ lexstr(Lex *l, Pos start)
|
||||
return t;
|
||||
}
|
||||
if (c == '"') { lget(l); break; }
|
||||
int ch;
|
||||
/* Escape-decoded values are codepoints and UTF-8-encode into
|
||||
* 1-4 bytes (mirrors Hare's memio::appendrune in lex_string,
|
||||
* ref/hare/hare/lex/lex.ha:431). Raw source bytes are already
|
||||
* UTF-8 and pass through unchanged — re-encoding them would
|
||||
* double-encode the >0x7F continuation bytes. */
|
||||
char enc[4];
|
||||
int el;
|
||||
if (c == '\\') {
|
||||
int ch;
|
||||
lget(l);
|
||||
if (escape(l, &ch) < 0)
|
||||
ch = 0;
|
||||
el = utf8enc((u32)ch, enc);
|
||||
} else {
|
||||
ch = lget(l);
|
||||
enc[0] = (char)lget(l);
|
||||
el = 1;
|
||||
}
|
||||
if (n + 1 >= cap) {
|
||||
if (n + el >= cap) {
|
||||
u64 ncap = cap * 2;
|
||||
while (n + el >= ncap)
|
||||
ncap *= 2;
|
||||
char *nb = amalloc(l->a, ncap);
|
||||
memcpy(nb, buf, n);
|
||||
buf = nb;
|
||||
cap = ncap;
|
||||
}
|
||||
buf[n++] = (char)ch;
|
||||
for (int i = 0; i < el; i++)
|
||||
buf[n++] = enc[i];
|
||||
}
|
||||
buf[n] = '\0';
|
||||
Tok t = (Tok){ TK_STR, start, buf, n, {0}, TK_NONE };
|
||||
|
||||
Reference in New Issue
Block a user