@thi.ng/wasm-api 0.10.0 → 0.11.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.
@@ -1,7 +1,7 @@
1
1
  import { BIGINT_ARRAY_CTORS, BIT_SHIFTS, TYPEDARRAY_CTORS, } from "@thi.ng/api/typedarray";
2
2
  import { isString } from "@thi.ng/checks/is-string";
3
3
  import { PKG_NAME, USIZE, USIZE_SIZE, } from "../api.js";
4
- import { isBigNumeric, isNumeric, isWasmPrim, isWasmString, prefixLines, } from "./utils.js";
4
+ import { enumName, isBigNumeric, isNumeric, isPadding, isStringSlice, isWasmPrim, isWasmString, pointerFields, prefixLines, stringFields, withIndentation, } from "./utils.js";
5
5
  /**
6
6
  * TypeScript code generator. Call with options and then pass to
7
7
  * {@link generateTypes} (see its docs for further usage).
@@ -13,156 +13,190 @@ import { isBigNumeric, isNumeric, isWasmPrim, isWasmString, prefixLines, } from
13
13
  *
14
14
  * @param opts
15
15
  */
16
- export const TYPESCRIPT = (opts) => {
17
- const { indent, stringType, uppercaseEnums } = {
16
+ export const TYPESCRIPT = (opts = {}) => {
17
+ const { indent } = {
18
18
  indent: "\t",
19
19
  stringType: "slice",
20
- uppercaseEnums: true,
21
20
  ...opts,
22
21
  };
23
- const I = indent;
24
- const I2 = I + I;
25
- const I3 = I2 + I;
22
+ const SCOPES = [/\{$/, /\}\)?[;,]?$/];
26
23
  const gen = {
27
- pre: `import type { WasmTypeBase, WasmTypeConstructor } from "${PKG_NAME}";`,
28
- doc: (doc, indent, acc) => {
24
+ pre: (opts) => `// @ts-ignore possibly includes unused imports
25
+ import { Pointer, ${__stringImpl(opts)}, WasmTypeBase, WasmTypeConstructor } from "${PKG_NAME}";${opts.pre ? `\n${opts.pre}` : ""}`,
26
+ post: () => opts.post || "",
27
+ doc: (doc, acc) => {
29
28
  if (doc.indexOf("\n") !== -1) {
30
- acc.push(indent + "/**", prefixLines(indent + " * ", doc), indent + " */");
29
+ acc.push("/**", prefixLines(indent + " * ", doc), " */");
31
30
  }
32
31
  else {
33
- acc.push(`${indent}/** ${doc} */`);
32
+ acc.push(`/** ${doc} */`);
34
33
  }
35
34
  },
36
- enum: (type, _, acc) => {
37
- const e = type;
38
- acc.push(`export enum ${e.name} {`);
35
+ enum: (e, _, acc, opts) => {
36
+ const res = [];
37
+ res.push(`export enum ${e.name} {`);
39
38
  for (let v of e.values) {
40
- let line = indent;
39
+ let line;
41
40
  if (!isString(v)) {
42
- v.doc && gen.doc(v.doc, indent, acc);
43
- line += uppercaseEnums ? v.name.toUpperCase() : v.name;
41
+ v.doc && gen.doc(v.doc, res);
42
+ line = enumName(opts, v.name);
44
43
  if (v.value != null)
45
44
  line += ` = ${v.value}`;
46
45
  }
47
46
  else {
48
- line += uppercaseEnums ? v.toUpperCase() : v;
47
+ line = enumName(opts, v);
49
48
  }
50
- acc.push(line + ",");
49
+ res.push(line + ",");
51
50
  }
52
- acc.push("}\n");
53
- return acc;
51
+ res.push("}", "");
52
+ acc.push(...withIndentation(res, indent, ...SCOPES));
54
53
  },
55
- struct: (type, types, acc) => {
56
- const struct = type;
57
- const returnTypes = {};
54
+ struct: (struct, types, acc, opts) => {
55
+ const fieldTypes = {};
56
+ const $stringImpl = __stringImpl(opts);
57
+ const lines = [];
58
58
  // interface definition
59
- acc.push(`export interface ${struct.name} extends WasmTypeBase {`);
59
+ lines.push(`export interface ${struct.name} extends WasmTypeBase {`);
60
60
  for (let f of struct.fields) {
61
- f.doc && gen.doc(f.doc, indent, acc);
62
- let line = `${indent}${f.name}: `;
63
- let rtype = "";
64
- if (f.tag == "array" || f.tag == "slice" || f.tag === "vec") {
65
- rtype = isNumeric(f.type)
66
- ? TYPEDARRAY_CTORS[f.type].name
67
- : isBigNumeric(f.type)
68
- ? BIGINT_ARRAY_CTORS[f.type].name
69
- : f.type + "[]";
70
- }
71
- else if (!f.tag || f.tag === "scalar" || f.tag === "ptr") {
72
- rtype = isBigNumeric(f.type)
73
- ? "bigint"
74
- : isNumeric(f.type)
75
- ? "number"
76
- : f.type;
77
- }
78
- returnTypes[f.name] = rtype;
79
- acc.push(line + rtype + ";");
61
+ if (isPadding(f))
62
+ continue;
63
+ f.doc && gen.doc(f.doc, lines);
64
+ const ftype = __fieldType(f, opts);
65
+ fieldTypes[f.name] = ftype;
66
+ lines.push(`${f.name}: ${ftype};`);
80
67
  }
81
- acc.push("}\n");
68
+ lines.push("}", "");
69
+ const pointerDecls = pointerFields(struct.fields).map((x) => {
70
+ return `let $${x.name}: ${fieldTypes[x.name]} | null = null;`;
71
+ });
72
+ const stringDecls = stringFields(struct.fields).map((x) => {
73
+ const suffix = x.tag === "array" || x.tag === "slice" ? "[]" : "";
74
+ return `let $${x.name}: ${$stringImpl}${suffix} | null = null;`;
75
+ });
82
76
  // type implementation
83
- acc.push(`export const $${struct.name}: WasmTypeConstructor<${struct.name}> = (mem) => ({`, `${I}get align() { return ${struct.__align}; },`, `${I}get size() { return ${struct.__size}; },`, `${I}instance: (base) => ({`, `${I2}get __base() { return base; },`, `${I2}get __bytes() { return mem.u8.subarray(base, base + ${struct.__size}); },`);
77
+ lines.push(`export const $${struct.name}: WasmTypeConstructor<${struct.name}> = (mem) => ({`, `get align() {`, `return ${struct.__align};`, `},`, `get size() {`, `return ${struct.__size};`, `},`, `instance: (base) => {`, ...pointerDecls, ...stringDecls, `return {`, `get __base() {`, `return base;`, `},`, `get __bytes() {`, `return mem.u8.subarray(base, base + ${struct.__size});`, `},`);
84
78
  for (let f of struct.fields) {
79
+ // skip explicit padding fields
80
+ if (isPadding(f))
81
+ continue;
82
+ const ftype = fieldTypes[f.name];
85
83
  const offset = f.__offset || 0;
86
- acc.push(`${I2}get ${f.name}(): ${returnTypes[f.name]} {`);
84
+ lines.push(`get ${f.name}(): ${ftype} {`);
87
85
  const isPrim = isWasmPrim(f.type);
88
86
  const isStr = isWasmString(f.type);
87
+ const isConst = isStr ? f.const !== false : !!f.const;
89
88
  if (f.tag === "ptr") {
90
89
  if (isPrim) {
91
- acc.push(`${I3}return mem.${f.type}[${__ptrShift(offset, f.type)}];`);
90
+ const fn = f.len
91
+ ? `(base) => mem.${f.type}.subarray(${__addrShift(0, f.type)}, (${__addrShift(0, f.type)}) + ${f.len})`
92
+ : `(base) => mem.${f.type}[${__addrShift(0, f.type)}]`;
93
+ lines.push(`return $${f.name} || ($${f.name} = new ${ftype}(mem, ${__addr(offset)}, ${fn}));`);
92
94
  }
93
95
  else if (isStr) {
94
- acc.push(
95
- // double deref
96
- stringType === "slice"
97
- ? `${I3}return mem.getString(mem.${USIZE}[${__ptr(offset)} >>> ${USIZE_SIZE}])`
98
- : `${I3}return mem.getString(${__ptr(offset)})`);
96
+ const fn = f.len
97
+ ? [
98
+ `(addr) => {`,
99
+ ...__mapStringArray("buf", $stringImpl, f.len, isConst, true),
100
+ `}`,
101
+ ]
102
+ : [
103
+ `(addr) => new ${$stringImpl}(mem, addr, ${isConst})`,
104
+ ];
105
+ lines.push(`return $${f.name} || ($${f.name} = new ${ftype}(mem, ${__addr(offset)},`, ...fn, `));`);
99
106
  }
100
107
  else {
101
- acc.push(`${I3}return $${f.type}.instance(${__ptr(offset)});`);
108
+ const fn = f.len
109
+ ? [`(addr) => {`, ...__mapArray(f, f.len), `}`]
110
+ : [`(addr) => new $${f.type}.instance(addr)`];
111
+ lines.push(`return $${f.name} || ($${f.name} = new ${ftype}(mem, ${__addr(offset)},`, ...fn, `));`);
102
112
  }
103
113
  }
104
114
  else if (f.tag === "slice") {
105
- acc.push(`${I3}const len = ${__ptr(offset + 4)};`);
115
+ lines.push(`const len = ${__ptr(offset + 4)};`);
106
116
  if (isPrim) {
107
- acc.push(`${I3}const addr = ${__ptrShift(offset, f.type)};`, `${I3}return mem.${f.type}.subarray(addr, addr + len);`);
117
+ lines.push(`const addr = ${__ptrShift(offset, f.type)};`, `return mem.${f.type}.subarray(addr, addr + len);`);
108
118
  }
109
119
  else if (isStr) {
110
- acc.push(`${I3}const addr = ${__ptr(offset)};`, __mapStringArray(I3, stringType));
120
+ lines.push(`const addr = ${__ptr(offset)};`, ...__mapStringArray("buf", $stringImpl, "len", isConst));
111
121
  }
112
122
  else {
113
- acc.push(`${I3}const addr = ${__ptr(offset)};`, __mapArray(f, I3));
123
+ lines.push(`const addr = ${__ptr(offset)};`, ...__mapArray(f));
114
124
  }
115
125
  }
116
126
  else if (f.tag === "array" || f.tag === "vec") {
117
127
  if (isPrim) {
118
- acc.push(`${I3}const addr = ${__addrShift(offset, f.type)};`, `${I3}return mem.${f.type}.subarray(addr, addr + ${f.len});`);
128
+ lines.push(`const addr = ${__addrShift(offset, f.type)};`, `return mem.${f.type}.subarray(addr, addr + ${f.len});`);
119
129
  }
120
130
  else if (isStr) {
121
- acc.push(`${I3}const addr = ${__addr(offset)};`, __mapStringArray(I3, stringType, f.len));
131
+ lines.push(`if ($${f.name}) return $${f.name};`, `const addr = ${__addr(offset)};`, ...__mapStringArray(f.name, $stringImpl, f.len, isConst));
122
132
  }
123
133
  else {
124
- acc.push(`${I3}const addr = ${__addr(offset)};`, __mapArray(f, I3, f.len));
134
+ lines.push(`const addr = ${__addr(offset)};`, ...__mapArray(f, f.len));
125
135
  }
126
136
  }
127
137
  else {
128
138
  let setter;
129
139
  if (isPrim) {
130
140
  const addr = __mem(f.type, f.__offset);
131
- acc.push(`${I3}return ${addr};`);
141
+ lines.push(`return ${addr};`);
132
142
  setter = `${addr} = x`;
133
143
  }
134
144
  else if (isStr) {
135
- acc.push(`${I3}return mem.getString(${__ptr(offset)})`);
136
- setter =
137
- stringType === "slice"
138
- ? `mem.setString(x, ${__ptr(offset)}, ${__ptr(offset + 4)} + 1, true);`
139
- : `throw new Error("unsupported for raw string pointers")`;
145
+ lines.push(`return $${f.name} || ($${f.name} = new ${$stringImpl}(mem, ${__addr(offset)}, ${isConst}));`);
140
146
  }
141
147
  else if (types[f.type].type === "enum") {
142
148
  const tag = types[f.type].tag;
143
149
  const addr = __mem(tag, f.__offset);
144
- acc.push(`${I3}return ${addr};`);
150
+ lines.push(`return ${addr};`);
145
151
  setter = `${addr} = x`;
146
152
  }
147
153
  else {
148
- acc.push(`${I3}return $${f.type}(mem).instance(${__addr(offset)});`);
154
+ lines.push(`return $${f.type}(mem).instance(${__addr(offset)});`);
149
155
  setter = `mem.u8.set(x.__bytes, ${__addr(offset)})`;
150
156
  }
151
- // close getter
152
- acc.push(`${I2}},`);
153
157
  // setter
154
- acc.push(`${I2}set ${f.name}(x: ${returnTypes[f.name]}) {`, `${I3}${setter};`);
158
+ if (setter) {
159
+ lines.push(
160
+ // close getter
161
+ `},`, `set ${f.name}(x: ${fieldTypes[f.name]}) {`, `${setter};`);
162
+ }
155
163
  }
156
164
  // close field accessor
157
- acc.push(`${I2}},`);
165
+ lines.push(`},`);
158
166
  }
159
- acc.push(`${I}})\n});\n`);
160
- return acc;
167
+ lines.push("};", "}", "});", "");
168
+ acc.push(...withIndentation(lines, indent, ...SCOPES));
161
169
  },
162
170
  };
163
171
  return gen;
164
172
  };
165
173
  /** @internal */
174
+ const __stringImpl = (opts) => isStringSlice(opts.stringType) ? "WasmStringSlice" : "WasmStringPtr";
175
+ /** @internal */
176
+ const __fieldType = (f, opts) => {
177
+ const ftype = f.tag == "array" ||
178
+ f.tag == "slice" ||
179
+ f.tag === "vec" ||
180
+ (f.tag === "ptr" && f.len)
181
+ ? isNumeric(f.type)
182
+ ? TYPEDARRAY_CTORS[f.type].name
183
+ : isBigNumeric(f.type)
184
+ ? BIGINT_ARRAY_CTORS[f.type].name
185
+ : isWasmString(f.type)
186
+ ? __stringImpl(opts) + "[]"
187
+ : f.type + "[]"
188
+ : !f.tag || f.tag === "scalar" || f.tag === "ptr"
189
+ ? isBigNumeric(f.type)
190
+ ? "bigint"
191
+ : isNumeric(f.type)
192
+ ? "number"
193
+ : isWasmString(f.type)
194
+ ? __stringImpl(opts)
195
+ : f.type
196
+ : f.type;
197
+ return f.tag === "ptr" ? `Pointer<${ftype}>` : ftype;
198
+ };
199
+ /** @internal */
166
200
  const __shift = (type) => BIT_SHIFTS[type];
167
201
  /** @internal */
168
202
  const __addr = (offset) => (offset > 0 ? `(base + ${offset})` : "base");
@@ -177,13 +211,15 @@ const __ptr = (offset) => `mem.${USIZE}[${__addrShift(offset, USIZE)}]`;
177
211
  const __ptrShift = (offset, shift) => __ptr(offset) + " >>> " + __shift(shift);
178
212
  const __mem = (type, offset) => `mem.${type}[${__addrShift(offset, type)}]`;
179
213
  /** @internal */
180
- const __mapArray = (f, indent, len = "len") => prefixLines(indent, `const inst = $${f.type}(mem);
181
- const slice: ${f.type}[] = [];
182
- for(let i = 0; i < ${len}; i++) slice.push(inst.instance(addr + i * ${f.__size}));
183
- return slice;`);
214
+ const __mapArray = (f, len = "len") => [
215
+ `const inst = $${f.type}(mem);`,
216
+ `const slice: ${f.type}[] = [];`,
217
+ `for(let i = 0; i < ${len}; i++) slice.push(inst.instance(addr + i * ${f.__size}));`,
218
+ `return slice;`,
219
+ ];
184
220
  /** @internal */
185
- const __mapStringArray = (indent, type, len = "len") => prefixLines(indent, [
186
- "const slice: string[] = [];",
187
- `for(let i = 0; i < ${len}; i++) slice.push(mem.getString(mem.${USIZE}[(addr + i * ${USIZE_SIZE * (type === "slice" ? 2 : 1)}) >>> ${__shift(USIZE)}]));`,
188
- "return slice;",
189
- ]);
221
+ const __mapStringArray = (name, type, len, isConst, isLocal = false) => [
222
+ isLocal ? `const $${name}: ${type}[] = [];` : `$${name} = [];`,
223
+ `for(let i = 0; i < ${len}; i++) $${name}.push(new ${type}(mem, addr + i * ${USIZE_SIZE * (type === "WasmStringSlice" ? 2 : 1)}, ${isConst}));`,
224
+ `return $${name};`,
225
+ ];
@@ -1,5 +1,5 @@
1
1
  import type { BigType } from "@thi.ng/api";
2
- import type { WasmPrim, WasmPrim32 } from "../api.js";
2
+ import type { CodeGenOpts, StructField, WasmPrim, WasmPrim32 } from "../api.js";
3
3
  /**
4
4
  * Returns true iff `x` is a {@link WasmPrim32}.
5
5
  *
@@ -19,6 +19,7 @@ export declare const isBigNumeric: (x: string) => x is BigType;
19
19
  */
20
20
  export declare const isWasmPrim: (x: string) => x is WasmPrim;
21
21
  export declare const isWasmString: (x: string) => x is "string";
22
+ export declare const isPadding: (f: StructField) => boolean;
22
23
  /**
23
24
  * Takes an array of strings or splits given string into lines, prefixes each
24
25
  * line with given `prefix` and then returns rejoined result.
@@ -27,4 +28,50 @@ export declare const isWasmString: (x: string) => x is "string";
27
28
  * @param str
28
29
  */
29
30
  export declare const prefixLines: (prefix: string, str: string | string[]) => string;
31
+ /**
32
+ * Returns true if `type` is "slice".
33
+ *
34
+ * @param type
35
+ */
36
+ export declare const isStringSlice: (type: CodeGenOpts["stringType"]) => type is "slice";
37
+ /**
38
+ * Returns filtered array of struct fields of with "ptr" tag.
39
+ *
40
+ * @param fields
41
+ *
42
+ * @internal
43
+ */
44
+ export declare const pointerFields: (fields: StructField[]) => StructField[];
45
+ /**
46
+ * Returns filtered array of struct fields of only "string" fields.
47
+ *
48
+ * @param fields
49
+ *
50
+ * @internal
51
+ */
52
+ export declare const stringFields: (fields: StructField[]) => StructField[];
53
+ /**
54
+ * Returns enum identifier formatted according to given opts.
55
+ *
56
+ * @param opts
57
+ * @param name
58
+ *
59
+ * @internal
60
+ */
61
+ export declare const enumName: (opts: CodeGenOpts, name: string) => string;
62
+ /**
63
+ * Yields iterator of given lines, each with applied indentation based on given
64
+ * scope regexp's which are applied to each line to increase or decrease
65
+ * indentation level (the initial indentation level can be specified via
66
+ * optional `level` arg, default 0). If `scopeStart` succeeds, the indent is
67
+ * increased for the _next_ line. If `scopeEnd` succeeds the level is decreased
68
+ * for the _current_ line. ...
69
+ *
70
+ * @param lines
71
+ * @param indent
72
+ * @param scopeStart
73
+ * @param scopeEnd
74
+ * @param level
75
+ */
76
+ export declare function withIndentation(lines: string[], indent: string, scopeStart: RegExp, scopeEnd: RegExp, level?: number): Generator<string, void, unknown>;
30
77
  //# sourceMappingURL=utils.d.ts.map
package/codegen/utils.js CHANGED
@@ -18,6 +18,7 @@ export const isBigNumeric = (x) => /^[iu]64$/.test(x);
18
18
  */
19
19
  export const isWasmPrim = (x) => isNumeric(x) || isBigNumeric(x);
20
20
  export const isWasmString = (x) => x === "string";
21
+ export const isPadding = (f) => f.pad != null && f.pad > 0;
21
22
  /**
22
23
  * Takes an array of strings or splits given string into lines, prefixes each
23
24
  * line with given `prefix` and then returns rejoined result.
@@ -28,3 +29,57 @@ export const isWasmString = (x) => x === "string";
28
29
  export const prefixLines = (prefix, str) => (isString(str) ? str.split("\n") : str)
29
30
  .map((line) => prefix + line)
30
31
  .join("\n");
32
+ /**
33
+ * Returns true if `type` is "slice".
34
+ *
35
+ * @param type
36
+ */
37
+ export const isStringSlice = (type) => type === "slice";
38
+ /**
39
+ * Returns filtered array of struct fields of with "ptr" tag.
40
+ *
41
+ * @param fields
42
+ *
43
+ * @internal
44
+ */
45
+ export const pointerFields = (fields) => fields.filter((f) => f.tag === "ptr");
46
+ /**
47
+ * Returns filtered array of struct fields of only "string" fields.
48
+ *
49
+ * @param fields
50
+ *
51
+ * @internal
52
+ */
53
+ export const stringFields = (fields) => fields.filter((f) => isWasmString(f.type) && f.tag !== "ptr");
54
+ /**
55
+ * Returns enum identifier formatted according to given opts.
56
+ *
57
+ * @param opts
58
+ * @param name
59
+ *
60
+ * @internal
61
+ */
62
+ export const enumName = (opts, name) => opts.uppercaseEnums ? name.toUpperCase() : name;
63
+ /**
64
+ * Yields iterator of given lines, each with applied indentation based on given
65
+ * scope regexp's which are applied to each line to increase or decrease
66
+ * indentation level (the initial indentation level can be specified via
67
+ * optional `level` arg, default 0). If `scopeStart` succeeds, the indent is
68
+ * increased for the _next_ line. If `scopeEnd` succeeds the level is decreased
69
+ * for the _current_ line. ...
70
+ *
71
+ * @param lines
72
+ * @param indent
73
+ * @param scopeStart
74
+ * @param scopeEnd
75
+ * @param level
76
+ */
77
+ export function* withIndentation(lines, indent, scopeStart, scopeEnd, level = 0) {
78
+ const stack = new Array(level).fill(indent);
79
+ for (let l of lines) {
80
+ scopeEnd.test(l) && stack.pop();
81
+ const curr = stack.length ? stack[stack.length - 1] : "";
82
+ yield curr + l;
83
+ scopeStart.test(l) && stack.push(curr + indent);
84
+ }
85
+ }
package/codegen/zig.d.ts CHANGED
@@ -1,15 +1,8 @@
1
- import type { ICodeGen } from "../api.js";
1
+ import type { CodeGenOptsBase, ICodeGen } from "../api.js";
2
2
  /**
3
3
  * Zig code generator options.
4
4
  */
5
- export interface ZigOpts {
6
- /**
7
- * If true, generates various struct & struct field analysis functions
8
- * (sizes, alignment, offsets etc.).
9
- *
10
- * @defaultValue false
11
- */
12
- debug: boolean;
5
+ export interface ZigOpts extends CodeGenOptsBase {
13
6
  }
14
7
  /**
15
8
  * Zig code generator. Call with options and then pass to {@link generateTypes}
package/codegen/zig.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { isString } from "@thi.ng/checks/is-string";
2
- import { prefixLines } from "./utils.js";
2
+ import { enumName, isPadding, isStringSlice, prefixLines, withIndentation, } from "./utils.js";
3
3
  /**
4
4
  * Zig code generator. Call with options and then pass to {@link generateTypes}
5
5
  * (see its docs for further usage).
@@ -10,64 +10,90 @@ import { prefixLines } from "./utils.js";
10
10
  *
11
11
  * @param opts
12
12
  */
13
- export const ZIG = (opts) => {
14
- const { debug } = { debug: false, ...opts };
13
+ export const ZIG = (opts = {}) => {
14
+ const INDENT = " ";
15
+ const SCOPES = [/\{$/, /\}\)?[;,]?$/];
15
16
  const gen = {
16
- doc: (doc, indent, acc, topLevel = false) => {
17
- acc.push(prefixLines(topLevel ? "//! " : indent + "/// ", doc));
17
+ pre: () => opts.pre || "",
18
+ post: () => opts.post || "",
19
+ doc: (doc, acc, topLevel = false) => {
20
+ acc.push(prefixLines(topLevel ? "//! " : "/// ", doc));
18
21
  },
19
- enum: (e, _, acc) => {
20
- acc.push(`pub const ${e.name} = enum(${e.tag}) {`);
22
+ enum: (e, _, acc, opts) => {
23
+ const lines = [];
24
+ lines.push(`pub const ${e.name} = enum(${e.tag}) {`);
21
25
  for (let v of e.values) {
22
- let line = ` `;
26
+ let line;
23
27
  if (!isString(v)) {
24
- v.doc && gen.doc(v.doc, " ", acc);
25
- line += v.name;
28
+ v.doc && gen.doc(v.doc, lines);
29
+ line = enumName(opts, v.name);
26
30
  if (v.value != null)
27
31
  line += ` = ${v.value}`;
28
32
  }
29
33
  else {
30
- line += v;
34
+ line = enumName(opts, v);
31
35
  }
32
- acc.push(line + ",");
36
+ lines.push(line + ",");
33
37
  }
34
- acc.push("};\n");
38
+ lines.push("};", "");
39
+ acc.push(...withIndentation(lines, INDENT, ...SCOPES));
35
40
  },
36
- struct: (struct, _, acc) => {
41
+ struct: (struct, _, acc, opts) => {
37
42
  const name = struct.name;
38
- acc.push(`pub const ${name} = struct {`);
43
+ const res = [];
44
+ res.push(`pub const ${name} = ${struct.tag ? struct.tag + " " : ""}struct {`);
39
45
  const ftypes = {};
46
+ let padID = 0;
40
47
  for (let f of struct.fields) {
41
- f.doc && gen.doc(f.doc, " ", acc);
42
- let ftype = f.type === "string" ? "[]const u8" : f.type;
48
+ // autolabel explicit padding fields
49
+ if (isPadding(f)) {
50
+ res.push(`__pad${padID++}: [${f.pad}]u8,`);
51
+ continue;
52
+ }
53
+ f.doc && gen.doc(f.doc, res);
54
+ let ftype = f.type === "string"
55
+ ? isStringSlice(opts.stringType)
56
+ ? f.const !== false
57
+ ? "[]const u8"
58
+ : "[]u8"
59
+ : f.const !== false
60
+ ? "[*:0]const u8"
61
+ : "[*:0]u8"
62
+ : f.type;
43
63
  switch (f.tag) {
44
64
  case "array":
45
65
  ftype = `[${f.len}]${ftype}`;
46
66
  break;
47
67
  case "slice":
48
- ftype = `[]${ftype}`;
68
+ ftype = `[]${f.const ? "const " : ""}${ftype}`;
49
69
  break;
50
70
  case "vec":
51
71
  ftype = `@Vector(${f.len}, ${ftype})`;
52
72
  break;
53
73
  case "ptr":
54
- ftype = `*${f.len ? `[${f.len}]` : ""}${ftype}`;
74
+ ftype = `*${f.const ? "const " : ""}${f.len ? `[${f.len}]` : ""}${ftype}`;
55
75
  break;
56
76
  case "scalar":
57
77
  default:
58
78
  }
59
79
  ftypes[f.name] = ftype;
60
- acc.push(` ${f.name}: ${ftype},`);
80
+ res.push(`${f.name}: ${ftype},`);
61
81
  }
62
- acc.push("};\n");
63
- if (!debug)
64
- return;
65
- const fn = (fname, body) => `export fn ${name}_${fname}() usize { return ${body}; }`;
66
- acc.push(fn("align", `@alignOf(${name})`), fn("size", `@sizeOf(${name})`));
67
- for (let f of struct.fields) {
68
- acc.push(fn(f.name + "_align", `@alignOf(${ftypes[f.name]})`), fn(f.name + "_offset", `@offsetOf(${name}, "${f.name}")`), fn(f.name + "_size", `@sizeOf(${ftypes[f.name]})`));
82
+ res.push("};");
83
+ if (opts.debug) {
84
+ const fn = (fname, body) => res.push("", `export fn ${name}_${fname}() usize {`, `return ${body};`, `}`);
85
+ fn("align", `@alignOf(${name})`);
86
+ fn("size", `@sizeOf(${name})`);
87
+ for (let f of struct.fields) {
88
+ if (isPadding(f))
89
+ continue;
90
+ fn(f.name + "_align", `@alignOf(${ftypes[f.name]})`);
91
+ fn(f.name + "_offset", `@offsetOf(${name}, "${f.name}")`);
92
+ fn(f.name + "_size", `@sizeOf(${ftypes[f.name]})`);
93
+ }
69
94
  }
70
- acc.push("");
95
+ res.push("");
96
+ acc.push(...withIndentation(res, INDENT, ...SCOPES));
71
97
  },
72
98
  };
73
99
  return gen;
package/codegen.d.ts CHANGED
@@ -1,26 +1,4 @@
1
- import { ICodeGen, TypeColl } from "./api.js";
2
- /**
3
- * Global/shared code generator options.
4
- */
5
- export interface CodeGenOpts {
6
- /**
7
- * Optional string to be injected before generated type defs (but after
8
- * codegen's own prelude, if any)
9
- */
10
- pre: string;
11
- /**
12
- * Optional string to be injected after generated type defs (but before
13
- * codegen's own epilogue, if any)
14
- */
15
- post: string;
16
- /**
17
- * Identifier how strings are stored on WASM side, e.g. in Zig string
18
- * literals are slices (8 bytes), in C just plain pointers (4 bytes).
19
- *
20
- * @defaultValue "slice"
21
- */
22
- stringType: "slice" | "ptr";
23
- }
1
+ import { CodeGenOpts, ICodeGen, TypeColl } from "./api.js";
24
2
  /**
25
3
  * Takes a type collection and analyzes each analyzed to compute individual
26
4
  * alignments and sizes.