@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.
- package/CHANGELOG.md +48 -1
- package/README.md +146 -120
- package/api.d.ts +76 -11
- package/bridge.d.ts +11 -1
- package/bridge.js +13 -8
- package/cli.js +27 -6
- package/codegen/c.d.ts +33 -0
- package/codegen/c.js +100 -0
- package/codegen/c11.d.ts +22 -0
- package/codegen/c11.js +125 -0
- package/codegen/typescript.d.ts +2 -12
- package/codegen/typescript.js +120 -84
- package/codegen/utils.d.ts +48 -1
- package/codegen/utils.js +55 -0
- package/codegen/zig.d.ts +2 -9
- package/codegen/zig.js +54 -28
- package/codegen.d.ts +1 -23
- package/codegen.js +31 -8
- package/doc/assets/main.js +52 -0
- package/doc/assets/search.js +1 -0
- package/include/wasmapi.h +34 -20
- package/include/wasmapi.zig +53 -46
- package/index.d.ts +3 -0
- package/index.js +3 -0
- package/package.json +31 -19
- package/pointer.d.ts +23 -0
- package/pointer.js +27 -0
- package/string.d.ts +97 -0
- package/string.js +145 -0
package/codegen/typescript.js
CHANGED
|
@@ -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
|
|
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
|
|
24
|
-
const I2 = I + I;
|
|
25
|
-
const I3 = I2 + I;
|
|
22
|
+
const SCOPES = [/\{$/, /\}\)?[;,]?$/];
|
|
26
23
|
const gen = {
|
|
27
|
-
pre:
|
|
28
|
-
|
|
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(
|
|
29
|
+
acc.push("/**", prefixLines(indent + " * ", doc), " */");
|
|
31
30
|
}
|
|
32
31
|
else {
|
|
33
|
-
acc.push(
|
|
32
|
+
acc.push(`/** ${doc} */`);
|
|
34
33
|
}
|
|
35
34
|
},
|
|
36
|
-
enum: (
|
|
37
|
-
const
|
|
38
|
-
|
|
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
|
|
39
|
+
let line;
|
|
41
40
|
if (!isString(v)) {
|
|
42
|
-
v.doc && gen.doc(v.doc,
|
|
43
|
-
line
|
|
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
|
|
47
|
+
line = enumName(opts, v);
|
|
49
48
|
}
|
|
50
|
-
|
|
49
|
+
res.push(line + ",");
|
|
51
50
|
}
|
|
52
|
-
|
|
53
|
-
|
|
51
|
+
res.push("}", "");
|
|
52
|
+
acc.push(...withIndentation(res, indent, ...SCOPES));
|
|
54
53
|
},
|
|
55
|
-
struct: (
|
|
56
|
-
const
|
|
57
|
-
const
|
|
54
|
+
struct: (struct, types, acc, opts) => {
|
|
55
|
+
const fieldTypes = {};
|
|
56
|
+
const $stringImpl = __stringImpl(opts);
|
|
57
|
+
const lines = [];
|
|
58
58
|
// interface definition
|
|
59
|
-
|
|
59
|
+
lines.push(`export interface ${struct.name} extends WasmTypeBase {`);
|
|
60
60
|
for (let f of struct.fields) {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
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
|
-
|
|
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
|
-
|
|
115
|
+
lines.push(`const len = ${__ptr(offset + 4)};`);
|
|
106
116
|
if (isPrim) {
|
|
107
|
-
|
|
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
|
-
|
|
120
|
+
lines.push(`const addr = ${__ptr(offset)};`, ...__mapStringArray("buf", $stringImpl, "len", isConst));
|
|
111
121
|
}
|
|
112
122
|
else {
|
|
113
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
141
|
+
lines.push(`return ${addr};`);
|
|
132
142
|
setter = `${addr} = x`;
|
|
133
143
|
}
|
|
134
144
|
else if (isStr) {
|
|
135
|
-
|
|
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
|
-
|
|
150
|
+
lines.push(`return ${addr};`);
|
|
145
151
|
setter = `${addr} = x`;
|
|
146
152
|
}
|
|
147
153
|
else {
|
|
148
|
-
|
|
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
|
-
|
|
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
|
-
|
|
165
|
+
lines.push(`},`);
|
|
158
166
|
}
|
|
159
|
-
|
|
160
|
-
|
|
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,
|
|
181
|
-
const
|
|
182
|
-
|
|
183
|
-
|
|
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 = (
|
|
186
|
-
|
|
187
|
-
`for(let i = 0; i < ${len}; i++)
|
|
188
|
-
|
|
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
|
+
];
|
package/codegen/utils.d.ts
CHANGED
|
@@ -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
|
|
13
|
+
export const ZIG = (opts = {}) => {
|
|
14
|
+
const INDENT = " ";
|
|
15
|
+
const SCOPES = [/\{$/, /\}\)?[;,]?$/];
|
|
15
16
|
const gen = {
|
|
16
|
-
|
|
17
|
-
|
|
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
|
-
|
|
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,
|
|
25
|
-
line
|
|
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
|
|
34
|
+
line = enumName(opts, v);
|
|
31
35
|
}
|
|
32
|
-
|
|
36
|
+
lines.push(line + ",");
|
|
33
37
|
}
|
|
34
|
-
|
|
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
|
-
|
|
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
|
-
|
|
42
|
-
|
|
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
|
-
|
|
80
|
+
res.push(`${f.name}: ${ftype},`);
|
|
61
81
|
}
|
|
62
|
-
|
|
63
|
-
if (
|
|
64
|
-
return;
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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
|
-
|
|
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.
|