Files
ww/cmd/wcc/tok.c
Hojun-Cho 2c33228b7e ww: rename toolchain to w-prefix + hare-style build/run/test driver
Plan 9-style w-prefix on the per-arch tools, disambiguating from the
real Plan 9 6c/6a/6l in ref/plan9front/:

    cmd/wwc/      → cmd/wcc/        libwwc.a → libwcc.a
    cmd/6{c,a,l}  → cmd/w6{c,a,l}   binary names too
    test/wwc/     → test/wcc/       6 test files w/ w6 prefix
    selfhost/cmd  mirror in lockstep
    bootstrap/amd64/{w6c,w6a,w6l}   snapshot binaries (gitignored)
    WW_6{C,A,L}   → WW_W6{C,A,L}    env-var overrides

Plan 9 source-tree refs ("Plan 9 6c shape", ref/plan9front/, etc.)
preserved. Hare-style driver, both C and ww sides:

    ww test [path]   discover *_test.ww in a directory module, run
                     each; single-file mode for `ww test foo.ww`
    Module-by-name   `ww build foo` resolves to foo.ww or foo/foo.ww
                     via search path (cwd : -I dirs : $WW_LIB)
    Default-to-cwd   `ww build` / `ww test` build the cwd module
    Run pass-through `ww run path arg1 arg2` reaches the program

lib/os: getcwd (79) and getdents64 (217) syscalls power `.` resolution
and directory enumeration on the ww side.

