@exornea/zeno-compiler 1.7.0 → 2.2.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 (58) hide show
  1. package/bin/zeno-codegen.mjs +79 -31
  2. package/bin/zeno-diff-layout.mjs +60 -0
  3. package/bin/zeno-inspect.mjs +67 -0
  4. package/dist/analyzer.d.ts.map +1 -1
  5. package/dist/analyzer.js +48 -26
  6. package/dist/analyzer.js.map +1 -1
  7. package/dist/emitter-ast.d.ts +8 -0
  8. package/dist/emitter-ast.d.ts.map +1 -0
  9. package/dist/emitter-ast.js +34 -0
  10. package/dist/emitter-ast.js.map +1 -0
  11. package/dist/emitter-capabilities.d.ts +8 -0
  12. package/dist/emitter-capabilities.d.ts.map +1 -0
  13. package/dist/emitter-capabilities.js +59 -0
  14. package/dist/emitter-capabilities.js.map +1 -0
  15. package/dist/emitter-input.d.ts +3 -0
  16. package/dist/emitter-input.d.ts.map +1 -0
  17. package/dist/emitter-input.js +45 -0
  18. package/dist/emitter-input.js.map +1 -0
  19. package/dist/emitter-names.d.ts +5 -0
  20. package/dist/emitter-names.d.ts.map +1 -0
  21. package/dist/emitter-names.js +10 -0
  22. package/dist/emitter-names.js.map +1 -0
  23. package/dist/emitter-runtime-imports.d.ts +3 -0
  24. package/dist/emitter-runtime-imports.d.ts.map +1 -0
  25. package/dist/emitter-runtime-imports.js +73 -0
  26. package/dist/emitter-runtime-imports.js.map +1 -0
  27. package/dist/emitter-scan-kernels.d.ts +10 -0
  28. package/dist/emitter-scan-kernels.d.ts.map +1 -0
  29. package/dist/emitter-scan-kernels.js +174 -0
  30. package/dist/emitter-scan-kernels.js.map +1 -0
  31. package/dist/emitter-template.d.ts +7 -0
  32. package/dist/emitter-template.d.ts.map +1 -1
  33. package/dist/emitter-template.js +8 -3
  34. package/dist/emitter-template.js.map +1 -1
  35. package/dist/emitter.d.ts +8 -1
  36. package/dist/emitter.d.ts.map +1 -1
  37. package/dist/emitter.js +247 -335
  38. package/dist/emitter.js.map +1 -1
  39. package/dist/index.d.ts +5 -2
  40. package/dist/index.d.ts.map +1 -1
  41. package/dist/index.js +5 -2
  42. package/dist/index.js.map +1 -1
  43. package/dist/layout-manifest.d.ts +37 -0
  44. package/dist/layout-manifest.d.ts.map +1 -0
  45. package/dist/layout-manifest.js +251 -0
  46. package/dist/layout-manifest.js.map +1 -0
  47. package/dist/lowering.d.ts +5 -2
  48. package/dist/lowering.d.ts.map +1 -1
  49. package/dist/lowering.js +86 -27
  50. package/dist/lowering.js.map +1 -1
  51. package/dist/source-map.d.ts +10 -0
  52. package/dist/source-map.d.ts.map +1 -0
  53. package/dist/source-map.js +371 -0
  54. package/dist/source-map.js.map +1 -0
  55. package/dist/validator.d.ts.map +1 -1
  56. package/dist/validator.js +34 -16
  57. package/dist/validator.js.map +1 -1
  58. package/package.json +5 -3
package/dist/emitter.js CHANGED
@@ -1,10 +1,20 @@
1
1
  import { scalarGetterMethod, scalarSetterMethod, scalarTsType, } from "@exornea/zeno-schema";
2
- import { canWriteFixedArrayStructElement, collectFixedArrayRuntimeImports, emitFixedArrayFieldAccessor, emitFixedArrayObjectFieldWrite, fixedArrayInputElementType, } from "./emitter-fixed-array.js";
2
+ import { emitFixedArrayFieldAccessor, emitFixedArrayObjectFieldWrite, } from "./emitter-fixed-array.js";
3
+ import { emitAstCheckedSource } from "./emitter-ast.js";
4
+ import { canEmitObjectWriter, hasTailWriterFields, isTailWriterField, structFieldHasTailFields, } from "./emitter-capabilities.js";
5
+ import { emitInputInterface } from "./emitter-input.js";
6
+ import { encodingLiteral, toLittleEndianLiteral, toPascalCase } from "./emitter-names.js";
7
+ import { collectRuntimeImports } from "./emitter-runtime-imports.js";
8
+ import { emitScalarScanKernels, emitScanRangeHelper, hasScanKernels, normalizeScanKernelMode, } from "./emitter-scan-kernels.js";
3
9
  import { method } from "./emitter-template.js";
10
+ import { createProjectionSourceMap } from "./source-map.js";
4
11
  export function emitStructView(layout, options = {}) {
5
12
  return emitProjectionFile([layout], options);
6
13
  }
