@asmlift/core 0.8.1 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) hide show
  1. package/README.md +36 -31
  2. package/package.json +1 -1
  3. package/src/aggregate.ts +397 -0
  4. package/src/backend/c.ts +6 -2
  5. package/src/backend/cfamily.ts +370 -56
  6. package/src/backend/cpp.ts +116 -10
  7. package/src/backend/pascal.ts +69 -11
  8. package/src/cdecl/constant.ts +59 -0
  9. package/src/cdecl/lex.ts +209 -0
  10. package/src/cdecl/parse.ts +975 -0
  11. package/src/cdecl/spell.ts +69 -0
  12. package/src/codegen-flags.ts +38 -6
  13. package/src/compiler-diagnostics.ts +194 -0
  14. package/src/contracts.ts +777 -84
  15. package/src/declare.ts +132 -13
  16. package/src/frontend/asmdata.ts +26 -6
  17. package/src/frontend/call-plan.ts +1590 -0
  18. package/src/frontend/dead-reads.ts +628 -0
  19. package/src/frontend/device-pins.ts +296 -0
  20. package/src/frontend/emit.ts +4 -3
  21. package/src/frontend/errors.ts +5 -0
  22. package/src/frontend/flags-edge.ts +101 -0
  23. package/src/frontend/fpu.ts +374 -0
  24. package/src/frontend/frame-objects.ts +3171 -0
  25. package/src/frontend/frontend.ts +64 -3
  26. package/src/frontend/high-half.ts +55 -5
  27. package/src/frontend/jump-table.ts +530 -0
  28. package/src/frontend/local-object.ts +616 -0
  29. package/src/frontend/mips.ts +2392 -208
  30. package/src/frontend/opaque.ts +97 -18
  31. package/src/frontend/own-struct-return.ts +500 -0
  32. package/src/frontend/ppc.ts +3137 -267
  33. package/src/frontend/register-struct-call.ts +484 -0
  34. package/src/frontend/reloc-symbol.ts +109 -13
  35. package/src/frontend/splat.ts +108 -5
  36. package/src/frontend/ssa.ts +2098 -89
  37. package/src/frontend/stackargs.ts +172 -42
  38. package/src/frontend/thumb.ts +4563 -2091
  39. package/src/ir/alias.ts +71 -0
  40. package/src/ir/bits.ts +681 -1
  41. package/src/ir/core.ts +199 -6
  42. package/src/ir/discipline.ts +313 -0
  43. package/src/ir/entry.ts +159 -0
  44. package/src/ir/float-bits.ts +47 -0
  45. package/src/{structure → ir}/loops.ts +6 -2
  46. package/src/ir/opcodes.ts +193 -121
  47. package/src/ir/parse.ts +1 -0
  48. package/src/ir/types.ts +202 -2
  49. package/src/ir/verify.ts +219 -3
  50. package/src/l3/address.ts +5 -0
  51. package/src/l3/argcopy.ts +38 -6
  52. package/src/l3/ast.ts +467 -23
  53. package/src/l3/basecse.ts +33 -5
  54. package/src/l3/coalesce.ts +21 -13
  55. package/src/l3/constinit.ts +382 -0
  56. package/src/l3/dce.ts +34 -12
  57. package/src/l3/gates.ts +9 -3
  58. package/src/l3/hoist.ts +8 -7
  59. package/src/l3/initfirst.ts +68 -19
  60. package/src/l3/intcell.ts +98 -0
  61. package/src/l3/mentions.ts +10 -4
  62. package/src/l3/narrowdecl.ts +259 -0
  63. package/src/l3/nearbase.ts +10 -5
  64. package/src/l3/parkfirst.ts +8 -5
  65. package/src/l3/pollguard.ts +11 -4
  66. package/src/l3/ptrcell.ts +354 -0
  67. package/src/l3/ptrfield.ts +123 -8
  68. package/src/l3/regspell.ts +26 -7
  69. package/src/l3/reindex.ts +293 -103
  70. package/src/l3/storage.ts +98 -1
  71. package/src/l3/symbol-refs.ts +182 -23
  72. package/src/l3/tailmerge.ts +9 -20
  73. package/src/l3/typing.ts +237 -32
  74. package/src/l3/unreduce.ts +10 -9
  75. package/src/l3/volatileptr.ts +17 -54
  76. package/src/l3/volstore.ts +96 -7
  77. package/src/mangle.ts +41 -1
  78. package/src/pattern/engine.ts +373 -82
  79. package/src/pipeline.ts +589 -27
  80. package/src/proto-context.ts +1648 -0
  81. package/src/proto.ts +1203 -49
  82. package/src/raise/aggmembers.ts +290 -0
  83. package/src/raise/arrays.ts +8 -3
  84. package/src/raise/declared-volatile.ts +307 -0
  85. package/src/raise/divpow2.ts +2 -2
  86. package/src/raise/errors.ts +17 -0
  87. package/src/raise/floathelpers.ts +464 -0
  88. package/src/raise/globalshape.ts +11 -5
  89. package/src/raise/gvn.ts +29 -8
  90. package/src/raise/latch.ts +162 -6
  91. package/src/raise/memberarrays.ts +10 -4
  92. package/src/raise/narrow.ts +29 -2
  93. package/src/raise/narrowlocal.ts +11 -6
  94. package/src/raise/offsetnames.ts +494 -0
  95. package/src/raise/pairparams.ts +114 -0
  96. package/src/raise/paramwidth.ts +203 -6
  97. package/src/raise/pre-recovery.ts +174 -17
  98. package/src/raise/pre-structure.ts +54 -0
  99. package/src/raise/recover.ts +26 -15
  100. package/src/raise/reroll.ts +694 -0
  101. package/src/raise/retsink.ts +1 -1
  102. package/src/raise/shortcircuit.ts +66 -30
  103. package/src/raise/softdiv.ts +21 -23
  104. package/src/raise/storesign.ts +153 -0
  105. package/src/raise/struct-arrays.ts +34 -14
  106. package/src/raise/structs.ts +674 -98
  107. package/src/raise/truncload.ts +367 -0
  108. package/src/raise/unthread.ts +400 -0
  109. package/src/raise/widehelpers.ts +106 -0
  110. package/src/rank-declare.ts +143 -12
  111. package/src/rank-variations.ts +117 -31
  112. package/src/rank.ts +563 -122
  113. package/src/runtime-helpers.ts +405 -0
  114. package/src/stillborn.ts +289 -0
  115. package/src/structure/analysis.ts +1449 -449
  116. package/src/structure/arith-ops.ts +23 -0
  117. package/src/structure/globalaccess.ts +111 -9
  118. package/src/structure/hazards.ts +641 -114
  119. package/src/structure/local-statics.ts +517 -0
  120. package/src/structure/namecoalesce.ts +2 -2
  121. package/src/structure/pointer-spelling.ts +1106 -0
  122. package/src/structure/redundant-test.ts +99 -0
  123. package/src/structure/structure.ts +3478 -975
  124. package/src/structure/switch-recover.ts +403 -178
  125. package/src/symbols.ts +44 -3
  126. package/src/target.ts +1387 -71
  127. package/src/trace.ts +66 -28
  128. package/src/variation-definitions.ts +243 -13
  129. package/src/variation-gates.ts +2 -0
  130. package/src/variation-tokens.ts +25 -7
