toolchain: banner purge + WHY-only comment sweep (rule 8)
selfhost/, cmd/, internal/ join the tree-wide sweep: every section banner dies (91 selfhost + the cmd C-style dividers -> 0); narration and stale contracts deleted (pre-#22 bundler notes, retired single-PT_LOAD and no-archive claims, superseded ABI tables); every ref/harec/qbe cite, task cite, encoding/ELF contract, and rule-10 twin pointer kept; lost lifetime/rationale lines restored where the sweep over-cut (elf_globals ownership, kwtab linear-scan). Comment- only proven: all five wwstage tool binaries byte-identical across the sweep; test-commit, test-byteid (161+1399, 0 pinned-divergent), and test-bootstrap (fixed point + 991-995 byte-id) all exit 0. The read-through banked 66 latent-bug leads (checkpoint).
This commit is contained in:
@@ -1,9 +1,5 @@
|
||||
/*
|
||||
* 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.
|
||||
* Printer output is rigid and deterministic so golden tests can diff.
|
||||
*/
|
||||
#include "ww.h"
|
||||
#include <string.h>
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
/*
|
||||
* check.c — name resolution + type checking pass.
|
||||
*
|
||||
* Two-stage:
|
||||
* 1) collect: walk top-level decls and install Syms with stub types.
|
||||
* 2) resolve: expand types, check fn bodies and def initialisers.
|
||||
@@ -1046,8 +1044,6 @@ resolve_type(Checker *c, Node *n)
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- expressions -------------------------------------------------- */
|
||||
|
||||
static Type *
|
||||
unify_arith(Checker *c, Pos p, Type *a, Type *b)
|
||||
{
|
||||
@@ -2007,7 +2003,6 @@ cexpr(Checker *c, Node *n)
|
||||
(void)cexpr(c, n->rhs);
|
||||
return n->type = ty_void;
|
||||
}
|
||||
/* Reject assignment to a const-bound name. */
|
||||
if (n->lhs && n->lhs->kind == N_IDENT && n->lhs->str) {
|
||||
Sym *s = scope_lookup_prefer(c->cur, c->cur_mod,
|
||||
n->lhs->str);
|
||||
@@ -2387,8 +2382,6 @@ cexpr(Checker *c, Node *n)
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- statements --------------------------------------------------- */
|
||||
|
||||
static void
|
||||
clet(Checker *c, Node *n)
|
||||
{
|
||||
@@ -2731,8 +2724,6 @@ cstmt(Checker *c, Node *n)
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- top-level ---------------------------------------------------- */
|
||||
|
||||
static Type *
|
||||
build_fn_type(Checker *c, Node *fn)
|
||||
{
|
||||
|
||||
@@ -1,10 +1,4 @@
|
||||
/*
|
||||
* err.c — diagnostics.
|
||||
*
|
||||
* fatal prints, sets exit(1).
|
||||
* errorf prints with source location, increments nerrors.
|
||||
* warnf prints with source location, increments nwarnings.
|
||||
*
|
||||
* Plan 9 style: short, no levels beyond fatal/error/warn, no colour.
|
||||
*/
|
||||
#include "ww.h"
|
||||
|
||||
@@ -1,16 +1,4 @@
|
||||
/*
|
||||
* lex.c — hand-rolled DFA. UTF-8 source, ASCII operators.
|
||||
*
|
||||
* Comments: //... and (slash-star ... star-slash). Both stripped.
|
||||
* Whitespace: space, tab, CR, NL.
|
||||
* Identifiers: [A-Za-z_][A-Za-z0-9_]* — also matches keywords; we
|
||||
* look up the kw table after lexing the run.
|
||||
* Integer: 0x[0-9a-fA-F_]+, 0o[0-7_]+, 0b[01_]+, [0-9][0-9_]*
|
||||
* Float: [0-9]+'.'[0-9]+([eE][+-]?[0-9]+)?
|
||||
* Rune: 'x' with C-like escapes
|
||||
* String: "..." with C-like escapes
|
||||
* Operators: longest match.
|
||||
*
|
||||
* No automatic semicolon insertion (Hare rule). The lexer only emits
|
||||
* what is in the source; the parser is responsible for non-empty rules.
|
||||
*/
|
||||
@@ -81,7 +69,6 @@ ishex(int c)
|
||||
(c >= 'A' && c <= 'F');
|
||||
}
|
||||
|
||||
/* skip whitespace and comments. returns 0 on EOF, else 1. */
|
||||
static int
|
||||
skipws(Lex *l)
|
||||
{
|
||||
@@ -94,7 +81,7 @@ skipws(Lex *l)
|
||||
continue;
|
||||
}
|
||||
if (c == '/' && lpeek(l, 1) == '/') {
|
||||
lget(l); lget(l); /* consume '//' */
|
||||
lget(l); lget(l);
|
||||
/* #16 option-B: the driver emits `//ww:module-reset`
|
||||
* before a package-less file's bytes; recognize the
|
||||
* whole-line directive (without consuming differently)
|
||||
@@ -379,10 +366,7 @@ lexnum(Lex *l, Pos start)
|
||||
}
|
||||
}
|
||||
|
||||
/* Typed suffix: i8/i16/i32/i64, u8/u16/u32/u64, f32/f64.
|
||||
* Must be glued (no whitespace) to the digits. We grab the
|
||||
* adjacent identifier-like run and accept it only if it's one
|
||||
* of the recognised type names. */
|
||||
/* A typed suffix must be glued (no whitespace) to the digits. */
|
||||
if (isidstart(lpeek(l, 0))) {
|
||||
u64 sb = l->pos;
|
||||
while (isidcont(lpeek(l, 0))) lget(l);
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
/*
|
||||
* mem.c — arena allocator. No free per allocation; freearena releases
|
||||
* the whole chain. Aligned to 16 so structs with 8-byte fields and
|
||||
* doubles are happy.
|
||||
*
|
||||
* Hot allocations in the compiler land in arenas: tokens, AST nodes,
|
||||
* symbols, types. The chunk size doubles up to a cap so we don't
|
||||
* fragment on huge inputs.
|
||||
* No free per allocation; freearena releases the whole chain.
|
||||
* Aligned to 16 so structs with 8-byte fields and doubles are happy.
|
||||
* The chunk size doubles up to a cap so we don't fragment on huge
|
||||
* inputs.
|
||||
*/
|
||||
#include "ww.h"
|
||||
#include <stdlib.h>
|
||||
@@ -43,7 +40,6 @@ grow(Arena *a, u64 need)
|
||||
if (ncap < need)
|
||||
ncap = roundup(need, ALIGN);
|
||||
|
||||
/* push current chunk onto chain, allocate fresh head */
|
||||
Arena *old = malloc(sizeof *old);
|
||||
if (old == NULL)
|
||||
fatal("arena: oom");
|
||||
|
||||
@@ -100,8 +100,6 @@ static Node *parsetype(Parser *p);
|
||||
static Node *parseblock(Parser *p);
|
||||
static Node *parsestmt(Parser *p);
|
||||
|
||||
/* ------- type expressions ------------------------------------------ */
|
||||
|
||||
static Node *
|
||||
parseparams(Parser *p)
|
||||
{
|
||||
@@ -376,8 +374,6 @@ parsetype(Parser *p)
|
||||
}
|
||||
}
|
||||
|
||||
/* ------- expressions (Pratt) ---------------------------------------- */
|
||||
|
||||
/* binary precedence; 0 = not a binary op */
|
||||
static int
|
||||
bprec(Tkind k)
|
||||
@@ -663,10 +659,9 @@ parseprimary(Parser *p)
|
||||
n->str = t.text;
|
||||
n->strlen = t.tlen;
|
||||
advance(p);
|
||||
/* dotted ident chain folded into one IDENT for type-ish refs */
|
||||
while (p->cur.kind == TK_DOT && peek(p).kind == TK_IDENT) {
|
||||
advance(p);
|
||||
n = (Node*)n; /* keep stable */
|
||||
n = (Node*)n;
|
||||
Node *mr = newnode(p->a, N_DOT, pp);
|
||||
mr->lhs = n;
|
||||
mr->str = p->cur.text;
|
||||
@@ -674,7 +669,6 @@ parseprimary(Parser *p)
|
||||
advance(p);
|
||||
n = mr;
|
||||
}
|
||||
/* struct literal: ident '{' ... '}' (only if ident-shaped) */
|
||||
if (p->cur.kind == TK_LBRACE) {
|
||||
/* #76: bare `Foo{}` keeps the N_IDENT fast-path; a
|
||||
* qualified `pkg.Type{}` (N_DOT chain) flattens first. */
|
||||
@@ -902,8 +896,6 @@ parseexpr_top(Parser *p)
|
||||
return parseexpr(p);
|
||||
}
|
||||
|
||||
/* ------- statements ------------------------------------------------- */
|
||||
|
||||
static Node *
|
||||
parselet(Parser *p, int top)
|
||||
{
|
||||
@@ -944,7 +936,6 @@ parselet(Parser *p, int top)
|
||||
return m;
|
||||
}
|
||||
|
||||
/* parse first binding */
|
||||
Pos lp = p->cur.pos;
|
||||
Node *first = newnode(p->a, N_LET, lp);
|
||||
first->str = expectbindname(p);
|
||||
@@ -952,7 +943,6 @@ parselet(Parser *p, int top)
|
||||
first->lhs = parsetype(p);
|
||||
|
||||
if (p->cur.kind == TK_COMMA) {
|
||||
/* multi-let: collect (name, type) pairs, then '=' rhs */
|
||||
Node *m = newnode(p->a, N_MLET, pp);
|
||||
Node *head = first, *tail = first;
|
||||
while (accept(p, TK_COMMA)) {
|
||||
@@ -1038,7 +1028,7 @@ parsefor(Parser *p)
|
||||
* Tuple destructure: `let (a, b) .. expr`. */
|
||||
Tok save_cur = p->cur;
|
||||
(void)save_cur;
|
||||
advance(p); /* consume LET */
|
||||
advance(p);
|
||||
if (p->cur.kind == TK_LPAREN) {
|
||||
advance(p);
|
||||
Node *names = NULL, *tail = NULL;
|
||||
@@ -1067,8 +1057,8 @@ parsefor(Parser *p)
|
||||
int isunder = p->cur.kind == TK_UNDER;
|
||||
Tok la = peek(p);
|
||||
if (la.kind == TK_DOTDOT) {
|
||||
advance(p); /* consume IDENT/UNDER */
|
||||
advance(p); /* consume DOTDOT */
|
||||
advance(p);
|
||||
advance(p);
|
||||
Node *rng = newnode(p->a, N_FORRANGE, pp);
|
||||
rng->str = isunder ? "" : nm;
|
||||
rng->lhs = parseexpr(p);
|
||||
@@ -1283,8 +1273,6 @@ parseblock(Parser *p)
|
||||
return n;
|
||||
}
|
||||
|
||||
/* ------- top-level decls ------------------------------------------- */
|
||||
|
||||
/* `import encoding.utf8;` — the driver resolves the dotted path to a
|
||||
* directory; the checker only needs the leaf (`utf8`) as the module
|
||||
* bareword for n_use→decl disambiguation, mirroring Hare's
|
||||
|
||||
@@ -1,9 +1,3 @@
|
||||
/*
|
||||
* sym.c — symbol table. Plan 9-flavoured: a per-scope hashtable
|
||||
* chained to the parent scope. Lookup walks up. Duplicate definitions
|
||||
* within the same scope are flagged by the caller (we just refuse the
|
||||
* insert and return the first one).
|
||||
*/
|
||||
#include "ww.h"
|
||||
#include <string.h>
|
||||
|
||||
@@ -54,13 +48,6 @@ scope_lookup(Scope *s, const char *name)
|
||||
}
|
||||
|
||||
/*
|
||||
* scope_lookup_in_module — module-filtered chain walk.
|
||||
*
|
||||
* Same FNV bucket + hashnext chain + parent walk as scope_lookup,
|
||||
* plus a (b->mod != NULL && strcmp(b->mod, mod) == 0) filter. When
|
||||
* `mod` is NULL we fall back to unfiltered scope_lookup semantics,
|
||||
* so callers that don't care about disambiguation get the default.
|
||||
*
|
||||
* Used by resolve_typename and the cexpr N_DOT branch to pick the
|
||||
* right same-leaf-name type when two imports each export it
|
||||
* (`bufio.stream` vs `io.stream`).
|
||||
@@ -80,19 +67,13 @@ scope_lookup_in_module(Scope *s, const char *mod, const char *name)
|
||||
}
|
||||
|
||||
/*
|
||||
* scope_lookup_prefer — bare-leaf lookup with same-module preference.
|
||||
*
|
||||
* Walks the same FNV bucket + hashnext chain + parent walk scope_lookup
|
||||
* uses. Within each scope's bucket: Pass 1 prefers entries whose
|
||||
* `sym.mod` matches the caller's `mod`; Pass 2 falls back to the first
|
||||
* match regardless of mod (the existing scope_lookup semantics). We
|
||||
* only descend to the parent scope when the current scope has no
|
||||
* matching entry at all — so a local binding in a closer scope still
|
||||
* shadows a same-name fn from a parent scope, even when the parent
|
||||
* entry mod-matches.
|
||||
*
|
||||
* When `mod` is NULL we just call scope_lookup — there's no module
|
||||
* identity to prefer.
|
||||
* Within each scope's bucket: Pass 1 prefers entries whose `sym.mod`
|
||||
* matches the caller's `mod`; Pass 2 falls back to the first match
|
||||
* regardless of mod (the existing scope_lookup semantics). We only
|
||||
* descend to the parent scope when the current scope has no matching
|
||||
* entry at all — so a local binding in a closer scope still shadows a
|
||||
* same-name fn from a parent scope, even when the parent entry
|
||||
* mod-matches.
|
||||
*
|
||||
* Used at bare-leaf lookup sites inside a known current module so that
|
||||
* a bare `read` inside lib/os resolves to os.read rather than the
|
||||
@@ -117,15 +98,12 @@ scope_lookup_prefer(Scope *s, const char *mod, const char *name)
|
||||
}
|
||||
|
||||
/*
|
||||
* scope_lookup_type — kind-filtered bare-leaf lookup for type position.
|
||||
*
|
||||
* Same FNV bucket + hashnext chain + parent walk and same-module
|
||||
* preference as scope_lookup_prefer, but skips every Sym whose kind
|
||||
* isn't SK_TYPE and KEEPS scanning — so it returns the innermost
|
||||
* SK_TYPE of `name`, looking past a same-named value binding (SK_VAR/
|
||||
* SK_PARAM/SK_FN) that shadows it in a closer scope. ww keeps type and
|
||||
* value namespaces separate (wwstage already does; #225 conformance
|
||||
* gap): a param `off` must not hide the global `type off`.
|
||||
* Skips every Sym whose kind isn't SK_TYPE and KEEPS scanning — so it
|
||||
* returns the innermost SK_TYPE of `name`, looking past a same-named
|
||||
* value binding (SK_VAR/SK_PARAM/SK_FN) that shadows it in a closer
|
||||
* scope. ww keeps type and value namespaces separate (wwstage already
|
||||
* does; #225 conformance gap): a param `off` must not hide the global
|
||||
* `type off`.
|
||||
*/
|
||||
Sym *
|
||||
scope_lookup_type(Scope *s, const char *mod, const char *name)
|
||||
@@ -151,10 +129,8 @@ scope_define(Scope *s, const char *name, Skind k, Type *t, Node *decl)
|
||||
}
|
||||
|
||||
/*
|
||||
* scope_define_in_module — bucket insert with per-mod dedup.
|
||||
*
|
||||
* Same insertion as scope_define, but the duplicate-rejection key is
|
||||
* (name, mod) rather than name alone. This lets two imports each
|
||||
* The duplicate-rejection key is (name, mod) rather than name alone.
|
||||
* This lets two imports each
|
||||
* register their own `stream` SK_TYPE in the flat scope, and lets the
|
||||
* primary register `stream` (mod=NULL) alongside imported `stream`s.
|
||||
*
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
/*
|
||||
* 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>
|
||||
|
||||
@@ -13,7 +6,8 @@ struct kwent {
|
||||
Tkind kind;
|
||||
};
|
||||
|
||||
/* keep alphabetised, so kwlookup is easy to read. */
|
||||
/* 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 },
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
/*
|
||||
* type.c — Type values and structural equality.
|
||||
*
|
||||
* Built-in types are constructed once and exposed as globals so the
|
||||
* rest of the compiler can `==`-compare them. Compound types (ptr,
|
||||
* slice, array, fn, struct, chan) are constructed on demand and
|
||||
* de-duplicated when equality is cheap (only ptr/slice for now).
|
||||
* rest of the compiler can `==`-compare them.
|
||||
*/
|
||||
#include "ww.h"
|
||||
#include <string.h>
|
||||
@@ -147,8 +143,7 @@ type_named(Arena *a, const char *name, Type *under)
|
||||
return t;
|
||||
}
|
||||
|
||||
/* type_chase_named — walk the TY_NAMED.under chain to the deepest non-
|
||||
* named type. Chain-of-aliases (#22): `type b = a; type a = struct;`
|
||||
/* Chain-of-aliases (#22): `type b = a; type a = struct;`
|
||||
* stacks two TY_NAMED layers — a single peel leaves `t` pointing at
|
||||
* the inner alias (still TY_NAMED), so kind-gated arms (TY_STRUCT,
|
||||
* TY_SLICE, TY_TAGGED, TY_PTR) miss and the consumer silently falls
|
||||
@@ -372,7 +367,6 @@ type_assignable(Type *dst, Type *src)
|
||||
}
|
||||
}
|
||||
|
||||
/* Untyped → typed: only if the typed kind can hold the value. */
|
||||
if (type_isuntyped(src)) {
|
||||
Type *du = type_chase_named(dst);
|
||||
if (src->kind == TY_UNTYPED_INT && type_isnum(dst)) return 1;
|
||||
|
||||
14
cmd/wcc/ww.h
14
cmd/wcc/ww.h
@@ -1,6 +1,4 @@
|
||||
/*
|
||||
* ww.h — central header for libwcc.a (the ww frontend library).
|
||||
*
|
||||
* Plan 9 in spirit. This file mirrors cc/cc.h's role: one shared
|
||||
* header that declares everything every translation unit in the
|
||||
* frontend cares about.
|
||||
@@ -19,7 +17,6 @@
|
||||
/* version banner — printed by `ww -V` */
|
||||
#define WW_VERSION "0.0"
|
||||
|
||||
/* short integer aliases, Plan 9 / Hare-flavoured */
|
||||
typedef int8_t i8;
|
||||
typedef int16_t i16;
|
||||
typedef int32_t i32;
|
||||
@@ -29,7 +26,6 @@ typedef uint16_t u16;
|
||||
typedef uint32_t u32;
|
||||
typedef uint64_t u64;
|
||||
|
||||
/* forward decls — concrete shapes appear in their phases. */
|
||||
typedef struct Tok Tok;
|
||||
typedef struct Lex Lex;
|
||||
typedef struct Node Node;
|
||||
@@ -54,8 +50,6 @@ char *astrndup(Arena*, const char*, u64);
|
||||
char *aprintf(Arena*, const char*, ...);
|
||||
void freearena(Arena*);
|
||||
|
||||
/* err.c — diagnostics. Phase 0 has only fatal/warn; later phases add
|
||||
* source-location-bearing variants. */
|
||||
typedef struct Pos Pos;
|
||||
struct Pos {
|
||||
const char *file;
|
||||
@@ -72,15 +66,12 @@ void fatal(const char*, ...) __attribute__((noreturn, format(printf, 1, 2)));
|
||||
void errorf(Pos, const char*, ...) __attribute__((format(printf, 2, 3)));
|
||||
void warnf(Pos, const char*, ...) __attribute__((format(printf, 2, 3)));
|
||||
|
||||
/* tiny helpers */
|
||||
#define nelem(a) ((sizeof(a) / sizeof((a)[0])))
|
||||
|
||||
/* ---- lexer (lex.c, tok.c) ----------------------------------------- */
|
||||
typedef enum {
|
||||
/* zero is "no token" so memset-zero structs read sane */
|
||||
TK_NONE = 0,
|
||||
|
||||
/* trivial */
|
||||
TK_EOF,
|
||||
TK_ERR,
|
||||
TK_IDENT,
|
||||
@@ -117,7 +108,6 @@ typedef enum {
|
||||
TK_CONST, /* const binding */
|
||||
TK_UNDER, /* bare '_' discard */
|
||||
|
||||
/* punct + operators */
|
||||
TK_LPAREN, /* ( */
|
||||
TK_RPAREN, /* ) */
|
||||
TK_LBRACE, /* { */
|
||||
@@ -233,7 +223,6 @@ const char *tokname(Tkind); /* canonical spelling, e.g. "fn", "+=" */
|
||||
void tokprint(FILE*, Tok); /* one line, "%s:%d:%d: %s %q" */
|
||||
Tkind kwlookup(const char *s, u64 n); /* TK_NONE if not a keyword */
|
||||
|
||||
/* ---- AST (ast.c, parse.c) ----------------------------------------- */
|
||||
typedef enum {
|
||||
N_NONE = 0,
|
||||
|
||||
@@ -399,7 +388,6 @@ void parserinit(Parser*, Arena*, Lex*);
|
||||
Node *parsefile(Parser*);
|
||||
Node *parseexpr_top(Parser*); /* for testing: parse one expression */
|
||||
|
||||
/* ---- types (type.c) ----------------------------------------------- */
|
||||
typedef enum {
|
||||
TY_NONE = 0,
|
||||
TY_VOID,
|
||||
@@ -525,7 +513,6 @@ int type_isuntyped(Type *t);
|
||||
int type_assignable(Type *dst, Type *src);
|
||||
Type *type_default(Type *t); /* untyped → default concrete */
|
||||
|
||||
/* ---- symbols (sym.c) ---------------------------------------------- */
|
||||
typedef enum {
|
||||
SK_NONE = 0,
|
||||
SK_VAR,
|
||||
@@ -580,7 +567,6 @@ Sym *scope_lookup_in_module(Scope*, const char *mod, const char *name);
|
||||
Sym *scope_lookup_prefer(Scope*, const char *mod, const char *name);
|
||||
Sym *scope_lookup_type(Scope*, const char *mod, const char *name);
|
||||
|
||||
/* ---- checker (check.c) -------------------------------------------- */
|
||||
typedef struct Checker Checker;
|
||||
struct Checker {
|
||||
Arena *a;
|
||||
|
||||
Reference in New Issue
Block a user