A type prefixed with `!` is flagged as an error variant. When any variant in a tagged union carries the flag, `?` propagation uses those (and only those) as the error subset; the unflagged variant is the success type. The legacy "first variant = success" rule still applies when no `!`-flag is present, so existing code keeps working. - TK_NOT in parsetype → N_TBANG wrapper (lhs = inner type expr). Appended to Nkind tail for wwdump-diff byte stability. - resolve_type N_TBANG: wraps primitives in a fresh Type copy so the iserror bit doesn't taint shared globals like ty_str/ty_i32; flips the bit in place on NAMED (already unique per alias decl). - Type.iserror; type_named and typedecl inherit it from under. - New check.c helpers: tagged_has_errflag, tagged_is_error_variant, tagged_success_type. N_TRYPROP uses them to find the error subset and verify each error variant is propagatable to the enclosing return. - cgen mirrors with cg_tagged_success_tag + cg_variant_is_error. `?` compares AX against the success tag (no longer always 0) and remaps each error variant's tag for the enclosing fn. `!` aborts on any non-success tag. strconv.invalid and strconv.overflow now use `!`-flagged shape (`!i32` and `!void`) — visible signal in the API surface that they are error types, matching Hare. The (i64 | invalid | overflow) return shape and behavior are unchanged for callers; their match arms still bind the same way. Selfhost: lib/ww/parse/parse.ww recognises `!T` and emits N_TBANG. The selfhost typechecker and cgen ignore the flag — none of the selfhost sources use `!`, so byte-identity gates are unaffected. The selfhost mirror catches up when there's a source using it.
194 lines
4.5 KiB
C
194 lines
4.5 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_TBANG: return "tbang";
|
|
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);
|
|
}
|