@thi.ng/wasm-api 0.14.0 → 0.16.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.
- package/CHANGELOG.md +60 -1
- package/README.md +173 -108
- package/api.d.ts +126 -28
- package/api.js +10 -4
- package/bridge.d.ts +3 -29
- package/bridge.js +2 -28
- package/cli.js +8 -4
- package/codegen/align.d.ts +14 -0
- package/codegen/align.js +35 -0
- package/codegen/c11.js +71 -55
- package/codegen/typescript.js +34 -14
- package/codegen/utils.d.ts +20 -11
- package/codegen/utils.js +28 -9
- package/codegen/zig.d.ts +6 -1
- package/codegen/zig.js +99 -71
- package/codegen.d.ts +2 -1
- package/codegen.js +77 -54
- package/package.json +25 -21
- package/string.d.ts +4 -0
- package/string.js +4 -0
- package/zig/managed-index.zig +223 -0
- package/{include → zig}/wasmapi.zig +2 -0
package/codegen/typescript.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
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
|
-
import { PKG_NAME,
|
|
4
|
-
import { enumName, isBigNumeric, isNumeric, isPadding, isStringSlice, isWasmPrim, isWasmString, pointerFields, prefixLines, stringFields, withIndentation, } from "./utils.js";
|
|
3
|
+
import { PKG_NAME, } from "../api.js";
|
|
4
|
+
import { ensureLines, enumName, isBigNumeric, isNumeric, isPadding, isStringSlice, isWasmPrim, isWasmString, pointerFields, prefixLines, stringFields, withIndentation, } from "./utils.js";
|
|
5
|
+
import { fieldType as zigFieldType } from "./zig.js";
|
|
5
6
|
/**
|
|
6
7
|
* TypeScript code generator. Call with options and then pass to
|
|
7
8
|
* {@link generateTypes} (see its docs for further usage).
|
|
@@ -22,7 +23,7 @@ export const TYPESCRIPT = (opts = {}) => {
|
|
|
22
23
|
const SCOPES = [/\{$/, /\}\)?[;,]?$/];
|
|
23
24
|
const gen = {
|
|
24
25
|
pre: (opts) => `// @ts-ignore possibly includes unused imports
|
|
25
|
-
import { Pointer, ${__stringImpl(opts)}, WasmTypeBase, WasmTypeConstructor } from "${PKG_NAME}";${opts.pre ? `\n${opts.pre}` : ""}`,
|
|
26
|
+
import { MemorySlice, Pointer, ${__stringImpl(opts)}, WasmTypeBase, WasmTypeConstructor } from "${PKG_NAME}";${opts.pre ? `\n${opts.pre}` : ""}`,
|
|
26
27
|
post: () => opts.post || "",
|
|
27
28
|
doc: (doc, acc, opts) => {
|
|
28
29
|
acc.push("/**", ...prefixLines(" * ", doc, opts.lineWidth), " */");
|
|
@@ -55,11 +56,15 @@ import { Pointer, ${__stringImpl(opts)}, WasmTypeBase, WasmTypeConstructor } fro
|
|
|
55
56
|
for (let f of struct.fields) {
|
|
56
57
|
if (isPadding(f))
|
|
57
58
|
continue;
|
|
58
|
-
|
|
59
|
+
const doc = __docType(struct, f, opts);
|
|
60
|
+
doc && gen.doc(doc, lines, opts);
|
|
59
61
|
const ftype = __fieldType(f, opts);
|
|
60
62
|
fieldTypes[f.name] = ftype;
|
|
61
63
|
lines.push(`${f.name}: ${ftype};`);
|
|
62
64
|
}
|
|
65
|
+
if (struct.body?.ts) {
|
|
66
|
+
lines.push("", ...ensureLines(struct.body.ts, "decl"));
|
|
67
|
+
}
|
|
63
68
|
lines.push("}", "");
|
|
64
69
|
const pointerDecls = pointerFields(struct.fields).map((x) => {
|
|
65
70
|
return `let $${x.name}: ${fieldTypes[x.name]} | null = null;`;
|
|
@@ -91,7 +96,7 @@ import { Pointer, ${__stringImpl(opts)}, WasmTypeBase, WasmTypeConstructor } fro
|
|
|
91
96
|
const fn = f.len
|
|
92
97
|
? [
|
|
93
98
|
`(addr) => {`,
|
|
94
|
-
...__mapStringArray("buf", $stringImpl, f.len, isConst, true),
|
|
99
|
+
...__mapStringArray(opts.target, "buf", $stringImpl, f.len, isConst, true),
|
|
95
100
|
`}`,
|
|
96
101
|
]
|
|
97
102
|
: [
|
|
@@ -107,15 +112,15 @@ import { Pointer, ${__stringImpl(opts)}, WasmTypeBase, WasmTypeConstructor } fro
|
|
|
107
112
|
}
|
|
108
113
|
}
|
|
109
114
|
else if (f.tag === "slice") {
|
|
110
|
-
lines.push(`const len = ${__ptr(offset + 4)};`);
|
|
115
|
+
lines.push(`const len = ${__ptr(opts.target, offset + 4)};`);
|
|
111
116
|
if (isPrim) {
|
|
112
|
-
lines.push(`const addr = ${__ptrShift(offset, f.type)};`, `return mem.${f.type}.subarray(addr, addr + len);`);
|
|
117
|
+
lines.push(`const addr = ${__ptrShift(opts.target, offset, f.type)};`, `return mem.${f.type}.subarray(addr, addr + len);`);
|
|
113
118
|
}
|
|
114
119
|
else if (isStr) {
|
|
115
|
-
lines.push(`const addr = ${__ptr(offset)};`, ...__mapStringArray("buf", $stringImpl, "len", isConst));
|
|
120
|
+
lines.push(`const addr = ${__ptr(opts.target, offset)};`, ...__mapStringArray(opts.target, "buf", $stringImpl, "len", isConst));
|
|
116
121
|
}
|
|
117
122
|
else {
|
|
118
|
-
lines.push(`const addr = ${__ptr(offset)};`, ...__mapArray(f));
|
|
123
|
+
lines.push(`const addr = ${__ptr(opts.target, offset)};`, ...__mapArray(f));
|
|
119
124
|
}
|
|
120
125
|
}
|
|
121
126
|
else if (f.tag === "array" || f.tag === "vec") {
|
|
@@ -123,7 +128,7 @@ import { Pointer, ${__stringImpl(opts)}, WasmTypeBase, WasmTypeConstructor } fro
|
|
|
123
128
|
lines.push(`const addr = ${__addrShift(offset, f.type)};`, `return mem.${f.type}.subarray(addr, addr + ${f.len});`);
|
|
124
129
|
}
|
|
125
130
|
else if (isStr) {
|
|
126
|
-
lines.push(`if ($${f.name}) return $${f.name};`, `const addr = ${__addr(offset)};`, ...__mapStringArray(f.name, $stringImpl, f.len, isConst));
|
|
131
|
+
lines.push(`if ($${f.name}) return $${f.name};`, `const addr = ${__addr(offset)};`, ...__mapStringArray(opts.target, f.name, $stringImpl, f.len, isConst));
|
|
127
132
|
}
|
|
128
133
|
else {
|
|
129
134
|
lines.push(`const addr = ${__addr(offset)};`, ...__mapArray(f, f.len));
|
|
@@ -159,9 +164,15 @@ import { Pointer, ${__stringImpl(opts)}, WasmTypeBase, WasmTypeConstructor } fro
|
|
|
159
164
|
// close field accessor
|
|
160
165
|
lines.push(`},`);
|
|
161
166
|
}
|
|
167
|
+
if (struct.body?.ts) {
|
|
168
|
+
lines.push("", ...ensureLines(struct.body.ts, "impl"), "");
|
|
169
|
+
}
|
|
162
170
|
lines.push("};", "}", "});", "");
|
|
163
171
|
acc.push(...withIndentation(lines, indent, ...SCOPES));
|
|
164
172
|
},
|
|
173
|
+
union: (type, coll, acc, opts) => {
|
|
174
|
+
gen.struct(type, coll, acc, opts);
|
|
175
|
+
},
|
|
165
176
|
};
|
|
166
177
|
return gen;
|
|
167
178
|
};
|
|
@@ -201,9 +212,9 @@ const __addrShift = (offset, shift) => {
|
|
|
201
212
|
return __addr(offset) + (bits ? " >>> " + bits : "");
|
|
202
213
|
};
|
|
203
214
|
/** @internal */
|
|
204
|
-
const __ptr = (offset) => `mem.${
|
|
215
|
+
const __ptr = (target, offset) => `mem.${target.usize}[${__addrShift(offset, target.usize)}]`;
|
|
205
216
|
/** @internal */
|
|
206
|
-
const __ptrShift = (offset, shift) => __ptr(offset) + " >>> " + __shift(shift);
|
|
217
|
+
const __ptrShift = (target, offset, shift) => __ptr(target, offset) + " >>> " + __shift(shift);
|
|
207
218
|
const __mem = (type, offset) => `mem.${type}[${__addrShift(offset, type)}]`;
|
|
208
219
|
/** @internal */
|
|
209
220
|
const __mapArray = (f, len = "len") => [
|
|
@@ -213,8 +224,17 @@ const __mapArray = (f, len = "len") => [
|
|
|
213
224
|
`return slice;`,
|
|
214
225
|
];
|
|
215
226
|
/** @internal */
|
|
216
|
-
const __mapStringArray = (name, type, len, isConst, isLocal = false) => [
|
|
227
|
+
const __mapStringArray = (target, name, type, len, isConst, isLocal = false) => [
|
|
217
228
|
isLocal ? `const $${name}: ${type}[] = [];` : `$${name} = [];`,
|
|
218
|
-
`for(let i = 0; i < ${len}; i++) $${name}.push(new ${type}(mem, addr + i * ${
|
|
229
|
+
`for(let i = 0; i < ${len}; i++) $${name}.push(new ${type}(mem, addr + i * ${target.usizeBytes * (type === "WasmStringSlice" ? 2 : 1)}, ${isConst}));`,
|
|
219
230
|
`return $${name};`,
|
|
220
231
|
];
|
|
232
|
+
const __docType = (parent, f, opts) => {
|
|
233
|
+
const doc = [...ensureLines(f.doc || [])];
|
|
234
|
+
if (isWasmPrim(f.type)) {
|
|
235
|
+
if (doc.length)
|
|
236
|
+
doc.push("");
|
|
237
|
+
doc.push(`WASM type: ${zigFieldType(parent, f, opts).type}`);
|
|
238
|
+
}
|
|
239
|
+
return doc.length ? doc : undefined;
|
|
240
|
+
};
|
package/codegen/utils.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { BigType } from "@thi.ng/api";
|
|
2
|
-
import type { CodeGenOpts,
|
|
2
|
+
import type { CodeGenOpts, Field, InjectedBody, WasmPrim, WasmPrim32 } from "../api.js";
|
|
3
3
|
/**
|
|
4
4
|
* Returns true iff `x` is a {@link WasmPrim32}.
|
|
5
5
|
*
|
|
@@ -19,17 +19,15 @@ 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:
|
|
22
|
+
export declare const isPadding: (f: Field) => boolean;
|
|
23
|
+
export declare const isPointer: (f: Field) => boolean;
|
|
24
|
+
export declare const isSlice: (f: Field) => boolean;
|
|
23
25
|
/**
|
|
24
|
-
*
|
|
25
|
-
* then prefixes each line with given `width` and `prefix`. Returns array of new
|
|
26
|
-
* lines.
|
|
26
|
+
* Returns true iff the struct field is a pointer, slice or "string" type
|
|
27
27
|
*
|
|
28
|
-
* @param
|
|
29
|
-
* @param str
|
|
30
|
-
* @param width
|
|
28
|
+
* @param f
|
|
31
29
|
*/
|
|
32
|
-
export declare const
|
|
30
|
+
export declare const isPointerLike: (f: Field) => boolean;
|
|
33
31
|
/**
|
|
34
32
|
* Returns true if `type` is "slice".
|
|
35
33
|
*
|
|
@@ -43,7 +41,7 @@ export declare const isStringSlice: (type: CodeGenOpts["stringType"]) => type is
|
|
|
43
41
|
*
|
|
44
42
|
* @internal
|
|
45
43
|
*/
|
|
46
|
-
export declare const pointerFields: (fields:
|
|
44
|
+
export declare const pointerFields: (fields: Field[]) => Field[];
|
|
47
45
|
/**
|
|
48
46
|
* Returns filtered array of struct fields of only "string" fields.
|
|
49
47
|
*
|
|
@@ -51,7 +49,7 @@ export declare const pointerFields: (fields: StructField[]) => StructField[];
|
|
|
51
49
|
*
|
|
52
50
|
* @internal
|
|
53
51
|
*/
|
|
54
|
-
export declare const stringFields: (fields:
|
|
52
|
+
export declare const stringFields: (fields: Field[]) => Field[];
|
|
55
53
|
/**
|
|
56
54
|
* Returns enum identifier formatted according to given opts.
|
|
57
55
|
*
|
|
@@ -61,6 +59,17 @@ export declare const stringFields: (fields: StructField[]) => StructField[];
|
|
|
61
59
|
* @internal
|
|
62
60
|
*/
|
|
63
61
|
export declare const enumName: (opts: CodeGenOpts, name: string) => string;
|
|
62
|
+
/**
|
|
63
|
+
* Takes an array of strings or splits given string into lines, word wraps and
|
|
64
|
+
* then prefixes each line with given `width` and `prefix`. Returns array of new
|
|
65
|
+
* lines.
|
|
66
|
+
*
|
|
67
|
+
* @param prefix
|
|
68
|
+
* @param str
|
|
69
|
+
* @param width
|
|
70
|
+
*/
|
|
71
|
+
export declare const prefixLines: (prefix: string, str: string | string[], width: number) => string[];
|
|
72
|
+
export declare const ensureLines: (src: string | string[] | InjectedBody, key?: keyof InjectedBody) => Iterable<string>;
|
|
64
73
|
/**
|
|
65
74
|
* Yields iterator of given lines, each with applied indentation based on given
|
|
66
75
|
* scope regexp's which are applied to each line to increase or decrease
|
package/codegen/utils.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { isArray } from "@thi.ng/checks/is-array";
|
|
1
2
|
import { isString } from "@thi.ng/checks/is-string";
|
|
3
|
+
import { split } from "@thi.ng/strings/split";
|
|
2
4
|
import { wordWrapLine, wordWrapLines } from "@thi.ng/strings/word-wrap";
|
|
3
5
|
/**
|
|
4
6
|
* Returns true iff `x` is a {@link WasmPrim32}.
|
|
@@ -20,18 +22,14 @@ export const isBigNumeric = (x) => /^[iu]64$/.test(x);
|
|
|
20
22
|
export const isWasmPrim = (x) => isNumeric(x) || isBigNumeric(x);
|
|
21
23
|
export const isWasmString = (x) => x === "string";
|
|
22
24
|
export const isPadding = (f) => f.pad != null && f.pad > 0;
|
|
25
|
+
export const isPointer = (f) => f.tag === "ptr";
|
|
26
|
+
export const isSlice = (f) => f.tag === "slice";
|
|
23
27
|
/**
|
|
24
|
-
*
|
|
25
|
-
* then prefixes each line with given `width` and `prefix`. Returns array of new
|
|
26
|
-
* lines.
|
|
28
|
+
* Returns true iff the struct field is a pointer, slice or "string" type
|
|
27
29
|
*
|
|
28
|
-
* @param
|
|
29
|
-
* @param str
|
|
30
|
-
* @param width
|
|
30
|
+
* @param f
|
|
31
31
|
*/
|
|
32
|
-
export const
|
|
33
|
-
? wordWrapLines(str, { width: width - prefix.length })
|
|
34
|
-
: str.flatMap((x) => wordWrapLine(x, { width: width - prefix.length }))).map((line) => prefix + line);
|
|
32
|
+
export const isPointerLike = (f) => isPointer(f) || isSlice(f) || isWasmString(f.type);
|
|
35
33
|
/**
|
|
36
34
|
* Returns true if `type` is "slice".
|
|
37
35
|
*
|
|
@@ -63,6 +61,27 @@ export const stringFields = (fields) => fields.filter((f) => isWasmString(f.type
|
|
|
63
61
|
* @internal
|
|
64
62
|
*/
|
|
65
63
|
export const enumName = (opts, name) => opts.uppercaseEnums ? name.toUpperCase() : name;
|
|
64
|
+
/**
|
|
65
|
+
* Takes an array of strings or splits given string into lines, word wraps and
|
|
66
|
+
* then prefixes each line with given `width` and `prefix`. Returns array of new
|
|
67
|
+
* lines.
|
|
68
|
+
*
|
|
69
|
+
* @param prefix
|
|
70
|
+
* @param str
|
|
71
|
+
* @param width
|
|
72
|
+
*/
|
|
73
|
+
export const prefixLines = (prefix, str, width) => (isString(str)
|
|
74
|
+
? wordWrapLines(str, { width: width - prefix.length })
|
|
75
|
+
: str.flatMap((x) => wordWrapLine(x, { width: width - prefix.length }))).map((line) => prefix + line);
|
|
76
|
+
export const ensureLines = (src, key) => isString(src)
|
|
77
|
+
? split(src)
|
|
78
|
+
: isArray(src)
|
|
79
|
+
? src
|
|
80
|
+
: key
|
|
81
|
+
? src[key]
|
|
82
|
+
? ensureLines(src[key], key)
|
|
83
|
+
: []
|
|
84
|
+
: [];
|
|
66
85
|
/**
|
|
67
86
|
* Yields iterator of given lines, each with applied indentation based on given
|
|
68
87
|
* scope regexp's which are applied to each line to increase or decrease
|
package/codegen/zig.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CodeGenOptsBase, ICodeGen } from "../api.js";
|
|
1
|
+
import type { CodeGenOpts, CodeGenOptsBase, Field, ICodeGen, Struct, Union } from "../api.js";
|
|
2
2
|
/**
|
|
3
3
|
* Zig code generator options.
|
|
4
4
|
*/
|
|
@@ -15,4 +15,9 @@ export interface ZigOpts extends CodeGenOptsBase {
|
|
|
15
15
|
* @param opts
|
|
16
16
|
*/
|
|
17
17
|
export declare const ZIG: (opts?: Partial<ZigOpts>) => ICodeGen;
|
|
18
|
+
/** @internal */
|
|
19
|
+
export declare const fieldType: (parent: Struct | Union, f: Field, opts: CodeGenOpts) => {
|
|
20
|
+
type: string;
|
|
21
|
+
defaultVal: string;
|
|
22
|
+
};
|
|
18
23
|
//# sourceMappingURL=zig.d.ts.map
|
package/codegen/zig.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { isNumber } from "@thi.ng/checks/is-number";
|
|
2
2
|
import { isString } from "@thi.ng/checks/is-string";
|
|
3
3
|
import { unsupported } from "@thi.ng/errors/unsupported";
|
|
4
|
-
import {
|
|
5
|
-
import { enumName, isPadding, isStringSlice, prefixLines, withIndentation, } from "./utils.js";
|
|
4
|
+
import { ensureLines, enumName, isPadding, isStringSlice, isWasmString, prefixLines, withIndentation, } from "./utils.js";
|
|
6
5
|
/**
|
|
7
6
|
* Zig code generator. Call with options and then pass to {@link generateTypes}
|
|
8
7
|
* (see its docs for further usage).
|
|
@@ -39,81 +38,110 @@ export const ZIG = (opts = {}) => {
|
|
|
39
38
|
lines.push(line + ",");
|
|
40
39
|
}
|
|
41
40
|
if (e.body?.zig) {
|
|
42
|
-
lines.push("", ...
|
|
41
|
+
lines.push("", ...ensureLines(e.body.zig, "impl"), "");
|
|
43
42
|
}
|
|
44
43
|
lines.push("};", "");
|
|
45
44
|
acc.push(...withIndentation(lines, INDENT, ...SCOPES));
|
|
46
45
|
},
|
|
47
46
|
struct: (struct, _, acc, opts) => {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
}
|
|
59
|
-
f.doc && gen.doc(f.doc, res, opts);
|
|
60
|
-
let ftype = f.type === "string"
|
|
61
|
-
? isStringSlice(opts.stringType)
|
|
62
|
-
? f.const !== false
|
|
63
|
-
? "[]const u8"
|
|
64
|
-
: "[]u8"
|
|
65
|
-
: f.const !== false
|
|
66
|
-
? "[*:0]const u8"
|
|
67
|
-
: "[*:0]u8"
|
|
68
|
-
: f.type;
|
|
69
|
-
let defaultVal = "";
|
|
70
|
-
switch (f.tag) {
|
|
71
|
-
case "array":
|
|
72
|
-
ftype =
|
|
73
|
-
f.sentinel !== undefined
|
|
74
|
-
? `[${f.len}:${f.sentinel}]${ftype}`
|
|
75
|
-
: `[${f.len}]${ftype}`;
|
|
76
|
-
break;
|
|
77
|
-
case "slice":
|
|
78
|
-
ftype = `[${f.sentinel !== undefined ? ":" + f.sentinel : ""}]${f.const ? "const " : ""}${ftype}`;
|
|
79
|
-
break;
|
|
80
|
-
case "vec":
|
|
81
|
-
ftype = `@Vector(${f.len}, ${ftype})`;
|
|
82
|
-
break;
|
|
83
|
-
case "ptr":
|
|
84
|
-
ftype = `*${f.const ? "const " : ""}${f.len ? `[${f.len}]` : ""}${ftype}`;
|
|
85
|
-
break;
|
|
86
|
-
case "scalar":
|
|
87
|
-
default:
|
|
88
|
-
if (f.default != undefined) {
|
|
89
|
-
if (!(isString(f.default) || isNumber(f.default))) {
|
|
90
|
-
unsupported(`wrong default value for ${name}.${f.name} (${f.default})`);
|
|
91
|
-
}
|
|
92
|
-
defaultVal = ` = ${JSON.stringify(f.default)}`;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
ftypes[f.name] = ftype;
|
|
96
|
-
res.push(`${f.name}: ${ftype}${defaultVal},`);
|
|
97
|
-
}
|
|
98
|
-
if (struct.body?.zig) {
|
|
99
|
-
res.push("", ...split(struct.body.zig), "");
|
|
100
|
-
}
|
|
101
|
-
res.push("};");
|
|
102
|
-
if (opts.debug) {
|
|
103
|
-
const fn = (fname, body) => res.push("", `export fn ${name}_${fname}() usize {`, `return ${body};`, `}`);
|
|
104
|
-
fn("align", `@alignOf(${name})`);
|
|
105
|
-
fn("size", `@sizeOf(${name})`);
|
|
106
|
-
for (let f of struct.fields) {
|
|
107
|
-
if (isPadding(f))
|
|
108
|
-
continue;
|
|
109
|
-
fn(f.name + "_align", `@alignOf(${ftypes[f.name]})`);
|
|
110
|
-
fn(f.name + "_offset", `@offsetOf(${name}, "${f.name}")`);
|
|
111
|
-
fn(f.name + "_size", `@sizeOf(${ftypes[f.name]})`);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
res.push("");
|
|
115
|
-
acc.push(...withIndentation(res, INDENT, ...SCOPES));
|
|
47
|
+
acc.push(...withIndentation([
|
|
48
|
+
`pub const ${struct.name} = ${struct.tag ? struct.tag + " " : ""}struct {`,
|
|
49
|
+
...__generateFields(gen, struct, opts),
|
|
50
|
+
], INDENT, ...SCOPES));
|
|
51
|
+
},
|
|
52
|
+
union: (union, _, acc, opts) => {
|
|
53
|
+
acc.push(...withIndentation([
|
|
54
|
+
`pub const ${union.name} = ${union.tag ? union.tag + " " : ""}union {`,
|
|
55
|
+
...__generateFields(gen, union, opts),
|
|
56
|
+
], INDENT, ...SCOPES));
|
|
116
57
|
},
|
|
117
58
|
};
|
|
118
59
|
return gen;
|
|
119
60
|
};
|
|
61
|
+
const __generateFields = (gen, parent, opts) => {
|
|
62
|
+
const res = [];
|
|
63
|
+
const ftypes = {};
|
|
64
|
+
const isUnion = parent.type === "union";
|
|
65
|
+
const name = parent.name;
|
|
66
|
+
let padID = 0;
|
|
67
|
+
for (let f of parent.fields) {
|
|
68
|
+
// autolabel explicit padding fields
|
|
69
|
+
if (isPadding(f)) {
|
|
70
|
+
parent.tag === "packed"
|
|
71
|
+
? __packedPadding(padID, f.pad, res)
|
|
72
|
+
: res.push(`__pad${padID}: [${f.pad}]u8,`);
|
|
73
|
+
padID++;
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
f.doc && gen.doc(f.doc, res, opts);
|
|
77
|
+
const { type, defaultVal } = fieldType(parent, f, opts);
|
|
78
|
+
ftypes[f.name] = type;
|
|
79
|
+
res.push(`${f.name}: ${type}${defaultVal},`);
|
|
80
|
+
}
|
|
81
|
+
if (parent.body?.zig) {
|
|
82
|
+
res.push("", ...ensureLines(parent.body.zig, "impl"), "");
|
|
83
|
+
}
|
|
84
|
+
res.push("};");
|
|
85
|
+
if (opts.debug) {
|
|
86
|
+
const fn = (fname, body) => res.push("", `export fn ${name}_${fname}() usize {`, `return ${body};`, `}`);
|
|
87
|
+
fn("align", `@alignOf(${name})`);
|
|
88
|
+
fn("size", `@sizeOf(${name})`);
|
|
89
|
+
for (let f of parent.fields) {
|
|
90
|
+
if (isPadding(f))
|
|
91
|
+
continue;
|
|
92
|
+
fn(f.name + "_align", `@alignOf(${ftypes[f.name]})`);
|
|
93
|
+
!isUnion &&
|
|
94
|
+
fn(f.name + "_offset", `@offsetOf(${name}, "${f.name}")`);
|
|
95
|
+
fn(f.name + "_size", `@sizeOf(${ftypes[f.name]})`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
res.push("");
|
|
99
|
+
return res;
|
|
100
|
+
};
|
|
101
|
+
/** @internal */
|
|
102
|
+
export const fieldType = (parent, f, opts) => {
|
|
103
|
+
let type = isWasmString(f.type)
|
|
104
|
+
? isStringSlice(opts.stringType)
|
|
105
|
+
? f.const !== false
|
|
106
|
+
? "[]const u8"
|
|
107
|
+
: "[]u8"
|
|
108
|
+
: f.const !== false
|
|
109
|
+
? "[*:0]const u8"
|
|
110
|
+
: "[*:0]u8"
|
|
111
|
+
: f.type;
|
|
112
|
+
let defaultVal = "";
|
|
113
|
+
switch (f.tag) {
|
|
114
|
+
case "array":
|
|
115
|
+
type =
|
|
116
|
+
f.sentinel !== undefined
|
|
117
|
+
? `[${f.len}:${f.sentinel}]${type}`
|
|
118
|
+
: `[${f.len}]${type}`;
|
|
119
|
+
break;
|
|
120
|
+
case "slice":
|
|
121
|
+
type = `[${f.sentinel !== undefined ? ":" + f.sentinel : ""}]${f.const ? "const " : ""}${type}`;
|
|
122
|
+
break;
|
|
123
|
+
case "vec":
|
|
124
|
+
type = `@Vector(${f.len}, ${type})`;
|
|
125
|
+
break;
|
|
126
|
+
case "ptr":
|
|
127
|
+
type = `*${f.const ? "const " : ""}${f.len ? `[${f.len}]` : ""}${type}`;
|
|
128
|
+
break;
|
|
129
|
+
case "scalar":
|
|
130
|
+
default:
|
|
131
|
+
if (f.default != undefined) {
|
|
132
|
+
if (!(isString(f.default) || isNumber(f.default))) {
|
|
133
|
+
unsupported(`wrong default value for ${parent.name}.${f.name} (${f.default})`);
|
|
134
|
+
}
|
|
135
|
+
defaultVal = ` = ${JSON.stringify(f.default)}`;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return { type, defaultVal };
|
|
139
|
+
};
|
|
140
|
+
const __packedPadding = (id, n, res) => {
|
|
141
|
+
let i = 0;
|
|
142
|
+
n <<= 3;
|
|
143
|
+
for (; n >= 128; n -= 128, i++) {
|
|
144
|
+
res.push(`__pad${id}_${i}: u128,`);
|
|
145
|
+
}
|
|
146
|
+
res.push(`__pad${id}_${i}: u${n},`);
|
|
147
|
+
};
|
package/codegen.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { CodeGenOpts, ICodeGen, TypeColl } from "./api.js";
|
|
2
|
+
export declare const DEFAULT_CODEGEN_OPTS: CodeGenOpts;
|
|
2
3
|
/**
|
|
3
4
|
* Takes a type collection and analyzes each analyzed to compute individual
|
|
4
5
|
* alignments and sizes.
|
|
@@ -11,7 +12,7 @@ import { CodeGenOpts, ICodeGen, TypeColl } from "./api.js";
|
|
|
11
12
|
*
|
|
12
13
|
* @internal
|
|
13
14
|
*/
|
|
14
|
-
export declare const prepareTypes: (types: TypeColl, opts: CodeGenOpts) =>
|
|
15
|
+
export declare const prepareTypes: (types: TypeColl, opts: CodeGenOpts) => TypeColl;
|
|
15
16
|
/**
|
|
16
17
|
* Code generator main entry point. Takes an object of {@link TopLevelType}
|
|
17
18
|
* definitions, an actual code generator implementation for a single target
|