Makefile: wwstage tool deps now include lib/os/os.ww (+ lib/strconv
for wwdump_ww) so lib/* edits force their rebuild instead of leaving
stale binaries — surfaced when test 995 first failed against a stale
w6c_ww built before the lib/os additions.

Test 993 byte-identical parity gate (C-side ww vs ww-side ww_ww on a
build corpus) stays green; all 19 tests pass.
2026-05-11 13:49:27 +09:00

200 lines
4.7 KiB
C

/*
* tok.c — token names, keyword lookup, debug printer.
*
* One table-of-records keyed by kind. The keyword subset is also
* scanned linearly during lexing — fewer than 25 entries, a hash
* isn't worth it.
*/
#include "ww.h"
#include <string.h>
struct kwent {
const char *s;
Tkind kind;
};
/* keep alphabetised, so kwlookup is easy to read. */
static const struct kwent kwtab[] = {
{ "as", TK_AS },
{ "break", TK_BREAK },
{ "case", TK_CASE },
{ "chan", TK_CHAN },
{ "continue", TK_CONTINUE },
{ "def", TK_DEF },
{ "defer", TK_DEFER },
{ "else", TK_ELSE },
{ "export", TK_EXPORT },
{ "false", TK_FALSE },
{ "fn", TK_FN },
{ "for", TK_FOR },
{ "if", TK_IF },
{ "let", TK_LET },
{ "match", TK_MATCH },
{ "nil", TK_NIL },
{ "proc", TK_PROC },
{ "return", TK_RETURN },
{ "static", TK_STATIC },
{ "struct", TK_STRUCT },
{ "switch", TK_SWITCH },
{ "true", TK_TRUE },
{ "type", TK_TYPE },
{ "use", TK_USE }
};
Tkind
kwlookup(const char *s, u64 n)
{
/* linear scan: small N, predictable, branchy fall-through is fine. */
for (u64 i = 0; i < nelem(kwtab); i++) {
const char *k = kwtab[i].s;
if (strlen(k) == n && memcmp(k, s, n) == 0)
return kwtab[i].kind;
}
return TK_NONE;
}
const char *
tokname(Tkind k)
{
switch (k) {
case TK_NONE: return "<none>";
case TK_EOF: return "EOF";
case TK_ERR: return "ERR";
case TK_IDENT: return "IDENT";
case TK_INT: return "INT";
case TK_FLOAT: return "FLOAT";
case TK_RUNE: return "RUNE";
case TK_STR: return "STR";
case TK_FN: return "fn";
case TK_LET: return "let";
case TK_DEF: return "def";
case TK_IF: return "if";
case TK_ELSE: return "else";
case TK_FOR: return "for";
case TK_SWITCH: return "switch";
case TK_CASE: return "case";
case TK_RETURN: return "return";
case TK_USE: return "use";
case TK_TYPE: return "type";
case TK_STRUCT: return "struct";
case TK_DEFER: return "defer";
case TK_BREAK: return "break";
case TK_CONTINUE: return "continue";
case TK_EXPORT: return "export";
case TK_PROC: return "proc";
case TK_CHAN: return "chan";
case TK_NIL: return "nil";
case TK_TRUE: return "true";
case TK_FALSE: return "false";
case TK_AS: return "as";
case TK_STATIC: return "static";
case TK_MATCH: return "match";
case TK_LPAREN: return "(";
case TK_RPAREN: return ")";
case TK_LBRACE: return "{";
case TK_RBRACE: return "}";
case TK_LBRACK: return "[";
case TK_RBRACK: return "]";
case TK_COMMA: return ",";
case TK_SEMI: return ";";
case TK_COLON: return ":";
case TK_DOT: return ".";
case TK_ELLIPSIS: return "...";
case TK_DOTDOT: return "..";
case TK_AT: return "@";
case TK_QUESTION: return "?";
case TK_ASSIGN: return "=";
case TK_PLUSEQ: return "+=";
case TK_MINUSEQ: return "-=";
case TK_STAREQ: return "*=";
case TK_SLASHEQ: return "/=";
case TK_PERCENTEQ: return "%=";
case TK_AMPEQ: return "&=";
case TK_PIPEEQ: return "|=";
case TK_CARETEQ: return "^=";
case TK_LSHIFTEQ: return "<<=";
case TK_RSHIFTEQ: return ">>=";
case TK_PLUS: return "+";
case TK_MINUS: return "-";
case TK_STAR: return "*";
case TK_SLASH: return "/";
case TK_PERCENT: return "%";
case TK_AMP: return "&";
case TK_PIPE: return "|";
case TK_CARET: return "^";
case TK_TILDE: return "~";
case TK_LSHIFT: return "<<";
case TK_RSHIFT: return ">>";
case TK_EQ: return "==";
case TK_NEQ: return "!=";
case TK_LT: return "<";
case TK_LE: return "<=";
case TK_GT: return ">";
case TK_GE: return ">=";
case TK_AND: return "&&";
case TK_OR: return "||";
case TK_NOT: return "!";
case TK_LARROW: return "<-";
case TK_ARROW: return "->";
case TK_FATARROW: return "=>";
case TK_LAST: return "<last>";
}
return "<?>";
}
static void
fputq(FILE *f, const char *s, u64 n)
{
fputc('"', f);
for (u64 i = 0; i < n; i++) {
unsigned char c = (unsigned char)s[i];
switch (c) {
case '\\': fputs("\\\\", f); break;
case '"': fputs("\\\"", f); break;
case '\n': fputs("\\n", f); break;
case '\t': fputs("\\t", f); break;
case '\r': fputs("\\r", f); break;
default:
if (c < 0x20 || c == 0x7f)
fprintf(f, "\\x%02x", c);
else
fputc(c, f);
}
}
fputc('"', f);
}
void
tokprint(FILE *f, Tok t)
{
fprintf(f, "%s:%d:%d %s",
t.pos.file ? t.pos.file : "<none>", t.pos.line, t.pos.col,
tokname(t.kind));
switch (t.kind) {
case TK_IDENT:
case TK_STR:
case TK_ERR:
fputc(' ', f);
fputq(f, t.text, t.tlen);
break;
case TK_INT:
case TK_RUNE:
fprintf(f, " %llu", (unsigned long long)t.v.uval);
break;
case TK_FLOAT:
fprintf(f, " %g", t.v.fval);
break;
default:
break;
}
fputc('\n', f);
}