@@ -0,0 +1,975 @@
1
+ // asmlift — the DECLARATIONS of a C or C++ declaration context, read by recursive descent over its
2
+ // tokens (`lex.ts`).
3
+ //
4
+ // WHAT IS READ. Top-level declarations and function definitions, descending into `extern "C" { }`.
5
+ // Skipped whole, as nothing they declare has C linkage: `namespace` and `extern "C++"` blocks,
6
+ // `template` and `using` statements, and a top-level `asm("…")`. A function's body and a tag's body
7
+ // are kept as token ranges and not read: a struct or union body's members are read when a layout
8
+ // asks for them (`memberDeclarations`).
9
+ //
10
+ // A DECLARATOR IS READ AS C READS IT, from the name outward: `(*rows)[8]` is a pointer to an array
11
+ // of 8, `*rows[8]` an array of 8 pointers, and `(*getcb(void))(u8)` a function returning a pointer
12
+ // to a function. So the type of every name is its specifiers plus its derivations, in order.
13
+ //
14
+ // A STATEMENT THIS CANNOT READ IS COUNTED, never guessed at: its first token goes to `unread`, and
15
+ // reading resumes after its `;` or its body. A function's parameter list that cannot be read is
16
+ // counted apart, in `unreadLists`, and its derivation carries no parameters: the declaration around
17
+ // it is still read.
18
+ //
19
+ // THE LANGUAGE DECIDES THE KEYWORDS. A word C++ alone reserves (`class`, `operator`, `friend`, …) is
20
+ // an identifier in C, which may name a parameter or a member with it.
21
+ //
22
+ // A NAME IN PARENTHESES IN A PARAMETER IS A TYPE WHERE IT NAMES ONE (C99 6.7.5.3p11): with `typedef
23
+ // int T;`, `char (T)` is a function taking a T, and `char (c)` is a char named c. The names a context
24
+ // declares as types are gathered as it is read, and a name it does not declare as one may still be a
25
+ // type it never saw (a raw header's), so a parameter that parenthesises one is not read — unless a
26
+ // list or an extent follows, as in `void (callback)(void *)`: a type there would make a function
27
+ // that returns a function or an array.
28
+ //
29
+ // UNKNOWN WORDS. A run of identifiers between the type and the declarator is read as unknown words,
30
+ // the last identifier being the name: `struct Blob64 EWRAM_FN makeblob(…)` is a raw header's
31
+ // unexpanded macro, and the type it qualifies is still the one spelled. So is an identifier ahead of
32
+ // a type keyword: `NAKED void f(…)`, `EWRAM_FN struct Blob64 f(…)`. Valid C puts an identifier in
33
+ // neither place.
34
+ import { type Tokens, lex } from './lex';
35
+ import { written } from './spell';
36
+
37
+ /** A half-open run of token indices: `from` is the first token, `to` is past the last. */
38
+ export interface Range {
39
+ readonly from: number;
40
+ readonly to: number;
41
+ }
42
+
43
+ export type Qualifier = 'const' | 'volatile';
44
+
45
+ export type TagKeyword = 'struct' | 'union' | 'enum' | 'class';
46
+
47
+ /** An `__attribute__((…))` or `__declspec(…)`, by its source text and where it was written: among
48
+ * the specifiers ahead of a tag's body, straight after that body, elsewhere among the specifiers,
49
+ * or on a declarator. A member's attributes are its own declaration's. */
50
+ export interface Attribute {
51
+ readonly text: string;
52
+ readonly site: 'specifier' | 'before-body' | 'after-body' | 'declarator';
53
+ }
54
+
55
+ export type TypeSpecifier =
56
+ /** `unsigned int`, `Vec`, `A::B`, `TVec3<f32>`; none for a constructor, a destructor or a
57
+ * conversion operator */
58
+ | { readonly kind: 'words'; readonly words: string[] }
59
+ /** `base`: a C++ base clause or an enum's underlying type ahead of the body */
60
+ | {
61
+ readonly kind: 'tag';
62
+ readonly keyword: TagKeyword;
63
+ readonly tag?: string;
64
+ readonly body?: Range;
65
+ readonly base: boolean;
66
+ };
67
+
68
+ export interface Specifiers {
69
+ typedef: boolean;
70
+ /** `extern`, `static`, `inline`, mwcc's `asm`, C++'s `virtual` … */
71
+ storage: string[];
72
+ qualifiers: Qualifier[];
73
+ type: TypeSpecifier;
74
+ /** identifiers ahead of a type keyword or between the type and the declarator: an unexpanded
75
+ * macro */
76
+ unknownWords: string[];
77
+ attributes: Attribute[];
78
+ /** the type and the unknown words in the order they are written, one space apart: `u8`,
79
+ * `struct R2`, `struct Blob64 EWRAM_FN`. Neither the qualifiers nor a tag's body are part of it */
80
+ spelling: string;
81
+ }
82
+
83
+ export type Derivation =
84
+ /** `member`: the class of a C++ pointer to member (`void (T::*)(A)`) */
85
+ | { readonly kind: 'pointer'; readonly qualifiers: Qualifier[]; readonly member?: string }
86
+ | { readonly kind: 'reference' }
87
+ | { readonly kind: 'array'; readonly size?: Range }
88
+ /** `list`: the tokens between the parentheses. `params` is absent where they could not be read;
89
+ * `()` is an empty list */
90
+ | {
91
+ readonly kind: 'function';
92
+ readonly list: Range;
93
+ readonly params?: Parameter[];
94
+ readonly variadic: boolean;
95
+ };
96
+
97
+ export interface Declarator {
98
+ /** absent for an abstract declarator and an unnamed bit-field */
99
+ name?: string;
100
+ /** from the name outward, as C reads it: `(*f)[8]` is [pointer, array 8] */
101
+ derivations: Derivation[];
102
+ attributes: Attribute[];
103
+ /** a member's bit width */
104
+ bits?: Range;
105
+ }
106
+
107
+ export interface Parameter {
108
+ specifiers: Specifiers;
109
+ declarator: Declarator;
110
+ }
111
+
112
+ /** A type as one declarator declares it: its specifiers' type, qualifiers and unknown words, and its
113
+ * derivations. */
114
+ export interface DeclaredType {
115
+ readonly qualifiers: readonly Qualifier[];
116
+ readonly type: TypeSpecifier;
117
+ readonly unknownWords: readonly string[];
118
+ /** as `Specifiers.spelling` */
119
+ readonly spelling: string;
120
+ readonly derivations: readonly Derivation[];
121
+ }
122
+
123
+ export const declaredType = (s: Specifiers, derivations: readonly Derivation[]): DeclaredType => ({
124
+ qualifiers: s.qualifiers,
125
+ type: s.type,
126
+ unknownWords: s.unknownWords,
127
+ spelling: s.spelling,
128
+ derivations,
129
+ });
130
+
131
+ export interface Declaration {
132
+ specifiers: Specifiers;
133
+ declarators: Declarator[];
134
+ /** a function definition: its body, braces excluded */
135
+ body?: Range;
136
+ }
137
+
138
+ export type Language = 'c' | 'c++';
139
+
140
+ export interface ParsedContext {
141
+ declarations: Declaration[];
142
+ /** the first token of each statement that was not read */
143
+ unread: number[];
144
+ /** the `(` of each parameter list that was not read */
145
+ unreadLists: number[];
146
+ tokens: Tokens;
147
+ language: Language;
148
+ /** the names the top level declares as types: each typedef's, and in C++ each tag's */
149
+ typeNames: ReadonlySet<string>;
150
+ }
151
+
152
+ export function parseDeclarations(src: string, language: Language): ParsedContext {
153
+ const tokens = lex(src);
154
+ const typeNames = new Set<string>();
155
+ const parser = new Parser(tokens, language, typeNames);
156
+ const declarations: Declaration[] = [];
157
+ parser.topLevel(0, tokens.count, declarations, typeNames);
158
+ return { declarations, unread: parser.unread, unreadLists: parser.unreadLists, tokens, language, typeNames };
159
+ }
160
+
161
+ /** A type name, `const u8 *` or `f32 (*)[3]`: its specifiers and abstract declarator, over its own
162
+ * tokens. */
163
+ export interface TypeName extends Parameter {
164
+ tokens: Tokens;
165
+ }
166
+
167
+ /** The type name `src` spells, or undefined where it is anything else. A type name declares no name,
168
+ * so every name in it is a type. */
169
+ export function parseTypeName(src: string): TypeName | undefined {
170
+ const tokens = lex(src);
171
+ const p = new Parser(tokens, 'c++', 'every');
172
+ const specifiers = p.specifiers(tokens.count, 'parameter');
173
+ const declarator = specifiers === null ? null : p.declarator(tokens.count, 'parameter');
174
+ return specifiers !== null && declarator !== null && declarator.name === undefined && p.i === tokens.count
175
+ ? { specifiers, declarator, tokens }
176
+ : undefined;
177
+ }
178
+
179
+ /** A struct, union or class body's member declarations, or undefined when one of them cannot be
180
+ * read. A C++ access label is not a member, and a member template or `using` declares no storage.
181
+ * A member's parameter list that cannot be read leaves the member read, as at the top level. */
182
+ export function memberDeclarations(ctx: ParsedContext, body: Range): Declaration[] | undefined {
183
+ const p = new Parser(ctx.tokens, ctx.language, ctx.typeNames);
184
+ const out: Declaration[] = [];
185
+ p.i = body.from;
186
+ while (p.i < body.to) {
187
+ const k = p.i;
188
+ if (p.t.char(k) === SEMICOLON) {
189
+ p.i++;
190
+ } else if (p.cxx && ACCESS.has(p.t.text(k)) && p.t.char(k + 1) === COLON && !p.t.is(k + 1, '::')) {
191
+ p.i += 2;
192
+ } else if (p.cxx && p.t.is(k, 'using')) {
193
+ p.skipStatement(body.to);
194
+ } else if (p.cxx && p.t.is(k, 'template')) {
195
+ if (!p.template(body.to, 'member')) {
196
+ return undefined;
197
+ }
198
+ } else {
199
+ const d = p.declaration(body.to, 'member');
200
+ if (d === null) {
201
+ return undefined;
202
+ }
203
+ out.push(d);
204
+ }
205
+ }
206
+ return out;
207
+ }
208
+
209
+ type Context = 'top' | 'member' | 'parameter';
210
+
211
+ const SEMICOLON = 59;
212
+ const COLON = 58;
213
+ const COMMA = 44;
214
+ const EQUALS = 61;
215
+ const STAR = 42;
216
+ const AMPERSAND = 38;
217
+ const TILDE = 126;
218
+ const OPEN_PAREN = 40;
219
+ const CLOSE_PAREN = 41;
220
+ const OPEN_BRACKET = 91;
221
+ const OPEN_BRACE = 123;
222
+ const CLOSE_BRACE = 125;
223
+ const LESS = 60;
224
+ const GREATER = 62;
225
+ const CARET = 94;
226
+
227
+ const QUALIFIERS: ReadonlyMap<string, Qualifier> = new Map([
228
+ ['const', 'const'],
229
+ ['volatile', 'volatile'],
230
+ ]);
231
+ const C_STORAGE = [
232
+ 'extern',
233
+ 'static',
234
+ 'inline',
235
+ '__inline',
236
+ '__inline__',
237
+ 'register',
238
+ 'auto',
239
+ 'asm',
240
+ '__asm',
241
+ '__asm__',
242
+ ];
243
+ const STORAGE: Record<Language, ReadonlySet<string>> = {
244
+ c: new Set(C_STORAGE),
245
+ 'c++': new Set([...C_STORAGE, 'virtual', 'explicit', 'friend', 'mutable']),
246
+ };
247
+ const BASIC = new Set(['void', 'char', 'short', 'int', 'long', 'float', 'double', 'signed', 'unsigned']);
248
+ /** a type word that combines with no other: after one, it is the name being declared, as C89
249
+ * reserves none of them (`typedef unsigned long bool;`) */
250
+ const LONE = new Set(['bool', '_Bool', 'wchar_t']);
251
+ const TAGS: Record<Language, ReadonlySet<string>> = {
252
+ c: new Set<TagKeyword>(['struct', 'union', 'enum']),
253
+ 'c++': new Set<TagKeyword>(['struct', 'union', 'enum', 'class']),
254
+ };
255
+ const ATTRIBUTES = new Set(['__attribute__', '__attribute', '__declspec']);
256
+ const ASM = new Set(['asm', '__asm', '__asm__']);
257
+ const ACCESS = new Set(['public', 'private', 'protected']);
258
+ /** what may follow a function declarator's `)`: a member function's qualifiers and specifiers */
259
+ const FUNCTION_SUFFIXES = new Set(['const', 'volatile', 'throw', 'noexcept', 'override', 'final']);
260
+
261
+ class Parser {
262
+ readonly t: Tokens;
263
+ i = 0;
264
+ readonly unread: number[] = [];
265
+ readonly unreadLists: number[] = [];
266
+ readonly cxx: boolean;
267
+ readonly storage: ReadonlySet<string>;
268
+ readonly tags: ReadonlySet<string>;
269
+ /** the names declared as types so far; in a type name, every name */
270
+ readonly types: ReadonlySet<string> | 'every';
271
+
272
+ constructor(t: Tokens, language: Language, types: ReadonlySet<string> | 'every') {
273
+ this.t = t;
274
+ this.cxx = language === 'c++';
275
+ this.storage = STORAGE[language];
276
+ this.tags = TAGS[language];
277
+ this.types = types;
278
+ }
279
+
280
+ /** The declarations from `from` to `to`, into `out`, and the names they declare as types, into
281
+ * `types`. */
282
+ topLevel(from: number, to: number, out: Declaration[], types: Set<string>): void {
283
+ this.i = from;
284
+ while (this.i < to) {
285
+ const k = this.i;
286
+ const c = this.t.char(k);
287
+ if (c === SEMICOLON) {
288
+ this.i++;
289
+ } else if (c === CLOSE_BRACE) {
290
+ this.unread.push(k);
291
+ this.i++;
292
+ } else if (this.t.is(k, 'extern') && this.t.kind(k + 1) === 'string' && this.t.char(k + 2) === OPEN_BRACE) {
293
+ const close = this.closing(k + 2, to);
294
+ if (this.t.is(k + 1, '"C"')) {
295
+ this.topLevel(k + 3, close, out, types);
296
+ }
297
+ this.i = Math.min(close + 1, to);
298
+ } else if (this.cxx && this.t.is(k, 'namespace')) {
299
+ let j = k + 1;
300
+ while (j < to && this.t.char(j) !== OPEN_BRACE && this.t.char(j) !== SEMICOLON) {
301
+ j++;
302
+ }
303
+ this.i = this.t.char(j) === OPEN_BRACE ? Math.min(this.closing(j, to) + 1, to) : j + 1;
304
+ } else if (
305
+ (this.cxx && this.t.is(k, 'using')) ||
306
+ (ASM.has(this.t.text(k)) && this.t.char(k + 1) === OPEN_PAREN)
307
+ ) {
308
+ this.skipStatement(to);
309
+ } else if (this.cxx && this.t.is(k, 'template')) {
310
+ this.template(to, 'top');
311
+ } else {
312
+ const d = this.declaration(to, 'top');
313
+ if (d === null) {
314
+ this.unread.push(k);
315
+ this.i = k;
316
+ this.skipStatement(to);
317
+ } else {
318
+ out.push(d);
319
+ declaresTypes(d, this.cxx, types);
320
+ }
321
+ }
322
+ }
323
+ }
324
+
325
+ /** A `template <…>` declaration, read and discarded: what it declares is never called by a C
326
+ * name. One that cannot be read is counted. */
327
+ template(to: number, context: Context): boolean {
328
+ const k = this.i;
329
+ while (this.t.is(this.i, 'template')) {
330
+ this.i++;
331
+ if (this.t.char(this.i) === LESS && !this.angles(to)) {
332
+ break;
333
+ }
334
+ }
335
+ if (!this.t.is(this.i, 'template') && this.declaration(to, context) !== null) {
336
+ return true;
337
+ }
338
+ this.unread.push(k);
339
+ this.i = k;
340
+ this.skipStatement(to);
341
+ return false;
342
+ }
343
+
344
+ /** Past the statement at `i`: its `;`, or the end of a block that ends it. */
345
+ skipStatement(to: number): void {
346
+ while (this.i < to) {
347
+ const c = this.t.char(this.i);
348
+ if (c === SEMICOLON) {
349
+ this.i++;
350
+ return;
351
+ }
352
+ const close = this.t.match(this.i);
353
+ if ((c === OPEN_BRACE || c === OPEN_PAREN || c === OPEN_BRACKET) && close > this.i && close < to) {
354
+ this.i = close + 1;
355
+ if (c === OPEN_BRACE) {
356
+ if (this.t.char(this.i) === SEMICOLON) {
357
+ this.i++;
358
+ }
359
+ return;
360
+ }
361
+ } else {
362
+ this.i++;
363
+ }
364
+ }
365
+ }
366
+
367
+ declaration(to: number, context: Context): Declaration | null {
368
+ const specifiers = this.specifiers(to, context);
369
+ if (specifiers === null) {
370
+ return null;
371
+ }
372
+ const declarators: Declarator[] = [];
373
+ if (this.t.char(this.i) === SEMICOLON) {
374
+ this.i++;
375
+ return { specifiers, declarators };
376
+ }
377
+ for (;;) {
378
+ const d = this.declarator(to, context);
379
+ if (d === null) {
380
+ return null;
381
+ }
382
+ this.declaratorTail(d);
383
+ const fn = d.derivations[0]?.kind === 'function';
384
+ const c = this.t.char(this.i);
385
+ if (c === COLON && !this.t.is(this.i, '::')) {
386
+ if (fn) {
387
+ // a constructor's initializer list, up to its body
388
+ while (this.i < to && this.t.char(this.i) !== OPEN_BRACE && this.t.char(this.i) !== SEMICOLON) {
389
+ this.step(to);
390
+ }
391
+ } else if (context === 'member') {
392
+ this.i++;
393
+ const from = this.i;
394
+ this.skipInitializer(to);
395
+ d.bits = { from, to: this.i };
396
+ } else {
397
+ // mwcc's `extern volatile u16 REG : 0xCC006000;` places the variable at an address
398
+ this.skipInitializer(to);
399
+ }
400
+ } else if (c === EQUALS || (c === OPEN_BRACE && !fn)) {
401
+ this.skipInitializer(to);
402
+ }
403
+ if (d.name === undefined && d.bits === undefined) {
404
+ return null;
405
+ }
406
+ declarators.push(d);
407
+ const next = this.t.char(this.i);
408
+ if (next === COMMA) {
409
+ this.i++;
410
+ } else if (next === SEMICOLON) {
411
+ this.i++;
412
+ return { specifiers, declarators };
413
+ } else if (next === OPEN_BRACE && fn && declarators.length === 1) {
414
+ const close = this.t.match(this.i);
415
+ if (close < 0 || close >= to) {
416
+ return null;
417
+ }
418
+ const body = { from: this.i + 1, to: close };
419
+ this.i = close + 1;
420
+ return { specifiers, declarators, body };
421
+ } else {
422
+ return null;
423
+ }
424
+ }
425
+ }
426
+
427
+ /** The specifiers at `i`, or null where there are none. A declaration may have none ahead of a
428
+ * constructor's, destructor's or conversion operator's name. */
429
+ specifiers(to: number, context: Context): Specifiers | null {
430
+ const from = this.i;
431
+ const basic: string[] = [];
432
+ const written: string[] = [];
433
+ const s: Specifiers = {
434
+ typedef: false,
435
+ storage: [],
436
+ qualifiers: [],
437
+ type: { kind: 'words', words: basic },
438
+ unknownWords: [],
439
+ attributes: [],
440
+ spelling: '',
441
+ };
442
+ let typed: 'none' | 'basic' | 'name' | 'tag' = 'none';
443
+ let afterBody = false;
444
+ while (this.i < to) {
445
+ const k = this.i;
446
+ if (this.isAttribute(k)) {
447
+ s.attributes.push(this.attribute(afterBody ? 'after-body' : 'specifier'));
448
+ continue;
449
+ }
450
+ afterBody = false;
451
+ const kind = this.t.kind(k);
452
+ if (kind === 'string' && this.t.is(k - 1, 'extern')) {
453
+ this.i++;
454
+ continue;
455
+ }
456
+ if (kind !== 'identifier') {
457
+ if (typed === 'none' && this.t.is(k, '::') && this.t.kind(k + 1) === 'identifier') {
458
+ s.type = { kind: 'words', words: [this.typeName(to)] };
459
+ written.push(s.type.words[0]);
460
+ typed = 'name';
461
+ continue;
462
+ }
463
+ break;
464
+ }
465
+ const w = this.t.text(k);
466
+ const qualifier = QUALIFIERS.get(w);
467
+ if (w === 'typedef') {
468
+ s.typedef = true;
469
+ this.i++;
470
+ } else if (qualifier !== undefined) {
471
+ s.qualifiers.push(qualifier);
472
+ this.i++;
473
+ } else if (this.storage.has(w)) {
474
+ s.storage.push(w);
475
+ this.i++;
476
+ } else if (this.cxx && w === 'typename') {
477
+ this.i++;
478
+ } else if (typed === 'name' && (BASIC.has(w) || this.tags.has(w))) {
479
+ // what was read as the type is an unknown word ahead of it
480
+ s.unknownWords.unshift(...(s.type.kind === 'words' ? s.type.words : []));
481
+ s.type = { kind: 'words', words: basic };
482
+ typed = 'none';
483
+ } else if (BASIC.has(w) || (LONE.has(w) && typed === 'none')) {
484
+ if (typed !== 'none' && typed !== 'basic') {
485
+ break;
486
+ }
487
+ basic.push(w);
488
+ written.push(w);
489
+ typed = 'basic';
490
+ this.i++;
491
+ } else if (this.tags.has(w)) {
492
+ if (typed !== 'none') {
493
+ break;
494
+ }
495
+ const tag = this.tag(s, w as TagKeyword, to, context);
496
+ s.type = tag;
497
+ written.push(tag.tag === undefined ? w : `${w} ${tag.tag}`);
498
+ typed = 'tag';
499
+ afterBody = tag.body !== undefined;
500
+ if (afterBody) {
501
+ s.attributes = s.attributes.map((a) => (a.site === 'specifier' ? { ...a, site: 'before-body' } : a));
502
+ }
503
+ } else if (this.cxx && w === 'operator') {
504
+ break;
505
+ } else if (typed === 'none') {
506
+ if (context !== 'parameter' && this.namesDeclarator(k)) {
507
+ break;
508
+ }
509
+ s.type = { kind: 'words', words: [this.typeName(to)] };
510
+ written.push(s.type.words[0]);
511
+ typed = 'name';
512
+ } else if (this.isUnknownWord(k)) {
513
+ s.unknownWords.push(w);
514
+ written.push(w);
515
+ this.i++;
516
+ } else {
517
+ break;
518
+ }
519
+ }
520
+ s.spelling = written.join(' ');
521
+ if (this.i > from) {
522
+ return s;
523
+ }
524
+ return context !== 'parameter' && this.startsName(from) ? s : null;
525
+ }
526
+
527
+ /** A struct, union, enum or class specifier: its tag, its base clause, its body. */
528
+ tag(s: Specifiers, keyword: TagKeyword, to: number, context: Context): Extract<TypeSpecifier, { kind: 'tag' }> {
529
+ this.i++;
530
+ this.beforeBody(s);
531
+ const tag = this.t.kind(this.i) === 'identifier' && !this.isAttribute(this.i) ? this.typeName(to) : undefined;
532
+ this.beforeBody(s);
533
+ let base = false;
534
+ if (this.t.char(this.i) === COLON && !this.t.is(this.i, '::') && context !== 'parameter') {
535
+ // a base clause or an enum base runs to the body; a `:` with no body after it is a bit width
536
+ let j = this.i;
537
+ for (; j < to; j++) {
538
+ const c = this.t.char(j);
539
+ if (c === OPEN_BRACE || c === SEMICOLON || c === CLOSE_PAREN) {
540
+ break;
541
+ }
542
+ if ((c === OPEN_PAREN || c === OPEN_BRACKET) && this.t.match(j) > j) {
543
+ j = this.t.match(j);
544
+ }
545
+ }
546
+ if (this.t.char(j) === OPEN_BRACE) {
547
+ base = true;
548
+ this.i = j;
549
+ }
550
+ }
551
+ let body: Range | undefined;
552
+ const close = this.t.match(this.i);
553
+ if (this.t.char(this.i) === OPEN_BRACE && close > this.i && close < to) {
554
+ body = { from: this.i + 1, to: close };
555
+ this.i = close + 1;
556
+ }
557
+ return { kind: 'tag', keyword, ...(tag !== undefined ? { tag } : {}), ...(body ? { body } : {}), base };
558
+ }
559
+
560
+ beforeBody(s: Specifiers): void {
561
+ while (this.isAttribute(this.i)) {
562
+ s.attributes.push(this.attribute('before-body'));
563
+ }
564
+ }
565
+
566
+ /** A type's name at `i`: `Vec`, `::Vec`, `A::B`, `TVec3<f32>`, `A<B>::C`. */
567
+ typeName(to: number): string {
568
+ const from = this.i;
569
+ if (this.t.is(this.i, '::')) {
570
+ this.i++;
571
+ }
572
+ for (;;) {
573
+ this.i++;
574
+ if (this.t.char(this.i) === LESS && !this.angles(to)) {
575
+ break;
576
+ }
577
+ if (!this.t.is(this.i, '::') || this.t.kind(this.i + 1) !== 'identifier' || this.t.is(this.i + 1, 'operator')) {
578
+ break;
579
+ }
580
+ this.i++;
581
+ }
582
+ return written(this.t, { from, to: this.i });
583
+ }
584
+
585
+ /** Past a template argument list at `i` (`<`), or false, having moved nothing, where none closes
586
+ * before the statement ends. */
587
+ angles(to: number): boolean {
588
+ const from = this.i;
589
+ let depth = 0;
590
+ for (let j = from; j < to; j++) {
591
+ const c = this.t.char(j);
592
+ if (c === LESS) {
593
+ depth++;
594
+ } else if (c === GREATER && --depth === 0) {
595
+ this.i = j + 1;
596
+ return true;
597
+ } else if (c === SEMICOLON || c === OPEN_BRACE) {
598
+ return false;
599
+ } else if ((c === OPEN_PAREN || c === OPEN_BRACKET) && this.t.match(j) > j) {
600
+ j = this.t.match(j);
601
+ }
602
+ }
603
+ return false;
604
+ }
605
+
606
+ /** Whether the identifier at `k`, where no type has been read, is a declarator's name: a
607
+ * destructor's or an operator's qualified name, or a name called like a function — a
608
+ * constructor's, or C's implicit `int` — and not a type ahead of `(*`. */
609
+ namesDeclarator(k: number): boolean {
610
+ let j = k;
611
+ while (this.t.is(j + 1, '::')) {
612
+ if (this.t.char(j + 2) === TILDE || this.t.is(j + 2, 'operator')) {
613
+ return true;
614
+ }
615
+ if (this.t.kind(j + 2) !== 'identifier') {
616
+ return false;
617
+ }
618
+ j += 2;
619
+ }
620
+ return this.t.char(j + 1) === OPEN_PAREN && !this.opensPointer(j + 1);
621
+ }
622
+
623
+ /** Whether a declarator's name, with no specifiers ahead of it, starts at `k`. */
624
+ startsName(k: number): boolean {
625
+ return (
626
+ (this.t.char(k) === TILDE && this.t.kind(k + 1) === 'identifier') ||
627
+ (this.cxx && this.t.is(k, 'operator')) ||
628
+ (this.t.kind(k) === 'identifier' && this.namesDeclarator(k))
629
+ );
630
+ }
631
+
632
+ /** Whether the identifier at `k`, after the type, is an unknown word rather than the name: another
633
+ * identifier, or a declarator's `*` or `&`, follows it. */
634
+ isUnknownWord(k: number): boolean {
635
+ const n = k + 1;
636
+ const c = this.t.char(n);
637
+ if (c === STAR || c === AMPERSAND) {
638
+ return true;
639
+ }
640
+ if (this.t.kind(n) !== 'identifier') {
641
+ return false;
642
+ }
643
+ const w = this.t.text(n);
644
+ return !this.isAttribute(n) && !ASM.has(w) && !QUALIFIERS.has(w) && !FUNCTION_SUFFIXES.has(w);
645
+ }
646
+
647
+ isAttribute(k: number): boolean {
648
+ return (
649
+ this.t.char(k + 1) === OPEN_PAREN &&
650
+ this.t.kind(k) === 'identifier' &&
651
+ ATTRIBUTES.has(this.t.text(k)) &&
652
+ this.t.match(k + 1) > k
653
+ );
654
+ }
655
+
656
+ /** The attribute at `i`, read past. */
657
+ attribute(site: Attribute['site']): Attribute {
658
+ const close = this.t.match(this.i + 1);
659
+ const text = this.t.src.slice(this.t.start(this.i), this.t.end(close));
660
+ this.i = close + 1;
661
+ return { text, site };
662
+ }
663
+
664
+ /** A declarator at `i`, abstract or named. */
665
+ declarator(to: number, context: Context): Declarator | null {
666
+ const prefix: Derivation[] = [];
667
+ const attributes: Attribute[] = [];
668
+ for (;;) {
669
+ const c = this.t.char(this.i);
670
+ const member = c === -1 ? this.memberPointer() : undefined;
671
+ if (c === STAR || member !== undefined) {
672
+ this.i++;
673
+ const qualifiers = this.pointerQualifiers(attributes);
674
+ prefix.push({ kind: 'pointer', qualifiers, ...(member !== undefined ? { member } : {}) });
675
+ } else if (c === AMPERSAND) {
676
+ this.i++;
677
+ if (this.t.char(this.i) === AMPERSAND) {
678
+ this.i++;
679
+ }
680
+ prefix.push({ kind: 'reference' });
681
+ } else {
682
+ break;
683
+ }
684
+ }
685
+ let inner: Declarator | undefined;
686
+ let name: string | undefined;
687
+ const groups = this.t.char(this.i) === OPEN_PAREN && this.groupsDeclarator(this.i, context);
688
+ if (groups === undefined) {
689
+ return null;
690
+ }
691
+ if (groups) {
692
+ const close = this.t.match(this.i);
693
+ if (close < 0 || close >= to) {
694
+ return null;
695
+ }
696
+ this.i++;
697
+ const d = this.declarator(close, context);
698
+ if (d === null || this.i !== close) {
699
+ return null;
700
+ }
701
+ inner = d;
702
+ this.i = close + 1;
703
+ } else if (this.startsName(this.i) || this.isName(this.i)) {
704
+ name = this.declaratorName(to);
705
+ }
706
+ const suffixes: Derivation[] = [];
707
+ for (;;) {
708
+ const c = this.t.char(this.i);
709
+ const close = this.t.match(this.i);
710
+ if ((c === OPEN_BRACKET || c === OPEN_PAREN) && (close < 0 || close >= to)) {
711
+ return null;
712
+ }
713
+ if (c === OPEN_BRACKET) {
714
+ suffixes.push({ kind: 'array', ...(close > this.i + 1 ? { size: { from: this.i + 1, to: close } } : {}) });
715
+ this.i = close + 1;
716
+ } else if (c === OPEN_PAREN) {
717
+ const read = this.parameters(this.i + 1, close);
718
+ if (read === undefined) {
719
+ this.unreadLists.push(this.i);
720
+ }
721
+ suffixes.push({
722
+ kind: 'function',
723
+ list: { from: this.i + 1, to: close },
724
+ ...(read ? { params: read.params } : {}),
725
+ variadic: read?.variadic ?? false,
726
+ });
727
+ this.i = close + 1;
728
+ this.functionSuffixes(attributes);
729
+ } else {
730
+ break;
731
+ }
732
+ }
733
+ return {
734
+ ...(inner?.name !== undefined ? { name: inner.name } : name !== undefined ? { name } : {}),
735
+ derivations: [...(inner?.derivations ?? []), ...suffixes, ...prefix.reverse()],
736
+ attributes: [...(inner?.attributes ?? []), ...attributes],
737
+ };
738
+ }
739
+
740
+ /** The class of a pointer to member at `i` (`T::*`), with `i` left on its `*`; or undefined. */
741
+ memberPointer(): string | undefined {
742
+ let j = this.i;
743
+ while (this.t.kind(j) === 'identifier' && this.t.is(j + 1, '::')) {
744
+ if (this.t.char(j + 2) === STAR) {
745
+ const member = written(this.t, { from: this.i, to: j + 1 });
746
+ this.i = j + 2;
747
+ return member;
748
+ }
749
+ j += 2;
750
+ }
751
+ return undefined;
752
+ }
753
+
754
+ pointerQualifiers(attributes: Attribute[]): Qualifier[] {
755
+ const qualifiers: Qualifier[] = [];
756
+ for (;;) {
757
+ const q = QUALIFIERS.get(this.t.text(this.i));
758
+ if (this.t.kind(this.i) === 'identifier' && q !== undefined) {
759
+ qualifiers.push(q);
760
+ this.i++;
761
+ } else if (this.isAttribute(this.i)) {
762
+ attributes.push(this.attribute('declarator'));
763
+ } else {
764
+ return qualifiers;
765
+ }
766
+ }
767
+ }
768
+
769
+ /** Whether the `(` at `k`, where a declarator's name could start, opens a nested declarator
770
+ * (`(*f)`, `(T::*m)`, `(name)`) rather than a parameter list; undefined in a parameter, where a
771
+ * name in it that is no type may be either. */
772
+ groupsDeclarator(k: number, context: Context): boolean | undefined {
773
+ if (this.opensPointer(k)) {
774
+ return true;
775
+ }
776
+ if (this.t.kind(k + 1) !== 'identifier' || this.isAttribute(k + 1)) {
777
+ return false;
778
+ }
779
+ if (context !== 'parameter') {
780
+ return true;
781
+ }
782
+ if (this.startsType(k + 1)) {
783
+ return false;
784
+ }
785
+ const next = this.t.char(k + 3);
786
+ return this.t.match(k) === k + 2 && (next === OPEN_PAREN || next === OPEN_BRACKET) ? true : undefined;
787
+ }
788
+
789
+ /** Whether the identifier at `k` starts a type: a word only a declaration's specifiers start
790
+ * with, a name declared as a type, or a C++ qualified or template name. */
791
+ startsType(k: number): boolean {
792
+ const w = this.t.text(k);
793
+ return (
794
+ QUALIFIERS.has(w) ||
795
+ BASIC.has(w) ||
796
+ this.storage.has(w) ||
797
+ this.tags.has(w) ||
798
+ (this.cxx && (LONE.has(w) || w === 'typename' || this.t.is(k + 1, '::') || this.t.char(k + 1) === LESS)) ||
799
+ this.types === 'every' ||
800
+ this.types.has(w)
801
+ );
802
+ }
803
+
804
+ /** Whether the `(` at `k` opens a pointer's or a reference's declarator: `(*`, `(&`, `(T::*`. */
805
+ opensPointer(k: number): boolean {
806
+ const c = this.t.char(k + 1);
807
+ if (c === STAR || c === AMPERSAND || c === CARET) {
808
+ return true;
809
+ }
810
+ const save = this.i;
811
+ this.i = k + 1;
812
+ const member = this.memberPointer() !== undefined;
813
+ this.i = save;
814
+ return member;
815
+ }
816
+
817
+ isName(k: number): boolean {
818
+ if (this.t.kind(k) !== 'identifier' || this.isAttribute(k)) {
819
+ return false;
820
+ }
821
+ const w = this.t.text(k);
822
+ return !QUALIFIERS.has(w) && !BASIC.has(w) && !this.tags.has(w);
823
+ }
824
+
825
+ /** A declarator's name at `i`: `f`, `A::f`, `A::~A`, `operator==`, `A::operator new[]`. */
826
+ declaratorName(to: number): string {
827
+ const parts: string[] = [];
828
+ for (;;) {
829
+ if (this.t.char(this.i) === TILDE) {
830
+ parts.push(`~${this.t.text(this.i + 1)}`);
831
+ this.i += 2;
832
+ } else if (this.cxx && this.t.is(this.i, 'operator')) {
833
+ parts.push(this.operatorName(to));
834
+ } else {
835
+ parts.push(this.t.text(this.i));
836
+ this.i++;
837
+ if (this.t.char(this.i) === LESS) {
838
+ const from = this.i;
839
+ if (this.angles(to) && (this.t.char(this.i) === OPEN_PAREN || this.t.is(this.i, '::'))) {
840
+ parts[parts.length - 1] += written(this.t, { from, to: this.i });
841
+ } else {
842
+ this.i = from;
843
+ }
844
+ }
845
+ }
846
+ const next = this.i + 1;
847
+ if (
848
+ !this.t.is(this.i, '::') ||
849
+ !(this.t.kind(next) === 'identifier' || (this.t.char(next) === TILDE && this.t.kind(next + 1) === 'identifier'))
850
+ ) {
851
+ return parts.join('::');
852
+ }
853
+ this.i++;
854
+ }
855
+ }
856
+
857
+ /** `operator` and what it names, up to its parameter list: `operator()`, `operator[]`,
858
+ * `operator new[]`, `operator==`, `operator int`. */
859
+ operatorName(to: number): string {
860
+ const from = this.i;
861
+ this.i++;
862
+ if (this.t.char(this.i) === OPEN_PAREN && this.t.match(this.i) === this.i + 1) {
863
+ this.i += 2;
864
+ }
865
+ while (this.i < to && this.t.char(this.i) !== OPEN_PAREN) {
866
+ this.i++;
867
+ }
868
+ return written(this.t, { from, to: this.i });
869
+ }
870
+
871
+ /** What may follow a function declarator's `)`: `const`, `throw(…)`, `noexcept`, attributes. */
872
+ functionSuffixes(attributes: Attribute[]): void {
873
+ for (;;) {
874
+ if (this.isAttribute(this.i)) {
875
+ attributes.push(this.attribute('declarator'));
876
+ } else if (this.t.kind(this.i) === 'identifier' && FUNCTION_SUFFIXES.has(this.t.text(this.i))) {
877
+ this.i++;
878
+ if (this.t.char(this.i) === OPEN_PAREN && this.t.match(this.i) > this.i) {
879
+ this.i = this.t.match(this.i) + 1;
880
+ }
881
+ } else {
882
+ return;
883
+ }
884
+ }
885
+ }
886
+
887
+ /** What a declaration may write after a declarator: attributes, and a symbol name `asm("sym")`. */
888
+ declaratorTail(d: Declarator): void {
889
+ for (;;) {
890
+ if (this.isAttribute(this.i)) {
891
+ d.attributes.push(this.attribute('declarator'));
892
+ } else if (
893
+ ASM.has(this.t.text(this.i)) &&
894
+ this.t.kind(this.i) === 'identifier' &&
895
+ this.t.char(this.i + 1) === OPEN_PAREN &&
896
+ this.t.match(this.i + 1) > this.i
897
+ ) {
898
+ this.i = this.t.match(this.i + 1) + 1;
899
+ } else {
900
+ return;
901
+ }
902
+ }
903
+ }
904
+
905
+ /** The parameters between `from` and `to`, or undefined where they cannot be read. */
906
+ parameters(from: number, to: number): { params: Parameter[]; variadic: boolean } | undefined {
907
+ const save = this.i;
908
+ this.i = from;
909
+ const params: Parameter[] = [];
910
+ let variadic = false;
911
+ let read = true;
912
+ while (read && this.i < to) {
913
+ if (this.t.is(this.i, '...')) {
914
+ variadic = true;
915
+ this.i++;
916
+ } else {
917
+ const specifiers = this.specifiers(to, 'parameter');
918
+ const declarator = specifiers === null ? null : this.declarator(to, 'parameter');
919
+ if (specifiers === null || declarator === null) {
920
+ read = false;
921
+ break;
922
+ }
923
+ this.declaratorTail(declarator);
924
+ if (this.t.char(this.i) === EQUALS) {
925
+ this.skipInitializer(to);
926
+ }
927
+ params.push({ specifiers, declarator });
928
+ }
929
+ if (this.i < to) {
930
+ read = this.t.char(this.i) === COMMA && this.i + 1 < to && !variadic;
931
+ this.i++;
932
+ }
933
+ }
934
+ this.i = save;
935
+ return read ? { params, variadic } : undefined;
936
+ }
937
+
938
+ /** Past an initializer, a bit width or an address, up to the `,` or `;` after it. */
939
+ skipInitializer(to: number): void {
940
+ while (this.i < to && this.t.char(this.i) !== COMMA && this.t.char(this.i) !== SEMICOLON) {
941
+ this.step(to);
942
+ }
943
+ }
944
+
945
+ /** One token on, or past the bracket the token at `i` opens. */
946
+ step(to: number): void {
947
+ const c = this.t.char(this.i);
948
+ const close = this.t.match(this.i);
949
+ this.i =
950
+ (c === OPEN_PAREN || c === OPEN_BRACKET || c === OPEN_BRACE) && close > this.i && close < to
951
+ ? close + 1
952
+ : this.i + 1;
953
+ }
954
+
955
+ /** The `}` or `)` closing the bracket at `k`, or `to` where none closes it before `to`. */
956
+ closing(k: number, to: number): number {
957
+ const close = this.t.match(k);
958
+ return close > k && close < to ? close : to;
959
+ }
960
+ }
961
+
962
+ /** The names a top-level declaration declares as types, into `types`: a typedef's, and in C++ the tag
963
+ * it names. */
964
+ function declaresTypes(d: Declaration, cxx: boolean, types: Set<string>): void {
965
+ if (d.specifiers.typedef) {
966
+ for (const x of d.declarators) {
967
+ if (x.name !== undefined) {
968
+ types.add(x.name);
969
+ }
970
+ }
971
+ }
972
+ if (cxx && d.specifiers.type.kind === 'tag' && d.specifiers.type.tag !== undefined) {
973
+ types.add(d.specifiers.type.tag);
974
+ }
975
+ }