ww: rename toolchain to w-prefix + hare-style build/run/test driver
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.
This commit is contained in:
462
cmd/wcc/ww.h
Normal file
462
cmd/wcc/ww.h
Normal file
@@ -0,0 +1,462 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* Phases add to this file (lexer/parser/checker), they do not branch
|
||||
* a sibling header. There is one frontend; there is one ww.h.
|
||||
*/
|
||||
#ifndef WW_H
|
||||
#define WW_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
|
||||
/* 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;
|
||||
typedef int64_t i64;
|
||||
typedef uint8_t u8;
|
||||
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;
|
||||
typedef struct Sym Sym;
|
||||
typedef struct Type Type;
|
||||
typedef struct Scope Scope;
|
||||
typedef struct Arena Arena;
|
||||
|
||||
/* mem.c — bump arena (no free; reset/destroy at end of phase) */
|
||||
struct Arena {
|
||||
u8 *buf; /* base of current chunk */
|
||||
u64 off; /* bytes used in current chunk */
|
||||
u64 cap; /* capacity of current chunk */
|
||||
struct Arena *next; /* older chunks (linked list, head = current) */
|
||||
u64 total; /* across all chunks, debug only */
|
||||
};
|
||||
|
||||
Arena *newarena(void);
|
||||
void *amalloc(Arena*, u64); /* zeroed, aligned to 16 */
|
||||
char *astrdup(Arena*, const char*);
|
||||
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;
|
||||
i32 line;
|
||||
i32 col;
|
||||
};
|
||||
|
||||
extern Pos noPos;
|
||||
extern int nerrors;
|
||||
extern int nwarnings;
|
||||
extern FILE *errout;
|
||||
|
||||
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,
|
||||
TK_INT,
|
||||
TK_FLOAT,
|
||||
TK_RUNE,
|
||||
TK_STR,
|
||||
|
||||
/* keywords — stay grouped, used by tok.c kwtab */
|
||||
TK_FN,
|
||||
TK_LET,
|
||||
TK_DEF,
|
||||
TK_IF,
|
||||
TK_ELSE,
|
||||
TK_FOR,
|
||||
TK_SWITCH,
|
||||
TK_CASE,
|
||||
TK_RETURN,
|
||||
TK_USE,
|
||||
TK_TYPE,
|
||||
TK_STRUCT,
|
||||
TK_DEFER,
|
||||
TK_BREAK,
|
||||
TK_CONTINUE,
|
||||
TK_EXPORT,
|
||||
TK_PROC,
|
||||
TK_CHAN,
|
||||
TK_NIL,
|
||||
TK_TRUE,
|
||||
TK_FALSE,
|
||||
TK_AS, /* reserved for future cast spelling, not active */
|
||||
TK_STATIC, /* Hare-style storage-class qualifier */
|
||||
TK_MATCH, /* match expression head */
|
||||
|
||||
/* punct + operators */
|
||||
TK_LPAREN, /* ( */
|
||||
TK_RPAREN, /* ) */
|
||||
TK_LBRACE, /* { */
|
||||
TK_RBRACE, /* } */
|
||||
TK_LBRACK, /* [ */
|
||||
TK_RBRACK, /* ] */
|
||||
TK_COMMA, /* , */
|
||||
TK_SEMI, /* ; */
|
||||
TK_COLON, /* : */
|
||||
TK_DOT, /* . */
|
||||
TK_ELLIPSIS, /* ... */
|
||||
TK_DOTDOT, /* .. (range op) */
|
||||
TK_AT, /* @ */
|
||||
TK_QUESTION, /* ? */
|
||||
|
||||
TK_ASSIGN, /* = */
|
||||
TK_PLUSEQ, /* += */
|
||||
TK_MINUSEQ, /* -= */
|
||||
TK_STAREQ, /* *= */
|
||||
TK_SLASHEQ, /* /= */
|
||||
TK_PERCENTEQ, /* %= */
|
||||
TK_AMPEQ, /* &= */
|
||||
TK_PIPEEQ, /* |= */
|
||||
TK_CARETEQ, /* ^= */
|
||||
TK_LSHIFTEQ, /* <<= */
|
||||
TK_RSHIFTEQ, /* >>= */
|
||||
|
||||
TK_PLUS, /* + */
|
||||
TK_MINUS, /* - */
|
||||
TK_STAR, /* * */
|
||||
TK_SLASH, /* / */
|
||||
TK_PERCENT, /* % */
|
||||
TK_AMP, /* & */
|
||||
TK_PIPE, /* | */
|
||||
TK_CARET, /* ^ */
|
||||
TK_TILDE, /* ~ */
|
||||
TK_LSHIFT, /* << */
|
||||
TK_RSHIFT, /* >> */
|
||||
|
||||
TK_EQ, /* == */
|
||||
TK_NEQ, /* != */
|
||||
TK_LT, /* < */
|
||||
TK_LE, /* <= */
|
||||
TK_GT, /* > */
|
||||
TK_GE, /* >= */
|
||||
|
||||
TK_AND, /* && */
|
||||
TK_OR, /* || */
|
||||
TK_NOT, /* ! */
|
||||
|
||||
TK_LARROW, /* <- (chan recv) */
|
||||
TK_ARROW, /* -> (reserved) */
|
||||
TK_FATARROW, /* => (match arms) */
|
||||
|
||||
TK_LAST /* sentinel for tables */
|
||||
} Tkind;
|
||||
|
||||
struct Tok {
|
||||
Tkind kind;
|
||||
Pos pos;
|
||||
const char *text; /* lexeme (arena-owned, NUL-terminated) */
|
||||
u64 tlen; /* byte length of lexeme (sans NUL) */
|
||||
/* numeric values pre-parsed; string/rune unescaped */
|
||||
union {
|
||||
u64 uval; /* TK_INT, TK_RUNE */
|
||||
double fval; /* TK_FLOAT */
|
||||
} v;
|
||||
/* for typed numeric literals: "i32", "u8", "f64", ... or NULL. */
|
||||
const char *tsuffix;
|
||||
};
|
||||
|
||||
struct Lex {
|
||||
const char *file;
|
||||
const char *src; /* full source, NUL-terminated */
|
||||
u64 srclen;
|
||||
u64 pos; /* current byte offset */
|
||||
i32 line;
|
||||
i32 col;
|
||||
Arena *a; /* token-text arena */
|
||||
int errs;
|
||||
};
|
||||
|
||||
void lexinit(Lex*, Arena*, const char *file, const char *src, u64 len);
|
||||
Tok lexnext(Lex*);
|
||||
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,
|
||||
|
||||
/* literals */
|
||||
N_INTLIT,
|
||||
N_FLOATLIT,
|
||||
N_STRLIT,
|
||||
N_RUNELIT,
|
||||
N_TRUE,
|
||||
N_FALSE,
|
||||
N_NIL,
|
||||
N_IDENT,
|
||||
|
||||
/* expressions */
|
||||
N_BIN, /* op, lhs, rhs */
|
||||
N_UN, /* op, lhs */
|
||||
N_CALL, /* lhs=callee, list=args */
|
||||
N_INDEX, /* lhs=base, rhs=index */
|
||||
N_DOT, /* lhs=base, str=field */
|
||||
N_CAST, /* lhs=expr, rhs=type-expr */
|
||||
N_STRUCTLIT, /* lhs=type-expr, list=N_FIELD */
|
||||
N_ARRLIT, /* list=elements (for [a,b,...]) */
|
||||
N_FIELD, /* str=name, lhs=value */
|
||||
N_ASSIGN, /* op, lhs, rhs */
|
||||
N_ALLOC, /* lhs=expr, rhs=size-or-null */
|
||||
N_FREE, /* lhs=expr */
|
||||
N_RECV, /* lhs (chan recv: <-c) */
|
||||
N_SLICE, /* lhs=base, rhs=lo or NULL, cond=hi or NULL */
|
||||
N_SPREAD, /* lhs (variadic spread in arg position: e...) */
|
||||
|
||||
/* statements */
|
||||
N_BLOCK, /* list=stmts */
|
||||
N_EXPRSTMT, /* lhs=expr */
|
||||
N_LET, /* str=name, lhs=type-expr|NULL, rhs=init|NULL */
|
||||
N_RETURN, /* lhs=expr|NULL */
|
||||
N_IF, /* cond, body, els */
|
||||
N_FOR, /* lhs=init, cond, rhs=post, body */
|
||||
N_FORRANGE, /* str=elem name, lhs=slice expr, body=block */
|
||||
N_DEFER, /* lhs=expr */
|
||||
N_BREAK,
|
||||
N_CONTINUE,
|
||||
N_SWITCH, /* lhs=scrutinee, list=cases */
|
||||
N_CASE, /* list=exprs (empty=default), body */
|
||||
|
||||
/* declarations */
|
||||
N_FILE, /* list=top decls */
|
||||
N_USE, /* str=path */
|
||||
N_DEF, /* str=name, lhs=type|NULL, rhs=init */
|
||||
N_TYPEDECL, /* str=name, lhs=type-expr */
|
||||
N_FNDECL, /* str=name, list=params, lhs=ret-type, body|NULL */
|
||||
N_PARAM, /* str=name, lhs=type-expr */
|
||||
|
||||
/* type expressions */
|
||||
N_TNAME, /* str */
|
||||
N_TPTR, /* lhs=inner */
|
||||
N_TSLICE, /* lhs=inner */
|
||||
N_TARRAY, /* lhs=element, rhs=len-expr */
|
||||
N_TFN, /* list=params, lhs=ret */
|
||||
N_TSTRUCT, /* list=fields */
|
||||
N_TFIELD, /* str=name, lhs=type */
|
||||
N_TCHAN, /* lhs=element */
|
||||
|
||||
/* attribute on a decl */
|
||||
N_ATTR, /* str=name, list=args */
|
||||
|
||||
/* multi-value (tuple) plumbing */
|
||||
N_TTUPLE, /* type expr: (T1, T2, ...). list = element type exprs */
|
||||
N_TTAGGED, /* type expr: (T1 | T2 | ...). list = variant type exprs */
|
||||
N_TUPLE, /* expr: (e1, e2, ...). list = element exprs */
|
||||
N_MATCH, /* match (lhs) { list of cases }; cases are N_MCASE */
|
||||
N_MCASE, /* str=binding name (or NULL), lhs=variant type expr or NULL, body */
|
||||
N_TRYPROP, /* lhs? — propagate error variant */
|
||||
N_TRYUNW, /* lhs! — abort on error variant */
|
||||
N_MLET, /* let a, b = expr; list = N_LET stubs (str, lhs=type), rhs = expr */
|
||||
N_MASSIGN, /* a, b = expr; list = lvalue exprs, rhs = expr */
|
||||
|
||||
N_LAST
|
||||
} Nkind;
|
||||
|
||||
struct Node {
|
||||
Nkind kind;
|
||||
Pos pos;
|
||||
Tkind op; /* for N_BIN/N_UN/N_ASSIGN */
|
||||
const char *str; /* identifier/literal/name/path */
|
||||
u64 strlen;
|
||||
u64 uval; /* int/rune lit */
|
||||
double fval; /* float lit */
|
||||
Node *lhs;
|
||||
Node *rhs;
|
||||
Node *cond;
|
||||
Node *body;
|
||||
Node *els;
|
||||
Node *list; /* head of singly-linked sibling chain */
|
||||
Node *next; /* sibling link inside `list` */
|
||||
Node *attr; /* @attribute chain (N_ATTR list) */
|
||||
int export;
|
||||
Type *type; /* filled in by checker */
|
||||
const char *tsuffix; /* typed numeric literal suffix */
|
||||
};
|
||||
|
||||
Node *newnode(Arena*, Nkind, Pos);
|
||||
void astprint(FILE*, Node*); /* s-expr, deterministic, one-line per node */
|
||||
|
||||
typedef struct Parser Parser;
|
||||
struct Parser {
|
||||
Lex *l;
|
||||
Arena *a;
|
||||
Tok cur;
|
||||
Tok la; /* one-token lookahead buffer */
|
||||
int hasla;
|
||||
int errs;
|
||||
int nocast; /* in case-selector ctx, ':' is a separator */
|
||||
};
|
||||
|
||||
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,
|
||||
TY_BOOL,
|
||||
TY_RUNE,
|
||||
TY_I8, TY_I16, TY_I32, TY_I64,
|
||||
TY_U8, TY_U16, TY_U32, TY_U64,
|
||||
TY_UINT, TY_INT,
|
||||
TY_UINTPTR,
|
||||
TY_F32, TY_F64,
|
||||
TY_STR,
|
||||
TY_PTR,
|
||||
TY_SLICE,
|
||||
TY_ARRAY,
|
||||
TY_STRUCT,
|
||||
TY_FN,
|
||||
TY_CHAN,
|
||||
TY_NAMED,
|
||||
TY_TUPLE,
|
||||
TY_TAGGED, /* (T1 | T2 | ...) — Hare-style sum type */
|
||||
TY_ERR,
|
||||
/* untyped constants (not surfaced to users; checker-internal) */
|
||||
TY_UNTYPED_INT,
|
||||
TY_UNTYPED_FLOAT,
|
||||
TY_UNTYPED_STR,
|
||||
TY_UNTYPED_RUNE,
|
||||
TY_UNTYPED_BOOL,
|
||||
TY_UNTYPED_NIL
|
||||
} TypeKind;
|
||||
|
||||
typedef struct Tfield Tfield;
|
||||
struct Tfield {
|
||||
const char *name;
|
||||
Type *type;
|
||||
u64 offset;
|
||||
Tfield *next;
|
||||
};
|
||||
|
||||
typedef struct Tparam Tparam;
|
||||
struct Tparam {
|
||||
const char *name;
|
||||
Type *type;
|
||||
Tparam *next;
|
||||
};
|
||||
|
||||
struct Type {
|
||||
TypeKind kind;
|
||||
u64 size;
|
||||
u64 align;
|
||||
Type *sub; /* ptr/slice/array/chan element */
|
||||
u64 alen; /* array length */
|
||||
Tfield *fields;/* struct */
|
||||
Tparam *params;/* fn */
|
||||
Type *ret; /* fn */
|
||||
int variadic;
|
||||
const char *name; /* named alias / debug */
|
||||
Type *under; /* underlying resolved type for NAMED */
|
||||
};
|
||||
|
||||
extern Type *ty_void, *ty_bool, *ty_rune;
|
||||
extern Type *ty_i8, *ty_i16, *ty_i32, *ty_i64;
|
||||
extern Type *ty_u8, *ty_u16, *ty_u32, *ty_u64;
|
||||
extern Type *ty_int, *ty_uint, *ty_uintptr;
|
||||
extern Type *ty_f32, *ty_f64, *ty_str;
|
||||
extern Type *ty_err;
|
||||
extern Type *ty_untyped_int, *ty_untyped_float, *ty_untyped_str;
|
||||
extern Type *ty_untyped_rune, *ty_untyped_bool, *ty_untyped_nil;
|
||||
|
||||
void typesinit(Arena*);
|
||||
Type *newtype(Arena*, TypeKind);
|
||||
Type *type_ptr(Arena*, Type *sub);
|
||||
Type *type_slice(Arena*, Type *sub);
|
||||
Type *type_array(Arena*, Type *sub, u64 len);
|
||||
Type *type_chan(Arena*, Type *sub);
|
||||
Type *type_named(Arena*, const char *name, Type *under);
|
||||
const char *type_name(Arena*, Type*); /* arena'd debug string */
|
||||
int type_eq(Type *a, Type *b); /* structural equality */
|
||||
int type_isint(Type *t);
|
||||
int type_isfloat(Type *t);
|
||||
int type_isnum(Type *t);
|
||||
int type_isunsigned(Type *t);
|
||||
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,
|
||||
SK_PARAM,
|
||||
SK_DEF,
|
||||
SK_TYPE,
|
||||
SK_FN,
|
||||
SK_USE,
|
||||
SK_FIELD /* not stored in scope; used by check internally */
|
||||
} Skind;
|
||||
|
||||
struct Sym {
|
||||
const char *name;
|
||||
Skind kind;
|
||||
Type *type;
|
||||
Node *decl;
|
||||
int exported;
|
||||
Sym *next; /* iteration */
|
||||
Sym *hashnext; /* bucket chain */
|
||||
Scope *scope;
|
||||
};
|
||||
|
||||
struct Scope {
|
||||
Scope *parent;
|
||||
Sym *first, *last;
|
||||
Sym **buckets;
|
||||
u64 nbuckets;
|
||||
Arena *a;
|
||||
};
|
||||
|
||||
Scope *newscope(Arena*, Scope *parent);
|
||||
Sym *scope_define(Scope*, const char *name, Skind, Type*, Node *decl);
|
||||
Sym *scope_lookup(Scope*, const char *name); /* walk up parents */
|
||||
Sym *scope_lookup_local(Scope*, const char *name);
|
||||
|
||||
/* ---- checker (check.c) -------------------------------------------- */
|
||||
typedef struct Checker Checker;
|
||||
struct Checker {
|
||||
Arena *a;
|
||||
Scope *top; /* file scope */
|
||||
Scope *cur; /* current scope */
|
||||
Type *ret; /* expected return type of current fn (or NULL) */
|
||||
int loops; /* nesting count for break/continue */
|
||||
int errs;
|
||||
};
|
||||
|
||||
void check_init(Checker*, Arena*);
|
||||
void check_file(Checker*, Node *file);
|
||||
|
||||
#endif /* WW_H */
|
||||
Reference in New Issue
Block a user