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:
2026-06-18 22:47:53 +09:00
parent 64cf3c4094
commit 0197dfb9e6
7 changed files with 508 additions and 63 deletions

View File

@@ -167,14 +167,17 @@ wwi_quote(FILE *of, const char *s, u64 n)
static void
wwi_rune(FILE *of, u64 cp)
{
/* The lexer's rune escapes stop at \xHH (no \u/\U — task #50), so a
* codepoint above 0xff can't render as a re-parseable rune literal;
* fail loud rather than emit a malformed one. No exported def names
* such a rune today (RUNE_MAX is written as an int-cast for the same
* reason). */
if (cp > 0xff)
fatal("wwi: rune codepoint U+%llx exceeds \\xHH (task #50)",
(unsigned long long)cp);
/* #50: the lexer now reads \u/\U, so wide codepoints round-trip as
* those escapes (write-twin of lex.c lexunicode). Codepoints <=0xff
* keep the \xHH spelling they already round-tripped as. */
if (cp > 0xffff) {
fprintf(of, "'\\U%08x'", (unsigned)cp);
return;
}
if (cp > 0xff) {
fprintf(of, "'\\u%04x'", (unsigned)cp);
return;
}
unsigned char c = (unsigned char)cp;
fputc('\'', of);
switch (c) {