7
14
  export function emitProjectionFile(layouts, options = {}) {
15
+ return emitAstCheckedSource(emitProjectionFileText(layouts, options), "zeno.view.ts").code;
16
+ }
17
+ function emitProjectionFileText(layouts, options = {}) {
8
18
  const lines = [];
9
19
  const runtimeImports = collectRuntimeImports(layouts);
10
20
  const layoutMap = new Map(layouts.map((layout) => [layout.name, layout]));
@@ -20,109 +30,13 @@ export function emitProjectionFile(layouts, options = {}) {
20
30
  }
21
31
  return lines.join("\n");
22
32
  }
23
- function collectRuntimeImports(layouts) {
24
- const imports = new Set(["ProjectionView"]);
25
- const layoutMap = new Map(layouts.map((layout) => [layout.name, layout]));
26
- for (const layout of layouts) {
27
- if (hasTailWriterFields(layout, layoutMap)) {
28
- imports.add("DynamicLayoutWriter");
29
- }
30
- for (const field of layout.fields) {
31
- collectFieldRuntimeImports(field, imports);
32
- }
33
- }
34
- return Array.from(imports).sort();
35
- }
36
- function collectFieldRuntimeImports(field, imports) {
37
- switch (field.kind) {
38
- case "fixed-bytes":
39
- imports.add("fixedBytesView");
40
- imports.add("writeFixedBytes");
41
- return;
42
- case "fixed-string":
43
- imports.add("decodeFixedText");
44
- imports.add("fixedBytesView");
45
- imports.add("writeFixedText");
46
- return;
47
- case "dynamic-string":
48
- imports.add("Utf8SpanView");
49
- return;
50
- case "dynamic-bytes":
51
- imports.add("BytesSpanView");
52
- return;
53
- case "vector":
54
- switch (field.element.kind) {
55
- case "scalar":
56
- imports.add("ScalarVectorView");
57
- return;
58
- case "dynamic-string":
59
- imports.add("Utf8VectorView");
60
- return;
61
- case "dynamic-bytes":
62
- imports.add("BytesVectorView");
63
- return;
64
- case "fixed-bytes":
65
- imports.add("FixedBytesVectorView");
66
- return;
67
- case "fixed-string":
68
- imports.add("FixedStringVectorView");
69
- return;
70
- case "struct":
71
- imports.add("StructVectorView");
72
- return;
73
- case "pointer":
74
- imports.add("PointerVectorView");
75
- return;
76
- default:
77
- return;
78
- }
79
- case "fixed-array":
80
- collectFixedArrayRuntimeImports(field.element, imports);
81
- return;
82
- default:
83
- return;
84
- }
85
- }
86
- function emitInputInterface(layout) {
87
- const lines = [`export interface ${layout.name}ViewInput {`];
88
- for (const field of layout.fields) {
89
- lines.push(` readonly ${field.name}: ${fieldInputType(field)};`);
90
- }
91
- lines.push("}");
92
- return lines;
93
- }
94
- function fieldInputType(field) {
95
- switch (field.kind) {
96
- case "scalar":
97
- return scalarTsType(field.scalar);
98
- case "fixed-bytes":
99
- case "dynamic-bytes":
100
- return "ArrayLike<number> | Uint8Array";
101
- case "fixed-string":
102
- case "dynamic-string":
103
- return "string";
104
- case "struct":
105
- return `${field.typeName}ViewInput`;
106
- case "pointer":
107
- return "number | null";
108
- case "fixed-array":
109
- return `readonly ${fixedArrayInputElementType(field.element)}[]`;
110
- case "vector":
111
- switch (field.element.kind) {
112
- case "scalar":
113
- return `readonly ${scalarTsType(field.element.scalar)}[]`;
114
- case "fixed-bytes":
115
- case "dynamic-bytes":
116
- return "readonly (ArrayLike<number> | Uint8Array)[]";
117
- case "fixed-string":
118
- case "dynamic-string":
119
- return "readonly string[]";
120
- case "struct":
121
- return `readonly ${field.element.typeName}ViewInput[]`;
122
- case "pointer":
123
- return "readonly (number | null)[]";
124
- }
125
- }
33
+ export function emitProjectionFileWithSourceMap(layouts, generatedFile, options = {}) {
34
+ const sourceMapUrl = `${generatedFile.split(/[\\/]/).pop() ?? generatedFile}.map`;
35
+ const code = `${emitAstCheckedSource(emitProjectionFileText(layouts, options), generatedFile).code}\n//# sourceMappingURL=${sourceMapUrl}\n`;
36
+ return {
37
+ code,
38
+ sourceMap: createProjectionSourceMap(code, layouts, generatedFile),
39
+ };
126
40
  }
127
41
  function emitLayoutConstants(layout) {
128
42
  const lines = [];
@@ -134,8 +48,8 @@ function emitLayoutConstants(layout) {
134
48
  return lines;
135
49
  }
136
50
  function emitStructClass(layout, options, layoutMap) {
137
- const scalarFields = layout.fields.filter((field) => field.kind === "scalar");
138
51
  const littleEndianDefault = toLittleEndianLiteral(layout);
52
+ const scanKernelMode = normalizeScanKernelMode(options.scanKernels);
139
53
  const lines = [`export class ${layout.name}View extends ProjectionView {`];
140
54
  lines.push(...method `
141
55
  static readonly byteLength = ${layout.byteLength};
@@ -162,60 +76,25 @@ private static assertPointerTargetRange(view: DataView, targetOffset: number, by
162
76
  }
163
77
  }`);
164
78
  }
165
- if (options.optimizeCursorOffsets && scalarFields.length > 0) {
79
+ if (hasScanKernels(layout, scanKernelMode)) {
166
80
  lines.push("");
167
- lines.push(...method `${scalarFields.map((field) => `private $${field.name}Offset = ${field.offset};`)}`);
81
+ lines.push(...emitScanRangeHelper(layout));
168
82
  }
169
83
  lines.push("");
170
- if (options.optimizeCursorOffsets && scalarFields.length > 0) {
171
- lines.push(...method `
172
- constructor(view: DataView, baseOffset = 0, littleEndian = ${littleEndianDefault}) {
173
- super(view, baseOffset, littleEndian);
174
- this.$refreshOffsets();
175
- }
176
-
177
- private $refreshOffsets(): void {
178
- ${scalarFields.map((field) => ` this.$${field.name}Offset = this.baseOffset + ${field.offset};`)}
179
- }
180
-
181
- override rebase(baseOffset: number): this {
182
- super.rebase(baseOffset);
183
- this.$refreshOffsets();
184
- return this;
185
- }
186
-
187
- override rebaseUnchecked(baseOffset: number): this {
188
- super.rebaseUnchecked(baseOffset);
189
- this.$refreshOffsets();
190
- return this;
191
- }`);
192
- }
193
- else {
194
- lines.push(...method `
84
+ lines.push(...method `
195
85
  constructor(view: DataView, baseOffset = 0, littleEndian = ${littleEndianDefault}) {
196
86
  super(view, baseOffset, littleEndian);
197
87
  }`);
198
- }
199
88
  lines.push("");
200
89
  lines.push(...method `
201
90
  static at(view: DataView, baseOffset = 0, littleEndian = ${littleEndianDefault}): ${layout.name}View {
202
91
  return new ${layout.name}View(view, baseOffset, littleEndian);
203
92
  }`);
204
93
  lines.push("");
205
- if (options.optimizeCursorOffsets && scalarFields.length > 0) {
206
- lines.push(...method `
207
- moveTo(index: number): this {
208
- this.moveToIndex(index, ${layout.name}View.byteLength);
209
- this.$refreshOffsets();
210
- return this;
211
- }`);
212
- }
213
- else {
214
- lines.push(...method `
94
+ lines.push(...method `
215
95
  moveTo(index: number): this {
216
96
  return this.moveToIndex(index, ${layout.name}View.byteLength);
217
97
  }`);
218
- }
219
98
  lines.push("");
220
99
  lines.push(...method `
221
100
  moveToUnchecked(index: number): this {
@@ -237,14 +116,14 @@ moveToUnchecked(index: number): this {
237
116
  lines.push("");
238
117
  }
239
118
  for (const field of layout.fields) {
240
- const staticAccessorLines = emitStaticFieldAccessor(layout, field);
119
+ const staticAccessorLines = emitStaticFieldAccessor(layout, field, scanKernelMode);
241
120
  for (const line of staticAccessorLines) {
242
121
  lines.push(line);
243
122
  }
244
123
  if (staticAccessorLines.length > 0) {
245
124
  lines.push("");
246
125
  }
247
- for (const line of emitField(layout, field, options)) {
126
+ for (const line of emitField(layout, field)) {
248
127
  lines.push(line);
249
128
  }
250
129
  lines.push("");
@@ -315,6 +194,18 @@ static write${pascalName}(writer: DynamicLayoutWriter, values: readonly ${scalar
315
194
  return method `
316
195
  static write${pascalName}(writer: DynamicLayoutWriter, values: readonly ${field.element.typeName}ViewInput[]) {
317
196
  return writer.writeStructVector(${layout.name}View.${field.name}Offset, values, ${field.element.byteLength}, (view, value, baseOffset, littleEndian) => ${field.element.typeName}View.write(view, value, baseOffset, littleEndian), ${alignment});
197
+ }`;
198
+ },
199
+ "dynamic-struct": ({ layout, layoutMap, field, pascalName }) => {
200
+ const elementLayout = layoutMap.get(field.element.typeName);
201
+ const alignment = elementLayout?.alignment ?? 1;
202
+ const elementByteLength = elementLayout?.byteLength ?? field.element.byteLength;
203
+ const writeElement = elementLayout !== undefined && hasTailWriterFields(elementLayout, layoutMap)
204
+ ? `${field.element.typeName}View.writeInto(view, elementWriter, value, baseOffset, littleEndian)`
205
+ : `${field.element.typeName}View.write(view, value, baseOffset, littleEndian)`;
206
+ return method `
207
+ static write${pascalName}(writer: DynamicLayoutWriter, values: readonly ${field.element.typeName}ViewInput[]) {
208
+ return writer.writeDynamicStructVector(${layout.name}View.${field.name}Offset, values, ${elementByteLength}, (view, elementWriter, value, baseOffset, littleEndian) => ${writeElement}, ${alignment});
318
209
  }`;
319
210
  },
320
211
  pointer: ({ layout, field, pascalName }) => method `
@@ -334,20 +225,28 @@ function emitObjectWriterMethod(layout, layoutMap) {
334
225
  const returnType = hasTailFields ? "DynamicLayoutWriter" : "void";
335
226
  const bodyLines = [];
336
227
  if (hasTailFields) {
337
- bodyLines.push(` const writer = ${layout.name}View.createWriter(view, baseOffset, ${layout.name}View.byteLength, littleEndian);`);
338
- }
339
- for (const field of layout.fields) {
340
- bodyLines.push(...emitObjectFieldWrite(layout, field));
228
+ bodyLines.push(` const writer = ${layout.name}View.createWriter(view, baseOffset, ${layout.name}View.byteLength, littleEndian);`, ` ${layout.name}View.writeInto(view, writer, value, baseOffset, littleEndian);`, " return writer;");
341
229
  }
342
- if (hasTailFields) {
343
- bodyLines.push(" return writer;");
230
+ else {
231
+ for (const field of layout.fields) {
232
+ bodyLines.push(...emitObjectFieldWrite(layout, layoutMap, field));
233
+ }
344
234
  }
345
- return method `
235
+ const lines = method `
346
236
  static write(view: DataView, value: ${layout.name}ViewInput, baseOffset = 0, littleEndian = ${toLittleEndianLiteral(layout)}): ${returnType} {
347
237
  ${bodyLines}
348
238
  }`;
239
+ if (hasTailFields) {
240
+ const writeIntoLines = layout.fields.flatMap((field) => emitObjectFieldWriteAtBase(layout, layoutMap, field));
241
+ lines.push("");
242
+ lines.push(...method `
243
+ static writeInto(view: DataView, writer: DynamicLayoutWriter, value: ${layout.name}ViewInput, baseOffset = 0, littleEndian = ${toLittleEndianLiteral(layout)}): void {
244
+ ${writeIntoLines}
245
+ }`);
246
+ }
247
+ return lines;
349
248
  }
350
- function emitObjectFieldWrite(layout, field) {
249
+ function emitObjectFieldWrite(layout, layoutMap, field) {
351
250
  const pascalName = toPascalCase(field.name);
352
251
  switch (field.kind) {
353
252
  case "scalar":
@@ -366,6 +265,11 @@ function emitObjectFieldWrite(layout, field) {
366
265
  case "dynamic-bytes":
367
266
  return [` ${layout.name}View.write${pascalName}(writer, value.${field.name});`];
368
267
  case "struct":
268
+ if (structFieldHasTailFields(layoutMap, field.typeName)) {
269
+ return [
270
+ ` ${field.typeName}View.writeInto(view, writer, value.${field.name}, baseOffset + ${field.offset}, littleEndian);`,
271
+ ];
272
+ }
369
273
  return [
370
274
  ` ${field.typeName}View.write(view, value.${field.name}, baseOffset + ${field.offset}, littleEndian);`,
371
275
  ];
@@ -379,13 +283,140 @@ function emitObjectFieldWrite(layout, field) {
379
283
  return [` ${layout.name}View.write${pascalName}(writer, value.${field.name});`];
380
284
  }
381
285
  }
382
- function emitStaticFieldAccessor(layout, field) {
286
+ function emitObjectFieldWriteAtBase(layout, layoutMap, field) {
287
+ const pascalName = toPascalCase(field.name);
288
+ switch (field.kind) {
289
+ case "scalar":
290
+ return [
291
+ ` ${layout.name}View.set${pascalName}(view, value.${field.name}, baseOffset, littleEndian);`,
292
+ ];
293
+ case "fixed-bytes":
294
+ return [
295
+ ` writeFixedBytes(view.buffer, view.byteOffset + baseOffset + ${field.offset}, ${field.byteLength}, value.${field.name});`,
296
+ ];
297
+ case "fixed-string":
298
+ return [
299
+ ` writeFixedText(view.buffer, view.byteOffset + baseOffset + ${field.offset}, ${field.byteLength}, value.${field.name}, ${encodingLiteral(field.encoding)});`,
300
+ ];
301
+ case "dynamic-string":
302
+ return [
303
+ ` writer.writeTextAtBase(baseOffset, ${layout.name}View.${field.name}Offset, value.${field.name}, ${encodingLiteral(field.encoding)});`,
304
+ ];
305
+ case "dynamic-bytes":
306
+ return [
307
+ ` writer.writeBytesAtBase(baseOffset, ${layout.name}View.${field.name}Offset, value.${field.name});`,
308
+ ];
309
+ case "struct":
310
+ if (structFieldHasTailFields(layoutMap, field.typeName)) {
311
+ return [
312
+ ` ${field.typeName}View.writeInto(view, writer, value.${field.name}, baseOffset + ${field.offset}, littleEndian);`,
313
+ ];
314
+ }
315
+ return [
316
+ ` ${field.typeName}View.write(view, value.${field.name}, baseOffset + ${field.offset}, littleEndian);`,
317
+ ];
318
+ case "pointer":
319
+ return [
320
+ ` ${layout.name}View.set${pascalName}TargetOffset(view, value.${field.name}, baseOffset, littleEndian);`,
321
+ ];
322
+ case "fixed-array":
323
+ return emitFixedArrayObjectFieldWrite(field, encodingLiteral);
324
+ case "vector":
325
+ return emitVectorObjectFieldWriteAtBase(layout, layoutMap, field);
326
+ }
327
+ }
328
+ function emitVectorObjectFieldWriteAtBase(layout, layoutMap, field) {
329
+ switch (field.element.kind) {
330
+ case "dynamic-string":
331
+ return [
332
+ ` writer.writeTextVectorAtBase(baseOffset, ${layout.name}View.${field.name}Offset, value.${field.name}, ${encodingLiteral(field.element.encoding)});`,
333
+ ];
334
+ case "dynamic-bytes":
335
+ return [
336
+ ` writer.writeBytesVectorAtBase(baseOffset, ${layout.name}View.${field.name}Offset, value.${field.name});`,
337
+ ];
338
+ case "fixed-bytes":
339
+ return [
340
+ ` writer.writeFixedBytesVectorAtBase(baseOffset, ${layout.name}View.${field.name}Offset, value.${field.name}, ${field.element.byteLength});`,
341
+ ];
342
+ case "fixed-string":
343
+ return [
344
+ ` writer.writeFixedTextVectorAtBase(baseOffset, ${layout.name}View.${field.name}Offset, value.${field.name}, ${field.element.byteLength}, ${encodingLiteral(field.element.encoding)});`,
345
+ ];
346
+ case "scalar":
347
+ return [
348
+ ` writer.writeScalarVectorAtBase(baseOffset, ${layout.name}View.${field.name}Offset, "${field.element.scalar}", value.${field.name});`,
349
+ ];
350
+ case "struct": {
351
+ const elementLayout = layoutMap.get(field.element.typeName);
352
+ const alignment = elementLayout?.alignment ?? 1;
353
+ return [
354
+ ` writer.writeStructVectorAtBase(baseOffset, ${layout.name}View.${field.name}Offset, value.${field.name}, ${field.element.byteLength}, (view, elementValue, elementBaseOffset, elementLittleEndian) => ${field.element.typeName}View.write(view, elementValue, elementBaseOffset, elementLittleEndian), ${alignment});`,
355
+ ];
356
+ }
357
+ case "dynamic-struct": {
358
+ const elementLayout = layoutMap.get(field.element.typeName);
359
+ const alignment = elementLayout?.alignment ?? 1;
360
+ const elementByteLength = elementLayout?.byteLength ?? field.element.byteLength;
361
+ const writeElement = elementLayout !== undefined && hasTailWriterFields(elementLayout, layoutMap)
362
+ ? `${field.element.typeName}View.writeInto(view, elementWriter, elementValue, elementBaseOffset, elementLittleEndian)`
363
+ : `${field.element.typeName}View.write(view, elementValue, elementBaseOffset, elementLittleEndian)`;
364
+ return [
365
+ ` writer.writeDynamicStructVectorAtBase(baseOffset, ${layout.name}View.${field.name}Offset, value.${field.name}, ${elementByteLength}, (view, elementWriter, elementValue, elementBaseOffset, elementLittleEndian) => ${writeElement}, ${alignment});`,
366
+ ];
367
+ }
368
+ case "pointer":
369
+ return [
370
+ ` writer.writePointerVectorAtBase(baseOffset, ${layout.name}View.${field.name}Offset, value.${field.name}, ${field.element.targetTypeName}View.byteLength);`,
371
+ ];
372
+ }
373
+ }
374
+ function emitStaticFieldAccessor(layout, field, scanKernelMode) {
383
375
  if (field.kind === "pointer") {
384
- const pascalName = toPascalCase(field.name);
385
- const littleEndianDefault = toLittleEndianLiteral(layout);
386
- const indexOffset = `index * ${layout.byteLength} + ${field.offset}`;
387
- const pointerPosition = `baseOffset + ${field.offset}`;
388
- return method `
376
+ return emitStaticPointerAccessor(layout, field);
377
+ }
378
+ return field.kind === "scalar" ? emitStaticScalarAccessor(layout, field, scanKernelMode) : [];
379
+ }
380
+ function emitStaticScalarAccessor(layout, field, scanKernelMode) {
381
+ const getterMethod = scalarGetterMethod(field.scalar);
382
+ const setterMethod = scalarSetterMethod(field.scalar);
383
+ const typeName = scalarTsType(field.scalar);
384
+ const littleEndianDefault = toLittleEndianLiteral(layout);
385
+ const endianArg = field.byteLength === 1 || field.scalar === "bool" ? "" : ", littleEndian";
386
+ const indexOffset = `index * ${layout.byteLength} + ${field.offset}`;
387
+ const pascalName = toPascalCase(field.name);
388
+ return [
389
+ ` static get${pascalName}(view: DataView, baseOffset = 0, littleEndian = ${littleEndianDefault}): ${typeName} {`,
390
+ emitStaticScalarReadBody(field, getterMethod, `baseOffset + ${field.offset}`, endianArg),
391
+ " }",
392
+ ` static set${pascalName}(view: DataView, value: ${typeName}, baseOffset = 0, littleEndian = ${littleEndianDefault}): void {`,
393
+ emitStaticScalarWriteBody(field, setterMethod, `baseOffset + ${field.offset}`, endianArg),
394
+ " }",
395
+ ` static get${pascalName}At(view: DataView, index: number, littleEndian = ${littleEndianDefault}): ${typeName} {`,
396
+ emitStaticScalarReadBody(field, getterMethod, indexOffset, endianArg),
397
+ " }",
398
+ ` static set${pascalName}At(view: DataView, value: ${typeName}, index: number, littleEndian = ${littleEndianDefault}): void {`,
399
+ emitStaticScalarWriteBody(field, setterMethod, indexOffset, endianArg),
400
+ " }",
401
+ ...emitScalarScanKernels(layout, field, getterMethod, littleEndianDefault, pascalName, scanKernelMode),
402
+ ];
403
+ }
404
+ function emitStaticScalarReadBody(field, getterMethod, offsetExpr, endianArg) {
405
+ return field.scalar === "bool"
406
+ ? ` return view.${getterMethod}(${offsetExpr}) !== 0;`
407
+ : ` return view.${getterMethod}(${offsetExpr}${endianArg});`;
408
+ }
409
+ function emitStaticScalarWriteBody(field, setterMethod, offsetExpr, endianArg) {
410
+ return field.scalar === "bool"
411
+ ? ` view.${setterMethod}(${offsetExpr}, value ? 1 : 0);`
412
+ : ` view.${setterMethod}(${offsetExpr}, value${endianArg});`;
413
+ }
414
+ function emitStaticPointerAccessor(layout, field) {
415
+ const pascalName = toPascalCase(field.name);
416
+ const littleEndianDefault = toLittleEndianLiteral(layout);
417
+ const indexOffset = `index * ${layout.byteLength} + ${field.offset}`;
418
+ const pointerPosition = `baseOffset + ${field.offset}`;
419
+ return method `
389
420
  static getRaw${pascalName}RelativeOffset(view: DataView, baseOffset = 0, littleEndian = ${littleEndianDefault}): number {
390
421
  return view.getUint32(baseOffset + ${field.offset}, littleEndian);
391
422
  }
@@ -448,87 +479,17 @@ static set${pascalName}RelativeOffsetAt(view: DataView, value: number | null, in
448
479
  ${layout.name}View.assertPointer32Payload(value);
449
480
  view.setInt32(${indexOffset}, value, littleEndian);
450
481
  }`;
451
- }
452
- if (field.kind !== "scalar") {
453
- return [];
454
- }
455
- const getterMethod = scalarGetterMethod(field.scalar);
456
- const setterMethod = scalarSetterMethod(field.scalar);
457
- const typeName = scalarTsType(field.scalar);
458
- const littleEndianDefault = toLittleEndianLiteral(layout);
459
- const endianArg = field.byteLength === 1 || field.scalar === "bool" ? "" : ", littleEndian";
460
- const getterBody = field.scalar === "bool"
461
- ? ` return view.${getterMethod}(baseOffset + ${field.offset}) !== 0;`
462
- : ` return view.${getterMethod}(baseOffset + ${field.offset}${endianArg});`;
463
- const setterBody = field.scalar === "bool"
464
- ? ` view.${setterMethod}(baseOffset + ${field.offset}, value ? 1 : 0);`
465
- : ` view.${setterMethod}(baseOffset + ${field.offset}, value${endianArg});`;
466
- const indexOffset = `index * ${layout.byteLength} + ${field.offset}`;
467
- const indexGetterBody = field.scalar === "bool"
468
- ? ` return view.${getterMethod}(${indexOffset}) !== 0;`
469
- : ` return view.${getterMethod}(${indexOffset}${endianArg});`;
470
- const indexSetterBody = field.scalar === "bool"
471
- ? ` view.${setterMethod}(${indexOffset}, value ? 1 : 0);`
472
- : ` view.${setterMethod}(${indexOffset}, value${endianArg});`;
473
- const pascalName = toPascalCase(field.name);
474
- return [
475
- ` static get${pascalName}(view: DataView, baseOffset = 0, littleEndian = ${littleEndianDefault}): ${typeName} {`,
476
- getterBody,
477
- " }",
478
- ` static set${pascalName}(view: DataView, value: ${typeName}, baseOffset = 0, littleEndian = ${littleEndianDefault}): void {`,
479
- setterBody,
480
- " }",
481
- ` static get${pascalName}At(view: DataView, index: number, littleEndian = ${littleEndianDefault}): ${typeName} {`,
482
- indexGetterBody,
483
- " }",
484
- ` static set${pascalName}At(view: DataView, value: ${typeName}, index: number, littleEndian = ${littleEndianDefault}): void {`,
485
- indexSetterBody,
486
- " }",
487
- ...emitScalarSumKernel(layout, field, getterMethod, littleEndianDefault, pascalName),
488
- ];
489
- }
490
- function emitScalarSumKernel(layout, field, getterMethod, littleEndianDefault, pascalName) {
491
- if (!isNumberSumScalar(field.scalar)) {
492
- return [];
493
- }
494
- const endianArg = field.byteLength === 1 ? "" : ", littleEndian";
495
- return method `
496
- static sum${pascalName}(view: DataView, count: number, baseOffset = 0, littleEndian = ${littleEndianDefault}): number {
497
- if (!Number.isInteger(count) || count < 0) {
498
- throw new RangeError(\`Invalid record count: \${count}\`);
499
- }
500
- if (!Number.isFinite(baseOffset) || !Number.isInteger(baseOffset) || baseOffset < 0) {
501
- throw new RangeError(\`Invalid base offset: \${baseOffset}\`);
502
- }
503
- if (count === 0) {
504
- return 0;
505
- }
506
- const start = baseOffset + ${field.offset};
507
- const limit = start + count * ${layout.byteLength};
508
- const lastByte = start + (count - 1) * ${layout.byteLength} + ${field.byteLength};
509
- if (lastByte > view.byteLength) {
510
- throw new RangeError(\`scan range exceeds DataView length \${view.byteLength}\`);
511
- }
512
- let sum = 0;
513
- for (let offset = start; offset < limit; offset += ${layout.byteLength}) {
514
- sum += view.${getterMethod}(offset${endianArg});
515
- }
516
- return sum;
517
- }`;
518
- }
519
- function isNumberSumScalar(kind) {
520
- return kind !== "i64" && kind !== "u64" && kind !== "bool";
521
482
  }
522
- function emitField(layout, field, options) {
483
+ function emitField(layout, field) {
484
+ // This switch intentionally mirrors the Layout IR field-kind surface.
485
+ // Split it only when a dispatch table removes real emitter complexity.
523
486
  switch (field.kind) {
524
487
  case "scalar": {
525
488
  const getterMethod = scalarGetterMethod(field.scalar);
526
489
  const setterMethod = scalarSetterMethod(field.scalar);
527
490
  const typeName = scalarTsType(field.scalar);
528
491
  const getterArgs = field.byteLength === 1 || field.scalar === "bool" ? "" : ", this.littleEndian";
529
- const instanceOffset = options.optimizeCursorOffsets
530
- ? `this.$${field.name}Offset`
531
- : `this.baseOffset + ${field.offset}`;
492
+ const instanceOffset = `this.baseOffset + ${field.offset}`;
532
493
  const getterBody = field.scalar === "bool"
533
494
  ? `return this.view.${getterMethod}(${instanceOffset}) !== 0;`
534
495
  : `return this.view.${getterMethod}(${instanceOffset}${getterArgs});`;
@@ -575,8 +536,58 @@ ${field.name}View(): ${field.typeName}View {
575
536
  return new ${field.typeName}View(this.view, this.absoluteOffset(${field.offset}), this.littleEndian);
576
537
  }`;
577
538
  case "pointer": {
578
- const pascalName = toPascalCase(field.name);
579
- return method `
539
+ return emitPointerField(layout, field);
540
+ }
541
+ case "fixed-array":
542
+ return emitFixedArrayFieldAccessor(field, encodingLiteral);
543
+ case "vector":
544
+ switch (field.element.kind) {
545
+ case "scalar":
546
+ return method `
547
+ ${field.name}View(): ScalarVectorView<${scalarTsType(field.element.scalar)}> {
548
+ return new ScalarVectorView(this.view, ${field.offset}, "${field.element.scalar}", this.baseOffset, this.littleEndian);
549
+ }`;
550
+ case "dynamic-string":
551
+ return method `
552
+ ${field.name}View(): Utf8VectorView {
553
+ return new Utf8VectorView(this.view, ${field.offset}, this.baseOffset, this.littleEndian, ${encodingLiteral(field.element.encoding)});
554
+ }`;
555
+ case "dynamic-bytes":
556
+ return method `
557
+ ${field.name}View(): BytesVectorView {
558
+ return new BytesVectorView(this.view, ${field.offset}, this.baseOffset, this.littleEndian);
559
+ }`;
560
+ case "fixed-bytes":
561
+ return method `
562
+ ${field.name}View(): FixedBytesVectorView {
563
+ return new FixedBytesVectorView(this.view, ${field.offset}, ${field.element.byteLength}, this.baseOffset, this.littleEndian);
564
+ }`;
565
+ case "fixed-string":
566
+ return method `
567
+ ${field.name}View(): FixedStringVectorView {
568
+ return new FixedStringVectorView(this.view, ${field.offset}, ${field.element.byteLength}, this.baseOffset, this.littleEndian, ${encodingLiteral(field.element.encoding)});
569
+ }`;
570
+ case "struct":
571
+ return method `
572
+ ${field.name}View(): StructVectorView<${field.element.typeName}View> {
573
+ return new StructVectorView(this.view, ${field.offset}, ${field.element.byteLength}, (view, baseOffset, littleEndian) => new ${field.element.typeName}View(view, baseOffset, littleEndian), this.baseOffset, this.littleEndian);
574
+ }`;
575
+ case "dynamic-struct":
576
+ return method `
577
+ ${field.name}View(): DynamicStructVectorView<${field.element.typeName}View> {
578
+ return new DynamicStructVectorView(this.view, ${field.offset}, (view, baseOffset, littleEndian) => new ${field.element.typeName}View(view, baseOffset, littleEndian), this.baseOffset, this.littleEndian);
579
+ }`;
580
+ case "pointer":
581
+ return method `
582
+ ${field.name}View(): PointerVectorView<${field.element.targetTypeName}View> {
583
+ return new PointerVectorView(this.view, ${field.offset}, ${field.element.targetTypeName}View.byteLength, (view, baseOffset, littleEndian) => new ${field.element.targetTypeName}View(view, baseOffset, littleEndian), this.baseOffset, this.littleEndian);
584
+ }`;
585
+ }
586
+ }
587
+ }
588
+ function emitPointerField(layout, field) {
589
+ const pascalName = toPascalCase(field.name);
590
+ return method `
580
591
  get raw${pascalName}RelativeOffset(): number {
581
592
  return this.view.getUint32(this.baseOffset + ${field.offset}, this.littleEndian);
582
593
  }
@@ -643,104 +654,5 @@ ${field.name}Into(out: ${field.targetTypeName}View): boolean {
643
654
  out.moveToOffset(targetOffset, ${field.targetTypeName}View.byteLength);
644
655
  return true;
645
656
  }`;
646
- }
647
- case "fixed-array":
648
- return emitFixedArrayFieldAccessor(field, encodingLiteral);
649
- case "vector":
650
- switch (field.element.kind) {
651
- case "scalar":
652
- return method `
653
- ${field.name}View(): ScalarVectorView<${scalarTsType(field.element.scalar)}> {
654
- return new ScalarVectorView(this.view, ${field.offset}, "${field.element.scalar}", this.baseOffset, this.littleEndian);
655
- }`;
656
- case "dynamic-string":
657
- return method `
658
- ${field.name}View(): Utf8VectorView {
659
- return new Utf8VectorView(this.view, ${field.offset}, this.baseOffset, this.littleEndian, ${encodingLiteral(field.element.encoding)});
660
- }`;
661
- case "dynamic-bytes":
662
- return method `
663
- ${field.name}View(): BytesVectorView {
664
- return new BytesVectorView(this.view, ${field.offset}, this.baseOffset, this.littleEndian);
665
- }`;
666
- case "fixed-bytes":
667
- return method `
668
- ${field.name}View(): FixedBytesVectorView {
669
- return new FixedBytesVectorView(this.view, ${field.offset}, ${field.element.byteLength}, this.baseOffset, this.littleEndian);
670
- }`;
671
- case "fixed-string":
672
- return method `
673
- ${field.name}View(): FixedStringVectorView {
674
- return new FixedStringVectorView(this.view, ${field.offset}, ${field.element.byteLength}, this.baseOffset, this.littleEndian, ${encodingLiteral(field.element.encoding)});
675
- }`;
676
- case "struct":
677
- return method `
678
- ${field.name}View(): StructVectorView<${field.element.typeName}View> {
679
- return new StructVectorView(this.view, ${field.offset}, ${field.element.byteLength}, (view, baseOffset, littleEndian) => new ${field.element.typeName}View(view, baseOffset, littleEndian), this.baseOffset, this.littleEndian);
680
- }`;
681
- case "pointer":
682
- return method `
683
- ${field.name}View(): PointerVectorView<${field.element.targetTypeName}View> {
684
- return new PointerVectorView(this.view, ${field.offset}, ${field.element.targetTypeName}View.byteLength, (view, baseOffset, littleEndian) => new ${field.element.targetTypeName}View(view, baseOffset, littleEndian), this.baseOffset, this.littleEndian);
685
- }`;
686
- }
687
- }
688
- }
689
- function toPascalCase(name) {
690
- return name.slice(0, 1).toUpperCase() + name.slice(1);
691
- }
692
- function toLittleEndianLiteral(layout) {
693
- return layout.endianness === "little" ? "true" : "false";
694
- }
695
- function encodingLiteral(encoding) {
696
- return encoding === "ascii" ? "\"ascii\"" : "\"utf8\"";
697
- }
698
- function hasTailWriterFields(layout, layoutMap) {
699
- return layout.fields.some((field) => isTailWriterField(field, layoutMap));
700
- }
701
- function isTailWriterField(field, layoutMap) {
702
- switch (field.kind) {
703
- case "dynamic-string":
704
- case "dynamic-bytes":
705
- return true;
706
- case "vector":
707
- return (field.element.kind === "scalar" ||
708
- field.element.kind === "fixed-bytes" ||
709
- field.element.kind === "fixed-string" ||
710
- field.element.kind === "dynamic-string" ||
711
- field.element.kind === "dynamic-bytes" ||
712
- field.element.kind === "pointer" ||
713
- canWriteStructVectorElement(field.element, layoutMap));
714
- default:
715
- return false;
716
- }
717
- }
718
- function canEmitObjectWriter(layout, layoutMap) {
719
- return layout.fields.every((field) => canWriteObjectField(field, layoutMap));
720
- }
721
- function canWriteObjectField(field, layoutMap) {
722
- switch (field.kind) {
723
- case "scalar":
724
- case "fixed-bytes":
725
- case "fixed-string":
726
- case "dynamic-string":
727
- case "dynamic-bytes":
728
- case "struct":
729
- case "pointer":
730
- return true;
731
- case "fixed-array":
732
- return (field.element.kind !== "struct" ||
733
- canWriteFixedArrayStructElement(field.element, layoutMap, hasTailWriterFields));
734
- case "vector":
735
- return (field.element.kind !== "struct" ||
736
- canWriteStructVectorElement(field.element, layoutMap));
737
- }
738
- }
739
- function canWriteStructVectorElement(element, layoutMap) {
740
- if (element.kind !== "struct") {
741
- return false;
742
- }
743
- const elementLayout = layoutMap.get(element.typeName);
744
- return elementLayout !== undefined && !hasTailWriterFields(elementLayout, layoutMap);
745
657
  }
746
658
  //# sourceMappingURL=emitter.js.map