The TODO(#11) silent-accept of `for (let (a, a) .. xs)` rested on a stale premise -- resolvewalk has per-block scopes since #53 and IS the live w6c_ww checker pass. Bindings now install in a per-LOOP scope (the N_MCASE pattern), so a duplicate name within one pattern errs "redeclared in same scope" (cstage parity via stamptuplebinds' scopedefine-nil check) while sequential same-name loops stay legal. kwtab restores its stated alphabetical order (`import` before `is`, both stages, kinds swapped in lockstep with names).
209 lines
5.1 KiB
C
209 lines
5.1 KiB
C
#include "ww.h"
|
|
#include <string.h>
|
|
|
|
struct kwent {
|
|
const char *s;
|
|
Tkind kind;
|
|
};
|
|
|
|
/* keep alphabetised, so kwlookup is easy to read. Scanned linearly:
|
|
* fewer than 25 entries, a hash isn't worth it. */
|
|
static const struct kwent kwtab[] = {
|
|
{ "as", TK_AS },
|
|
{ "break", TK_BREAK },
|
|
{ "case", TK_CASE },
|
|
{ "chan", TK_CHAN },
|
|
{ "const", TK_CONST },
|
|
{ "continue", TK_CONTINUE },
|
|
{ "def", TK_DEF },
|
|
{ "defer", TK_DEFER },
|
|
{ "else", TK_ELSE },
|
|
{ "enum", TK_ENUM },
|
|
{ "export", TK_EXPORT },
|
|
{ "false", TK_FALSE },
|
|
{ "fn", TK_FN },
|
|
{ "for", TK_FOR },
|
|
{ "if", TK_IF },
|
|
{ "import", TK_USE },
|
|
{ "is", TK_IS },
|
|
{ "let", TK_LET },
|
|
{ "match", TK_MATCH },
|
|
{ "nil", TK_NIL },
|
|
{ "package", TK_MODULE },
|
|
{ "proc", TK_PROC },
|
|
{ "return", TK_RETURN },
|
|
{ "static", TK_STATIC },
|
|
{ "struct", TK_STRUCT },
|
|
{ "switch", TK_SWITCH },
|
|
{ "true", TK_TRUE },
|
|
{ "type", TK_TYPE },
|
|
{ "void", TK_VOID },
|
|
{ "yield", TK_YIELD }
|
|
};
|
|
|
|
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 "import";
|
|
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_IS: return "is";
|
|
case TK_VOID: return "void";
|
|
case TK_YIELD: return "yield";
|
|
case TK_STATIC: return "static";
|
|
case TK_MATCH: return "match";
|
|
case TK_CONST: return "const";
|
|
case TK_UNDER: return "_";
|
|
case TK_ENUM: return "enum";
|
|
case TK_MODULE: return "package";
|
|
case TK_MODRESET: return "//ww:module-reset";
|
|
case TK_MODPATH: return "//ww:module";
|
|
|
|
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);
|
|
}
|