398 lines
12 KiB
Plaintext
398 lines
12 KiB
Plaintext
// Port of cmd/wcc/ast.c (Node defs + printer).
|
|
//
|
|
// Calling-convention shim: same as tok/lex — `node` is too big to pass
|
|
// by value (8 *node pointers + 2 strs + a few ints), so callers always
|
|
// hand around `*node`. Only `newnode` allocates and returns a *node.
|
|
|
|
package syntax;
|
|
|
|
import os;
|
|
import strconv;
|
|
|
|
// Mirror of the C `Nkind` enum in cmd/wcc/ww.h (rule-6 data-shape
|
|
// mirror). Numeric values are explicit and must stay in sync with the
|
|
// C side. Tail-appended entries (TYPETEST onward) preserve every prior
|
|
// N_* value.
|
|
export type nkind = enum i32 {
|
|
N_NONE = 0,
|
|
|
|
N_INTLIT = 1,
|
|
N_FLOATLIT = 2,
|
|
N_STRLIT = 3,
|
|
N_RUNELIT = 4,
|
|
N_TRUE = 5,
|
|
N_FALSE = 6,
|
|
N_NIL = 7,
|
|
N_IDENT = 8,
|
|
|
|
N_BIN = 9,
|
|
N_UN = 10,
|
|
N_CALL = 11,
|
|
N_INDEX = 12,
|
|
N_DOT = 13,
|
|
N_CAST = 14,
|
|
N_STRUCTLIT = 15,
|
|
N_ARRLIT = 16,
|
|
N_FIELD = 17,
|
|
N_ASSIGN = 18,
|
|
N_ALLOC = 19,
|
|
N_FREE = 20,
|
|
N_RECV = 21,
|
|
N_SLICE = 22,
|
|
N_SPREAD = 23,
|
|
|
|
N_BLOCK = 24,
|
|
N_EXPRSTMT = 25,
|
|
N_LET = 26,
|
|
N_RETURN = 27,
|
|
N_IF = 28,
|
|
N_FOR = 29,
|
|
N_FORRANGE = 30,
|
|
N_DEFER = 31,
|
|
N_BREAK = 32,
|
|
N_CONTINUE = 33,
|
|
N_SWITCH = 34,
|
|
N_CASE = 35,
|
|
|
|
N_FILE = 36,
|
|
N_USE = 37,
|
|
N_DEF = 38,
|
|
N_TYPEDECL = 39,
|
|
N_FNDECL = 40,
|
|
N_PARAM = 41,
|
|
|
|
N_TNAME = 42,
|
|
N_TPTR = 43,
|
|
N_TSLICE = 44,
|
|
N_TARRAY = 45,
|
|
N_TFN = 46,
|
|
N_TSTRUCT = 47,
|
|
N_TFIELD = 48,
|
|
N_TCHAN = 49,
|
|
|
|
N_ATTR = 50,
|
|
N_TTUPLE = 51,
|
|
N_TTAGGED = 52,
|
|
N_TUPLE = 53,
|
|
N_MATCH = 54,
|
|
N_MCASE = 55,
|
|
N_TRYPROP = 56,
|
|
N_TRYUNW = 57,
|
|
N_MLET = 58,
|
|
N_MASSIGN = 59,
|
|
|
|
N_TYPETEST = 60,
|
|
N_TYPEASSERT = 61,
|
|
N_VOIDLIT = 62,
|
|
N_TBANG = 63,
|
|
N_YIELD = 64,
|
|
N_TENUM = 65,
|
|
N_TENUMMEMBER = 66,
|
|
|
|
// N_TPARAM — chain wrapper for N_TTUPLE.list elements. Mirror of
|
|
// cstage's Tparam (cmd/wcc/check.c:1437-1451) lifted to the AST so
|
|
// `exprtype` can return shared element-type nodes (sym.decl.lhs,
|
|
// struct field's .lhs, another N_TTUPLE's .list element) without
|
|
// corrupting source ASTs by reusing their .next. .lhs holds the
|
|
// element type AST (possibly shared); .next chains within the
|
|
// parent N_TTUPLE.list. Cstage keeps Tparam at the Type-layer; ww
|
|
// has no separate type layer for tuple chains, so the wrapper sits
|
|
// at the AST layer. Other node fields are unused. Never appears
|
|
// outside an N_TTUPLE.list; astprint unwraps transparently to keep
|
|
// the 990 -a byte-diff against cstage.
|
|
N_TPARAM = 67,
|
|
|
|
N_LAST = 68,
|
|
};
|
|
|
|
export type node = struct {
|
|
kind: nkind,
|
|
file: str,
|
|
line: i32,
|
|
col: i32,
|
|
op: tkind, // for nkind.N_BIN / nkind.N_UN / nkind.N_ASSIGN
|
|
str: str,
|
|
uval: u64,
|
|
fval: f64,
|
|
lhs: *node,
|
|
rhs: *node,
|
|
cond: *node,
|
|
body: *node,
|
|
els: *node,
|
|
list: *node,
|
|
next: *node,
|
|
attr: *node,
|
|
exported: i32, // bool — `export` keyword present
|
|
packed: i32, // N_TSTRUCT: `struct @packed` — no padding (harec ast.h:95)
|
|
type_: *void, // filled in by checker; type.ww treats it as *tinfo
|
|
tsuffix: str, // typed numeric literal suffix ("i32", "u64", ...)
|
|
nmod: str, // originating module from `// MODULE: foo`; "" if none
|
|
usesource: str, // N_USE: immutable dotted source spelling
|
|
usepath: str, // N_USE: canonical vendor-expanded identity
|
|
usealias: str, // N_USE: explicit file-local alias, or empty
|
|
usefile: str, // N_USE: first spec token (alias, otherwise path)
|
|
useline: i32,
|
|
usecol: i32,
|
|
usepathfile: str,// N_USE: first path token, independent of alias
|
|
usepathline: i32,
|
|
usepathcol: i32,
|
|
usepkgname: str,// N_USE: imported declared package name
|
|
useblank: i32, // N_USE: `_` spelling; no source binding
|
|
pkgname: str, // declared package name; independent of canonical nmod
|
|
sourceid: i32, // lexical source-file scope in an owner/export unit
|
|
used: i32, // N_USE: checker observed this file-local binding
|
|
initfn: i32, // special source `fn init`, absent from scope/API
|
|
initsynthetic: i32,// compiler-owned variable helper/package task
|
|
runtimeinit: i32,// module let lowered through an init helper
|
|
initorder: u64,// 1-based variable or init-function order
|
|
linksym: str, // raw compiler-private assembler symbol
|
|
refdecl: *node,// checker-resolved value declaration
|
|
initmark: u64,// checker-private initializer dependency walk mark
|
|
imported: i32, // M1 #22: decl reached via `//ww:module <path>` import
|
|
// boundary (vs root/primary); gates root-only bare main
|
|
};
|
|
|
|
export fn newnode(k: nkind, file: str, line: i32, col: i32) *node = {
|
|
// fval cast-init: 990's wwdump TK_FLOAT diff requires this file
|
|
// to tokenise identically through C and ww (lex.ww:382 has the
|
|
// same workaround for the cstage %g-formats vs ww-skips divergence).
|
|
let n: *node = alloc(node{kind=k, file=file, line=line, col=col, op=tkind.TK_NONE, str="", uval=0u64, fval=0: f64, lhs=nil, rhs=nil, cond=nil, body=nil, els=nil, list=nil, next=nil, attr=nil, exported=0, packed=0, type_=nil, tsuffix="", nmod="", usesource="", usepath="", usealias="", usefile="", useline=0, usecol=0, usepathfile="", usepathline=0, usepathcol=0, usepkgname="", useblank=0, pkgname="", sourceid=0, used=0, initfn=0, initsynthetic=0, runtimeinit=0, initorder=0u64, linksym="", refdecl=nil, initmark=0u64, imported=0})!;
|
|
return n;
|
|
};
|
|
|
|
export fn nkname(k: nkind) str = {
|
|
switch (k) {
|
|
case nkind.N_NONE: return "none";
|
|
case nkind.N_INTLIT: return "int";
|
|
case nkind.N_FLOATLIT: return "float";
|
|
case nkind.N_STRLIT: return "str";
|
|
case nkind.N_RUNELIT: return "rune";
|
|
case nkind.N_TRUE: return "true";
|
|
case nkind.N_FALSE: return "false";
|
|
case nkind.N_NIL: return "nil";
|
|
case nkind.N_IDENT: return "id";
|
|
|
|
case nkind.N_BIN: return "bin";
|
|
case nkind.N_UN: return "un";
|
|
case nkind.N_CALL: return "call";
|
|
case nkind.N_INDEX: return "index";
|
|
case nkind.N_DOT: return "dot";
|
|
case nkind.N_CAST: return "cast";
|
|
case nkind.N_STRUCTLIT: return "structlit";
|
|
case nkind.N_ARRLIT: return "arrlit";
|
|
case nkind.N_FIELD: return "field";
|
|
case nkind.N_ASSIGN: return "assign";
|
|
case nkind.N_ALLOC: return "alloc";
|
|
case nkind.N_FREE: return "free";
|
|
case nkind.N_RECV: return "recv";
|
|
case nkind.N_SLICE: return "slice";
|
|
case nkind.N_SPREAD: return "spread";
|
|
|
|
case nkind.N_BLOCK: return "block";
|
|
case nkind.N_EXPRSTMT: return "exprstmt";
|
|
case nkind.N_LET: return "let";
|
|
case nkind.N_RETURN: return "return";
|
|
case nkind.N_IF: return "if";
|
|
case nkind.N_FOR: return "for";
|
|
case nkind.N_FORRANGE: return "forrange";
|
|
case nkind.N_DEFER: return "defer";
|
|
case nkind.N_BREAK: return "break";
|
|
case nkind.N_CONTINUE: return "continue";
|
|
case nkind.N_SWITCH: return "switch";
|
|
case nkind.N_CASE: return "case";
|
|
|
|
case nkind.N_FILE: return "file";
|
|
case nkind.N_USE: return "use";
|
|
case nkind.N_DEF: return "def";
|
|
case nkind.N_TYPEDECL: return "typedecl";
|
|
case nkind.N_FNDECL: return "fn";
|
|
case nkind.N_PARAM: return "param";
|
|
|
|
case nkind.N_TNAME: return "tname";
|
|
case nkind.N_TPTR: return "tptr";
|
|
case nkind.N_TSLICE: return "tslice";
|
|
case nkind.N_TARRAY: return "tarray";
|
|
case nkind.N_TFN: return "tfn";
|
|
case nkind.N_TSTRUCT: return "tstruct";
|
|
case nkind.N_TFIELD: return "tfield";
|
|
case nkind.N_TCHAN: return "tchan";
|
|
|
|
case nkind.N_ATTR: return "attr";
|
|
case nkind.N_TTUPLE: return "ttuple";
|
|
case nkind.N_TTAGGED: return "ttagged";
|
|
case nkind.N_TUPLE: return "tuple";
|
|
case nkind.N_MATCH: return "match";
|
|
case nkind.N_MCASE: return "mcase";
|
|
case nkind.N_TRYPROP: return "tryprop";
|
|
case nkind.N_TRYUNW: return "tryunw";
|
|
case nkind.N_MLET: return "mlet";
|
|
case nkind.N_MASSIGN: return "massign";
|
|
|
|
case nkind.N_TYPETEST: return "typetest";
|
|
case nkind.N_TYPEASSERT: return "typeassert";
|
|
case nkind.N_VOIDLIT: return "voidlit";
|
|
case nkind.N_TBANG: return "tbang";
|
|
case nkind.N_YIELD: return "yield";
|
|
case nkind.N_TENUM: return "tenum";
|
|
case nkind.N_TENUMMEMBER: return "tenummember";
|
|
case nkind.N_TPARAM: return "tparam";
|
|
case nkind.N_LAST: return "last";
|
|
};
|
|
return "?";
|
|
};
|
|
|
|
fn ind(fd: i32, d: i32) void = {
|
|
let i: i32 = 0;
|
|
for (i < d) {
|
|
os.write(fd, " ".ptr, 2u64);
|
|
i += 1;
|
|
};
|
|
};
|
|
|
|
fn putc1(fd: i32, b: u8) void = {
|
|
let buf: [1]u8;
|
|
buf[0] = b;
|
|
os.write(fd, buf.ptr, 1u64);
|
|
};
|
|
|
|
fn putq(fd: i32, s: str) void = {
|
|
putc1(fd, '"');
|
|
let i: i32 = 0;
|
|
for (i < s.len) {
|
|
let c: u8 = s[i];
|
|
if (c == '"') {
|
|
os.write(fd, "\\\"".ptr, 2u64);
|
|
} else { if (c == '\\') {
|
|
os.write(fd, "\\\\".ptr, 2u64);
|
|
} else { if (c == '\n') {
|
|
os.write(fd, "\\n".ptr, 2u64);
|
|
} else { if (c == '\t') {
|
|
os.write(fd, "\\t".ptr, 2u64);
|
|
} else { if (c < 32u8) {
|
|
let hi: u8 = c >> 4u8;
|
|
let lo: u8 = c & 15u8;
|
|
let h: u8 = 0u8;
|
|
let l: u8 = 0u8;
|
|
if (hi < 10u8) { h = hi + 48u8; } else { h = (hi - 10u8) + 97u8; };
|
|
if (lo < 10u8) { l = lo + 48u8; } else { l = (lo - 10u8) + 97u8; };
|
|
let buf: [4]u8;
|
|
buf[0] = 92u8;
|
|
buf[1] = 120u8;
|
|
buf[2] = h;
|
|
buf[3] = l;
|
|
os.write(fd, buf.ptr, 4u64);
|
|
} else {
|
|
putc1(fd, c);
|
|
};};};};};
|
|
i += 1;
|
|
};
|
|
putc1(fd, 34u8);
|
|
};
|
|
|
|
fn pr(fd: i32, n: *node, d: i32) void = {
|
|
if (n == nil) {
|
|
ind(fd, d);
|
|
os.write(fd, "()\n".ptr, 3u64);
|
|
return;
|
|
};
|
|
// N_TPARAM wraps an N_TTUPLE.list element so exprtype can return
|
|
// shared element-type nodes without corrupting their .next chain.
|
|
// Cstage has no AST-level wrapper, so unwrap here to keep the 990
|
|
// -a byte-diff with cstage's astprint.
|
|
if (n.kind == nkind.N_TPARAM) {
|
|
pr(fd, n.lhs, d);
|
|
return;
|
|
};
|
|
ind(fd, d);
|
|
putc1(fd, '(');
|
|
let nm: str = nkname(n.kind);
|
|
os.write(fd, nm.ptr, nm.len: u64);
|
|
|
|
if (n.kind == nkind.N_INTLIT) {
|
|
putc1(fd, 32u8);
|
|
let s: str = strconv.u64tos(n.uval, strconv.base.DEC);
|
|
os.write(fd, s.ptr, s.len: u64);
|
|
} else { if (n.kind == nkind.N_RUNELIT) {
|
|
putc1(fd, 32u8);
|
|
let s: str = strconv.u64tos(n.uval, strconv.base.DEC);
|
|
os.write(fd, s.ptr, s.len: u64);
|
|
} else { if (
|
|
n.kind == nkind.N_STRLIT ||
|
|
n.kind == nkind.N_IDENT ||
|
|
n.kind == nkind.N_USE ||
|
|
n.kind == nkind.N_DOT ||
|
|
n.kind == nkind.N_DEF ||
|
|
n.kind == nkind.N_TYPEDECL ||
|
|
n.kind == nkind.N_FNDECL ||
|
|
n.kind == nkind.N_PARAM ||
|
|
n.kind == nkind.N_LET ||
|
|
n.kind == nkind.N_TNAME ||
|
|
n.kind == nkind.N_TFIELD ||
|
|
n.kind == nkind.N_TENUMMEMBER ||
|
|
n.kind == nkind.N_FIELD ||
|
|
n.kind == nkind.N_ATTR
|
|
) {
|
|
// Match C ast.c: print the str field whenever it's non-nil,
|
|
// even if its length is zero (e.g. an empty STRLIT prints
|
|
// `(str ""`).
|
|
let s: str = n.str;
|
|
if (s.ptr != nil) {
|
|
putc1(fd, 32u8);
|
|
putq(fd, s);
|
|
};
|
|
} else { if (
|
|
n.kind == nkind.N_BIN ||
|
|
n.kind == nkind.N_UN ||
|
|
n.kind == nkind.N_ASSIGN
|
|
) {
|
|
putc1(fd, 32u8);
|
|
let on: str = tokname(n.op);
|
|
os.write(fd, on.ptr, on.len: u64);
|
|
};};};};
|
|
|
|
if (n.kind == nkind.N_FNDECL) {
|
|
if (n.exported != 0) { os.write(fd, " export".ptr, 7u64); };
|
|
};
|
|
if (n.kind == nkind.N_DEF) {
|
|
if (n.exported != 0) { os.write(fd, " export".ptr, 7u64); };
|
|
};
|
|
if (n.kind == nkind.N_TYPEDECL) {
|
|
if (n.exported != 0) { os.write(fd, " export".ptr, 7u64); };
|
|
};
|
|
putc1(fd, '\n');
|
|
|
|
if (n.attr != nil) {
|
|
ind(fd, d + 1);
|
|
os.write(fd, "(@\n".ptr, 3u64);
|
|
let m: *node = n.attr;
|
|
for (m != nil) {
|
|
pr(fd, m, d + 2);
|
|
m = m.next;
|
|
};
|
|
ind(fd, d + 1);
|
|
os.write(fd, ")\n".ptr, 2u64);
|
|
};
|
|
if (n.lhs != nil) { pr(fd, n.lhs, d + 1); };
|
|
if (n.rhs != nil) { pr(fd, n.rhs, d + 1); };
|
|
if (n.cond != nil) { pr(fd, n.cond, d + 1); };
|
|
if (n.body != nil) { pr(fd, n.body, d + 1); };
|
|
if (n.els != nil) { pr(fd, n.els, d + 1); };
|
|
if (n.list != nil) {
|
|
ind(fd, d + 1);
|
|
os.write(fd, "(list\n".ptr, 6u64);
|
|
let m: *node = n.list;
|
|
for (m != nil) {
|
|
pr(fd, m, d + 2);
|
|
m = m.next;
|
|
};
|
|
ind(fd, d + 1);
|
|
os.write(fd, ")\n".ptr, 2u64);
|
|
};
|
|
ind(fd, d);
|
|
os.write(fd, ")\n".ptr, 2u64);
|
|
};
|
|
|
|
export fn astprint(fd: i32, n: *node) void = {
|
|
pr(fd, n, 0);
|
|
};
|