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.
200 lines
4.7 KiB
C
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);
|
|
}
|