skir-cc-gen 0.0.1

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.
package/src/index.ts ADDED
@@ -0,0 +1,2089 @@
1
+ import {
2
+ type CodeGenerator,
3
+ type Constant,
4
+ Doc,
5
+ Field,
6
+ type Method,
7
+ type Module,
8
+ type RecordKey,
9
+ type RecordLocation,
10
+ convertCase,
11
+ encodeInt32,
12
+ simpleHash,
13
+ } from "skir-internal";
14
+ import { z } from "zod";
15
+ import { EnumVariant, getEnumVariants } from "./enum_variant.js";
16
+ import { CC_KEYWORDS } from "./keywords.js";
17
+ import { RecursvityResolver } from "./recursivity_resolver.js";
18
+ import {
19
+ TypeSpeller,
20
+ getClassName,
21
+ modulePathToNamespace,
22
+ } from "./type_speller.js";
23
+
24
+ const Config = z.object({
25
+ writeGoogleTestHeaders: z.boolean(),
26
+ });
27
+
28
+ type Config = z.infer<typeof Config>;
29
+
30
+ class CcCodeGenerator implements CodeGenerator<Config> {
31
+ readonly id = "cc";
32
+ readonly configType = Config;
33
+ readonly version = "1.0.0";
34
+
35
+ generateCode(input: CodeGenerator.Input<Config>): CodeGenerator.Output {
36
+ const { recordMap, config } = input;
37
+ const outputFiles: CodeGenerator.OutputFile[] = [];
38
+
39
+ for (const module of input.modules) {
40
+ const generator = new CcLibFilesGenerator(module, recordMap);
41
+ outputFiles.push({
42
+ path: module.path.replace(/\.skir$/, ".h"),
43
+ code: generator.getCode(".h"),
44
+ });
45
+ outputFiles.push({
46
+ path: module.path.replace(/\.skir$/, ".cc"),
47
+ code: generator.getCode(".cc"),
48
+ });
49
+ if (config.writeGoogleTestHeaders) {
50
+ outputFiles.push({
51
+ path: module.path.replace(/\.skir$/, ".testing.h"),
52
+ code: generator.getCode(".testing.h"),
53
+ });
54
+ }
55
+ }
56
+
57
+ return { files: outputFiles };
58
+ }
59
+ }
60
+
61
+ /**
62
+ * Generates the code for one C++ library, made of one .h file and one .cc
63
+ * file.
64
+ */
65
+ class CcLibFilesGenerator {
66
+ constructor(
67
+ private readonly inModule: Module,
68
+ private readonly recordMap: ReadonlyMap<RecordKey, RecordLocation>,
69
+ ) {
70
+ this.typeSpeller = new TypeSpeller(recordMap, inModule, this.includes);
71
+ this.recursivityResolver = RecursvityResolver.resolve(recordMap, inModule);
72
+ this.includes.add('"skir.h"');
73
+ this.namespace = modulePathToNamespace(inModule.path);
74
+ this.generate();
75
+ }
76
+
77
+ getCode(extension: ".h" | ".cc" | ".testing.h"): string {
78
+ switch (extension) {
79
+ case ".h":
80
+ return fileContentsToCode(this.header);
81
+ case ".cc":
82
+ return fileContentsToCode(this.source);
83
+ case ".testing.h":
84
+ return fileContentsToCode(this.testingHeader);
85
+ }
86
+ }
87
+
88
+ private generate(): void {
89
+ this.header.namespace = this.namespace;
90
+ this.source.namespace = this.namespace;
91
+ this.testingHeader.namespace = this.namespace;
92
+ for (const record of this.recursivityResolver.reorderedRecords) {
93
+ this.writeCodeForRecord(record);
94
+ }
95
+ for (const method of this.inModule.methods) {
96
+ this.writeCodeForMethod(method);
97
+ }
98
+ for (const constant of this.inModule.constants) {
99
+ this.writeCodeForConstant(constant);
100
+ }
101
+ this.writeIncludes();
102
+ }
103
+
104
+ private writeCodeForRecord(record: RecordLocation): void {
105
+ const { recordType } = record.record;
106
+ if (recordType === "struct") {
107
+ this.writeCodeForStruct(record);
108
+ } else {
109
+ this.writeCodeForEnum(record);
110
+ }
111
+ this.writeCodeInHeaderForAdapter(record);
112
+ }
113
+
114
+ private writeCodeForStruct(struct: RecordLocation): void {
115
+ const { header, recordMap, source, testingHeader, typeSpeller } = this;
116
+
117
+ const { fields, numSlots, numSlotsInclRemovedNumbers, nestedRecords } =
118
+ struct.record;
119
+ const fieldsByName = [...struct.record.fields].sort((a, b) =>
120
+ a.name.text.localeCompare(b.name.text, "en-US"),
121
+ );
122
+ const fieldsByNumber = [...struct.record.fields].sort(
123
+ (a, b) => a.number - b.number,
124
+ );
125
+
126
+ const className = getClassName(struct);
127
+ const adapterName = `${className}Adapter`;
128
+ const qualifiedName = `::${this.namespace}::${className}`;
129
+ const constRefType = `const ${qualifiedName}&`;
130
+
131
+ // ------------------------------
132
+ // - APPEND CODE IN THE .h FILE -
133
+ // ------------------------------
134
+
135
+ header.mainTop.push(`struct ${className};`);
136
+
137
+ for (const field of fields) {
138
+ const fieldName = field.name.text;
139
+ const escapedFieldName = maybeEscapeLowerCaseName(fieldName);
140
+ const structName = `get_${fieldName}`;
141
+ if (!this.addskiroutSymbol(structName)) continue;
142
+ header.skirout.push(`#ifndef skirout_${structName}`);
143
+ header.skirout.push(`#define skirout_${structName}`);
144
+ header.skirout.push("template <typename other = ::skir::identity>");
145
+ header.skirout.push(`struct ${structName} {`);
146
+ header.skirout.push(" using other_type = other;");
147
+ header.skirout.push("");
148
+ header.skirout.push(
149
+ ` static constexpr absl::string_view kFieldName = "${fieldName}";`,
150
+ );
151
+ header.skirout.push("");
152
+ header.skirout.push(" template <typename T>");
153
+ header.skirout.push(` auto& operator()(T& input) const {`);
154
+ header.skirout.push(
155
+ ` return skir_internal::get(other()(input).${escapedFieldName});`,
156
+ );
157
+ header.skirout.push(" }");
158
+ header.skirout.push("};");
159
+ header.skirout.push("#endif");
160
+ header.skirout.push("");
161
+ }
162
+
163
+ header.mainMiddle.push(...commentify(docToCommentText(struct.record.doc)));
164
+ header.mainMiddle.push(`struct ${className} {`);
165
+ // Declare fields in alphabetical order. It helps users who want to
166
+ // initialize a struct using the designated initializer syntax. See:
167
+ // https://abseil.io/tips/172
168
+ for (const field of fieldsByName) {
169
+ const type = field.type!;
170
+ const fieldIsRecursive = this.recursivityResolver.isRecursive(field);
171
+ const ccType = typeSpeller.getCcType(type, {
172
+ fieldIsRecursive: fieldIsRecursive,
173
+ });
174
+ const fieldName = maybeEscapeLowerCaseName(field.name.text);
175
+ // Numeric types must be initialized.
176
+ let assignment = "";
177
+ if (type.kind === "primitive") {
178
+ if (type.primitive === "bool") {
179
+ assignment = " = false";
180
+ } else if (type.primitive.includes("int")) {
181
+ assignment = " = 0";
182
+ } else if (type.primitive.includes("float")) {
183
+ assignment = " = 0.0";
184
+ }
185
+ }
186
+ header.mainMiddle.push(...commentify(docToCommentText(field.doc), " "));
187
+ header.mainMiddle.push(` ${ccType} ${fieldName}${assignment};`);
188
+ }
189
+ header.mainMiddle.push("");
190
+ header.mainMiddle.push(" ::skir_internal::UnrecognizedFields<");
191
+ header.mainMiddle.push(
192
+ ` ::skir_internal::${this.namespace}::${adapterName}>`,
193
+ );
194
+ header.mainMiddle.push(" _unrecognized;");
195
+ header.mainMiddle.push("");
196
+ header.mainMiddle.push(
197
+ ` bool operator==(const ${className}& other) const;`,
198
+ );
199
+ header.mainMiddle.push("");
200
+ header.mainMiddle.push(
201
+ ` inline bool operator!=(const ${className}& other) const {`,
202
+ );
203
+ header.mainMiddle.push(" return !(*this == other);");
204
+ header.mainMiddle.push(" }");
205
+ header.mainMiddle.push("");
206
+ {
207
+ const commentLines = [
208
+ "Use this when you want to make sure you are specifying all the fields of",
209
+ "the struct. The compiler will error if one field is missing.",
210
+ ];
211
+ if (fields.length !== 0) {
212
+ commentLines.push(
213
+ ...[
214
+ "",
215
+ "Example:",
216
+ "",
217
+ ` ${className} value = ${className}::whole{`,
218
+ ],
219
+ );
220
+ for (const field of fieldsByName) {
221
+ const fieldName = maybeEscapeLowerCaseName(field.name.text);
222
+ commentLines.push(` .${fieldName} = ...,`);
223
+ }
224
+ commentLines.push(" };");
225
+ }
226
+ header.mainMiddle.push(...commentify(commentLines, " "));
227
+ }
228
+ header.mainMiddle.push(" struct whole {");
229
+ for (const field of fieldsByName) {
230
+ const type = field.type!;
231
+ const fieldIsRecursive = this.recursivityResolver.isRecursive(field);
232
+ const ccType = typeSpeller.getCcType(type, {
233
+ fieldIsRecursive: fieldIsRecursive,
234
+ });
235
+ const fieldName = maybeEscapeLowerCaseName(field.name.text);
236
+ header.mainMiddle.push(
237
+ ...commentify(docToCommentText(field.doc), " "),
238
+ );
239
+ header.mainMiddle.push(` ::skir::must_init<${ccType}> ${fieldName};`);
240
+ }
241
+ header.mainMiddle.push("");
242
+ header.mainMiddle.push(` operator ${className}();`);
243
+ header.mainMiddle.push(" };");
244
+ header.mainMiddle.push("");
245
+ for (const nestedRecord of nestedRecords) {
246
+ let typeAlias = nestedRecord.name.text;
247
+ if (typeAlias === className) {
248
+ typeAlias = `${typeAlias}_`;
249
+ }
250
+ const recordLocation = recordMap.get(nestedRecord.key)!;
251
+ const nestedClassName = getClassName(recordLocation);
252
+ header.mainMiddle.push(` using ${typeAlias} = ${nestedClassName};`);
253
+ }
254
+ header.mainMiddle.push("};");
255
+ header.mainMiddle.push("");
256
+ header.mainBottom.push("inline std::ostream& operator<<(");
257
+ header.mainBottom.push(" std::ostream& os,");
258
+ header.mainBottom.push(` ${constRefType} input) {`);
259
+ header.mainBottom.push(
260
+ " return os << ::skir_internal::ToDebugString(input);",
261
+ );
262
+ header.mainBottom.push("}");
263
+ header.mainBottom.push("");
264
+ {
265
+ header.mainBottom.push("template <typename H>");
266
+ header.mainBottom.push(`H AbslHashValue(H h, ${constRefType} input) {`);
267
+ const args = fields
268
+ .map((f) => `,\n input.${maybeEscapeLowerCaseName(f.name.text)}`)
269
+ .join("");
270
+ header.mainBottom.push(" return H::combine(");
271
+ header.mainBottom.push(` std::move(h)${args});`);
272
+ header.mainBottom.push("}");
273
+ header.mainBottom.push("");
274
+ }
275
+
276
+ // --------------------------------------
277
+ // - APPEND CODE IN THE .testing.h FILE -
278
+ // --------------------------------------
279
+
280
+ testingHeader.skirout.push("template <>");
281
+ testingHeader.skirout.push(`struct StructIs<${qualifiedName}> {`);
282
+ // Declare fields in alphabetical order. It helps users who want to
283
+ // initialize a struct using the designated initializer syntax. See:
284
+ // https://abseil.io/tips/172
285
+ for (const field of fieldsByName) {
286
+ const type = field.type!;
287
+ const fieldIsRecursive = this.recursivityResolver.isRecursive(field);
288
+ const fieldName = maybeEscapeLowerCaseName(field.name.text);
289
+ if (type.kind === "record") {
290
+ // Do not pass fieldIsRecursive.
291
+ const ccType = typeSpeller.getCcType(type, { forceNamespace: true });
292
+ const recordType = recordMap.get(type.key)!.record.recordType;
293
+ if (!fieldIsRecursive && recordType === "struct") {
294
+ testingHeader.skirout.push(` StructIs<${ccType}> ${fieldName};`);
295
+ } else {
296
+ testingHeader.skirout.push(` Matcher<${ccType}> ${fieldName} = _;`);
297
+ }
298
+ } else {
299
+ const ccType = typeSpeller.getCcType(type, {
300
+ fieldIsRecursive: fieldIsRecursive,
301
+ forceNamespace: true,
302
+ });
303
+ testingHeader.skirout.push(` Matcher<${ccType}> ${fieldName} = _;`);
304
+ }
305
+ }
306
+ testingHeader.skirout.push("");
307
+ testingHeader.skirout.push(
308
+ ` Matcher<${qualifiedName}> ToMatcher() const {`,
309
+ );
310
+ if (fields.length <= 0) {
311
+ testingHeader.skirout.push(" return _;");
312
+ } else {
313
+ testingHeader.skirout.push(
314
+ ` return ::testing::skir_internal::StructIs<${qualifiedName}>(`,
315
+ );
316
+ for (const field of fieldsByName) {
317
+ const type = field.type!;
318
+ const fieldIsRecursive = this.recursivityResolver.isRecursive(field);
319
+ const fieldName = field.name.text;
320
+ let matcherExpr = maybeEscapeLowerCaseName(fieldName);
321
+ if (type.kind === "record") {
322
+ const recordType = recordMap.get(type.key)!.record.recordType;
323
+ if (!fieldIsRecursive && recordType === "struct") {
324
+ matcherExpr += ".ToMatcher()";
325
+ }
326
+ }
327
+ const end = field === fieldsByName.at(-1) ? ");" : ",";
328
+ const getterExpr = `::skirout::get_${fieldName}()`;
329
+ testingHeader.skirout.push(
330
+ ` std::make_pair(${getterExpr}, ${matcherExpr})${end}`,
331
+ );
332
+ }
333
+ }
334
+ testingHeader.skirout.push(" }");
335
+ testingHeader.skirout.push("");
336
+ testingHeader.skirout.push(" template <typename T>");
337
+ testingHeader.skirout.push(" operator Matcher<T>() const {");
338
+ testingHeader.skirout.push(
339
+ " return ::testing::SafeMatcherCast<T>(ToMatcher());",
340
+ );
341
+ testingHeader.skirout.push(" }");
342
+ testingHeader.skirout.push("");
343
+ testingHeader.skirout.push("};");
344
+ testingHeader.skirout.push("");
345
+
346
+ // -------------------------------
347
+ // - APPEND CODE IN THE .cc FILE -
348
+ // -------------------------------
349
+
350
+ {
351
+ // _GetArrayLength(const T&)
352
+ source.anonymous.push("inline ::int32_t _GetArrayLength(");
353
+ source.anonymous.push(` ${constRefType} input,`);
354
+ source.anonymous.push(
355
+ " const std::shared_ptr<skir_internal::UnrecognizedFieldsData>& u,",
356
+ );
357
+ source.anonymous.push(" skir_internal::UnrecognizedFormat format) {");
358
+ source.anonymous.push(" if (u != nullptr && u->format == format)");
359
+ source.anonymous.push(" return u->array_len;");
360
+ for (const field of [...fieldsByNumber].reverse()) {
361
+ const { number, name } = field;
362
+ const fieldExpr = `input.${maybeEscapeLowerCaseName(name.text)}`;
363
+ source.anonymous.push(
364
+ ` if (!::skir_internal::IsDefault(${fieldExpr}))`,
365
+ );
366
+ source.anonymous.push(` return ${number + 1};`);
367
+ }
368
+ source.anonymous.push(" return 0;");
369
+ source.anonymous.push("}");
370
+ source.anonymous.push("");
371
+ }
372
+
373
+ {
374
+ // IsDefault(const T&)
375
+ source.internalMain.push(
376
+ `bool ${adapterName}::IsDefault(const type& input) {`,
377
+ );
378
+ const expression = fields.length
379
+ ? fields
380
+ .map((f) => {
381
+ const fieldName = maybeEscapeLowerCaseName(f.name.text);
382
+ return `::skir_internal::IsDefault(input.${fieldName})`;
383
+ })
384
+ .join("\n && ")
385
+ : "true";
386
+ source.internalMain.push(` return ${expression};`);
387
+ source.internalMain.push("}");
388
+ source.internalMain.push("");
389
+ }
390
+
391
+ {
392
+ // Append(const T&, DenseJson&)
393
+ source.internalMain.push(
394
+ `void ${adapterName}::Append(const type& input, DenseJson& out) {`,
395
+ );
396
+ source.internalMain.push(
397
+ " const auto& unrecognized = input._unrecognized.data;",
398
+ );
399
+ source.internalMain.push(
400
+ " const auto array_len = _GetArrayLength(input, unrecognized, skir_internal::UnrecognizedFormat::kDenseJson);",
401
+ );
402
+ source.internalMain.push(" if (array_len == 0) {");
403
+ source.internalMain.push(" out.out += {'[', ']'};");
404
+ source.internalMain.push(" return;");
405
+ source.internalMain.push(" }");
406
+ source.internalMain.push(" JsonArrayCloser closer(&out);");
407
+ let charLiterals = ["'['"];
408
+ let lastFieldNumber = -1;
409
+ for (const field of fieldsByNumber) {
410
+ const { number, name } = field;
411
+ // Append one 0 for every removed number.
412
+ for (let i = lastFieldNumber + 1; i < number; ++i) {
413
+ charLiterals.push("'0'");
414
+ charLiterals.push("','");
415
+ }
416
+ source.internalMain.push(` out.out += {${charLiterals.join(", ")}};`);
417
+ charLiterals = [];
418
+ const fieldExpr = `input.${maybeEscapeLowerCaseName(name.text)}`;
419
+ source.internalMain.push(
420
+ ` ::skir_internal::Append(${fieldExpr}, out);`,
421
+ );
422
+ source.internalMain.push(` if (array_len == ${number + 1}) return;`);
423
+ lastFieldNumber = number;
424
+ charLiterals.push("','");
425
+ }
426
+ for (let i = numSlots; i < numSlotsInclRemovedNumbers; ++i) {
427
+ charLiterals.push("'0'");
428
+ charLiterals.push("','");
429
+ }
430
+ source.internalMain.push(` out.out += {${charLiterals.join(", ")}};`);
431
+ source.internalMain.push(" unrecognized->values.AppendTo(out);");
432
+ source.internalMain.push("}");
433
+ source.internalMain.push("");
434
+ }
435
+
436
+ {
437
+ // Append(const T&, ReadableJson&)
438
+ source.internalMain.push(
439
+ `void ${adapterName}::Append(const type& input, ReadableJson& out) {`,
440
+ );
441
+ if (fields.length) {
442
+ source.internalMain.push(" JsonObjectWriter(&out)");
443
+ for (const field of fields) {
444
+ const isLastField = field === fields.at(-1);
445
+ const maybeSemicolon = isLastField ? ";" : "";
446
+ const name = field.name.text;
447
+ const fieldExpr = `input.${maybeEscapeLowerCaseName(name)}`;
448
+ source.internalMain.push(
449
+ ` .Write("${name}", ${fieldExpr})${maybeSemicolon}`,
450
+ );
451
+ }
452
+ } else {
453
+ source.internalMain.push(" out.out += {'{', '}'};");
454
+ }
455
+ source.internalMain.push("}");
456
+ source.internalMain.push("");
457
+ }
458
+
459
+ {
460
+ // Append(const T&, DebugString&)
461
+ source.internalMain.push(
462
+ `void ${adapterName}::Append(const type& input, DebugString& out) {`,
463
+ );
464
+ if (fields.length) {
465
+ source.internalMain.push(" DebugObjectWriter(&out)");
466
+ for (const field of fields) {
467
+ const isLastField = field === fields.at(-1);
468
+ const maybeSemicolon = isLastField ? ";" : "";
469
+ const name = maybeEscapeLowerCaseName(field.name.text);
470
+ const fieldExpr = `input.${name}`;
471
+ source.internalMain.push(
472
+ ` .Write("${name}", ${fieldExpr})${maybeSemicolon}`,
473
+ );
474
+ }
475
+ } else {
476
+ source.internalMain.push(" out.out += {'{', '}'};");
477
+ }
478
+ source.internalMain.push("}");
479
+ source.internalMain.push("");
480
+ }
481
+
482
+ {
483
+ // Append(const T&, ByteSink&)
484
+ source.internalMain.push(
485
+ `void ${adapterName}::Append(const type& input, ByteSink& out) {`,
486
+ );
487
+ source.internalMain.push(
488
+ " const auto& unrecognized = input._unrecognized.data;",
489
+ );
490
+ source.internalMain.push(
491
+ " const auto array_len = _GetArrayLength(input, unrecognized, skir_internal::UnrecognizedFormat::kBytes);",
492
+ );
493
+ source.internalMain.push(" if (array_len == 0) {");
494
+ source.internalMain.push(" out.Push(246);");
495
+ source.internalMain.push(" return;");
496
+ source.internalMain.push(" }");
497
+ source.internalMain.push(" AppendArrayPrefix(array_len, out);");
498
+ let lastFieldNumber = -1;
499
+ for (const field of fieldsByNumber) {
500
+ const { number, name } = field;
501
+ const isLastField = field === fieldsByNumber.at(-1);
502
+ if (lastFieldNumber < number - 1) {
503
+ // Append one 0 for every removed number.
504
+ const zeros = "0, ".repeat(number - lastFieldNumber - 1).slice(0, -2);
505
+ source.internalMain.push(` out.Push(${zeros});`);
506
+ }
507
+ const fieldExpr = `input.${maybeEscapeLowerCaseName(name.text)}`;
508
+ source.internalMain.push(
509
+ ` ::skir_internal::Append(${fieldExpr}, out);`,
510
+ );
511
+ if (!isLastField) {
512
+ source.internalMain.push(` if (array_len == ${number + 1}) return;`);
513
+ }
514
+ lastFieldNumber = number;
515
+ }
516
+ source.internalMain.push(
517
+ ` if (array_len == ${lastFieldNumber + 1}) return;`,
518
+ );
519
+ if (numSlots < numSlotsInclRemovedNumbers) {
520
+ // Append one 0 for every removed number at the end of the struct.
521
+ const zeros = "0, "
522
+ .repeat(numSlotsInclRemovedNumbers - numSlots)
523
+ .slice(0, -2);
524
+ source.internalMain.push(` out.Push(${zeros});`);
525
+ }
526
+ source.internalMain.push(" unrecognized->values.AppendTo(out);");
527
+ source.internalMain.push("}");
528
+ source.internalMain.push("");
529
+ }
530
+
531
+ {
532
+ // Parse(JsonTokenizer&, T&)
533
+ source.internalMain.push(`void ${adapterName}::Parse(`);
534
+ source.internalMain.push(" JsonTokenizer& tokenizer,");
535
+ source.internalMain.push(" type& out) {");
536
+ source.internalMain.push(" switch (tokenizer.state().token_type) {");
537
+ source.internalMain.push(" case JsonTokenType::kLeftSquareBracket: {");
538
+ source.internalMain.push(
539
+ " JsonArrayReader array_reader(&tokenizer);",
540
+ );
541
+ let lastNumber = -1;
542
+ for (const field of fieldsByNumber) {
543
+ const ccFieldName = maybeEscapeLowerCaseName(field.name.text);
544
+ source.internalMain.push(
545
+ " if (!array_reader.NextElement()) break;",
546
+ );
547
+ for (let i = lastNumber + 1; i < field.number; ++i) {
548
+ source.internalMain.push(" SkipValue(tokenizer);");
549
+ source.internalMain.push(
550
+ " if (!array_reader.NextElement()) break;",
551
+ );
552
+ }
553
+ source.internalMain.push(
554
+ ` ::skir_internal::Parse(tokenizer, out.${ccFieldName});`,
555
+ );
556
+ lastNumber = field.number;
557
+ }
558
+ source.internalMain.push(" if (!array_reader.NextElement()) break;");
559
+ source.internalMain.push(
560
+ " auto& unrecognized = out._unrecognized.data;",
561
+ );
562
+ const args = [
563
+ "array_reader",
564
+ numSlots,
565
+ numSlotsInclRemovedNumbers,
566
+ "unrecognized",
567
+ ].join(", ");
568
+ source.internalMain.push(
569
+ ` ::skir_internal::ParseUnrecognizedFields(${args});`,
570
+ );
571
+ source.internalMain.push(" break;");
572
+ source.internalMain.push(" }");
573
+ source.internalMain.push(" case JsonTokenType::kLeftCurlyBracket: {");
574
+ const parserExpr =
575
+ "(new StructJsonObjectParser<type>())" +
576
+ fields
577
+ .map((field) => {
578
+ const name = field.name.text;
579
+ const ccFieldName = maybeEscapeLowerCaseName(name);
580
+ const indent = " ";
581
+ return `\n${indent}->AddField("${name}", &type::${ccFieldName})`;
582
+ })
583
+ .join("");
584
+ source.internalMain.push(" static const auto* kParser =");
585
+ source.internalMain.push(` ${parserExpr};`);
586
+ source.internalMain.push(" kParser->Parse(tokenizer, out);");
587
+ source.internalMain.push(" break;");
588
+ source.internalMain.push(" }");
589
+ source.internalMain.push(" case JsonTokenType::kZero:");
590
+ source.internalMain.push(" tokenizer.Next();");
591
+ source.internalMain.push(" break;");
592
+ source.internalMain.push(" default: {");
593
+ source.internalMain.push(
594
+ " tokenizer.mutable_state().PushUnexpectedTokenError(\"'['\");",
595
+ );
596
+ source.internalMain.push(" }");
597
+ source.internalMain.push(" }");
598
+ source.internalMain.push("}");
599
+ source.internalMain.push("");
600
+ }
601
+
602
+ {
603
+ // Parse(ByteSource&, T&)
604
+ source.internalMain.push(
605
+ `void ${adapterName}::Parse(ByteSource& source, type& out) {`,
606
+ );
607
+ source.internalMain.push(" ::uint32_t array_len = 0;");
608
+ source.internalMain.push(" ParseArrayPrefix(source, array_len);");
609
+ let lastNumber = -1;
610
+ for (const field of fieldsByNumber) {
611
+ const ccFieldName = maybeEscapeLowerCaseName(field.name.text);
612
+ for (let i = lastNumber + 1; i < field.number; ++i) {
613
+ source.internalMain.push(` if (array_len == ${i}) return;`);
614
+ source.internalMain.push(" SkipValue(source);");
615
+ }
616
+ source.internalMain.push(` if (array_len == ${field.number}) return;`);
617
+ source.internalMain.push(
618
+ ` ::skir_internal::Parse(source, out.${ccFieldName});`,
619
+ );
620
+ lastNumber = field.number;
621
+ }
622
+ source.internalMain.push(` if (array_len == ${numSlots}) return;`);
623
+ source.internalMain.push(
624
+ " auto& unrecognized = out._unrecognized.data;",
625
+ );
626
+ const args = [
627
+ "source",
628
+ "array_len",
629
+ numSlots,
630
+ numSlotsInclRemovedNumbers,
631
+ "unrecognized",
632
+ ].join(", ");
633
+ source.internalMain.push(
634
+ ` ::skir_internal::ParseUnrecognizedFields(${args});`,
635
+ );
636
+ source.internalMain.push("}");
637
+ source.internalMain.push("");
638
+ }
639
+
640
+ {
641
+ // GetType(skir_type<T>)
642
+ source.internalMain.push(
643
+ `skir::reflection::Type ${adapterName}::GetType(skir_type<type>) {`,
644
+ );
645
+ const recordId = getRecordId(struct);
646
+ source.internalMain.push(
647
+ ` return skir::reflection::RecordType({"${recordId}"});`,
648
+ );
649
+ source.internalMain.push("}");
650
+ source.internalMain.push("");
651
+ }
652
+
653
+ {
654
+ // RegisterRecords(skir_type<T>, RecordRegistry&)
655
+ const recordId = getRecordId(struct);
656
+ source.internalMain.push(`void ${adapterName}::RegisterRecords(`);
657
+ source.internalMain.push(" skir_type<type>,");
658
+ source.internalMain.push(
659
+ " skir::reflection::RecordRegistry& registry) {",
660
+ );
661
+ source.internalMain.push(" const bool already_present =");
662
+ source.internalMain.push(
663
+ ` registry.find_or_null("${recordId}") != nullptr;`,
664
+ );
665
+ source.internalMain.push(" if (already_present) return;");
666
+ source.internalMain.push(" skir::reflection::Struct record = {");
667
+ source.internalMain.push(` "${recordId}",`);
668
+ source.internalMain.push(
669
+ ` ${JSON.stringify(struct.record.doc.text)},`,
670
+ );
671
+ source.internalMain.push(" {");
672
+ for (const field of fields) {
673
+ const ccType = typeSpeller.getCcType(field.type!, {
674
+ forceNamespace: true,
675
+ });
676
+ source.internalMain.push(" {");
677
+ source.internalMain.push(` "${field.name.text}",`);
678
+ source.internalMain.push(` ${field.number},`);
679
+ source.internalMain.push(
680
+ ` skir_internal::GetType<${ccType}>(),`,
681
+ );
682
+ source.internalMain.push(
683
+ ` ${JSON.stringify(field.doc.text)},`,
684
+ );
685
+ source.internalMain.push(" },");
686
+ }
687
+ source.internalMain.push(" },");
688
+ const removedNumbers = struct.record.removedNumbers.join(", ");
689
+ source.internalMain.push(` {${removedNumbers}},`);
690
+ source.internalMain.push(" };");
691
+ source.internalMain.push(" registry.push_back(std::move(record));");
692
+ for (const field of fields) {
693
+ const ccType = typeSpeller.getCcType(field.type!, {
694
+ forceNamespace: true,
695
+ });
696
+ source.internalMain.push(
697
+ ` skir_internal::RegisterRecords<${ccType}>(registry);`,
698
+ );
699
+ }
700
+ source.internalMain.push("}");
701
+ source.internalMain.push("");
702
+ }
703
+
704
+ {
705
+ // Struct::operator==(const T&)
706
+ source.mainBottom.push(
707
+ `bool ${className}::operator==(${constRefType} other) const {`,
708
+ );
709
+ const expression = fields.length
710
+ ? fields
711
+ .map((f) => {
712
+ const fieldName = maybeEscapeLowerCaseName(f.name.text);
713
+ return `this->${fieldName} == other.${fieldName}`;
714
+ })
715
+ .join("\n && ")
716
+ : "true";
717
+ source.mainBottom.push(` return ${expression};`);
718
+ source.mainBottom.push("}");
719
+ source.mainBottom.push("");
720
+ }
721
+
722
+ {
723
+ // Whole -> Struct
724
+ source.mainBottom.push(`${className}::whole::operator ${className}() {`);
725
+ source.mainBottom.push(` return ${className}{`);
726
+ for (const field of fieldsByName) {
727
+ const fieldName = maybeEscapeLowerCaseName(field.name.text);
728
+ source.mainBottom.push(` *std::move(${fieldName}),`);
729
+ }
730
+ source.mainBottom.push(" };");
731
+ source.mainBottom.push("}");
732
+ source.mainBottom.push("");
733
+ }
734
+ }
735
+
736
+ private writeCodeForEnum(record: RecordLocation): void {
737
+ const { header, recordMap, source, typeSpeller } = this;
738
+
739
+ const { nestedRecords } = record.record;
740
+ const variants = getEnumVariants(record.record.fields, typeSpeller);
741
+ const constVariants = variants.filter((f) => !f.valueType);
742
+ const wrapperVariants = variants.filter((f) => f.valueType);
743
+ const pointerVariants = wrapperVariants.filter((f) => f.usePointer);
744
+
745
+ for (const variant of constVariants) {
746
+ this.writeCodeForConstantVariant(variant);
747
+ }
748
+ for (const variant of wrapperVariants) {
749
+ this.writeCodeForWrapperVariant(variant);
750
+ }
751
+
752
+ const className = getClassName(record);
753
+ const adapterName = `${className}Adapter`;
754
+ const qualifiedName = `::${this.namespace}::${className}`;
755
+ const constRefType = `const ${qualifiedName}&`;
756
+
757
+ header.mainTop.push(`class ${className};`);
758
+
759
+ header.mainMiddle.push(...commentify(docToCommentText(record.record.doc)));
760
+ header.mainMiddle.push(`class ${className} {`);
761
+ header.mainMiddle.push(" public:");
762
+ for (const variant of wrapperVariants) {
763
+ const type = `::skirout::${variant.structType}<${variant.valueType}>`;
764
+ header.mainMiddle.push(` using ${variant.typeAlias} = ${type};`);
765
+ }
766
+ header.mainMiddle.push("");
767
+ header.mainMiddle.push(
768
+ ` // Identifies the possible variants held by a ${className}.`,
769
+ );
770
+ header.mainMiddle.push(" enum class kind_type {");
771
+ for (const variant of variants) {
772
+ header.mainMiddle.push(
773
+ ...commentify(
774
+ variant.isUnknownVariant
775
+ ? `Constant indicating an unknown ${className}.\n`
776
+ : docToCommentText(variant.doc),
777
+ " ",
778
+ ),
779
+ );
780
+ header.mainMiddle.push(` ${variant.kindEnumerator},`);
781
+ }
782
+ header.mainMiddle.push(" };");
783
+ header.mainMiddle.push("");
784
+ for (const variant of constVariants) {
785
+ const { identifier, isUnknownVariant, kindEnumerator, structType } =
786
+ variant;
787
+ const assignment = isUnknownVariant ? " = ::skirout::kUnknown" : "";
788
+ header.mainMiddle.push(
789
+ ` // For example: FunctionExpecting${className}(skirout::${identifier})`,
790
+ );
791
+ header.mainMiddle.push(
792
+ ` ${className}(::skirout::${structType}${assignment});`,
793
+ );
794
+ const body = isUnknownVariant
795
+ ? "{\n value_._unrecognized = nullptr;\n}"
796
+ : "{}";
797
+ source.mainMiddle.push(
798
+ `${className}::${className}(::skirout::${
799
+ structType
800
+ }) : kind_(kind_type::${kindEnumerator}) ${body}`,
801
+ );
802
+ }
803
+ source.mainMiddle.push("");
804
+ for (const variant of wrapperVariants) {
805
+ const { identifier, kindEnumerator, typeAlias, usePointer, variantName } =
806
+ variant;
807
+ header.mainMiddle.push(
808
+ ` // For example: FunctionExpecting${className}(skirout::${identifier}(...))`,
809
+ );
810
+ header.mainMiddle.push(` ${className}(${typeAlias});`);
811
+ source.mainMiddle.push(`${className}::${className}(${typeAlias} w)`);
812
+ source.mainMiddle.push(` : kind_(kind_type::${kindEnumerator}) {`);
813
+ if (usePointer) {
814
+ source.mainMiddle.push(
815
+ ` value_.${variantName}_ = new ${typeAlias}(std::move(w));`,
816
+ );
817
+ } else {
818
+ source.mainMiddle.push(` value_.${variantName}_ = w;`);
819
+ }
820
+ source.mainMiddle.push("}");
821
+ source.mainMiddle.push("");
822
+ }
823
+ header.mainMiddle.push("");
824
+ header.mainMiddle.push(` ${className}(const ${className}&);`);
825
+ source.mainMiddle.push(
826
+ `${className}::${className}(const ${className}& other) {`,
827
+ );
828
+ source.mainMiddle.push(" copy(other);");
829
+ source.mainMiddle.push("}");
830
+ source.mainMiddle.push("");
831
+
832
+ header.mainMiddle.push(` ${className}(${className}&&);`);
833
+ header.mainMiddle.push("");
834
+ source.mainMiddle.push(`${className}::${className}(${className}&& other)`);
835
+ source.mainMiddle.push(` : kind_(other.kind_),`);
836
+ source.mainMiddle.push(` value_(other.value_) {`);
837
+ source.mainMiddle.push(" other.kind_ = kind_type::kUnknown;");
838
+ source.mainMiddle.push(" other.value_._unrecognized = nullptr;");
839
+ source.mainMiddle.push("}");
840
+ source.mainMiddle.push("");
841
+ {
842
+ source.mainMiddle.push(
843
+ `${className}::${
844
+ className
845
+ }(unrecognized_variant u) : kind_(kind_type::kUnknown) {`,
846
+ );
847
+ source.mainMiddle.push(
848
+ " value_._unrecognized = new unrecognized_variant(std::move(u));",
849
+ );
850
+ source.mainMiddle.push("}");
851
+ source.mainMiddle.push("");
852
+ }
853
+ header.mainMiddle.push("");
854
+ header.mainMiddle.push(` ~${className}();`);
855
+ header.mainMiddle.push("");
856
+ source.mainMiddle.push(`${className}::~${className}() {`);
857
+ source.mainMiddle.push(" free_value();");
858
+ source.mainMiddle.push("}");
859
+ source.mainMiddle.push("");
860
+ for (const variant of constVariants) {
861
+ const { identifier } = variant;
862
+ header.mainMiddle.push(
863
+ ` static constexpr auto ${identifier} = ::skirout::${identifier};`,
864
+ );
865
+ }
866
+ header.mainMiddle.push("");
867
+ for (const variant of wrapperVariants) {
868
+ const { identifier, usePointer, valueType } = variant;
869
+ header.mainMiddle.push(
870
+ ` static ${className} ${identifier}(${valueType} value);`,
871
+ );
872
+ source.mainMiddle.push(
873
+ `${className} ${className}::${identifier}(${valueType} value) {`,
874
+ );
875
+ const maybeMoveValue = usePointer ? "std::move(value)" : "value";
876
+ const returnValue = `${className}(::skirout::${identifier}(${maybeMoveValue}))`;
877
+ source.mainMiddle.push(` return ${returnValue};`);
878
+ source.mainMiddle.push("}");
879
+ source.mainMiddle.push("");
880
+ }
881
+ header.mainMiddle.push("");
882
+ header.mainMiddle.push(
883
+ ` // Returns the kind of variant held by this ${className}.`,
884
+ );
885
+ header.mainMiddle.push(" kind_type kind() const { return kind_; }");
886
+ header.mainMiddle.push("");
887
+ for (const variant of wrapperVariants) {
888
+ const { variantName, kindEnumerator, usePointer, valueType } = variant;
889
+ header.mainMiddle.push(
890
+ ` // Returns true if this ${className} holds a ${variantName} variant.`,
891
+ );
892
+ header.mainMiddle.push(` inline bool is_${variantName}() const;`);
893
+ header.mainMiddle.push(
894
+ ` // Assuming this ${className} holds a ${variantName} variant, returns its value.`,
895
+ );
896
+ header.mainMiddle.push(
897
+ ` inline const ${valueType}& as_${variantName}() const;`,
898
+ );
899
+ header.mainMiddle.push(` inline ${valueType}& as_${variantName}();`);
900
+ header.mainMiddle.push("");
901
+ header.mainBottom.push(
902
+ `inline bool ${className}::is_${variantName}() const {`,
903
+ );
904
+ (header.mainBottom.push(
905
+ ` return kind_ == kind_type::${kindEnumerator};`,
906
+ ),
907
+ header.mainBottom.push("}"));
908
+ header.mainBottom.push("");
909
+ header.mainBottom.push(
910
+ `inline const ${valueType}& ${className}::as_${variantName}() const {`,
911
+ );
912
+ header.mainBottom.push(
913
+ ` return const_cast<${className}*>(this)->as_${variantName}();`,
914
+ );
915
+ header.mainBottom.push("}");
916
+ header.mainBottom.push("");
917
+ header.mainBottom.push(
918
+ `inline ${valueType}& ${className}::as_${variantName}() {`,
919
+ );
920
+ header.mainBottom.push(
921
+ ` ABSL_CHECK(is_${variantName}()) << "actual: " << *this;`,
922
+ );
923
+ const returnValue = usePointer
924
+ ? `value_.${variantName}_->value`
925
+ : `value_.${variantName}_.value`;
926
+ header.mainBottom.push(` return ${returnValue};`);
927
+ header.mainBottom.push("}");
928
+ header.mainBottom.push("");
929
+ }
930
+
931
+ {
932
+ const commentLines = ["Example:", "", " struct visitor {"];
933
+ for (const variant of constVariants) {
934
+ const { structType } = variant;
935
+ commentLines.push(
936
+ ` void operator()(skirout::${structType}) { ... }`,
937
+ );
938
+ }
939
+ for (const variant of wrapperVariants) {
940
+ const { structType } = variant;
941
+ commentLines.push(
942
+ ` void operator()(const ${className}::${structType}& w) {`,
943
+ );
944
+ commentLines.push(" const auto& value = w.value;");
945
+ commentLines.push(" ...");
946
+ commentLines.push(" }");
947
+ }
948
+ commentLines.push(" };");
949
+ commentLines.push(" e.visit(visitor());");
950
+ header.mainMiddle.push(...commentify(commentLines, " "));
951
+ }
952
+ header.mainMiddle.push(" template <typename Visitor>");
953
+ header.mainMiddle.push(" decltype(auto) visit(Visitor&& visitor) const {");
954
+ header.mainMiddle.push(
955
+ ` return visit_impl(*this, std::forward<Visitor>(visitor));`,
956
+ );
957
+ header.mainMiddle.push(" }");
958
+ header.mainMiddle.push(" template <typename Visitor>");
959
+ header.mainMiddle.push(" decltype(auto) visit(Visitor&& visitor) {");
960
+ header.mainMiddle.push(
961
+ ` return visit_impl(*this, std::forward<Visitor>(visitor));`,
962
+ );
963
+ header.mainMiddle.push(" }");
964
+ header.mainMiddle.push("");
965
+
966
+ header.mainMiddle.push(
967
+ ` ${className}& operator=(const ${className}& other);`,
968
+ );
969
+ source.mainMiddle.push(
970
+ `${className}& ${className}::operator=(const ${className}& other) {`,
971
+ );
972
+ source.mainMiddle.push(" free_value();");
973
+ source.mainMiddle.push(" copy(other);");
974
+ source.mainMiddle.push(" return *this;");
975
+ source.mainMiddle.push("}");
976
+ source.mainMiddle.push("");
977
+ header.mainMiddle.push(` ${className}& operator=(${className}&& other);`);
978
+ source.mainMiddle.push(
979
+ `${className}& ${className}::operator=(${className}&& other) {`,
980
+ );
981
+ source.mainMiddle.push(" free_value();");
982
+ source.mainMiddle.push(" kind_ = other.kind_;");
983
+ source.mainMiddle.push(" value_ = other.value_;");
984
+ source.mainMiddle.push(" other.kind_ = kind_type::kUnknown;");
985
+ source.mainMiddle.push(" other.value_._unrecognized = nullptr;");
986
+ source.mainMiddle.push(" return *this;");
987
+ source.mainMiddle.push("}");
988
+ source.mainMiddle.push("");
989
+ header.mainMiddle.push("");
990
+ for (const variant of constVariants) {
991
+ const { isUnknownVariant, kindEnumerator, structType } = variant;
992
+ header.mainMiddle.push(
993
+ ` ${className}& operator=(::skirout::${structType});`,
994
+ );
995
+ source.mainMiddle.push(
996
+ `${className}& ${className}::operator=(::skirout::${structType}) {`,
997
+ );
998
+ source.mainMiddle.push(" free_value();");
999
+ source.mainMiddle.push(` kind_ = kind_type::${kindEnumerator};`);
1000
+ if (isUnknownVariant) {
1001
+ source.mainMiddle.push(" value_._unrecognized = nullptr;");
1002
+ }
1003
+ source.mainMiddle.push(" return *this;");
1004
+ source.mainMiddle.push("}");
1005
+ source.mainMiddle.push("");
1006
+ }
1007
+ for (const variant of wrapperVariants) {
1008
+ const { variantName, kindEnumerator, typeAlias, usePointer } = variant;
1009
+ header.mainMiddle.push(` ${className}& operator=(${typeAlias});`);
1010
+ source.mainMiddle.push(
1011
+ `${className}& ${className}::operator=(${typeAlias} w) {`,
1012
+ );
1013
+ source.mainMiddle.push(" free_value();");
1014
+ source.mainMiddle.push(` kind_ = kind_type::${kindEnumerator};`);
1015
+ if (usePointer) {
1016
+ source.mainMiddle.push(
1017
+ ` value_.${variantName}_ = new ${typeAlias}(std::move(w));`,
1018
+ );
1019
+ } else {
1020
+ source.mainMiddle.push(` value_.${variantName}_ = w;`);
1021
+ }
1022
+ source.mainMiddle.push(" return *this;");
1023
+ source.mainMiddle.push("}");
1024
+ source.mainMiddle.push("");
1025
+ }
1026
+ header.mainMiddle.push("");
1027
+
1028
+ if (wrapperVariants.length) {
1029
+ header.mainMiddle.push(` bool operator==(const ${className}&) const;`);
1030
+
1031
+ source.mainMiddle.push(
1032
+ `bool ${className}::operator==(const ${className}& other) const {`,
1033
+ );
1034
+ source.mainMiddle.push(" if (other.kind_ != kind_) return false;");
1035
+ source.mainMiddle.push(" switch (kind_) {");
1036
+ for (const variant of wrapperVariants) {
1037
+ const { variantName, kindEnumerator, usePointer } = variant;
1038
+ const dotOrStar = usePointer ? "->" : ".";
1039
+ const a = `value_.${variantName}_${dotOrStar}value`;
1040
+ const b = `other.value_.${variantName}_${dotOrStar}value`;
1041
+ source.mainMiddle.push(` case kind_type::${kindEnumerator}:`);
1042
+ source.mainMiddle.push(` return ${a} == ${b};`);
1043
+ }
1044
+ source.mainMiddle.push(` default:`);
1045
+ source.mainMiddle.push(" return true;");
1046
+ source.mainMiddle.push(" }");
1047
+ source.mainMiddle.push("}");
1048
+ source.mainMiddle.push("");
1049
+ } else {
1050
+ header.mainMiddle.push(
1051
+ ` inline bool operator==(const ${className}& other) const {`,
1052
+ );
1053
+ header.mainMiddle.push(" return other.kind_ == kind_;");
1054
+ header.mainMiddle.push(" }");
1055
+ }
1056
+ header.mainMiddle.push("");
1057
+ header.mainMiddle.push(
1058
+ ` inline bool operator!=(const ${className}& other) const {`,
1059
+ );
1060
+ header.mainMiddle.push(" return !(*this == other);");
1061
+ header.mainMiddle.push(" }");
1062
+ header.mainMiddle.push("");
1063
+ for (const nestedRecord of nestedRecords) {
1064
+ let typeAlias = nestedRecord.name.text;
1065
+ if (typeAlias === className) {
1066
+ typeAlias = `${typeAlias}_`;
1067
+ }
1068
+ const recordLocation = recordMap.get(nestedRecord.key)!;
1069
+ const nestedClassName = getClassName(recordLocation);
1070
+ header.mainMiddle.push(` using ${typeAlias} = ${nestedClassName};`);
1071
+ }
1072
+ header.mainMiddle.push("");
1073
+ header.mainMiddle.push(" private:");
1074
+ header.mainMiddle.push(
1075
+ " using unrecognized_variant = ::skir_internal::UnrecognizedVariant;",
1076
+ );
1077
+ header.mainMiddle.push("");
1078
+ header.mainMiddle.push(` ${className}(unrecognized_variant);`);
1079
+ header.mainMiddle.push("");
1080
+ header.mainMiddle.push(" kind_type kind_;");
1081
+ header.mainMiddle.push("");
1082
+ header.mainMiddle.push(" union value_wrapper {");
1083
+ header.mainMiddle.push(" value_wrapper() {}");
1084
+ header.mainMiddle.push(" unrecognized_variant* _unrecognized;");
1085
+ for (const variant of wrapperVariants) {
1086
+ const { variantName, typeAlias } = variant;
1087
+ const maybeStar = variant.usePointer ? "*" : "";
1088
+ header.mainMiddle.push(` ${typeAlias}${maybeStar} ${variantName}_;`);
1089
+ }
1090
+ header.mainMiddle.push(" };");
1091
+ header.mainMiddle.push(" value_wrapper value_;");
1092
+ header.mainMiddle.push("");
1093
+ header.mainMiddle.push(` void copy(const ${className}&);`);
1094
+
1095
+ source.mainMiddle.push(
1096
+ `void ${className}::copy(const ${className}& other) {`,
1097
+ );
1098
+ source.mainMiddle.push(" kind_ = other.kind_;");
1099
+ source.mainMiddle.push(" switch (other.kind_) {");
1100
+ source.mainMiddle.push(" case kind_type::kUnknown: {");
1101
+ source.mainMiddle.push(
1102
+ " const unrecognized_variant* u = other.value_._unrecognized;",
1103
+ );
1104
+ source.mainMiddle.push(
1105
+ " value_._unrecognized = u != nullptr ? new unrecognized_variant(*u) : nullptr;",
1106
+ );
1107
+ source.mainMiddle.push(" break;");
1108
+ source.mainMiddle.push(" }");
1109
+ for (const variant of wrapperVariants) {
1110
+ const { variantName, kindEnumerator, typeAlias, usePointer } = variant;
1111
+ source.mainMiddle.push(` case kind_type::${kindEnumerator}:`);
1112
+ if (usePointer) {
1113
+ const expr = `new ${typeAlias}(*other.value_.${variantName}_)`;
1114
+ source.mainMiddle.push(` value_.${variantName}_ = ${expr};`);
1115
+ } else {
1116
+ source.mainMiddle.push(
1117
+ ` value_.${variantName}_ = other.value_.${variantName}_;`,
1118
+ );
1119
+ }
1120
+ source.mainMiddle.push(" break;");
1121
+ }
1122
+ source.mainMiddle.push(" default:");
1123
+ source.mainMiddle.push(" break;");
1124
+ source.mainMiddle.push(" }");
1125
+ source.mainMiddle.push("}");
1126
+ source.mainMiddle.push("");
1127
+ header.mainMiddle.push(" void free_value() const;");
1128
+
1129
+ source.mainMiddle.push(`void ${className}::free_value() const {`);
1130
+ source.mainMiddle.push(" switch (kind_) {");
1131
+ source.mainMiddle.push(" case kind_type::kUnknown:");
1132
+ source.mainMiddle.push(
1133
+ " ::std::unique_ptr<unrecognized_variant>(value_._unrecognized);",
1134
+ );
1135
+ source.mainMiddle.push(" break;");
1136
+ for (const variant of pointerVariants) {
1137
+ const { variantName, kindEnumerator, typeAlias } = variant;
1138
+ source.mainMiddle.push(` case kind_type::${kindEnumerator}:`);
1139
+ source.mainMiddle.push(
1140
+ ` ::std::unique_ptr<${typeAlias}>(value_.${variantName}_);`,
1141
+ );
1142
+ source.mainMiddle.push(" break;");
1143
+ }
1144
+ source.mainMiddle.push(" default:");
1145
+ source.mainMiddle.push(" break;");
1146
+ source.mainMiddle.push(" }");
1147
+ source.mainMiddle.push("}");
1148
+ source.mainMiddle.push("");
1149
+ header.mainMiddle.push("");
1150
+ header.mainMiddle.push(" template <typename E, typename Visitor>");
1151
+ header.mainMiddle.push(
1152
+ " static decltype(auto) visit_impl(E& e, Visitor&& visitor) {",
1153
+ );
1154
+ header.mainMiddle.push(" switch (e.kind_) {");
1155
+ for (const variant of constVariants) {
1156
+ const { kindEnumerator, structType } = variant;
1157
+ header.mainMiddle.push(` case kind_type::${kindEnumerator}:`);
1158
+ header.mainMiddle.push(
1159
+ ` return std::forward<Visitor>(visitor)(::skirout::${
1160
+ structType
1161
+ }());`,
1162
+ );
1163
+ }
1164
+ for (const variant of wrapperVariants) {
1165
+ const { variantName, kindEnumerator, usePointer } = variant;
1166
+ const maybeStar = usePointer ? "*" : "";
1167
+ header.mainMiddle.push(` case kind_type::${kindEnumerator}:`);
1168
+ header.mainMiddle.push(
1169
+ ` return std::forward<Visitor>(visitor)(${maybeStar}e.value_.${
1170
+ variantName
1171
+ }_);`,
1172
+ );
1173
+ }
1174
+ header.mainMiddle.push(" }");
1175
+ header.mainMiddle.push(" ABSL_CHECK(false);");
1176
+ header.mainMiddle.push(" }");
1177
+ header.mainMiddle.push("");
1178
+ header.mainMiddle.push(
1179
+ ` friend class ::skir_internal::${this.namespace}::${adapterName};`,
1180
+ );
1181
+ header.mainMiddle.push("};");
1182
+ header.mainMiddle.push("");
1183
+
1184
+ for (const variant of constVariants) {
1185
+ const { kindEnumerator, structType } = variant;
1186
+ header.mainBottom.push("inline bool operator==(");
1187
+ header.mainBottom.push(` ${constRefType} a,`);
1188
+ header.mainBottom.push(` ::skirout::${structType}) {`);
1189
+ header.mainBottom.push(
1190
+ ` return a.kind() == ${qualifiedName}::kind_type::${kindEnumerator};`,
1191
+ );
1192
+ header.mainBottom.push("}");
1193
+ header.mainBottom.push("");
1194
+ header.mainBottom.push("inline bool operator!=(");
1195
+ header.mainBottom.push(` ${constRefType} a,`);
1196
+ header.mainBottom.push(` ::skirout::${structType} b) {`);
1197
+ header.mainBottom.push(" return !(a == b);");
1198
+ header.mainBottom.push("}");
1199
+ header.mainBottom.push("");
1200
+ header.mainBottom.push("inline bool operator==(");
1201
+ header.mainBottom.push(` ::skirout::${variant.structType},`);
1202
+ header.mainBottom.push(` ${constRefType} b) {`);
1203
+ header.mainBottom.push(
1204
+ ` return ${qualifiedName}::kind_type::${kindEnumerator} == b.kind();`,
1205
+ );
1206
+ header.mainBottom.push("}");
1207
+ header.mainBottom.push("");
1208
+ header.mainBottom.push("inline bool operator!=(");
1209
+ header.mainBottom.push(` ::skirout::${variant.structType} a,`);
1210
+ header.mainBottom.push(` ${constRefType} b) {`);
1211
+ header.mainBottom.push(" return !(a == b);");
1212
+ header.mainBottom.push("}");
1213
+ header.mainBottom.push("");
1214
+ }
1215
+ header.mainBottom.push("template <typename H>");
1216
+ header.mainBottom.push(`H AbslHashValue(H h, ${constRefType} input) {`);
1217
+ header.mainBottom.push(" struct visitor {");
1218
+ header.mainBottom.push(" H h;");
1219
+ for (const variant of constVariants) {
1220
+ const { variantName, structType } = variant;
1221
+ header.mainBottom.push(` H operator()(::skirout::${structType}) {`);
1222
+ const hash = simpleHash(variantName);
1223
+ header.mainBottom.push(` return H::combine(std::move(h), ${hash});`);
1224
+ header.mainBottom.push(" }");
1225
+ }
1226
+ for (const variant of wrapperVariants) {
1227
+ const { variantName, typeAlias } = variant;
1228
+ header.mainBottom.push(
1229
+ ` H operator()(const ${className}::${typeAlias}& w) {`,
1230
+ );
1231
+ const hash = simpleHash(variantName);
1232
+ header.mainBottom.push(
1233
+ ` return H::combine(std::move(h), ${hash}, w.value);`,
1234
+ );
1235
+ header.mainBottom.push(" }");
1236
+ }
1237
+ header.mainBottom.push(" };");
1238
+ header.mainBottom.push(" return input.visit(visitor{std::move(h)});");
1239
+ header.mainBottom.push("}");
1240
+ header.mainBottom.push("");
1241
+ header.mainMiddle.push("inline std::ostream& operator<<(");
1242
+ header.mainMiddle.push(" std::ostream& os,");
1243
+ header.mainMiddle.push(` ${constRefType} input) {`);
1244
+ header.mainMiddle.push(
1245
+ " return os << ::skir_internal::ToDebugString(input);",
1246
+ );
1247
+ header.mainMiddle.push("}");
1248
+ header.mainMiddle.push("");
1249
+
1250
+ {
1251
+ // IsDefault(const T&)
1252
+ source.internalMain.push(
1253
+ `bool ${adapterName}::IsDefault(const type& input) {`,
1254
+ );
1255
+ source.internalMain.push(" return input == ::skirout::kUnknown;");
1256
+ source.internalMain.push("}");
1257
+ source.internalMain.push("");
1258
+ }
1259
+
1260
+ {
1261
+ // Append(const T&, DenseJson&)
1262
+ source.internalMain.push(
1263
+ `void ${adapterName}::Append(const type& input, DenseJson& out) {`,
1264
+ );
1265
+ source.internalMain.push(" switch (input.kind_) {");
1266
+ for (const variant of constVariants) {
1267
+ const { variantNumber, kindEnumerator, isUnknownVariant } = variant;
1268
+ source.internalMain.push(
1269
+ ` case type::kind_type::${kindEnumerator}: {`,
1270
+ );
1271
+ if (isUnknownVariant) {
1272
+ source.internalMain.push(
1273
+ " AppendUnrecognizedVariant(input.value_._unrecognized, out);",
1274
+ );
1275
+ } else {
1276
+ source.internalMain.push(
1277
+ ` out.out += {${numberToCharLiterals(variantNumber)}};`,
1278
+ );
1279
+ }
1280
+ source.internalMain.push(" break;");
1281
+ source.internalMain.push(" }");
1282
+ }
1283
+ for (const variant of wrapperVariants) {
1284
+ const { variantName, variantNumber, kindEnumerator, usePointer } =
1285
+ variant;
1286
+ const dotOrArrow = usePointer ? "->" : ".";
1287
+ source.internalMain.push(
1288
+ ` case type::kind_type::${kindEnumerator}: {`,
1289
+ );
1290
+ source.internalMain.push(
1291
+ ` out.out += {'[', ${numberToCharLiterals(variantNumber)}, ','};`,
1292
+ );
1293
+ source.internalMain.push(
1294
+ ` ::skir_internal::Append(input.value_.${variantName}_${
1295
+ dotOrArrow
1296
+ }value, out);`,
1297
+ );
1298
+ source.internalMain.push(" out.out += ']';");
1299
+ source.internalMain.push(" break;");
1300
+ source.internalMain.push(" }");
1301
+ }
1302
+ source.internalMain.push(" }");
1303
+ source.internalMain.push("}");
1304
+ source.internalMain.push("");
1305
+ }
1306
+
1307
+ {
1308
+ // Append(const T&, ReadableJson&)
1309
+ source.internalMain.push(
1310
+ `void ${adapterName}::Append(const type& input, ReadableJson& out) {`,
1311
+ );
1312
+ source.internalMain.push(" struct visitor {");
1313
+ source.internalMain.push(" ReadableJson& out;");
1314
+ for (const variant of constVariants) {
1315
+ const { variantName, structType } = variant;
1316
+ source.internalMain.push(
1317
+ ` void operator()(::skirout::${structType}) {`,
1318
+ );
1319
+ source.internalMain.push(` out.out += "\\"${variantName}\\"";`);
1320
+ source.internalMain.push(" }");
1321
+ }
1322
+ for (const variant of wrapperVariants) {
1323
+ const { variantName, typeAlias } = variant;
1324
+ source.internalMain.push(
1325
+ ` void operator()(const type::${typeAlias}& w) {`,
1326
+ );
1327
+ source.internalMain.push(" out.new_line.Indent();");
1328
+ source.internalMain.push(
1329
+ ` absl::StrAppend(&out.out, "{", *out.new_line, "\\"kind\\": \\"${variantName}\\",",`,
1330
+ );
1331
+ source.internalMain.push(
1332
+ ' *out.new_line, "\\"value\\": ");',
1333
+ );
1334
+ source.internalMain.push(
1335
+ " ::skir_internal::Append(w.value, out);",
1336
+ );
1337
+ source.internalMain.push(
1338
+ ' absl::StrAppend(&out.out, out.new_line.Dedent(), "}");',
1339
+ );
1340
+ source.internalMain.push(" }");
1341
+ }
1342
+ source.internalMain.push(" };");
1343
+ source.internalMain.push(" input.visit(visitor{out});");
1344
+ source.internalMain.push("}");
1345
+ source.internalMain.push("");
1346
+ }
1347
+
1348
+ {
1349
+ // Append(const T&, DebugString&)
1350
+ source.internalMain.push(
1351
+ `void ${adapterName}::Append(const type& input, DebugString& out) {`,
1352
+ );
1353
+ source.internalMain.push(" struct visitor {");
1354
+ source.internalMain.push(" DebugString& out;");
1355
+ for (const variant of constVariants) {
1356
+ const { identifier, structType } = variant;
1357
+ source.internalMain.push(
1358
+ ` void operator()(::skirout::${structType}) {`,
1359
+ );
1360
+ source.internalMain.push(` out.out += "skirout::${identifier}";`);
1361
+ source.internalMain.push(" }");
1362
+ }
1363
+ for (const variant of wrapperVariants) {
1364
+ const { identifier, typeAlias } = variant;
1365
+ source.internalMain.push(
1366
+ ` void operator()(const ${qualifiedName}::${typeAlias}& w) {`,
1367
+ );
1368
+ source.internalMain.push(
1369
+ ` out.out += "::skirout::${identifier}(";`,
1370
+ );
1371
+ source.internalMain.push(
1372
+ " ::skir_internal::Append(w.value, out);",
1373
+ );
1374
+ source.internalMain.push(" out.out += ')';");
1375
+ source.internalMain.push(" }");
1376
+ }
1377
+ source.internalMain.push(" };");
1378
+ source.internalMain.push(" input.visit(visitor{out});");
1379
+ source.internalMain.push("}");
1380
+ source.internalMain.push("");
1381
+ }
1382
+
1383
+ {
1384
+ // Append(const T&, ByteSink&)
1385
+ source.internalMain.push(
1386
+ `void ${adapterName}::Append(const type& input, ByteSink& out) {`,
1387
+ );
1388
+ source.internalMain.push(" switch (input.kind_) {");
1389
+ for (const variant of constVariants) {
1390
+ const { variantNumber, isUnknownVariant, kindEnumerator } = variant;
1391
+ source.internalMain.push(
1392
+ ` case type::kind_type::${kindEnumerator}: {`,
1393
+ );
1394
+ if (isUnknownVariant) {
1395
+ source.internalMain.push(
1396
+ " AppendUnrecognizedVariant(input.value_._unrecognized, out);",
1397
+ );
1398
+ } else {
1399
+ const intLiterals = bytesToIntLiterals([
1400
+ ...encodeInt32(variantNumber),
1401
+ ]);
1402
+ source.internalMain.push(` out.Push(${intLiterals});`);
1403
+ }
1404
+ source.internalMain.push(" break;");
1405
+ source.internalMain.push(" }");
1406
+ }
1407
+ for (const variant of wrapperVariants) {
1408
+ const { variantName, variantNumber, kindEnumerator, usePointer } =
1409
+ variant;
1410
+ const intLiterals = bytesToIntLiterals(
1411
+ 1 <= variantNumber && variantNumber <= 4
1412
+ ? [variantNumber + 250]
1413
+ : [248, ...encodeInt32(variantNumber)],
1414
+ );
1415
+ const dotOrArrow = usePointer ? "->" : ".";
1416
+ source.internalMain.push(
1417
+ ` case type::kind_type::${kindEnumerator}: {`,
1418
+ );
1419
+ source.internalMain.push(` out.Push(${intLiterals});`);
1420
+ source.internalMain.push(
1421
+ ` ::skir_internal::Append(input.value_.${variantName}_${
1422
+ dotOrArrow
1423
+ }value, out);`,
1424
+ );
1425
+ source.internalMain.push(" break;");
1426
+ source.internalMain.push(" }");
1427
+ }
1428
+ source.internalMain.push(" }");
1429
+ source.internalMain.push("}");
1430
+ source.internalMain.push("");
1431
+ }
1432
+
1433
+ {
1434
+ // Parse(JsonTokenizer&, T&)
1435
+ source.internalMain.push(
1436
+ `void ${adapterName}::Parse(JsonTokenizer& tokenizer, type& out) {`,
1437
+ );
1438
+ source.internalMain.push(" switch (tokenizer.state().token_type) {");
1439
+ source.internalMain.push(" case JsonTokenType::kZero:");
1440
+ source.internalMain.push(" tokenizer.Next();");
1441
+ source.internalMain.push(" break;");
1442
+ source.internalMain.push(" case JsonTokenType::kUnsignedInteger: {");
1443
+ source.internalMain.push(
1444
+ " const int i = tokenizer.state().uint_value;",
1445
+ );
1446
+ source.internalMain.push(" switch (i) {");
1447
+ for (const variant of constVariants) {
1448
+ const { variantNumber, identifier } = variant;
1449
+ if (variant.variantNumber <= 0) continue;
1450
+ source.internalMain.push(` case ${variantNumber}:`);
1451
+ source.internalMain.push(` out = ::skirout::${identifier};`);
1452
+ source.internalMain.push(" break;");
1453
+ }
1454
+ source.internalMain.push(" default:");
1455
+ source.internalMain.push(
1456
+ " if (tokenizer.keep_unrecognized_values()) {",
1457
+ );
1458
+ source.internalMain.push(
1459
+ " out = type(UnrecognizedVariant{::skir_internal::UnrecognizedFormat::kDenseJson, i});",
1460
+ );
1461
+ source.internalMain.push(" }");
1462
+ source.internalMain.push(" }");
1463
+ source.internalMain.push(" tokenizer.Next();");
1464
+ source.internalMain.push(" break;");
1465
+ source.internalMain.push(" }");
1466
+ source.internalMain.push(" case JsonTokenType::kSignedInteger: {");
1467
+ source.internalMain.push(" tokenizer.Next();");
1468
+ source.internalMain.push(" break;");
1469
+ source.internalMain.push(" }");
1470
+ source.internalMain.push(" case JsonTokenType::kString: {");
1471
+ source.internalMain.push(
1472
+ " static const auto* kMap = new absl::flat_hash_map<std::string, type>({",
1473
+ );
1474
+ for (const variant of constVariants) {
1475
+ const { variantName, identifier } = variant;
1476
+ source.internalMain.push(
1477
+ ` {"${variantName}", type::${identifier}},`,
1478
+ );
1479
+ }
1480
+ source.internalMain.push(" });");
1481
+ source.internalMain.push(
1482
+ " const auto it = kMap->find(tokenizer.state().string_value);",
1483
+ );
1484
+ source.internalMain.push(" if (it == kMap->cend()) break;");
1485
+ source.internalMain.push(" out = it->second;");
1486
+ source.internalMain.push(" tokenizer.Next();");
1487
+ source.internalMain.push(" break;");
1488
+ source.internalMain.push(" }");
1489
+ source.internalMain.push(" case JsonTokenType::kLeftSquareBracket: {");
1490
+ source.internalMain.push(" EnumJsonArrayParser parser(&tokenizer);");
1491
+ source.internalMain.push(" const int number = parser.ReadNumber();");
1492
+ source.internalMain.push(" switch (number) {");
1493
+ for (const variant of wrapperVariants) {
1494
+ const { variantNumber, typeAlias } = variant;
1495
+ source.internalMain.push(` case ${variantNumber}: {`);
1496
+ source.internalMain.push(` type::${typeAlias} wrapper;`);
1497
+ source.internalMain.push(
1498
+ " ::skir_internal::Parse(tokenizer, wrapper.value);",
1499
+ );
1500
+ source.internalMain.push(" out = std::move(wrapper);");
1501
+ source.internalMain.push(" break;");
1502
+ source.internalMain.push(" }");
1503
+ }
1504
+ source.internalMain.push(" default: {");
1505
+ source.internalMain.push(
1506
+ " if (tokenizer.keep_unrecognized_values()) {",
1507
+ );
1508
+ source.internalMain.push(
1509
+ " UnrecognizedVariant unrecognized{::skir_internal::UnrecognizedFormat::kDenseJson, number};",
1510
+ );
1511
+ source.internalMain.push(
1512
+ " unrecognized.emplace_value().ParseFrom(tokenizer);",
1513
+ );
1514
+ source.internalMain.push(
1515
+ " out = type(std::move(unrecognized));",
1516
+ );
1517
+ source.internalMain.push(" } else {");
1518
+ source.internalMain.push(" SkipValue(tokenizer);");
1519
+ source.internalMain.push(" }");
1520
+ source.internalMain.push(" }");
1521
+ source.internalMain.push(" }");
1522
+ source.internalMain.push(" parser.Finish();");
1523
+ source.internalMain.push(" break;");
1524
+ source.internalMain.push(" }");
1525
+ source.internalMain.push(" case JsonTokenType::kLeftCurlyBracket: {");
1526
+ const parserExpr =
1527
+ "(new EnumJsonObjectParser<type>())" +
1528
+ wrapperVariants
1529
+ .map((variant) => {
1530
+ const { variantName, typeAlias } = variant;
1531
+ const indent = " ";
1532
+ return `\n${indent}->AddVariant<type::${typeAlias}>("${variantName}")`;
1533
+ })
1534
+ .join("");
1535
+ source.internalMain.push(" static const auto* kParser =");
1536
+ source.internalMain.push(` ${parserExpr};`);
1537
+ source.internalMain.push(" kParser->Parse(tokenizer, out);");
1538
+ source.internalMain.push(" break;");
1539
+ source.internalMain.push(" }");
1540
+ source.internalMain.push(" default: {");
1541
+ source.internalMain.push(
1542
+ " tokenizer.mutable_state().PushUnexpectedTokenError(\"number or '['\");",
1543
+ );
1544
+ source.internalMain.push(" }");
1545
+ source.internalMain.push(" }");
1546
+ source.internalMain.push("}");
1547
+ source.internalMain.push("");
1548
+ }
1549
+
1550
+ {
1551
+ // Parse(ByteSource&, T&)
1552
+ source.internalMain.push(
1553
+ `void ${adapterName}::Parse(ByteSource& source, type& out) {`,
1554
+ );
1555
+ source.internalMain.push(
1556
+ " const auto [has_value, number] = ParseEnumPrefix(source);",
1557
+ );
1558
+ source.internalMain.push(" if (has_value) {");
1559
+ source.internalMain.push(" switch (number) {");
1560
+ for (const variant of wrapperVariants) {
1561
+ const { variantNumber, typeAlias } = variant;
1562
+ source.internalMain.push(` case ${variantNumber}: {`);
1563
+ source.internalMain.push(` type::${typeAlias} wrapper;`);
1564
+ source.internalMain.push(
1565
+ " ::skir_internal::Parse(source, wrapper.value);",
1566
+ );
1567
+ source.internalMain.push(" out = std::move(wrapper);");
1568
+ source.internalMain.push(" break;");
1569
+ source.internalMain.push(" }");
1570
+ }
1571
+ source.internalMain.push(" default: {");
1572
+ source.internalMain.push(
1573
+ " if (source.keep_unrecognized_values) {",
1574
+ );
1575
+ source.internalMain.push(
1576
+ " UnrecognizedVariant unrecognized{::skir_internal::UnrecognizedFormat::kBytes, number};",
1577
+ );
1578
+ source.internalMain.push(
1579
+ " unrecognized.emplace_value().ParseFrom(source);",
1580
+ );
1581
+ source.internalMain.push(
1582
+ " out = type(std::move(unrecognized));",
1583
+ );
1584
+ source.internalMain.push(" } else {");
1585
+ source.internalMain.push(" SkipValue(source);");
1586
+ source.internalMain.push(" }");
1587
+ source.internalMain.push(" }");
1588
+ source.internalMain.push(" }");
1589
+ source.internalMain.push(" } else {");
1590
+ source.internalMain.push(" switch (number) {");
1591
+ source.internalMain.push(" case 0:");
1592
+ source.internalMain.push(" break;");
1593
+ for (const variant of constVariants) {
1594
+ const { variantNumber, identifier } = variant;
1595
+ if (variant.variantNumber === 0) continue;
1596
+ source.internalMain.push(` case ${variantNumber}:`);
1597
+ source.internalMain.push(` out = ::skirout::${identifier};`);
1598
+ source.internalMain.push(" break;");
1599
+ }
1600
+ source.internalMain.push(" default: {");
1601
+ source.internalMain.push(
1602
+ " if (source.keep_unrecognized_values) {",
1603
+ );
1604
+ source.internalMain.push(
1605
+ " out = type(UnrecognizedVariant{::skir_internal::UnrecognizedFormat::kBytes, number});",
1606
+ );
1607
+ source.internalMain.push(" }");
1608
+ source.internalMain.push(" }");
1609
+ source.internalMain.push(" }");
1610
+ source.internalMain.push(" }");
1611
+ source.internalMain.push("}");
1612
+ source.internalMain.push("");
1613
+ }
1614
+
1615
+ {
1616
+ // GetType(skir_type<T>)
1617
+ source.internalMain.push(
1618
+ `skir::reflection::Type ${adapterName}::GetType(skir_type<type>) {`,
1619
+ );
1620
+ const recordId = getRecordId(record);
1621
+ source.internalMain.push(
1622
+ ` return skir::reflection::RecordType({"${recordId}"});`,
1623
+ );
1624
+ source.internalMain.push("}");
1625
+ source.internalMain.push("");
1626
+ }
1627
+
1628
+ {
1629
+ // RegisterRecords(skir_type<T>, RecordRegistry&)
1630
+ const recordId = getRecordId(record);
1631
+ source.internalMain.push(`void ${adapterName}::RegisterRecords(`);
1632
+ source.internalMain.push(" skir_type<type>,");
1633
+ source.internalMain.push(
1634
+ " skir::reflection::RecordRegistry& registry) {",
1635
+ );
1636
+ source.internalMain.push(" const bool already_present =");
1637
+ source.internalMain.push(
1638
+ ` registry.find_or_null("${recordId}") != nullptr;`,
1639
+ );
1640
+ source.internalMain.push(" if (already_present) return;");
1641
+ source.internalMain.push(" skir::reflection::Enum record = {");
1642
+ source.internalMain.push(` "${recordId}",`);
1643
+ source.internalMain.push(
1644
+ ` ${JSON.stringify(record.record.doc.text)},`,
1645
+ );
1646
+ source.internalMain.push(" {");
1647
+ for (const variant of constVariants) {
1648
+ if (variant.isUnknownVariant) {
1649
+ continue;
1650
+ }
1651
+ source.internalMain.push(" {");
1652
+ source.internalMain.push(` "${variant.variantName}",`);
1653
+ source.internalMain.push(` ${variant.variantNumber},`);
1654
+ source.internalMain.push(` absl::nullopt,`);
1655
+ source.internalMain.push(
1656
+ ` ${JSON.stringify(variant.doc.text)},`,
1657
+ );
1658
+ source.internalMain.push(" },");
1659
+ }
1660
+ for (const variant of wrapperVariants) {
1661
+ const { variantName, variantNumber, valueTypeWithNamespace } = variant;
1662
+ source.internalMain.push(" {");
1663
+ source.internalMain.push(` "${variantName}",`);
1664
+ source.internalMain.push(` ${variantNumber},`);
1665
+ source.internalMain.push(
1666
+ ` skir_internal::GetType<${valueTypeWithNamespace}>(),`,
1667
+ );
1668
+ source.internalMain.push(" },");
1669
+ }
1670
+ source.internalMain.push(" },");
1671
+ const removedNumbers = record.record.removedNumbers.join(", ");
1672
+ source.internalMain.push(` {${removedNumbers}},`);
1673
+ source.internalMain.push(" };");
1674
+ source.internalMain.push(" registry.push_back(std::move(record));");
1675
+ for (const variant of wrapperVariants) {
1676
+ const { valueTypeWithNamespace } = variant;
1677
+ source.internalMain.push(
1678
+ ` skir_internal::RegisterRecords<${
1679
+ valueTypeWithNamespace
1680
+ }>(registry);`,
1681
+ );
1682
+ }
1683
+ source.internalMain.push("}");
1684
+ source.internalMain.push("");
1685
+ }
1686
+ }
1687
+
1688
+ private writeCodeInHeaderForAdapter(record: RecordLocation): void {
1689
+ const { header } = this;
1690
+
1691
+ const { fields, recordType } = record.record;
1692
+ const className = getClassName(record);
1693
+ const adapterName = `${className}Adapter`;
1694
+ const qualifiedName = `::${this.namespace}::${className}`;
1695
+
1696
+ header.internalMainTop.push(`class ${adapterName};`);
1697
+ header.internalMain.push(`class ${adapterName} {`);
1698
+ header.internalMain.push(" public:");
1699
+ header.internalMain.push(` using type = ${qualifiedName};`);
1700
+ const tupleName =
1701
+ recordType === "struct" ? "fields_tuple" : "variants_tuple";
1702
+ if (fields.length || recordType === "enum") {
1703
+ const fieldToReflectionType = (f: Field): string => {
1704
+ const fieldName = f.name.text;
1705
+ if (recordType === "struct") {
1706
+ return `struct_field<type, skirout::get_${fieldName}<>>`;
1707
+ } else if (f.type) {
1708
+ return `enum_wrapper_variant<type, skirout::reflection::${fieldName}_variant>`;
1709
+ } else {
1710
+ return `skir::reflection::enum_const_variant<skirout::k_${fieldName.toLowerCase()}>`;
1711
+ }
1712
+ };
1713
+ const reflectionTypes = fields
1714
+ .map(fieldToReflectionType)
1715
+ .concat(
1716
+ recordType === "enum"
1717
+ ? ["skir::reflection::enum_const_variant<skirout::k_unknown>"]
1718
+ : [],
1719
+ )
1720
+ .join(",\n ");
1721
+ header.internalMain.push(
1722
+ ` using ${tupleName} = std::tuple<\n ${reflectionTypes}>;`,
1723
+ );
1724
+ } else {
1725
+ header.internalMain.push(` using ${tupleName} = std::tuple<>;`);
1726
+ }
1727
+ header.internalMain.push("");
1728
+ header.internalMain.push(" static bool IsDefault(const type&);");
1729
+ header.internalMain.push(" static void Append(const type&, DenseJson&);");
1730
+ header.internalMain.push(
1731
+ " static void Append(const type&, ReadableJson&);",
1732
+ );
1733
+ header.internalMain.push(
1734
+ " static void Append(const type&, DebugString&);",
1735
+ );
1736
+ header.internalMain.push(" static void Append(const type&, ByteSink&);");
1737
+ header.internalMain.push(" static void Parse(JsonTokenizer&, type&);");
1738
+ header.internalMain.push(" static void Parse(ByteSource&, type&);");
1739
+ header.internalMain.push(
1740
+ " static skir::reflection::Type GetType(skir_type<type>);",
1741
+ );
1742
+ header.internalMain.push(" static void RegisterRecords(");
1743
+ header.internalMain.push(" skir_type<type>,");
1744
+ header.internalMain.push(" skir::reflection::RecordRegistry&);");
1745
+ header.internalMain.push(
1746
+ ` static constexpr bool IsStruct() { return ${
1747
+ recordType === "struct"
1748
+ }; }`,
1749
+ );
1750
+ header.internalMain.push(
1751
+ ` static constexpr bool IsEnum() { return ${recordType === "enum"}; }`,
1752
+ );
1753
+ header.internalMain.push("};");
1754
+ header.internalMain.push("");
1755
+ header.internal.push(
1756
+ `inline ::skir_internal::${this.namespace}::${adapterName} GetAdapter(`,
1757
+ );
1758
+ header.internal.push(` ::skir_internal::skir_type<${qualifiedName}>);`);
1759
+ header.internal.push("");
1760
+ }
1761
+
1762
+ private writeCodeForConstantVariant(variant: EnumVariant): void {
1763
+ if (!this.addskiroutSymbol(variant.structType)) return;
1764
+ const { skirout } = this.header;
1765
+ skirout.push(`#ifndef skirout_${variant.structType}`);
1766
+ skirout.push(`#define skirout_${variant.structType}`);
1767
+ skirout.push(`struct ${variant.structType} {`);
1768
+ skirout.push(
1769
+ ` static constexpr absl::string_view kVariantName = "${variant.variantName}";`,
1770
+ );
1771
+ skirout.push("};");
1772
+ skirout.push("");
1773
+ skirout.push(
1774
+ `constexpr auto ${variant.identifier} = ${variant.structType}();`,
1775
+ );
1776
+ skirout.push("#endif");
1777
+ skirout.push("");
1778
+ }
1779
+
1780
+ private writeCodeForWrapperVariant(variant: EnumVariant): void {
1781
+ const { variantName, structType } = variant;
1782
+ if (!this.addskiroutSymbol(structType)) return;
1783
+ const variantType = `${variantName}_variant`;
1784
+ {
1785
+ const { skirout } = this.header;
1786
+ skirout.push(`#ifndef skirout_${structType}`);
1787
+ skirout.push(`#define skirout_${structType}`);
1788
+ skirout.push("template <typename T>");
1789
+ skirout.push(`struct ${structType};`);
1790
+ skirout.push("");
1791
+ skirout.push("namespace reflection {");
1792
+ skirout.push(`struct ${variantType} {`);
1793
+ skirout.push(
1794
+ ` static constexpr absl::string_view kVariantName = "${variantName}";`,
1795
+ );
1796
+ skirout.push("");
1797
+ skirout.push(" template <typename T>");
1798
+ skirout.push(` static ${structType}<T> wrap(T input) {`);
1799
+ skirout.push(` return ${structType}(std::move(input));`);
1800
+ skirout.push(" }");
1801
+ skirout.push("");
1802
+ skirout.push(" template <typename Enum>");
1803
+ skirout.push(" static auto* get_or_null(Enum& e) {");
1804
+ skirout.push(
1805
+ ` return e.is_${variantName}() ? &e.as_${variantName}() : nullptr;`,
1806
+ );
1807
+ skirout.push(" }");
1808
+ skirout.push("};");
1809
+ skirout.push("} // namespace reflection");
1810
+ skirout.push("");
1811
+ skirout.push("template <typename T>");
1812
+ skirout.push(`struct ${structType} {`);
1813
+ skirout.push(" using value_type = T;");
1814
+ skirout.push(
1815
+ ` using variant_type = ::skirout::reflection::${variantType};`,
1816
+ );
1817
+ skirout.push("");
1818
+ skirout.push(" T value{};");
1819
+ skirout.push("");
1820
+ skirout.push(` ${structType}() = default;`);
1821
+ skirout.push(
1822
+ ` explicit ${structType}(T value): value(std::move(value)) {}`,
1823
+ );
1824
+ skirout.push("};");
1825
+ skirout.push("#endif");
1826
+ skirout.push("");
1827
+ }
1828
+ {
1829
+ const { skirout } = this.testingHeader;
1830
+ skirout.push(`#ifndef TESTING_skirout_${structType}`);
1831
+ skirout.push(`#define TESTING_skirout_${structType}`);
1832
+ skirout.push("template <typename ValueMatcher = decltype(_)>");
1833
+ const functionName = "Is" + convertCase(variantName, "UpperCamel");
1834
+ skirout.push(`auto ${functionName}(ValueMatcher matcher = _) {`);
1835
+ skirout.push(" using ::testing::skir_internal::EnumValueIsMatcher;");
1836
+ skirout.push(` using Variant = ::skirout::reflection::${variantType};`);
1837
+ skirout.push(
1838
+ " return EnumValueIsMatcher<Variant, ValueMatcher>(std::move(matcher));",
1839
+ );
1840
+ skirout.push("}");
1841
+ skirout.push("#endif");
1842
+ skirout.push("");
1843
+ }
1844
+ }
1845
+
1846
+ private writeCodeForMethod(method: Method): void {
1847
+ const { typeSpeller } = this;
1848
+ const { mainMiddle } = this.header;
1849
+ const methodName = method.name.text;
1850
+ const requestType = typeSpeller.getCcType(method.requestType!);
1851
+ const responseType = typeSpeller.getCcType(method.responseType!);
1852
+ const doc = method.doc.text;
1853
+ mainMiddle.push(...commentify(docToCommentText(method.doc)));
1854
+ mainMiddle.push(`struct ${methodName} {`);
1855
+ mainMiddle.push(` using request_type = ${requestType};`);
1856
+ mainMiddle.push(` using response_type = ${responseType};`);
1857
+ mainMiddle.push(
1858
+ ` static constexpr absl::string_view kMethodName = "${methodName}";`,
1859
+ );
1860
+ mainMiddle.push(` static constexpr uint32_t kNumber = ${method.number};`);
1861
+ mainMiddle.push(
1862
+ ` static constexpr absl::string_view kDoc = ${JSON.stringify(doc)};`,
1863
+ );
1864
+ mainMiddle.push("};");
1865
+ mainMiddle.push("");
1866
+ }
1867
+
1868
+ private writeCodeForConstant(constant: Constant): void {
1869
+ const { header, source, typeSpeller } = this;
1870
+ const name = `k_${constant.name.text.toLowerCase()}`;
1871
+ const type = typeSpeller.getCcType(constant.type!);
1872
+ const ccStringLiteral = JSON.stringify(
1873
+ JSON.stringify(constant.valueAsDenseJson),
1874
+ );
1875
+ header.mainMiddle.push(...commentify(docToCommentText(constant.doc)));
1876
+ header.mainMiddle.push(`const ${type}& ${name}();`);
1877
+ header.mainMiddle.push("");
1878
+ source.mainMiddle.push(`const ${type}& ${name}() {`);
1879
+ source.mainMiddle.push(` static const auto* kResult = new ${type}(`);
1880
+ source.mainMiddle.push(` ::skir::Parse<${type}>(`);
1881
+ source.mainMiddle.push(` ${ccStringLiteral})`);
1882
+ source.mainMiddle.push(" .value());");
1883
+ source.mainMiddle.push(" return *kResult;");
1884
+ source.mainMiddle.push("}");
1885
+ source.mainMiddle.push("");
1886
+ }
1887
+
1888
+ private writeIncludes(): void {
1889
+ const { header, source, testingHeader } = this;
1890
+ {
1891
+ const headerPath =
1892
+ "skirout/" + this.inModule.path.replace(/\.skir$/, ".h");
1893
+ source.includes.push(`#include "${headerPath}"`);
1894
+ testingHeader.includes.push(`#include "${headerPath}"`);
1895
+ }
1896
+ for (const h of [...this.includes].sort()) {
1897
+ header.includes.push(`#include ${h}`);
1898
+ testingHeader.includes.push(
1899
+ `#include ${h.replace(/\.h"$/, '.testing.h"')}`,
1900
+ );
1901
+ }
1902
+ }
1903
+
1904
+ private addskiroutSymbol(symbol: string): boolean {
1905
+ if (this.seenskiroutSymbols.has(symbol)) return false;
1906
+ this.seenskiroutSymbols.add(symbol);
1907
+ return true;
1908
+ }
1909
+
1910
+ private readonly includes = new Set<string>();
1911
+ private readonly typeSpeller: TypeSpeller;
1912
+ private readonly recursivityResolver: RecursvityResolver;
1913
+ private readonly namespace: string;
1914
+ private readonly seenskiroutSymbols = new Set<string>();
1915
+
1916
+ readonly header: FileContents = new FileContents(".h");
1917
+ readonly source: FileContents = new FileContents(".cc");
1918
+ readonly testingHeader: FileContents = new FileContents(".testing.h");
1919
+ }
1920
+
1921
+ function commentify(
1922
+ textOrLines: string | readonly string[],
1923
+ indent: "" | " " | " " = "",
1924
+ ): readonly string[] {
1925
+ const text = (
1926
+ typeof textOrLines === "string" ? textOrLines : textOrLines.join("\n")
1927
+ )
1928
+ .replace(/^\s*\n+/g, "")
1929
+ .replace(/\n+\s*$/g, "")
1930
+ .replace(/\n{3,}/g, "\n\n");
1931
+
1932
+ if (text.length <= 0) {
1933
+ return [];
1934
+ }
1935
+
1936
+ return text.split("\n").map((line) => `${indent}// ${line}`);
1937
+ }
1938
+
1939
+ function docToCommentText(doc: Doc): string {
1940
+ return doc.pieces
1941
+ .map((p) => {
1942
+ switch (p.kind) {
1943
+ case "text":
1944
+ return p.text;
1945
+ case "reference":
1946
+ return `'${p.referenceRange.text.slice(1, -1)}'`;
1947
+ }
1948
+ })
1949
+ .join("");
1950
+ }
1951
+
1952
+ class FileContents {
1953
+ constructor(readonly extension: ".h" | ".cc" | ".testing.h") {}
1954
+
1955
+ namespace: string = "";
1956
+
1957
+ readonly includes: string[] = [];
1958
+ /** Group within the ::skirout namespace. */
1959
+ readonly skirout: string[] = [];
1960
+ /** First group within the ::skirout_my_module namespace. */
1961
+ readonly mainTop: string[] = [];
1962
+ /** Second group within the ::skirout_my_module namespace. */
1963
+ readonly mainMiddle: string[] = [];
1964
+ /** Third group within the ::skirout_my_module namespace. */
1965
+ readonly mainBottom: string[] = [];
1966
+ /** Group within the anonymous namespace. Only in the .cc. */
1967
+ readonly anonymous: string[] = [];
1968
+ /** Group within the ::skir_internal namespace. */
1969
+ readonly internal: string[] = [];
1970
+ /**
1971
+ * First group within the ::skir_internal_my_module namespace. Only in the
1972
+ * .h.
1973
+ */
1974
+ readonly internalMainTop: string[] = [];
1975
+ /** Group within the ::skir_internal::my::module namespace. */
1976
+ readonly internalMain: string[] = [];
1977
+ }
1978
+
1979
+ function fileContentsToCode(fileContents: FileContents): string {
1980
+ const { extension, namespace } = fileContents;
1981
+ const lines = [
1982
+ "// ______ _ _ _ _",
1983
+ "// | _ \\ | | | |(_)| |",
1984
+ "// | | | | ___ _ __ ___ | |_ ___ __| | _ | |_",
1985
+ "// | | | | / _ \\ | '_ \\ / _ \\ | __| / _ \\ / _` || || __|",
1986
+ "// | |/ / | (_) | | | | || (_) || |_ | __/| (_| || || |_ ",
1987
+ "// |___/ \\___/ |_| |_| \\___/ \\__| \\___| \\__,_||_| \\__|",
1988
+ "",
1989
+ ];
1990
+ if (extension === ".h" || extension === ".testing.h") {
1991
+ const includeGuard =
1992
+ `${namespace}${extension.replace(/\./g, "_")}`.toUpperCase();
1993
+ lines.push(`#ifndef ${includeGuard}`);
1994
+ lines.push(`#define ${includeGuard}`);
1995
+ lines.push("");
1996
+ }
1997
+ fileContents.includes.forEach((l) => lines.push(l));
1998
+ lines.push("");
1999
+ if (extension === ".h") {
2000
+ lines.push("namespace skir_internal {");
2001
+ lines.push(`namespace ${namespace} {`);
2002
+ fileContents.internalMainTop.forEach((l) => lines.push(l));
2003
+ lines.push(`} // namespace ${namespace}`);
2004
+ lines.push("} // namespace skir_internal");
2005
+ lines.push("");
2006
+ } else if (extension === ".cc") {
2007
+ lines.push("namespace {");
2008
+ lines.push("");
2009
+ fileContents.anonymous.forEach((l) => lines.push(l));
2010
+ lines.push("");
2011
+ lines.push("} // namespace");
2012
+ lines.push("");
2013
+ }
2014
+ if (extension === ".testing.h") {
2015
+ lines.push("namespace testing {");
2016
+ }
2017
+ lines.push("namespace skirout {");
2018
+ fileContents.skirout.forEach((l) => lines.push(l));
2019
+ lines.push("} // namespace skirout");
2020
+ if (extension === ".h" || extension === ".cc") {
2021
+ lines.push("");
2022
+ lines.push(`namespace ${namespace} {`);
2023
+ fileContents.mainTop.forEach((l) => lines.push(l));
2024
+ lines.push("");
2025
+ fileContents.mainMiddle.forEach((l) => lines.push(l));
2026
+ lines.push("");
2027
+ fileContents.mainBottom.forEach((l) => lines.push(l));
2028
+ lines.push("");
2029
+ lines.push(`} // namespace ${namespace}`);
2030
+ lines.push("namespace skir_internal {");
2031
+ lines.push(`namespace ${namespace} {`);
2032
+ lines.push("");
2033
+ fileContents.internalMain.forEach((l) => lines.push(l));
2034
+ lines.push("");
2035
+ lines.push(`} // namespace ${namespace}`);
2036
+ lines.push("");
2037
+ fileContents.internal.forEach((l) => lines.push(l));
2038
+ lines.push("");
2039
+ lines.push("} // namespace skir_internal");
2040
+ lines.push("");
2041
+ } else {
2042
+ lines.push("} // namespace testing");
2043
+ lines.push("");
2044
+ }
2045
+ if (extension === ".h" || extension === ".testing.h") {
2046
+ lines.push("#endif");
2047
+ }
2048
+ return (
2049
+ lines
2050
+ .map((l) => `${l}\n`)
2051
+ .join("")
2052
+ // Remove empty line following "public" or "private".
2053
+ .replace(/((public:|private:)\n)\n+/g, "$1")
2054
+ // Remove empty line preceding a closed curly bracket.
2055
+ .replace(/\n(\n *\})/g, "$1")
2056
+ // Coalesce consecutive empty lines.
2057
+ .replace(/\n\n\n+/g, "\n\n")
2058
+ .replace(/\n\n$/g, "\n")
2059
+ );
2060
+ }
2061
+
2062
+ export const GENERATOR = new CcCodeGenerator();
2063
+
2064
+ function maybeEscapeLowerCaseName(name: string): string {
2065
+ return CC_KEYWORDS.has(name) ? `${name}_` : name;
2066
+ }
2067
+
2068
+ function numberToCharLiterals(n: number): string {
2069
+ const decimal = `${n}`;
2070
+ let result = "";
2071
+ for (let i = 0; i < decimal.length; ++i) {
2072
+ if (i !== 0) {
2073
+ result += ", ";
2074
+ }
2075
+ result += `'${decimal[i]}'`;
2076
+ }
2077
+ return result;
2078
+ }
2079
+
2080
+ function bytesToIntLiterals(bytes: readonly number[]): string {
2081
+ return bytes.map((b) => `${b}`).join(", ");
2082
+ }
2083
+
2084
+ function getRecordId(record: RecordLocation): string {
2085
+ const qualifiedName = record.recordAncestors
2086
+ .map((r) => r.name.text)
2087
+ .join(".");
2088
+ return `${record.modulePath}:${qualifiedName}`;
2089
+ }