@thi.ng/wasm-api 0.15.0 → 0.17.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # Change Log
2
2
 
3
- - **Last updated**: 2022-10-26T12:46:53Z
3
+ - **Last updated**: 2022-10-29T20:34:04Z
4
4
  - **Generator**: [thi.ng/monopub](https://thi.ng/monopub)
5
5
 
6
6
  All notable changes to this project will be documented in this file.
@@ -9,6 +9,44 @@ See [Conventional Commits](https://conventionalcommits.org/) for commit guidelin
9
9
  **Note:** Unlisted _patch_ versions only involve non-code or otherwise excluded changes
10
10
  and/or version bumps of transitive dependencies.
11
11
 
12
+ ## [0.17.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/wasm-api@0.17.0) (2022-10-29)
13
+
14
+ #### 🚀 Features
15
+
16
+ - update default value format & handling ([c7752fb](https://github.com/thi-ng/umbrella/commit/c7752fb))
17
+ - update Field.default to support lang-specific alts
18
+ - add defaultValue() helper
19
+ - update zig codegen
20
+
21
+ #### 🩹 Bug fixes
22
+
23
+ - fix idempotence checks for self-referential types ([2c3f670](https://github.com/thi-ng/umbrella/commit/2c3f670))
24
+ - update prepareType()
25
+
26
+ ## [0.16.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/wasm-api@0.16.0) (2022-10-28)
27
+
28
+ #### 🚀 Features
29
+
30
+ - update TS docstring generator ([5a060b6](https://github.com/thi-ng/umbrella/commit/5a060b6))
31
+ - include native/WASM type in docstring (use Zig type sigs)
32
+ - update ensureLines() helper
33
+ - update test fixtures
34
+ - update IWasmMemoryAccess & impls ([bb8a3ca](https://github.com/thi-ng/umbrella/commit/bb8a3ca))
35
+ - add MemorySlice tuple to describe a memory region
36
+ - update allocate() & free() to use MemorySlice, migrate to IWasmMemoryAccess
37
+ - migrate growMemory() to IWasmMemoryAccess
38
+ - add MemorySlice to default imports in TS codegen
39
+ - update docstring & user code codegen config ([68694ba](https://github.com/thi-ng/umbrella/commit/68694ba))
40
+ - allow doc strings & user codes to be given as string array (lines)
41
+ - add InjectedBody type to support multiple injection sites
42
+ (e.g. TS interface declarations vs. wrapper impls)
43
+ - update TS & Zig codegens
44
+ - add internal ensureLines() helper
45
+
46
+ #### 🩹 Bug fixes
47
+
48
+ - fix ensureLines(), update tests & fixtures ([f8a4668](https://github.com/thi-ng/umbrella/commit/f8a4668))
49
+
12
50
  ## [0.15.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/wasm-api@0.15.0) (2022-10-26)
13
51
 
14
52
  #### 🚀 Features
package/api.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { BigType, FloatType, Fn, Fn2, IDeref, ILength, IObjectOf } from "@thi.ng/api";
1
+ import type { BigType, FloatType, Fn, Fn2, IDeref, ILength, IObjectOf, NumOrString } from "@thi.ng/api";
2
2
  import type { Pow2 } from "@thi.ng/binary";
3
3
  import type { WasmBridge } from "./bridge.js";
4
4
  export declare const PKG_NAME = "@thi.ng/wasm-api";
@@ -53,7 +53,7 @@ export interface WasmExports {
53
53
  * @remarks
54
54
  * #### Zig
55
55
  *
56
- * Using the supplied Zig bindings (see `/include/wasmapi.zig`), it's the
56
+ * Using the supplied Zig bindings (see `/zig/wasmapi.zig`), it's the
57
57
  * user's responsibility to define a public `WASM_ALLOCATOR` in the root
58
58
  * source file to enable allocations, e.g. using the
59
59
  * [`std.heap.GeneralPurposeAllocator`](https://ziglang.org/documentation/master/#Choosing-an-Allocator)
@@ -81,6 +81,7 @@ export interface WasmExports {
81
81
  */
82
82
  _wasm_free(addr: number, numBytes: number): void;
83
83
  }
84
+ export declare type MemorySlice = [addr: number, len: number];
84
85
  export interface IWasmMemoryAccess {
85
86
  i8: Int8Array;
86
87
  u8: Uint8Array;
@@ -97,6 +98,41 @@ export interface IWasmMemoryAccess {
97
98
  * after growing the WASM memory and the previous buffer becoming detached).
98
99
  */
99
100
  ensureMemory(): void;
101
+ /**
102
+ * Attempts to grow the WASM memory by an additional `numPages` (64KB/page)
103
+ * and if successful updates all typed memory views to use the new
104
+ * underlying buffer.
105
+ *
106
+ * @param numPages
107
+ */
108
+ growMemory(numPages: number): void;
109
+ /**
110
+ * Attempts to allocate `numBytes` using the exported WASM core API function
111
+ * {@link WasmExports._wasm_allocate} (implementation specific) and returns
112
+ * start address of the new memory block. If unsuccessful, throws an
113
+ * {@link OutOfMemoryError}. If `clear` is true, the allocated region will
114
+ * be zero-filled.
115
+ *
116
+ * @remarks
117
+ * See {@link WasmExports._wasm_allocate} docs for further details.
118
+ *
119
+ * @param numBytes
120
+ * @param clear
121
+ */
122
+ allocate(numBytes: number, clear?: boolean): MemorySlice;
123
+ /**
124
+ * Frees a previous allocated memory region using the exported WASM core API
125
+ * function {@link WasmExports._wasm_free} (implementation specific). The
126
+ * `numBytes` value must be the same as previously given to
127
+ * {@link IWasmMemoryAccess.allocate}.
128
+ *
129
+ * @remarks
130
+ * This function always succeeds, regardless of presence of an active
131
+ * allocator on the WASM side or validity of given arguments.
132
+ *
133
+ * @param slice
134
+ */
135
+ free(slice: MemorySlice): void;
100
136
  /**
101
137
  * Reads UTF-8 encoded string from given address and optional byte length.
102
138
  * The default length is 0, which will be interpreted as a zero-terminated
@@ -188,7 +224,7 @@ export declare type WasmPrim32 = Exclude<WasmPrim, BigType>;
188
224
  export declare type ReadonlyWasmString = IDeref<string> & ILength & {
189
225
  readonly addr: number;
190
226
  };
191
- export declare type TypeColl = Record<string, TopLevelType>;
227
+ export declare type TypeColl = IObjectOf<TopLevelType>;
192
228
  export interface TypeInfo {
193
229
  /**
194
230
  * Auto-computed size (in bytes)
@@ -217,19 +253,24 @@ export interface TopLevelType extends TypeInfo {
217
253
  /**
218
254
  * Optional (multi-line) docstring for this type
219
255
  */
220
- doc?: string;
256
+ doc?: string | string[];
221
257
  /**
222
258
  * Type / kind
223
259
  */
224
260
  type: "enum" | "struct" | "union";
225
261
  /**
226
262
  * Optional object of user provided source codes to be injected into the
227
- * generated type. Keys are language IDs (same name as respective codegen).
263
+ * generated type (after generated fields). Keys of this object are language
264
+ * IDs (`ts` for {@link TYPESCRIPT}, `zig` for {@link ZIG}).
228
265
  *
229
266
  * @remarks
230
- * Currently only supported by the {@link ZIG} codegen, ignored otherwise.
267
+ * Currently only supported by the code gens mentioned, ignored otherwise.
231
268
  */
232
- body?: IObjectOf<string>;
269
+ body?: IObjectOf<string | string[] | InjectedBody>;
270
+ }
271
+ export interface InjectedBody {
272
+ decl?: string | string[];
273
+ impl?: string | string[];
233
274
  }
234
275
  export interface Struct extends TopLevelType {
235
276
  type: "struct";
@@ -259,6 +300,23 @@ export interface Struct extends TopLevelType {
259
300
  */
260
301
  align?: AlignStrategy;
261
302
  }
303
+ export interface Union extends TopLevelType {
304
+ type: "union";
305
+ /**
306
+ * Array of union fields.
307
+ */
308
+ fields: Field[];
309
+ /**
310
+ * Optional qualifier for the kind of struct to be emitted (codegen specific
311
+ * interpretation, currently only used by {@link ZIG}).
312
+ */
313
+ tag?: "extern" | "packed";
314
+ /**
315
+ * Optional user supplied {@link AlignStrategy}. By default uses
316
+ * {@link ALIGN_C} or {@link ALIGN_PACKED} (if using "packed" union).
317
+ */
318
+ align?: AlignStrategy;
319
+ }
262
320
  export declare type FieldTag = "scalar" | "array" | "ptr" | "slice" | "vec";
263
321
  export interface Field extends TypeInfo {
264
322
  /**
@@ -268,7 +326,7 @@ export interface Field extends TypeInfo {
268
326
  /**
269
327
  * Field docstring (can be multiline, will be formatted)
270
328
  */
271
- doc?: string;
329
+ doc?: string | string[];
272
330
  /**
273
331
  * Field type tag/qualifier (note: `slice` & `vec` are only supported by Zig
274
332
  * & TS).
@@ -312,10 +370,14 @@ export interface Field extends TypeInfo {
312
370
  */
313
371
  len?: number;
314
372
  /**
315
- * Currently only supported for {@link ZIG} scalar & string values,
316
- * otherwise ignored!
373
+ * Currently only supported for {@link ZIG}, otherwise ignored!
374
+ *
375
+ * @remarks
376
+ * The object form allows for different default values per language (in
377
+ * theory). So if given as object, the keys refer to the lang ID and the
378
+ * values as the defaults for those languages.
317
379
  */
318
- default?: number;
380
+ default?: NumOrString | IObjectOf<NumOrString>;
319
381
  /**
320
382
  * If defined and > 0, the field will be considered for padding purposes
321
383
  * only and the value provided is the number of bytes used. All other config
@@ -323,23 +385,6 @@ export interface Field extends TypeInfo {
323
385
  */
324
386
  pad?: number;
325
387
  }
326
- export interface Union extends TopLevelType {
327
- type: "union";
328
- /**
329
- * Array of union fields.
330
- */
331
- fields: Field[];
332
- /**
333
- * Optional qualifier for the kind of struct to be emitted (codegen specific
334
- * interpretation, currently only used by {@link ZIG}).
335
- */
336
- tag?: "extern" | "packed";
337
- /**
338
- * Optional user supplied {@link AlignStrategy}. By default uses
339
- * {@link ALIGN_C} or {@link ALIGN_PACKED} (if using "packed" union).
340
- */
341
- align?: AlignStrategy;
342
- }
343
388
  export interface Enum extends TopLevelType {
344
389
  type: "enum";
345
390
  /**
@@ -449,7 +494,7 @@ export interface ICodeGen {
449
494
  /**
450
495
  * Docstring codegen
451
496
  */
452
- doc: (doc: string, acc: string[], opts: CodeGenOpts, topLevel?: boolean) => void;
497
+ doc: (doc: string | string[], acc: string[], opts: CodeGenOpts, topLevel?: boolean) => void;
453
498
  /**
454
499
  * Codegen for enum types.
455
500
  */
package/bridge.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  /// <reference types="node" />
2
2
  import type { Event, INotify, Listener, NumericArray } from "@thi.ng/api";
3
3
  import type { ILogger } from "@thi.ng/logger";
4
- import { BigIntArray, CoreAPI, IWasmAPI, IWasmMemoryAccess, WasmExports } from "./api.js";
4
+ import { BigIntArray, CoreAPI, IWasmAPI, IWasmMemoryAccess, MemorySlice, WasmExports } from "./api.js";
5
5
  export declare const Panic: {
6
6
  new (msg?: string | undefined): {
7
7
  name: string;
@@ -136,34 +136,8 @@ export declare class WasmBridge<T extends WasmExports = WasmExports> implements
136
136
  * @param numPages
137
137
  */
138
138
  growMemory(numPages: number): void;
139
- /**
140
- * Attempts to allocate `numBytes` using the exported WASM core API function
141
- * {@link WasmExports._wasm_allocate} (implementation specific) and returns
142
- * start address of the new memory block. If unsuccessful, throws an
143
- * {@link OutOfMemoryError}. If `clear` is true, the allocated region will
144
- * be zero-filled.
145
- *
146
- * @remarks
147
- * See {@link WasmExports._wasm_allocate} docs for further details.
148
- *
149
- * @param numBytes
150
- * @param clear
151
- */
152
- allocate(numBytes: number, clear?: boolean): number;
153
- /**
154
- * Frees a previous allocated memory region using the exported WASM core API
155
- * function {@link WasmExports._wasm_free} (implementation specific). The
156
- * `numBytes` value must be the same as previously given to
157
- * {@link WasmBridge.allocate}.
158
- *
159
- * @remarks
160
- * This function always succeeds, regardless of presence of an active
161
- * allocator on the WASM side or validity of given arguments.
162
- *
163
- * @param addr
164
- * @param numBytes
165
- */
166
- free(addr: number, numBytes: number): void;
139
+ allocate(numBytes: number, clear?: boolean): MemorySlice;
140
+ free([addr, numBytes]: MemorySlice): void;
167
141
  getI8(addr: number): number;
168
142
  getU8(addr: number): number;
169
143
  getI16(addr: number): number;
package/bridge.js CHANGED
@@ -198,19 +198,6 @@ let WasmBridge = class WasmBridge {
198
198
  this.exports.memory.grow(numPages);
199
199
  this.ensureMemory();
200
200
  }
201
- /**
202
- * Attempts to allocate `numBytes` using the exported WASM core API function
203
- * {@link WasmExports._wasm_allocate} (implementation specific) and returns
204
- * start address of the new memory block. If unsuccessful, throws an
205
- * {@link OutOfMemoryError}. If `clear` is true, the allocated region will
206
- * be zero-filled.
207
- *
208
- * @remarks
209
- * See {@link WasmExports._wasm_allocate} docs for further details.
210
- *
211
- * @param numBytes
212
- * @param clear
213
- */
214
201
  allocate(numBytes, clear = false) {
215
202
  const addr = this.exports._wasm_allocate(numBytes);
216
203
  if (!addr)
@@ -218,22 +205,9 @@ let WasmBridge = class WasmBridge {
218
205
  this.logger.fine(() => `allocated ${numBytes} bytes @ 0x${U32(addr)} .. 0x${U32(addr + numBytes - 1)}`);
219
206
  this.ensureMemory();
220
207
  clear && this.u8.fill(0, addr, addr + numBytes);
221
- return addr;
208
+ return [addr, numBytes];
222
209
  }
223
- /**
224
- * Frees a previous allocated memory region using the exported WASM core API
225
- * function {@link WasmExports._wasm_free} (implementation specific). The
226
- * `numBytes` value must be the same as previously given to
227
- * {@link WasmBridge.allocate}.
228
- *
229
- * @remarks
230
- * This function always succeeds, regardless of presence of an active
231
- * allocator on the WASM side or validity of given arguments.
232
- *
233
- * @param addr
234
- * @param numBytes
235
- */
236
- free(addr, numBytes) {
210
+ free([addr, numBytes]) {
237
211
  this.logger.fine(() => `freeing memory @ 0x${U32(addr)} .. 0x${U32(addr + numBytes - 1)}`);
238
212
  this.exports._wasm_free(addr, numBytes);
239
213
  }
package/cli.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { flag, oneOf, oneOfMulti, parse, ParseError, string, strings, usage, } from "@thi.ng/args";
2
- import { isArray, isPlainObject } from "@thi.ng/checks";
2
+ import { isArray, isPlainObject, isString } from "@thi.ng/checks";
3
3
  import { illegalArgs } from "@thi.ng/errors";
4
4
  import { readJSON, readText, writeJSON, writeText } from "@thi.ng/file-io";
5
5
  import { ConsoleLogger } from "@thi.ng/logger";
@@ -84,7 +84,7 @@ const addTypeSpec = (ctx, path, coll, spec) => {
84
84
  invalidSpec(path, `${spec.name}.body must be an object`);
85
85
  for (let lang in spec.body) {
86
86
  const src = spec.body[lang];
87
- if (src[0] === "@") {
87
+ if (isString(src) && src[0] === "@") {
88
88
  spec.body[lang] = readText(src.substring(1), ctx.logger);
89
89
  }
90
90
  }
@@ -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
3
  import { PKG_NAME, } from "../api.js";
4
- import { enumName, isBigNumeric, isNumeric, isPadding, isStringSlice, isWasmPrim, isWasmString, pointerFields, prefixLines, stringFields, withIndentation, } from "./utils.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
- f.doc && gen.doc(f.doc, lines, opts);
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;`;
@@ -159,6 +164,9 @@ 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
  },
@@ -221,3 +229,12 @@ const __mapStringArray = (target, name, type, len, isConst, isLocal = false) =>
221
229
  `for(let i = 0; i < ${len}; i++) $${name}.push(new ${type}(mem, addr + i * ${target.usizeBytes * (type === "WasmStringSlice" ? 2 : 1)}, ${isConst}));`,
222
230
  `return $${name};`,
223
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
+ };
@@ -1,5 +1,5 @@
1
1
  import type { BigType } from "@thi.ng/api";
2
- import type { CodeGenOpts, Field, WasmPrim, WasmPrim32 } from "../api.js";
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
  *
@@ -28,16 +28,6 @@ export declare const isSlice: (f: Field) => boolean;
28
28
  * @param f
29
29
  */
30
30
  export declare const isPointerLike: (f: Field) => boolean;
31
- /**
32
- * Takes an array of strings or splits given string into lines, word wraps and
33
- * then prefixes each line with given `width` and `prefix`. Returns array of new
34
- * lines.
35
- *
36
- * @param prefix
37
- * @param str
38
- * @param width
39
- */
40
- export declare const prefixLines: (prefix: string, str: string | string[], width: number) => string[];
41
31
  /**
42
32
  * Returns true if `type` is "slice".
43
33
  *
@@ -69,6 +59,25 @@ export declare const stringFields: (fields: Field[]) => Field[];
69
59
  * @internal
70
60
  */
71
61
  export declare const enumName: (opts: CodeGenOpts, name: string) => string;
62
+ /**
63
+ * Returns given field's default value (or undefined). The `lang` ID is required
64
+ * to obtain the language specific value if the default is given as object.
65
+ *
66
+ * @param f
67
+ * @param lang
68
+ */
69
+ export declare const defaultValue: (f: Field, lang: string) => import("@thi.ng/api").NumOrString | undefined;
70
+ /**
71
+ * Takes an array of strings or splits given string into lines, word wraps and
72
+ * then prefixes each line with given `width` and `prefix`. Returns array of new
73
+ * lines.
74
+ *
75
+ * @param prefix
76
+ * @param str
77
+ * @param width
78
+ */
79
+ export declare const prefixLines: (prefix: string, str: string | string[], width: number) => string[];
80
+ export declare const ensureLines: (src: string | string[] | InjectedBody, key?: keyof InjectedBody) => Iterable<string>;
72
81
  /**
73
82
  * Yields iterator of given lines, each with applied indentation based on given
74
83
  * scope regexp's which are applied to each line to increase or decrease
package/codegen/utils.js CHANGED
@@ -1,4 +1,7 @@
1
+ import { isArray } from "@thi.ng/checks/is-array";
2
+ import { isPlainObject } from "@thi.ng/checks/is-plain-object";
1
3
  import { isString } from "@thi.ng/checks/is-string";
4
+ import { split } from "@thi.ng/strings/split";
2
5
  import { wordWrapLine, wordWrapLines } from "@thi.ng/strings/word-wrap";
3
6
  /**
4
7
  * Returns true iff `x` is a {@link WasmPrim32}.
@@ -28,18 +31,6 @@ export const isSlice = (f) => f.tag === "slice";
28
31
  * @param f
29
32
  */
30
33
  export const isPointerLike = (f) => isPointer(f) || isSlice(f) || isWasmString(f.type);
31
- /**
32
- * Takes an array of strings or splits given string into lines, word wraps and
33
- * then prefixes each line with given `width` and `prefix`. Returns array of new
34
- * lines.
35
- *
36
- * @param prefix
37
- * @param str
38
- * @param width
39
- */
40
- export const prefixLines = (prefix, str, width) => (isString(str)
41
- ? wordWrapLines(str, { width: width - prefix.length })
42
- : str.flatMap((x) => wordWrapLine(x, { width: width - prefix.length }))).map((line) => prefix + line);
43
34
  /**
44
35
  * Returns true if `type` is "slice".
45
36
  *
@@ -71,6 +62,39 @@ export const stringFields = (fields) => fields.filter((f) => isWasmString(f.type
71
62
  * @internal
72
63
  */
73
64
  export const enumName = (opts, name) => opts.uppercaseEnums ? name.toUpperCase() : name;
65
+ /**
66
+ * Returns given field's default value (or undefined). The `lang` ID is required
67
+ * to obtain the language specific value if the default is given as object.
68
+ *
69
+ * @param f
70
+ * @param lang
71
+ */
72
+ export const defaultValue = (f, lang) => f.default !== undefined
73
+ ? isPlainObject(f.default)
74
+ ? f.default[lang]
75
+ : f.default
76
+ : undefined;
77
+ /**
78
+ * Takes an array of strings or splits given string into lines, word wraps and
79
+ * then prefixes each line with given `width` and `prefix`. Returns array of new
80
+ * lines.
81
+ *
82
+ * @param prefix
83
+ * @param str
84
+ * @param width
85
+ */
86
+ export const prefixLines = (prefix, str, width) => (isString(str)
87
+ ? wordWrapLines(str, { width: width - prefix.length })
88
+ : str.flatMap((x) => wordWrapLine(x, { width: width - prefix.length }))).map((line) => prefix + line);
89
+ export const ensureLines = (src, key) => isString(src)
90
+ ? split(src)
91
+ : isArray(src)
92
+ ? src
93
+ : key
94
+ ? src[key]
95
+ ? ensureLines(src[key], key)
96
+ : []
97
+ : [];
74
98
  /**
75
99
  * Yields iterator of given lines, each with applied indentation based on given
76
100
  * 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
- import { isNumber } from "@thi.ng/checks/is-number";
1
+ import { isNumber } from "@thi.ng/checks";
2
2
  import { isString } from "@thi.ng/checks/is-string";
3
3
  import { unsupported } from "@thi.ng/errors/unsupported";
4
- import { split } from "@thi.ng/strings/split";
5
- import { enumName, isPadding, isStringSlice, isWasmString, prefixLines, withIndentation, } from "./utils.js";
4
+ import { defaultValue, 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,7 +38,7 @@ export const ZIG = (opts = {}) => {
39
38
  lines.push(line + ",");
40
39
  }
41
40
  if (e.body?.zig) {
42
- lines.push("", ...split(e.body.zig), "");
41
+ lines.push("", ...ensureLines(e.body.zig, "impl"), "");
43
42
  }
44
43
  lines.push("};", "");
45
44
  acc.push(...withIndentation(lines, INDENT, ...SCOPES));
@@ -75,46 +74,12 @@ const __generateFields = (gen, parent, opts) => {
75
74
  continue;
76
75
  }
77
76
  f.doc && gen.doc(f.doc, res, opts);
78
- let ftype = isWasmString(f.type)
79
- ? isStringSlice(opts.stringType)
80
- ? f.const !== false
81
- ? "[]const u8"
82
- : "[]u8"
83
- : f.const !== false
84
- ? "[*:0]const u8"
85
- : "[*:0]u8"
86
- : f.type;
87
- let defaultVal = "";
88
- switch (f.tag) {
89
- case "array":
90
- ftype =
91
- f.sentinel !== undefined
92
- ? `[${f.len}:${f.sentinel}]${ftype}`
93
- : `[${f.len}]${ftype}`;
94
- break;
95
- case "slice":
96
- ftype = `[${f.sentinel !== undefined ? ":" + f.sentinel : ""}]${f.const ? "const " : ""}${ftype}`;
97
- break;
98
- case "vec":
99
- ftype = `@Vector(${f.len}, ${ftype})`;
100
- break;
101
- case "ptr":
102
- ftype = `*${f.const ? "const " : ""}${f.len ? `[${f.len}]` : ""}${ftype}`;
103
- break;
104
- case "scalar":
105
- default:
106
- if (f.default != undefined) {
107
- if (!(isString(f.default) || isNumber(f.default))) {
108
- unsupported(`wrong default value for ${name}.${f.name} (${f.default})`);
109
- }
110
- defaultVal = ` = ${JSON.stringify(f.default)}`;
111
- }
112
- }
113
- ftypes[f.name] = ftype;
114
- res.push(`${f.name}: ${ftype}${defaultVal},`);
77
+ const { type, defaultVal } = fieldType(parent, f, opts);
78
+ ftypes[f.name] = type;
79
+ res.push(`${f.name}: ${type}${defaultVal},`);
115
80
  }
116
81
  if (parent.body?.zig) {
117
- res.push("", ...split(parent.body.zig), "");
82
+ res.push("", ...ensureLines(parent.body.zig, "impl"), "");
118
83
  }
119
84
  res.push("};");
120
85
  if (opts.debug) {
@@ -133,6 +98,47 @@ const __generateFields = (gen, parent, opts) => {
133
98
  res.push("");
134
99
  return res;
135
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 = defaultValue(f, "zig");
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 (defaultVal != undefined) {
132
+ if (!(isString(defaultVal) || isNumber(defaultVal))) {
133
+ unsupported(`wrong default value for ${parent.name}.${f.name} (${defaultVal})`);
134
+ }
135
+ }
136
+ }
137
+ return {
138
+ type,
139
+ defaultVal: defaultVal != undefined ? ` = ${defaultVal}` : "",
140
+ };
141
+ };
136
142
  const __packedPadding = (id, n, res) => {
137
143
  let i = 0;
138
144
  n <<= 3;
package/codegen.js CHANGED
@@ -113,13 +113,13 @@ const alignOf = defmulti((x) => x.type, {}, {
113
113
  });
114
114
  const prepareType = defmulti((x) => x.type, {}, {
115
115
  [DEFAULT]: (x, types, alignImpl, opts) => {
116
- if (x.__align && x.__size)
116
+ if (x.__align)
117
117
  return;
118
118
  alignOf(x, types, alignImpl, opts);
119
119
  sizeOf(x, types, alignImpl, opts);
120
120
  },
121
121
  struct: (x, types, align, opts) => {
122
- if (x.__align && x.__size)
122
+ if (x.__align)
123
123
  return;
124
124
  const struct = x;
125
125
  alignOf(struct, types, align, opts);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thi.ng/wasm-api",
3
- "version": "0.15.0",
3
+ "version": "0.17.0",
4
4
  "description": "Generic, modular, extensible API bridge, polyglot glue code and bindings code generators for hybrid JS & WebAssembly projects",
5
5
  "type": "module",
6
6
  "module": "./index.js",
@@ -36,27 +36,27 @@
36
36
  "test:build-zig": "zig build-lib -O ReleaseSmall -target wasm32-freestanding -dynamic --strip --pkg-begin wasmapi zig/wasmapi.zig --pkg-end test/custom.zig && wasm-dis -o custom.wast custom.wasm && cp custom.wasm test"
37
37
  },
38
38
  "dependencies": {
39
- "@thi.ng/api": "^8.4.3",
40
- "@thi.ng/args": "^2.2.6",
41
- "@thi.ng/binary": "^3.3.7",
42
- "@thi.ng/checks": "^3.3.1",
43
- "@thi.ng/compare": "^2.1.13",
44
- "@thi.ng/defmulti": "^2.1.18",
45
- "@thi.ng/errors": "^2.2.2",
46
- "@thi.ng/file-io": "^0.3.16",
47
- "@thi.ng/hex": "^2.2.1",
48
- "@thi.ng/idgen": "^2.1.15",
49
- "@thi.ng/logger": "^1.4.1",
50
- "@thi.ng/paths": "^5.1.19",
51
- "@thi.ng/strings": "^3.3.14"
39
+ "@thi.ng/api": "^8.4.4",
40
+ "@thi.ng/args": "^2.2.7",
41
+ "@thi.ng/binary": "^3.3.8",
42
+ "@thi.ng/checks": "^3.3.2",
43
+ "@thi.ng/compare": "^2.1.14",
44
+ "@thi.ng/defmulti": "^2.1.19",
45
+ "@thi.ng/errors": "^2.2.3",
46
+ "@thi.ng/file-io": "^0.3.17",
47
+ "@thi.ng/hex": "^2.2.2",
48
+ "@thi.ng/idgen": "^2.1.16",
49
+ "@thi.ng/logger": "^1.4.2",
50
+ "@thi.ng/paths": "^5.1.20",
51
+ "@thi.ng/strings": "^3.3.15"
52
52
  },
53
53
  "devDependencies": {
54
- "@microsoft/api-extractor": "^7.31.1",
55
- "@thi.ng/testament": "^0.3.3",
54
+ "@microsoft/api-extractor": "^7.33.5",
55
+ "@thi.ng/testament": "^0.3.4",
56
56
  "rimraf": "^3.0.2",
57
57
  "tools": "^0.0.1",
58
- "typedoc": "^0.22.17",
59
- "typescript": "^4.8.3"
58
+ "typedoc": "^0.23.18",
59
+ "typescript": "^4.8.4"
60
60
  },
61
61
  "keywords": [
62
62
  "allocator",
@@ -141,5 +141,5 @@
141
141
  "status": "alpha",
142
142
  "year": 2022
143
143
  },
144
- "gitHead": "cc61bc0a890288bea00b8df81ffd73bc4851ffd3\n"
144
+ "gitHead": "0402675247b1c312e4dd3d9bc6c01af5cc837c5d\n"
145
145
  }