Line data Source code
1 : // wok_ast -- THE SCHEMA. Every node's fields are written exactly once, here.
2 : //
3 : // From this one list the build derives: the tag enum, the per-node slot
4 : // indices, typed accessors and setters, and a descriptor table that the dump,
5 : // the reader and the coverage bitmap all walk generically. Adding a node means
6 : // adding one line; forgetting to teach the dump about it is impossible,
7 : // because the dump does not know about nodes individually.
8 : //
9 : // Each line says TWO things: the family the node belongs to, and -- per field --
10 : // the family that field demands. The second used to live only in wok_parse.c,
11 : // which meant the reader could not tell an expression from a type and a
12 : // corrupted dump read back as a different program. Now it can. A node added
13 : // here without a family does not compile; a family that disagrees with what the
14 : // parser builds fails in test_generative at startup, by name.
15 : //
16 : // Nodes are UNIFORM: a header plus a flexible array of slots. That trades a
17 : // little static typing for a schema a machine can walk, and two things buy
18 : // some of it back: an accessor's C type comes from the field's CLASS, so a
19 : // NODE slot cannot be read as a span; and every accessor asserts the node's
20 : // TAG in a debug build, so E_App_fn(someTypeNode) fires rather than returning
21 : // a misread word. Neither checks the FAMILY -- that is the reader's job, in
22 : // wok_sexpr.c, on the way in.
23 :
24 : #pragma once
25 :
26 : // The prelude comes FIRST: it carries the POSIX feature-test macros, which
27 : // have no effect once a system header has been read. wok_base.h hard-errors
28 : // if it is reached too late.
29 : #include "wok_base.h"
30 :
31 : #include <assert.h>
32 : #include <stdbool.h>
33 : #include <stddef.h>
34 : #include <stdint.h>
35 :
36 : #include "wok_arena.h"
37 :
38 : typedef struct WokNode WokNode;
39 :
40 : typedef struct {
41 : u32 off, len;
42 : } WokSpan;
43 :
44 : typedef struct {
45 : WokNode **items;
46 : u32 n;
47 : } WokSeq;
48 :
49 : // A sequence slot stores ONLY the items pointer; its length lives inline, in
50 : // the word immediately before items. Without that, WokSeq's {ptr, uint32}
51 : // would drag the whole union to 16 bytes and every NODE, INT, NAME and FLAG
52 : // slot would pay 8 bytes of padding it never uses -- and 77% of nodes have
53 : // just one or two slots.
54 : typedef union {
55 : WokNode *node;
56 : WokNode **seq;
57 : WokSpan span;
58 : u64 num;
59 : bool flag;
60 : } WokSlot;
61 :
62 : static_assert(sizeof(WokSlot) == 8, "a slot is one word; see the note above");
63 :
64 : static inline WokSeq wok_seq_empty(void) {
65 : return (WokSeq){.items = nullptr, .n = 0};
66 : }
67 :
68 : // Rebuilds the fat {items, n} view from a stored pointer. The count sits at
69 : // items[-1]; an empty sequence is a null pointer.
70 1069210 : static inline WokSeq wok_seq_unpack(WokNode **items) {
71 754488 : if (items == nullptr) return wok_seq_empty();
72 820597 : return (WokSeq){.items = items, .n = (u32)(uptr)items[-1]};
73 : }
74 :
75 : struct WokNode {
76 : u32 off, len; // source span, for diagnostics only
77 : // WHICH node this is: a WokTag, and the index every generic walk takes into
78 : // wok_node_desc[] to find the field list it then walks -- the dump, the
79 : // reader, the coverage bitmap, the printer's overflow report. It is the one
80 : // field that turns a uniform header-plus-slots block back into a typed node.
81 : //
82 : // Spelled u16 rather than WokTag only because the enum is DERIVED from
83 : // WOK_NODES further down this file, so the name does not exist yet here. The
84 : // enum is already `: u16`, so nothing is narrowed by saying so.
85 : u16 tag;
86 : // The arity its tag declares, copied here by wok_node so a walk never has to
87 : // reach for the schema just to bound a loop.
88 : u16 nslots;
89 : // The four bytes of padding the slot array's 8-byte alignment already
90 : // forced. 0 means no comments; otherwise this indexes a WokTrivia table
91 : // (wok_trivia.h). It is NOT a slot: comments are not part of a program's
92 : // identity, so nothing derived from the schema -- dump, reader, coverage --
93 : // can see this field.
94 : u32 trivia;
95 : WokSlot slot[];
96 : };
97 :
98 : // The trivia index must stay free. If either of these fires the header grew,
99 : // and every node in every parse just paid for it.
100 : static_assert(sizeof(WokNode) == 16, "the node header is 16 bytes");
101 : static_assert(offsetof(WokNode, slot) == 16, "slots follow the header");
102 :
103 : // Field classes. NODE is required, OPT may be null, SEQ is a slice into the
104 : // arena, NAME and TEXT are views into the source, INT is a decoded literal or
105 : // a schema constant, FLAG is a decided bit.
106 : #define WOK_FIELD_CLASSES(X) \
107 : X(NODE) X(OPT) X(SEQ) X(NAME) X(TEXT) X(INT) X(FLAG)
108 :
109 : typedef enum : unsigned char {
110 : #define WOK_X(c) WFC_##c,
111 : WOK_FIELD_CLASSES(WOK_X)
112 : #undef WOK_X
113 : WOK_FIELD_CLASS_COUNT
114 : } WokFieldClass;
115 :
116 : // Field FAMILIES. The class says a field holds a child; the family says WHICH
117 : // KIND of child, and that is the half the schema used to leave to wok_parse.c.
118 : // Every node declares the family it BELONGS to, every NODE/OPT/SEQ field the
119 : // family it DEMANDS, and the s-expression reader compares the two -- so a dump
120 : // with a type where an expression belongs is rejected rather than silently
121 : // read back as a different program.
122 : //
123 : // NONE is for NAME, TEXT, INT and FLAG fields, which hold no child at all.
124 : // ERROR is the damage marker, and it is a WILDCARD: mk_err plants an error
125 : // node wherever a production gave up -- in a type, a pattern, an alternative,
126 : // a clause -- so a family that refused it would make a damaged parse's dump
127 : // unreadable, which is exactly when reading one back is worth something.
128 : #define WOK_FAMILIES(X) \
129 : X(NONE) X(ERROR) \
130 : /* the big four, plus the file and the block item */ \
131 : X(FILE) X(DECL) X(TYPE) X(PAT) X(EXPR) X(STMT) \
132 : /* names, and a dotted path of them */ \
133 : X(NAME) X(PATH) \
134 : /* every helper is its own family: each is demanded by ONE kind of slot */ \
135 : X(LHS) X(BINDLHS) X(SIGNAME) X(TYPARAM) X(CONDEF) X(FIELDTYPE) X(OPSIG) \
136 : X(FOREIGNMEM) X(ROWENTRY) X(FIELDPAT) X(CHAINOP) X(BIND) X(USEBIND) \
137 : X(ALT) X(CLAUSE) X(FIELD) X(FIXREL)
138 :
139 : typedef enum : unsigned char {
140 : #define WOK_X(f) WFAM_##f,
141 : WOK_FAMILIES(WOK_X)
142 : #undef WOK_X
143 : WOK_FAMILY_COUNT
144 : } WokFamily;
145 :
146 : // The three places a family is WIDER than one node kind. Each is a fact about
147 : // the grammar, not a loosening: parse_stmt's last arm is a bare expression, and
148 : // parse_bind takes either a prefix head or a pattern. Nothing else subsumes.
149 239598 : WOK_PURE static inline bool wok_family_accepts(WokFamily want, WokFamily got) {
150 239598 : if (want == got) return true;
151 2992 : if (got == WFAM_ERROR) return true; // damage stands anywhere; see above
152 2991 : if (want == WFAM_STMT && got == WFAM_EXPR) return true;
153 1220 : if (want == WFAM_BINDLHS && (got == WFAM_LHS || got == WFAM_PAT)) return true;
154 : return false;
155 : }
156 :
157 : // --------------------------------------------------------------- constants
158 :
159 : // H_Clause kinds. Four are keyword-headed; the fifth is spelled by a COMMA
160 : // and nothing else (D25 cut `once`), which is why classification can consult
161 : // no name, type or count -- see parse_clause.
162 : enum {
163 : WOK_CLAUSE_PLAIN = 0, // op p1 p2 -> e auto-resume, tail-resumptive
164 : WOK_CLAUSE_CONTROL = 1, // op p1, k -> e control clause; k after the `,`
165 : WOK_CLAUSE_RETURN = 2, // return p -> e the value clause
166 : WOK_CLAUSE_VAR = 3, // var cur = e a frame baton
167 : WOK_CLAUSE_ABORT = 4, // abort op p1 -> e never resumes; binds no k
168 : };
169 :
170 : // H_RowEntry kinds (spec 1.3 / D20).
171 : enum {
172 : WOK_ROW_SLOT = 0, // `State U64` designation-slot obligation (P2)
173 : WOK_ROW_ROLE = 1, // `(from : State U64)` role obligation, parenthesized
174 : WOK_ROW_VAR = 2, // `eff e` row variable
175 : };
176 :
177 : // D_Fixity associativity. There is no `none`: `fixity` requires one of the
178 : // two words, so an operator that was declared at all has an answer for its
179 : // own ties.
180 : enum {
181 : WOK_ASSOC_LEFT = 0,
182 : WOK_ASSOC_RIGHT = 1,
183 : };
184 :
185 : // H_FixRel senses. `a tighter than b` and `b looser than a` state the SAME
186 : // edge from opposite ends, and both spellings are kept because which one
187 : // reads better depends on which operator you are declaring.
188 : enum {
189 : WOK_FIXREL_TIGHTER = 0,
190 : WOK_FIXREL_LOOSER = 1,
191 : };
192 :
193 : // T_Transfer modes (surface.md section 3: the FFI transfer law).
194 : enum {
195 : WOK_TRANSFER_OWN = 0, // consumed by C
196 : WOK_TRANSFER_LEND = 1, // read-only view
197 : WOK_TRANSFER_COPY = 2, // duplicated across the boundary
198 : };
199 :
200 : // ------------------------------------------------------------- the schema
201 : //
202 : // One line per node: X(tag, FAMILY). The FIELDS macro takes the field-emitting
203 : // macro F and the tag T, so the generated names can be prefixed per node, and
204 : // each field reads F(T, CLASS, name, FAMILY) -- the class it stores, and the
205 : // family it demands of whatever fills it.
206 :
207 : #define WOK_NODES(X) \
208 : /* names and paths */ \
209 : X(N_Name, NAME) X(N_ModPath, PATH) \
210 : /* declarations */ \
211 : X(D_Module, DECL) X(D_Import, DECL) X(D_Type, DECL) X(D_Alias, DECL) \
212 : X(D_Effect, DECL) X(D_Class, DECL) X(D_Instance, DECL) \
213 : X(D_Foreign, DECL) X(D_ExternType, DECL) X(D_Sig, DECL) \
214 : X(D_Fixity, DECL) X(D_Equation, DECL) X(D_Error, ERROR) \
215 : /* declaration helpers */ \
216 : X(H_TyParam, TYPARAM) X(H_ConDef, CONDEF) X(H_FieldType, FIELDTYPE) \
217 : X(H_OpSig, OPSIG) X(H_ForeignMember, FOREIGNMEM) X(H_SigName, SIGNAME) \
218 : X(H_FixRel, FIXREL) \
219 : X(L_Prefix, LHS) X(L_Infix, LHS) \
220 : /* types */ \
221 : X(T_Var, TYPE) X(T_Con, TYPE) X(T_App, TYPE) X(T_Fun, TYPE) \
222 : X(T_Qual, TYPE) X(T_With, TYPE) X(T_List, TYPE) X(T_Tuple, TYPE) \
223 : X(T_Unit, TYPE) X(T_RowArg, TYPE) X(T_Transfer, TYPE) \
224 : X(H_RowEntry, ROWENTRY) \
225 : /* patterns */ \
226 : X(P_Var, PAT) X(P_Wild, PAT) X(P_Int, PAT) X(P_Str, PAT) X(P_Char, PAT) \
227 : X(P_Con, PAT) X(P_Cons, PAT) X(P_Tuple, PAT) X(P_List, PAT) \
228 : X(P_Unit, PAT) X(P_As, PAT) X(P_Record, PAT) X(H_FieldPat, FIELDPAT) \
229 : /* expressions */ \
230 : X(E_Var, EXPR) X(E_Con, EXPR) X(E_Int, EXPR) X(E_Str, EXPR) \
231 : X(E_Char, EXPR) X(E_Unit, EXPR) X(E_OpRef, EXPR) X(E_App, EXPR) \
232 : X(E_Chain, EXPR) X(E_Dot, EXPR) X(E_Neg, EXPR) X(E_List, EXPR) \
233 : X(E_Tuple, EXPR) X(E_Lambda, EXPR) X(E_LetIn, EXPR) X(E_HandleIn, EXPR) \
234 : X(E_UseIn, EXPR) X(E_If, EXPR) X(E_Case, EXPR) X(E_Handler, EXPR) \
235 : X(E_Assign, EXPR) X(E_Record, EXPR) X(E_Block, EXPR) \
236 : X(E_Error, ERROR) \
237 : /* statements (D11: an indented block is a SEQUENCE) */ \
238 : X(S_Let, STMT) X(S_Handle, STMT) X(S_Use, STMT) X(S_Discard, STMT) \
239 : /* expression helpers */ \
240 : X(H_ChainOp, CHAINOP) X(H_Bind, BIND) X(H_UseBind, USEBIND) \
241 : X(H_Alt, ALT) X(H_Clause, CLAUSE) X(H_Field, FIELD) \
242 : /* the file */ \
243 : X(W_File, FILE)
244 :
245 : // names ---------------------------------------------------------------------
246 : #define N_Name_FIELDS(F, T) F(T, NAME, text, NONE) F(T, FLAG, upper, NONE)
247 : #define N_ModPath_FIELDS(F, T) F(T, SEQ, parts, NAME)
248 :
249 : // declarations --------------------------------------------------------------
250 : #define D_Module_FIELDS(F, T) F(T, NODE, path, PATH)
251 : // names is empty for a plain import; alias is null unless `as A` was written.
252 : #define D_Import_FIELDS(F, T) \
253 : F(T, NODE, path, PATH) F(T, SEQ, names, NAME) F(T, OPT, alias, NAME)
254 : #define D_Type_FIELDS(F, T) \
255 : F(T, NAME, name, NONE) F(T, SEQ, params, TYPARAM) F(T, SEQ, cons, CONDEF)
256 : #define D_Alias_FIELDS(F, T) \
257 : F(T, NAME, name, NONE) F(T, SEQ, params, TYPARAM) F(T, NODE, body, TYPE)
258 : #define D_Effect_FIELDS(F, T) \
259 : F(T, NAME, name, NONE) F(T, SEQ, params, TYPARAM) F(T, SEQ, ops, OPSIG)
260 : #define D_Class_FIELDS(F, T) \
261 : F(T, NAME, name, NONE) F(T, SEQ, params, TYPARAM) F(T, SEQ, body, DECL)
262 : #define D_Instance_FIELDS(F, T) \
263 : F(T, OPT, ctx, TYPE) F(T, NAME, name, NONE) F(T, SEQ, args, TYPE) \
264 : F(T, SEQ, body, DECL)
265 : #define D_Foreign_FIELDS(F, T) \
266 : F(T, NAME, name, NONE) F(T, TEXT, lib, NONE) F(T, SEQ, members, FOREIGNMEM)
267 : #define D_ExternType_FIELDS(F, T) \
268 : F(T, NAME, name, NONE) F(T, SEQ, params, TYPARAM)
269 : // `extern` marks a compiler hole: analyses trust the MARKER, never a name.
270 : #define D_Sig_FIELDS(F, T) \
271 : F(T, SEQ, names, SIGNAME) F(T, NODE, type, TYPE) F(T, FLAG, is_extern, NONE)
272 : #define D_Equation_FIELDS(F, T) \
273 : F(T, NODE, lhs, LHS) F(T, NODE, body, EXPR) F(T, SEQ, wheres, DECL)
274 : #define D_Error_FIELDS(F, T) F(T, TEXT, text, NONE)
275 :
276 : #define H_TyParam_FIELDS(F, T) F(T, NAME, name, NONE) F(T, FLAG, is_row, NONE)
277 : // name is empty for the elided record form `{ x : U64 }`.
278 : #define H_ConDef_FIELDS(F, T) \
279 : F(T, NAME, name, NONE) F(T, SEQ, args, TYPE) F(T, SEQ, fields, FIELDTYPE) \
280 : F(T, FLAG, is_record, NONE)
281 : #define H_FieldType_FIELDS(F, T) F(T, NAME, name, NONE) F(T, NODE, type, TYPE)
282 : #define H_OpSig_FIELDS(F, T) F(T, NAME, name, NONE) F(T, NODE, type, TYPE)
283 : // `fixity + left tighter than *`. `alpha` records which SPELLING the operator
284 : // was named by -- a symbol run (`+`) or a bare identifier used infix in
285 : // backticks (`div`) -- because the two are different names that may not be
286 : // told apart by their text alone downstream.
287 : #define D_Fixity_FIELDS(F, T) \
288 : F(T, NAME, name, NONE) F(T, FLAG, alpha, NONE) F(T, INT, assoc, NONE) \
289 : F(T, SEQ, rels, FIXREL)
290 : #define H_FixRel_FIELDS(F, T) \
291 : F(T, INT, sense, NONE) F(T, NAME, name, NONE) F(T, FLAG, alpha, NONE)
292 : #define H_ForeignMember_FIELDS(F, T) \
293 : F(T, NAME, name, NONE) F(T, TEXT, symbol, NONE) F(T, NODE, type, TYPE)
294 : // paren records `(+)` so the printer re-derives the brackets from the fact.
295 : #define H_SigName_FIELDS(F, T) F(T, NAME, name, NONE) F(T, FLAG, paren, NONE)
296 :
297 : #define L_Prefix_FIELDS(F, T) \
298 : F(T, NAME, name, NONE) F(T, FLAG, paren, NONE) F(T, SEQ, args, PAT)
299 : #define L_Infix_FIELDS(F, T) \
300 : F(T, NODE, left, PAT) F(T, NAME, op, NONE) F(T, FLAG, backtick, NONE) \
301 : F(T, NODE, right, PAT)
302 :
303 : // types ---------------------------------------------------------------------
304 : #define T_Var_FIELDS(F, T) F(T, NAME, name, NONE)
305 : #define T_Con_FIELDS(F, T) F(T, NODE, path, PATH)
306 : #define T_App_FIELDS(F, T) F(T, NODE, fn, TYPE) F(T, NODE, arg, TYPE)
307 : #define T_Fun_FIELDS(F, T) F(T, NODE, from, TYPE) F(T, NODE, to, TYPE)
308 : #define T_Qual_FIELDS(F, T) F(T, NODE, ctx, TYPE) F(T, NODE, body, TYPE)
309 : #define T_With_FIELDS(F, T) F(T, NODE, body, TYPE) F(T, SEQ, row, ROWENTRY)
310 : #define T_List_FIELDS(F, T) F(T, NODE, elem, TYPE)
311 : #define T_Tuple_FIELDS(F, T) F(T, SEQ, items, TYPE)
312 : #define T_Unit_FIELDS(F, T)
313 : #define T_RowArg_FIELDS(F, T) F(T, NAME, name, NONE)
314 : #define T_Transfer_FIELDS(F, T) F(T, INT, mode, NONE) F(T, NODE, body, TYPE)
315 : // label is empty for a slot obligation; kind is one of WOK_ROW_*.
316 : #define H_RowEntry_FIELDS(F, T) \
317 : F(T, INT, kind, NONE) F(T, NAME, label, NONE) F(T, OPT, type, TYPE)
318 :
319 : // patterns ------------------------------------------------------------------
320 : #define P_Var_FIELDS(F, T) F(T, NAME, name, NONE)
321 : #define P_Wild_FIELDS(F, T)
322 : #define P_Int_FIELDS(F, T) F(T, INT, value, NONE) F(T, FLAG, negative, NONE)
323 : #define P_Str_FIELDS(F, T) F(T, TEXT, text, NONE)
324 : #define P_Char_FIELDS(F, T) F(T, TEXT, text, NONE)
325 : #define P_Con_FIELDS(F, T) F(T, NODE, path, PATH) F(T, SEQ, args, PAT)
326 : #define P_Cons_FIELDS(F, T) F(T, NODE, head, PAT) F(T, NODE, tail, PAT)
327 : #define P_Tuple_FIELDS(F, T) F(T, SEQ, items, PAT)
328 : #define P_List_FIELDS(F, T) F(T, SEQ, items, PAT)
329 : #define P_Unit_FIELDS(F, T)
330 : #define P_As_FIELDS(F, T) F(T, NODE, pat, PAT) F(T, NAME, name, NONE)
331 : // rest is empty unless `..name` was written; open records also set is_open.
332 : #define P_Record_FIELDS(F, T) \
333 : F(T, NODE, path, PATH) F(T, SEQ, fields, FIELDPAT) F(T, FLAG, is_open, NONE) \
334 : F(T, NAME, rest, NONE)
335 : #define H_FieldPat_FIELDS(F, T) F(T, NAME, name, NONE) F(T, NODE, pat, PAT)
336 :
337 : // expressions ---------------------------------------------------------------
338 : #define E_Var_FIELDS(F, T) F(T, NAME, name, NONE)
339 : #define E_Con_FIELDS(F, T) F(T, NAME, name, NONE)
340 : #define E_Int_FIELDS(F, T) F(T, INT, value, NONE)
341 : #define E_Str_FIELDS(F, T) F(T, TEXT, text, NONE)
342 : #define E_Char_FIELDS(F, T) F(T, TEXT, text, NONE)
343 : #define E_Unit_FIELDS(F, T)
344 : #define E_OpRef_FIELDS(F, T) F(T, NAME, name, NONE)
345 : #define E_App_FIELDS(F, T) F(T, NODE, fn, EXPR) F(T, NODE, arg, EXPR)
346 : // FLAT: precedence and associativity are a later pass's job, exactly as
347 : // src/Wok/Reordering.hs already expects. No fixity table lives in the parser.
348 : #define E_Chain_FIELDS(F, T) F(T, NODE, head, EXPR) F(T, SEQ, ops, CHAINOP)
349 : #define H_ChainOp_FIELDS(F, T) \
350 : F(T, NAME, op, NONE) F(T, FLAG, backtick, NONE) F(T, NODE, rhs, EXPR)
351 : // ONE Dot node for `M.f`, `st.get` and `p.x`: spec 1.5 resolves qualifier /
352 : // label / projection later, and its collision rule needs them undistinguished
353 : // at parse time.
354 : #define E_Dot_FIELDS(F, T) \
355 : F(T, NODE, recv, EXPR) F(T, NAME, name, NONE) F(T, FLAG, upper, NONE)
356 : #define E_Neg_FIELDS(F, T) F(T, NODE, body, EXPR)
357 : #define E_List_FIELDS(F, T) F(T, SEQ, items, EXPR)
358 : #define E_Tuple_FIELDS(F, T) F(T, SEQ, items, EXPR)
359 : #define E_Lambda_FIELDS(F, T) F(T, SEQ, params, PAT) F(T, NODE, body, EXPR)
360 : #define E_LetIn_FIELDS(F, T) F(T, NODE, bind, BIND) F(T, NODE, body, EXPR)
361 : // label is empty when elided -- legal only in this delimited inline form
362 : // (D13 two-tier); the statement form S_Handle always writes one.
363 : #define E_HandleIn_FIELDS(F, T) \
364 : F(T, NAME, label, NONE) F(T, NODE, handler, EXPR) F(T, NODE, body, EXPR)
365 : #define E_UseIn_FIELDS(F, T) F(T, SEQ, binds, USEBIND) F(T, NODE, body, EXPR)
366 : #define E_If_FIELDS(F, T) \
367 : F(T, NODE, cond, EXPR) F(T, NODE, then_, EXPR) F(T, NODE, else_, EXPR)
368 : #define E_Case_FIELDS(F, T) F(T, NODE, scrut, EXPR) F(T, SEQ, alts, ALT)
369 : // `handler E` names its effect MANDATORILY (C3).
370 : #define E_Handler_FIELDS(F, T) \
371 : F(T, NAME, effect, NONE) F(T, SEQ, clauses, CLAUSE)
372 : #define E_Assign_FIELDS(F, T) F(T, NODE, target, EXPR) F(T, NODE, value, EXPR)
373 : #define E_Record_FIELDS(F, T) \
374 : F(T, NODE, path, EXPR) F(T, OPT, spread, EXPR) F(T, SEQ, fields, FIELD)
375 : #define E_Block_FIELDS(F, T) F(T, SEQ, stmts, STMT)
376 : #define E_Error_FIELDS(F, T) F(T, TEXT, text, NONE)
377 :
378 : #define S_Let_FIELDS(F, T) F(T, NODE, bind, BIND)
379 : #define S_Handle_FIELDS(F, T) F(T, NAME, label, NONE) F(T, NODE, handler, EXPR)
380 : #define S_Use_FIELDS(F, T) F(T, SEQ, binds, USEBIND)
381 : #define S_Discard_FIELDS(F, T) F(T, NODE, body, EXPR)
382 :
383 : #define H_Bind_FIELDS(F, T) F(T, NODE, lhs, BINDLHS) F(T, NODE, body, EXPR)
384 : #define H_UseBind_FIELDS(F, T) F(T, NAME, from, NONE) F(T, NAME, to, NONE)
385 : #define H_Alt_FIELDS(F, T) \
386 : F(T, NODE, pat, PAT) F(T, NODE, body, EXPR) F(T, SEQ, wheres, DECL)
387 : // A control clause's continuation is held apart from `pats`, so E-ARITY can
388 : // compare pattern count against op arity directly for every kind (D14). The
389 : // comma is what delimits it: left of a comma, binder count = op arity (C8,
390 : // amended). `k` is empty for every other kind, `abort` included -- an abort
391 : // clause has no continuation to name (D24).
392 : #define H_Clause_FIELDS(F, T) \
393 : F(T, INT, kind, NONE) F(T, NAME, name, NONE) F(T, SEQ, pats, PAT) \
394 : F(T, NAME, k, NONE) F(T, NODE, body, EXPR)
395 : #define H_Field_FIELDS(F, T) F(T, NAME, name, NONE) F(T, NODE, value, EXPR)
396 :
397 : #define W_File_FIELDS(F, T) F(T, SEQ, decls, DECL)
398 :
399 : // ------------------------------------------------------------ derived: tags
400 :
401 : typedef enum : u16 {
402 : #define WOK_X(tag, fam) tag,
403 : WOK_NODES(WOK_X)
404 : #undef WOK_X
405 : WOK_TAG_COUNT
406 : } WokTag;
407 :
408 : // ------------------------------------------------- derived: slot indices
409 :
410 : #define WOK_SLOT_INDEX(T, cls, name, fam) T##__##name,
411 : #define WOK_DECLARE_SLOTS(T, fam) \
412 : enum { T##_FIELDS(WOK_SLOT_INDEX, T) T##__NSLOTS };
413 : WOK_NODES(WOK_DECLARE_SLOTS)
414 : #undef WOK_DECLARE_SLOTS
415 :
416 : // ------------------------------------------- derived: accessors and setters
417 :
418 : #define WOK_CT_NODE WokNode *
419 : #define WOK_CT_OPT WokNode *
420 : #define WOK_CT_SEQ WokSeq
421 : #define WOK_CT_NAME WokSpan
422 : #define WOK_CT_TEXT WokSpan
423 : #define WOK_CT_INT u64
424 : #define WOK_CT_FLAG bool
425 :
426 : #define WOK_GET_NODE(s) ((s).node)
427 : #define WOK_GET_OPT(s) ((s).node)
428 : #define WOK_GET_SEQ(s) wok_seq_unpack((s).seq)
429 : #define WOK_GET_NAME(s) ((s).span)
430 : #define WOK_GET_TEXT(s) ((s).span)
431 : #define WOK_GET_INT(s) ((s).num)
432 : #define WOK_GET_FLAG(s) ((s).flag)
433 :
434 : #define WOK_MK_NODE(v) ((WokSlot){.node = (v)})
435 : #define WOK_MK_OPT(v) ((WokSlot){.node = (v)})
436 : #define WOK_MK_SEQ(v) ((WokSlot){.seq = (v).items})
437 : #define WOK_MK_NAME(v) ((WokSlot){.span = (v)})
438 : #define WOK_MK_TEXT(v) ((WokSlot){.span = (v)})
439 : #define WOK_MK_INT(v) ((WokSlot){.num = (v)})
440 : #define WOK_MK_FLAG(v) ((WokSlot){.flag = (v)})
441 :
442 : #define WOK_SLOT_ACCESS(T, cls, name, fam) \
443 : static inline WOK_CT_##cls T##_##name(const WokNode *n) { \
444 : assert(n->tag == T); \
445 : return WOK_GET_##cls(n->slot[T##__##name]); \
446 : } \
447 : static inline void T##_set_##name(WokNode *n, WOK_CT_##cls v) { \
448 : assert(n->tag == T); \
449 : n->slot[T##__##name] = WOK_MK_##cls(v); \
450 : }
451 : #define WOK_DECLARE_ACCESS(T, fam) T##_FIELDS(WOK_SLOT_ACCESS, T)
452 2864499 : WOK_NODES(WOK_DECLARE_ACCESS)
453 : #undef WOK_DECLARE_ACCESS
454 :
455 : // --------------------------------------------------- derived: descriptors
456 :
457 : typedef struct {
458 : const char *name;
459 : WokFieldClass cls;
460 : WokFamily family; // WFAM_NONE unless the field holds a child
461 : } WokFieldDesc;
462 :
463 : typedef struct {
464 : const char *tag;
465 : const WokFieldDesc *fields;
466 : u16 nfields;
467 : WokFamily family; // the family this node BELONGS to
468 : } WokNodeDesc;
469 :
470 : extern const WokNodeDesc wok_node_desc[WOK_TAG_COUNT];
471 :
472 : WOK_READONLY const char *wok_family_name(WokFamily);
473 :
474 : // ------------------------------------------------------------ construction
475 :
476 : // Allocates a node with the slot count its tag declares, zeroed. The parser
477 : // never states an arity: the schema does.
478 : WokNode *wok_node(WokArena *, WokTag, u32 off, u32 len);
479 : WokSeq wok_seq(WokArena *, WokNode *const *restrict items, u32 n);
480 :
481 :
482 352054 : static inline WokSpan wok_span(u32 off, u32 len) {
483 76405 : return (WokSpan){.off = off, .len = len};
484 : }
485 27472 : static inline bool wok_span_empty(WokSpan s) { return s.len == 0; }
486 :
487 : // A growable node list for the parser, backed by the arena.
488 : // The fields are named to be awkward on purpose: index 0 is RESERVED for the
489 : // inline count so wok_buf_seq can hand out the buffer without copying, which
490 : // means `raw`/`raw_n` do not mean what `items`/`n` used to. Reading them
491 : // directly is how a tree gets silently corrupted -- use the accessors.
492 : typedef struct {
493 : WokNode **raw;
494 : u32 raw_n, cap;
495 : WokArena *arena;
496 : } WokNodeBuf;
497 :
498 383135 : static inline u32 wok_buf_count(const WokNodeBuf *b) {
499 383135 : return b->raw_n > 0 ? b->raw_n - 1u : 0u;
500 : }
501 9022 : static inline WokNode *wok_buf_at(const WokNodeBuf *b, u32 i) {
502 9022 : return b->raw[i + 1u];
503 : }
504 :
505 : void wok_buf_init(WokNodeBuf *, WokArena *);
506 : void wok_buf_push(WokNodeBuf *, WokNode *);
507 : WokSeq wok_buf_seq(WokNodeBuf *);
508 :
509 : // --------------------------------------------------------------- coverage
510 : //
511 : // Production coverage, not line coverage. Every parse* function marks its tag
512 : // on entry; the corpus must reach 100%, and a form no file reaches names
513 : // itself rather than sitting silently uncovered.
514 :
515 : // The bitmap is extern and the mark inline: it runs once per node built, and
516 : // a store does not deserve a call. Single-threaded by design (a test
517 : // instrument); the readers stay out of line.
518 : extern bool wok_cover_bits[];
519 646530 : static inline void wok_cover_mark(WokTag t) { wok_cover_bits[t] = true; }
520 : void wok_cover_reset(void);
521 : bool wok_cover_seen(WokTag);
522 : usize wok_cover_missing(const WokTag **out);
523 :
|