Files
ww/cmd/wcc/ast.c
Hojun-Cho 4085742853 strconv: graduate to (T | invalid | overflow); add void expression
`type invalid = i32` (payload: byte index of first bad rune; mirrors
Hare's strconv::invalid = !size) and `type overflow = void` (Hare's
overflow = !void). stoi64/stou64 now return these instead of the
str-error placeholder. atoi64 dropped — lib/CLAUDE.md says graduate
in one go, don't keep both shapes around.

To produce the void variant payload, `void` is now a real
expression literal (TK_VOID kw, N_VOIDLIT). It evaluates to ty_void;
codegen emits MOVQ $0, AX. Both kinds are appended at the tail of
their enums to keep prior numeric values byte-stable for the
wwdump-diff fixtures.

check_file reorder: USE declarations are now installed in pass 1
alongside the type-decl placeholders so dotted type references
(`strconv.invalid` from a typedecl body) resolve. DEF/FN/LET silently
overwrite a USE-occupied slot — matches the old behavior where USE
silently no-op'd when a same-name fn/def existed (the conflict
manifested in selfhost main.combined.ww at `use parse;` colliding
with `export fn parse(a)`).

selfhost mirror: lib/ww/lex/tok.ww kwtab+name; lib/ww/ast.ww
N_VOIDLIT def+print; lib/ww/parse/{expr,parse}.ww TK_VOID handling;
selfhost/cmd/wcc/cgenexpr.ww N_VOIDLIT codegen.
2026-05-12 02:02:08 +09:00

193 lines
4.4 KiB
C

/*
* ast.c — Node constructor + s-expression printer.
*
* Constructor zeroes everything past kind/pos. Printer is rigid and
* deterministic so golden tests can diff. One node per logical line,
* children indented by 2 spaces.
*/
#include "ww.h"
#include <string.h>
Node *
newnode(Arena *a, Nkind k, Pos p)
{
Node *n = amalloc(a, sizeof *n);
n->kind = k;
n->pos = p;
return n;
}
static const char *
nkname(Nkind k)
{
switch (k) {
case N_NONE: return "none";
case N_INTLIT: return "int";
case N_FLOATLIT: return "float";
case N_STRLIT: return "str";
case N_RUNELIT: return "rune";
case N_TRUE: return "true";
case N_FALSE: return "false";
case N_NIL: return "nil";
case N_IDENT: return "id";
case N_BIN: return "bin";
case N_UN: return "un";
case N_CALL: return "call";
case N_INDEX: return "index";
case N_DOT: return "dot";
case N_CAST: return "cast";
case N_STRUCTLIT: return "structlit";
case N_ARRLIT: return "arrlit";
case N_FIELD: return "field";
case N_ASSIGN: return "assign";
case N_ALLOC: return "alloc";
case N_FREE: return "free";
case N_RECV: return "recv";
case N_SLICE: return "slice";
case N_SPREAD: return "spread";
case N_BLOCK: return "block";
case N_EXPRSTMT: return "exprstmt";
case N_LET: return "let";
case N_RETURN: return "return";
case N_IF: return "if";
case N_FOR: return "for";
case N_FORRANGE: return "forrange";
case N_DEFER: return "defer";
case N_BREAK: return "break";
case N_CONTINUE: return "continue";
case N_SWITCH: return "switch";
case N_CASE: return "case";
case N_FILE: return "file";
case N_USE: return "use";
case N_DEF: return "def";
case N_TYPEDECL: return "typedecl";
case N_FNDECL: return "fn";
case N_PARAM: return "param";
case N_TNAME: return "tname";
case N_TPTR: return "tptr";
case N_TSLICE: return "tslice";
case N_TARRAY: return "tarray";
case N_TFN: return "tfn";
case N_TSTRUCT: return "tstruct";
case N_TFIELD: return "tfield";
case N_TCHAN: return "tchan";
case N_ATTR: return "attr";
case N_TTUPLE: return "ttuple";
case N_TTAGGED: return "ttagged";
case N_TUPLE: return "tuple";
case N_MATCH: return "match";
case N_MCASE: return "mcase";
case N_TRYPROP: return "tryprop";
case N_TRYUNW: return "tryunw";
case N_MLET: return "mlet";
case N_MASSIGN: return "massign";
case N_TYPETEST: return "typetest";
case N_TYPEASSERT: return "typeassert";
case N_VOIDLIT: return "voidlit";
case N_LAST: return "last";
}
return "?";
}
static void
indent(FILE *f, int d)
{
for (int i = 0; i < d; i++) fputs(" ", f);
}
static void
printq(FILE *f, const char *s)
{
fputc('"', f);
for (; *s; s++) {
unsigned char c = (unsigned char)*s;
switch (c) {
case '"': fputs("\\\"", f); break;
case '\\': fputs("\\\\", f); break;
case '\n': fputs("\\n", f); break;
case '\t': fputs("\\t", f); break;
default:
if (c < 0x20) fprintf(f, "\\x%02x", c);
else fputc(c, f);
}
}
fputc('"', f);
}
static void pr(FILE*, Node*, int);
static void
prlist(FILE *f, const char *tag, Node *head, int d)
{
indent(f, d);
fprintf(f, "(%s\n", tag);
for (Node *n = head; n; n = n->next)
pr(f, n, d + 1);
indent(f, d);
fputs(")\n", f);
}
static void
pr(FILE *f, Node *n, int d)
{
if (n == NULL) {
indent(f, d); fputs("()\n", f); return;
}
indent(f, d);
fprintf(f, "(%s", nkname(n->kind));
switch (n->kind) {
case N_INTLIT:
fprintf(f, " %llu", (unsigned long long)n->uval);
break;
case N_FLOATLIT:
fprintf(f, " %g", n->fval);
break;
case N_RUNELIT:
fprintf(f, " %llu", (unsigned long long)n->uval);
break;
case N_STRLIT:
case N_IDENT:
case N_USE:
case N_DOT:
case N_DEF:
case N_TYPEDECL:
case N_FNDECL:
case N_PARAM:
case N_LET:
case N_TNAME:
case N_TFIELD:
case N_FIELD:
case N_ATTR:
if (n->str) { fputc(' ', f); printq(f, n->str); }
break;
case N_BIN:
case N_UN:
case N_ASSIGN:
fprintf(f, " %s", tokname(n->op));
break;
default: break;
}
if (n->kind == N_FNDECL && n->export)
fputs(" export", f);
if (n->kind == N_DEF && n->export)
fputs(" export", f);
if (n->kind == N_TYPEDECL && n->export)
fputs(" export", f);
fputc('\n', f);
if (n->attr)
prlist(f, "@", n->attr, d + 1);
if (n->lhs) pr(f, n->lhs, d + 1);
if (n->rhs) pr(f, n->rhs, d + 1);
if (n->cond) pr(f, n->cond, d + 1);
if (n->body) pr(f, n->body, d + 1);
if (n->els) pr(f, n->els, d + 1);
if (n->list) prlist(f, "list", n->list, d + 1);
indent(f, d); fputs(")\n", f);
}
void
astprint(FILE *f, Node *n)
{
pr(f, n, 0);
}