@thi.ng/wasm-api 0.11.0 → 0.13.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-09-21T21:37:59Z
3
+ - **Last updated**: 2022-10-04T10:33:14Z
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,47 @@ 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.13.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/wasm-api@0.13.0) (2022-10-04)
13
+
14
+ #### 🚀 Features
15
+
16
+ - update C11 code gen ([ff8037f](https://github.com/thi-ng/umbrella/commit/ff8037f))
17
+ - add `#ifdef __cplusplus` guards to pre/post
18
+ - add WasmStringSlice.setSlice() ([50cc798](https://github.com/thi-ng/umbrella/commit/50cc798))
19
+
20
+ #### ⏱ Performance improvements
21
+
22
+ - lazy eval log message args ([3e13397](https://github.com/thi-ng/umbrella/commit/3e13397))
23
+
24
+ ## [0.12.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/wasm-api@0.12.0) (2022-10-03)
25
+
26
+ #### 🚀 Features
27
+
28
+ - update CLI ([98d2414](https://github.com/thi-ng/umbrella/commit/98d2414))
29
+ - add optional type analytics JSON output
30
+ - add support for including user code via `@`-file references
31
+ - update config `@`-file refs to resolve relative to config file
32
+ - add support for sentinels & user type body injection ([d240046](https://github.com/thi-ng/umbrella/commit/d240046))
33
+ - update sizeOf() to include sentinels
34
+ - add TopLevelType.body for extra user code
35
+ - update Zig codegen to:
36
+ - support array/slice sentinels
37
+ - add optional user code injection
38
+ - add `std` import header
39
+ - add ReadonlyWasmString, update impls ([240a148](https://github.com/thi-ng/umbrella/commit/240a148))
40
+ - add default value support for Zig codegen ([3c37ae7](https://github.com/thi-ng/umbrella/commit/3c37ae7))
41
+ - add doc string word wrapping ([c0b8e15](https://github.com/thi-ng/umbrella/commit/c0b8e15))
42
+ - update prefixLines(), add target line width arg
43
+ - update ICodeGen & CodeGenOpts
44
+ - update all codegens
45
+ - update deps
46
+
47
+ #### 🩹 Bug fixes
48
+
49
+ - update zig panic handler sig for latest zig master version ([cb51bf6](https://github.com/thi-ng/umbrella/commit/cb51bf6))
50
+ - see https://github.com/ziglang/zig/commit/694fab484805088030fa36efe3e6b6e7ee385852
51
+ - minor fix word wrap line width ([053ba0f](https://github.com/thi-ng/umbrella/commit/053ba0f))
52
+
12
53
  ## [0.11.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/wasm-api@0.11.0) (2022-09-21)
13
54
 
14
55
  #### 🚀 Features
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  <!-- This file is generated - DO NOT EDIT! -->
2
2
 
3
- # ![wasm-api](https://media.thi.ng/umbrella/banners-20220914/thing-wasm-api.svg?eb6dbc93)
3
+ # ![wasm-api](https://media.thi.ng/umbrella/banners-20220914/thing-wasm-api.svg?be328da7)
4
4
 
5
5
  [![npm version](https://img.shields.io/npm/v/@thi.ng/wasm-api.svg)](https://www.npmjs.com/package/@thi.ng/wasm-api)
6
6
  ![npm downloads](https://img.shields.io/npm/dm/@thi.ng/wasm-api.svg)
@@ -19,8 +19,10 @@ This project is part of the
19
19
  - [Custom API modules](#custom-api-modules)
20
20
  - [Object indices & handles](#object-indices--handles)
21
21
  - [Status](#status)
22
+ - [Support packages](#support-packages)
22
23
  - [Installation](#installation)
23
24
  - [Dependencies](#dependencies)
25
+ - [Usage examples](#usage-examples)
24
26
  - [API](#api)
25
27
  - [Basic usage example](#basic-usage-example)
26
28
  - [Zig version](#zig-version)
@@ -39,9 +41,10 @@ This package provides a the following:
39
41
  class as interop basis and much reduced boilerplate for hybrid JS/WebAssembly
40
42
  applications.
41
43
  2. A minimal core API for debug output, string/pointer/typedarray accessors for
42
- 8/16/32/64 bit (u)ints and 32/64 bit floats, memory allocation (optional). In
43
- the future we aim to also supply support modules for DOM manipulation, WebGL,
44
- WebGPU, WebAudio etc.
44
+ 8/16/32/64 bit (u)ints and 32/64 bit floats, memory allocation (optional).
45
+ Additionally, a number of support modules for [DOM
46
+ manipulation](https://github.com/thi-ng/umbrella/tree/develop/packages/wasm-api-dom/),
47
+ WebGL, WebGPU, WebAudio etc. is being actively worked on.
45
48
  3. [Include files for C11/C++ and
46
49
  Zig](https://github.com/thi-ng/umbrella/tree/develop/packages/wasm-api/include),
47
50
  defining WASM imports of the extern JS [core
@@ -51,7 +54,7 @@ WebGPU, WebAudio etc.
51
54
  [Zig](https://ziglang.org). The latter also generates fully type checked
52
55
  memory-mapped (zero-copy) accessors of WASM-side data. In general, all
53
56
  languages with a WebAssembly target are supported, however currently only
54
- bindings for these few langs are included.
57
+ bindings for these mentioned langs are included.
55
58
  5. [CLI frontend/utility](#cli-generator) to invoke the code generator(s)
56
59
 
57
60
  ### Data bindings & code generators
@@ -59,7 +62,9 @@ bindings for these few langs are included.
59
62
  The package provides an extensible codegeneration framework to simplify the
60
63
  bilateral design & exchange of data structures shared between the WASM & JS host
61
64
  env. Currently, code generators for TypeScript & Zig are supplied (more are
62
- planned). A CLI wrapper is available too.
65
+ planned). A CLI wrapper is available too. See the
66
+ [@thi.ng/wasm-api-dom](https://github.com/thi-ng/umbrella/tree/develop/packages/wasm-api-dom/)
67
+ support package for a more thorough realworld example...
63
68
 
64
69
  #### CLI generator
65
70
 
@@ -73,7 +78,7 @@ $ npx @thi.ng/wasm-api
73
78
 
74
79
  █ █ █ │
75
80
  ██ █ │
76
- █ █ █ █ █ █ █ █ │ @thi.ng/wasm-api 0.11.0
81
+ █ █ █ █ █ █ █ █ │ @thi.ng/wasm-api 0.12.0
77
82
  █ █ █ █ █ █ █ █ █ │ Multi-language data bindings code generator
78
83
  █ │
79
84
  █ █ │
@@ -88,6 +93,7 @@ Flags:
88
93
 
89
94
  Main:
90
95
 
96
+ -a FILE, --analytics FILE output file path for raw codegen analytics
91
97
  -c FILE, --config FILE JSON config file with codegen options
92
98
  -l ID[,..], --lang ID[,..] [multiple] target language: "c11", "ts", "zig" (default: ["ts","zig"])
93
99
  -o FILE, --out FILE [multiple] output file path
@@ -195,42 +201,59 @@ codes:
195
201
  <details><summary>generated.ts (generated TypeScript source)</summary>
196
202
 
197
203
  ```ts
198
- /** Generated by @thi.ng/wasm-api at 2022-08-15T22:32:21.189Z - DO NOT EDIT! */
204
+ /**
205
+ * Generated by @thi.ng/wasm-api at 2022-10-03T15:32:12.339Z - DO NOT EDIT!
206
+ */
199
207
 
200
- import type { WasmTypeBase, WasmTypeConstructor } from "@thi.ng/wasm-api";
208
+ // @ts-ignore possibly includes unused imports
209
+ import { Pointer, WasmStringSlice, WasmTypeBase, WasmTypeConstructor } from "@thi.ng/wasm-api";
201
210
 
202
- /** Example struct */
211
+ /**
212
+ * Example struct
213
+ */
203
214
  export interface Foo extends WasmTypeBase {
204
215
  color: Float32Array;
205
216
  bars: Bar[];
206
- /** Unique ID */
217
+ /**
218
+ * Unique ID
219
+ */
207
220
  id: number;
208
221
  }
209
222
 
210
223
  export const $Foo: WasmTypeConstructor<Foo> = (mem) => ({
211
- get align() { return 16; },
212
- get size() { return 48; },
213
- instance: (base) => ({
214
- get __base() { return base; },
215
- get __bytes() { return mem.u8.subarray(base, base + 48); },
216
- get color(): Float32Array {
217
- const addr = base >>> 2;
218
- return mem.f32.subarray(addr, addr + 4);
219
- },
220
- get bars(): Bar[] {
221
- const addr = (base + 16);
222
- const inst = $Bar(mem);
223
- const slice: Bar[] = [];
224
- for(let i = 0; i < 3; i++) slice.push(inst.instance(addr + i * 24));
225
- return slice;
226
- },
227
- get id(): number {
228
- return mem.u8[(base + 40)];
229
- },
230
- set id(x: number) {
231
- mem.u8[(base + 40)] = x;
232
- },
233
- })
224
+ get align() {
225
+ return 16;
226
+ },
227
+ get size() {
228
+ return 48;
229
+ },
230
+ instance: (base) => {
231
+ return {
232
+ get __base() {
233
+ return base;
234
+ },
235
+ get __bytes() {
236
+ return mem.u8.subarray(base, base + 48);
237
+ },
238
+ get color(): Float32Array {
239
+ const addr = base >>> 2;
240
+ return mem.f32.subarray(addr, addr + 4);
241
+ },
242
+ get bars(): Bar[] {
243
+ const addr = (base + 16);
244
+ const inst = $Bar(mem);
245
+ const slice: Bar[] = [];
246
+ for(let i = 0; i < 3; i++) slice.push(inst.instance(addr + i * 24));
247
+ return slice;
248
+ },
249
+ get id(): number {
250
+ return mem.u8[(base + 40)];
251
+ },
252
+ set id(x: number) {
253
+ mem.u8[(base + 40)] = x;
254
+ },
255
+ };
256
+ }
234
257
  });
235
258
 
236
259
  export interface Bar extends WasmTypeBase {
@@ -239,24 +262,34 @@ export interface Bar extends WasmTypeBase {
239
262
  }
240
263
 
241
264
  export const $Bar: WasmTypeConstructor<Bar> = (mem) => ({
242
- get align() { return 4; },
243
- get size() { return 8; },
244
- instance: (base) => ({
245
- get __base() { return base; },
246
- get __bytes() { return mem.u8.subarray(base, base + 8); },
247
- get kind(): Kind {
248
- return mem.u16[base >>> 1];
249
- },
250
- set kind(x: Kind) {
251
- mem.u16[base >>> 1] = x;
252
- },
253
- get flags(): number {
254
- return mem.u32[(base + 4) >>> 2];
255
- },
256
- set flags(x: number) {
257
- mem.u32[(base + 4) >>> 2] = x;
258
- },
259
- })
265
+ get align() {
266
+ return 4;
267
+ },
268
+ get size() {
269
+ return 8;
270
+ },
271
+ instance: (base) => {
272
+ return {
273
+ get __base() {
274
+ return base;
275
+ },
276
+ get __bytes() {
277
+ return mem.u8.subarray(base, base + 8);
278
+ },
279
+ get kind(): Kind {
280
+ return mem.u16[base >>> 1];
281
+ },
282
+ set kind(x: Kind) {
283
+ mem.u16[base >>> 1] = x;
284
+ },
285
+ get flags(): number {
286
+ return mem.u32[(base + 4) >>> 2];
287
+ },
288
+ set flags(x: number) {
289
+ mem.u32[(base + 4) >>> 2] = x;
290
+ },
291
+ };
292
+ }
260
293
  });
261
294
 
262
295
  export enum Kind {
@@ -270,7 +303,9 @@ export enum Kind {
270
303
  <details><summary>generated.zig (generated Zig source)</summary>
271
304
 
272
305
  ```zig
273
- //! Generated by @thi.ng/wasm-api at 2022-08-15T22:32:21.191Z - DO NOT EDIT!
306
+ //! Generated by @thi.ng/wasm-api at 2022-10-03T15:32:12.341Z - DO NOT EDIT!
307
+
308
+ const std = @import("std");
274
309
 
275
310
  /// Example struct
276
311
  pub const Foo = struct {
@@ -286,9 +321,9 @@ pub const Bar = struct {
286
321
  };
287
322
 
288
323
  pub const Kind = enum(u16) {
289
- unknown,
290
- good = 100,
291
- best = 1000,
324
+ UNKNOWN,
325
+ GOOD = 100,
326
+ BEST = 1000,
292
327
  };
293
328
  ```
294
329
  </details>
@@ -538,6 +573,10 @@ canvases.delete(0);
538
573
 
539
574
  [Search or submit any issues for this package](https://github.com/thi-ng/umbrella/issues?q=%5Bwasm-api%5D+in%3Atitle)
540
575
 
576
+ ## Support packages
577
+
578
+ - [@thi.ng/wasm-api-dom](https://github.com/thi-ng/umbrella/tree/develop/packages/wasm-api-dom) - Browser DOM bridge API for hybrid TypeScript & Zig applications
579
+
541
580
  ## Installation
542
581
 
543
582
  ```bash
@@ -561,7 +600,7 @@ node --experimental-repl-await
561
600
  > const wasmApi = await import("@thi.ng/wasm-api");
562
601
  ```
563
602
 
564
- Package sizes (gzipped, pre-treeshake): ESM: 6.05 KB
603
+ Package sizes (gzipped, pre-treeshake): ESM: 6.29 KB
565
604
 
566
605
  **IMPORTANT:** The package includes code generators for various languages which
567
606
  are **not** required for just using the API bridge. Hence, the usual package
@@ -581,6 +620,19 @@ size in production will be MUCH smaller than what's stated here!
581
620
  - [@thi.ng/idgen](https://github.com/thi-ng/umbrella/tree/develop/packages/idgen)
582
621
  - [@thi.ng/logger](https://github.com/thi-ng/umbrella/tree/develop/packages/logger)
583
622
  - [@thi.ng/paths](https://github.com/thi-ng/umbrella/tree/develop/packages/paths)
623
+ - [@thi.ng/strings](https://github.com/thi-ng/umbrella/tree/develop/packages/strings)
624
+
625
+ ## Usage examples
626
+
627
+ Several demos in this repo's
628
+ [/examples](https://github.com/thi-ng/umbrella/tree/develop/examples)
629
+ directory are using this package.
630
+
631
+ A selection:
632
+
633
+ | Screenshot | Description | Live demo | Source |
634
+ |:------------------------------------------------------------------------------------------------------------------|:--------------------------------------------|:-------------------------------------------------|:------------------------------------------------------------------------------|
635
+ | <img src="https://raw.githubusercontent.com/thi-ng/umbrella/develop/assets/examples/zig-canvas.png" width="240"/> | Zig-based DOM creation & canvas drawing app | [Demo](https://demo.thi.ng/umbrella/zig-canvas/) | [Source](https://github.com/thi-ng/umbrella/tree/develop/examples/zig-canvas) |
584
636
 
585
637
  ## API
586
638
 
package/api.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { BigType, FloatType, Fn } from "@thi.ng/api";
1
+ import type { BigType, FloatType, Fn, IDeref, ILength, IObjectOf } from "@thi.ng/api";
2
2
  import type { WasmBridge } from "./bridge.js";
3
3
  export declare const PKG_NAME = "@thi.ng/wasm-api";
4
4
  export declare const EVENT_MEMORY_CHANGED = "memory-changed";
@@ -175,6 +175,9 @@ export declare type WasmUint = "u8" | "u16" | "u32" | "u64";
175
175
  export declare type WasmFloat = FloatType;
176
176
  export declare type WasmPrim = WasmInt | WasmUint | WasmFloat;
177
177
  export declare type WasmPrim32 = Exclude<WasmPrim, BigType>;
178
+ export declare type ReadonlyWasmString = IDeref<string> & ILength & {
179
+ readonly addr: number;
180
+ };
178
181
  export declare type TypeColl = Record<string, TopLevelType>;
179
182
  export interface TypeInfo {
180
183
  /**
@@ -209,6 +212,14 @@ export interface TopLevelType extends TypeInfo {
209
212
  * Type / kind
210
213
  */
211
214
  type: "struct" | "enum";
215
+ /**
216
+ * Optional object of user provided source codes to be injected into the
217
+ * generated type. Keys are language IDs (same name as respective codegen).
218
+ *
219
+ * @remarks
220
+ * Currently only supported by the {@link ZIG} codegen, ignored otherwise.
221
+ */
222
+ body?: IObjectOf<string>;
212
223
  }
213
224
  export interface Struct extends TopLevelType {
214
225
  type: "struct";
@@ -276,7 +287,8 @@ export interface StructField extends TypeInfo {
276
287
  */
277
288
  const?: boolean;
278
289
  /**
279
- * TODO currently unsupported & ignored!
290
+ * Currently only supported for {@link ZIG} arrays & slices, otherwise
291
+ * ignored!
280
292
  */
281
293
  sentinel?: number;
282
294
  /**
@@ -284,9 +296,10 @@ export interface StructField extends TypeInfo {
284
296
  */
285
297
  len?: number;
286
298
  /**
287
- * TODO currently unsupported & ignored!
299
+ * Currently only supported for {@link ZIG} scalar & string values,
300
+ * otherwise ignored!
288
301
  */
289
- default?: any;
302
+ default?: number;
290
303
  /**
291
304
  * If defined and > 0, the field will be considered for padding purposes only and
292
305
  * the value provided is the number of bytes used.
@@ -363,6 +376,12 @@ export interface CodeGenOpts extends CodeGenOptsBase {
363
376
  * @defaultValue false
364
377
  */
365
378
  debug: boolean;
379
+ /**
380
+ * Target line width for word wrapping doc strings
381
+ *
382
+ * @defaultValue 80
383
+ */
384
+ lineWidth: number;
366
385
  }
367
386
  export interface ICodeGen {
368
387
  /**
@@ -376,7 +395,7 @@ export interface ICodeGen {
376
395
  /**
377
396
  * Docstring codegen
378
397
  */
379
- doc: (doc: string, acc: string[], topLevel?: boolean) => void;
398
+ doc: (doc: string, acc: string[], opts: CodeGenOpts, topLevel?: boolean) => void;
380
399
  /**
381
400
  * Codegen for enum types.
382
401
  */
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { spawn } from "child_process";
4
+ import { realpathSync } from "fs";
5
+ import { join } from "path";
6
+ const [node, toolsDir, ...args] = process.argv;
7
+
8
+ const cli = join(realpathSync(toolsDir), "..", "..", "cli.js");
9
+
10
+ const child = spawn(node, ["--loader", "ts-node/esm", cli, ...args]);
11
+
12
+ child.stderr.on("data", (d) => {
13
+ const dStr = d.toString().trim();
14
+ !dStr.includes("ExperimentalWarning") ? console.log(dStr) : "";
15
+ });
16
+ child.on("exit", (err) => process.exit(err));
17
+ child.stdout.pipe(process.stdout);
package/bridge.js CHANGED
@@ -30,7 +30,7 @@ let WasmBridge = class WasmBridge {
30
30
  this.utf8Decoder = new TextDecoder();
31
31
  this.utf8Encoder = new TextEncoder();
32
32
  const logN = (x) => this.logger.debug(x);
33
- const logA = (method) => (addr, len) => this.logger.debug(method(addr, len).join(", "));
33
+ const logA = (method) => (addr, len) => this.logger.debug(() => method(addr, len).join(", "));
34
34
  this.api = {
35
35
  printI8: logN,
36
36
  printU8: logN,
@@ -42,10 +42,10 @@ let WasmBridge = class WasmBridge {
42
42
  printU64: (x) => this.logger.debug(x),
43
43
  printF32: logN,
44
44
  printF64: logN,
45
- printU8Hex: (x) => this.logger.debug(`0x${U8(x)}`),
46
- printU16Hex: (x) => this.logger.debug(`0x${U16(x)}`),
47
- printU32Hex: (x) => this.logger.debug(`0x${U32(x)}`),
48
- printU64Hex: (x) => this.logger.debug(`0x${U64BIG(x)}`),
45
+ printU8Hex: (x) => this.logger.debug(() => `0x${U8(x)}`),
46
+ printU16Hex: (x) => this.logger.debug(() => `0x${U16(x)}`),
47
+ printU32Hex: (x) => this.logger.debug(() => `0x${U32(x)}`),
48
+ printU64Hex: (x) => this.logger.debug(() => `0x${U64BIG(x)}`),
49
49
  _printI8Array: logA(this.getI8Array.bind(this)),
50
50
  _printU8Array: logA(this.getU8Array.bind(this)),
51
51
  _printI16Array: logA(this.getI16Array.bind(this)),
@@ -56,8 +56,8 @@ let WasmBridge = class WasmBridge {
56
56
  _printU64Array: logA(this.getU64Array.bind(this)),
57
57
  _printF32Array: logA(this.getF32Array.bind(this)),
58
58
  _printF64Array: logA(this.getF64Array.bind(this)),
59
- _printStr0: (addr) => this.logger.debug(this.getString(addr, 0)),
60
- _printStr: (addr, len) => this.logger.debug(this.getString(addr, len)),
59
+ _printStr0: (addr) => this.logger.debug(() => this.getString(addr, 0)),
60
+ _printStr: (addr, len) => this.logger.debug(() => this.getString(addr, len)),
61
61
  debug: () => {
62
62
  debugger;
63
63
  },
@@ -215,7 +215,7 @@ let WasmBridge = class WasmBridge {
215
215
  const addr = this.exports._wasm_allocate(numBytes);
216
216
  if (!addr)
217
217
  throw new OutOfMemoryError(`unable to allocate: ${numBytes}`);
218
- this.logger.debug(`allocated ${numBytes} bytes @ 0x${U32(addr)} .. 0x${U32(addr + numBytes - 1)}`);
218
+ this.logger.fine(() => `allocated ${numBytes} bytes @ 0x${U32(addr)} .. 0x${U32(addr + numBytes - 1)}`);
219
219
  this.ensureMemory();
220
220
  clear && this.u8.fill(0, addr, addr + numBytes);
221
221
  return addr;
@@ -234,7 +234,7 @@ let WasmBridge = class WasmBridge {
234
234
  * @param numBytes
235
235
  */
236
236
  free(addr, numBytes) {
237
- this.logger.debug(`freeing memory @ 0x${U32(addr)} .. 0x${U32(addr + numBytes - 1)}`);
237
+ this.logger.fine(() => `freeing memory @ 0x${U32(addr)} .. 0x${U32(addr + numBytes - 1)}`);
238
238
  this.exports._wasm_free(addr, numBytes);
239
239
  }
240
240
  getI8(addr) {
package/cli.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import { flag, oneOf, oneOfMulti, parse, ParseError, string, strings, usage, } from "@thi.ng/args";
2
2
  import { isArray, isPlainObject } from "@thi.ng/checks";
3
3
  import { illegalArgs } from "@thi.ng/errors";
4
- import { mutIn } from "@thi.ng/paths/mut-in";
5
- import { readJSON, readText, writeText } from "@thi.ng/file-io";
4
+ import { readJSON, readText, writeJSON, writeText } from "@thi.ng/file-io";
6
5
  import { ConsoleLogger } from "@thi.ng/logger";
7
- import { resolve } from "path";
6
+ import { mutIn } from "@thi.ng/paths";
7
+ import { dirname, resolve } from "path";
8
8
  import { generateTypes } from "./codegen.js";
9
9
  import { C11 } from "./codegen/c11.js";
10
10
  import { TYPESCRIPT } from "./codegen/typescript.js";
@@ -12,6 +12,11 @@ import { isPadding, isWasmPrim, isWasmString } from "./codegen/utils.js";
12
12
  import { ZIG } from "./codegen/zig.js";
13
13
  const GENERATORS = { c11: C11, ts: TYPESCRIPT, zig: ZIG };
14
14
  const argOpts = {
15
+ analytics: string({
16
+ alias: "a",
17
+ hint: "FILE",
18
+ desc: "output file path for raw codegen analytics",
19
+ }),
15
20
  config: string({
16
21
  alias: "c",
17
22
  hint: "FILE",
@@ -70,6 +75,16 @@ const addTypeSpec = (ctx, path, coll, spec) => {
70
75
  invalidSpec(path, `${spec.name} type: ${spec.type}`);
71
76
  if (coll[spec.name])
72
77
  invalidSpec(path, `duplicate name: ${spec.name}`);
78
+ if (spec.body) {
79
+ if (!isPlainObject(spec.body))
80
+ invalidSpec(path, `${spec.name}.body must be an object`);
81
+ for (let lang in spec.body) {
82
+ const src = spec.body[lang];
83
+ if (src[0] === "@") {
84
+ spec.body[lang] = readText(src.substring(1), ctx.logger);
85
+ }
86
+ }
87
+ }
73
88
  ctx.logger.debug(`registering ${spec.type}: ${spec.name}`);
74
89
  coll[spec.name] = spec;
75
90
  spec.__path = path;
@@ -141,20 +156,26 @@ try {
141
156
  opts,
142
157
  };
143
158
  if (opts.config) {
144
- ctx.config = readJSON(resolve(opts.config), ctx.logger);
159
+ opts.config = resolve(opts.config);
160
+ ctx.config = readJSON(opts.config, ctx.logger);
145
161
  for (let id in ctx.config) {
146
162
  const conf = ctx.config[id];
147
163
  if (conf.pre && conf.pre[0] === "@") {
148
- conf.pre = readText(conf.pre.substring(1), ctx.logger);
164
+ conf.pre = readText(resolve(dirname(opts.config), conf.pre.substring(1)), ctx.logger);
149
165
  }
150
166
  if (conf.post && conf.post[0] === "@") {
151
- conf.post = readText(conf.post.substring(1), ctx.logger);
167
+ conf.post = readText(resolve(dirname(opts.config), conf.post.substring(1)), ctx.logger);
152
168
  }
153
169
  }
154
170
  }
155
171
  opts.debug && mutIn(ctx, ["config", "global", "debug"], true);
156
172
  opts.string && mutIn(ctx, ["config", "global", "stringType"], opts.string);
157
- generateOutputs(ctx, parseTypeSpecs(ctx, rest));
173
+ const types = parseTypeSpecs(ctx, rest);
174
+ generateOutputs(ctx, types);
175
+ if (ctx.opts.analytics) {
176
+ // always write analytics, even if dry run
177
+ writeJSON(resolve(ctx.opts.analytics), types, undefined, "\t", ctx.logger);
178
+ }
158
179
  }
159
180
  catch (e) {
160
181
  if (!(e instanceof ParseError))
package/codegen/c11.js CHANGED
@@ -32,12 +32,17 @@ export const C11 = (opts = {}) => {
32
32
  const SCOPES = [/\{$/, /^\}[ A-Za-z0-9_]*[;,]?$/];
33
33
  const gen = {
34
34
  pre: (opts) => `#pragma once
35
+
36
+ #ifdef __cplusplus
37
+ extern "C" {
38
+ #endif
39
+
35
40
  ${opts.debug ? "\n#include <stdalign.h>" : ""}
36
41
  #include <stddef.h>
37
42
  #include <stdint.h>${opts.pre ? `\n${opts.pre}` : ""}`,
38
- post: () => opts.post || "",
39
- doc: (doc, acc) => {
40
- acc.push(prefixLines("// ", doc));
43
+ post: () => `${opts.post ? `${opts.post}\n` : ""}#ifdef __cplusplus\n}\n#endif`,
44
+ doc: (doc, acc, opts) => {
45
+ acc.push(...prefixLines("// ", doc, opts.lineWidth));
41
46
  },
42
47
  enum: (e, _, acc, opts) => {
43
48
  if (!(e.tag === "i32" || e.tag === "u32")) {
@@ -49,7 +54,7 @@ ${opts.debug ? "\n#include <stdalign.h>" : ""}
49
54
  for (let v of e.values) {
50
55
  let line;
51
56
  if (!isString(v)) {
52
- v.doc && gen.doc(v.doc, lines);
57
+ v.doc && gen.doc(v.doc, lines, opts);
53
58
  line = enumName(opts, v.name);
54
59
  if (v.value != null)
55
60
  line += ` = ${v.value}`;
@@ -74,7 +79,7 @@ ${opts.debug ? "\n#include <stdalign.h>" : ""}
74
79
  res.push(`uint8_t __pad${padID++}[${f.pad}];`);
75
80
  continue;
76
81
  }
77
- f.doc && gen.doc(f.doc, res);
82
+ f.doc && gen.doc(f.doc, res, opts);
78
83
  const fconst = f.const ? "const " : "";
79
84
  let ftype = f.type === "string"
80
85
  ? isStringSlice(opts.stringType)
@@ -24,13 +24,8 @@ export const TYPESCRIPT = (opts = {}) => {
24
24
  pre: (opts) => `// @ts-ignore possibly includes unused imports
25
25
  import { Pointer, ${__stringImpl(opts)}, WasmTypeBase, WasmTypeConstructor } from "${PKG_NAME}";${opts.pre ? `\n${opts.pre}` : ""}`,
26
26
  post: () => opts.post || "",
27
- doc: (doc, acc) => {
28
- if (doc.indexOf("\n") !== -1) {
29
- acc.push("/**", prefixLines(indent + " * ", doc), " */");
30
- }
31
- else {
32
- acc.push(`/** ${doc} */`);
33
- }
27
+ doc: (doc, acc, opts) => {
28
+ acc.push("/**", ...prefixLines(" * ", doc, opts.lineWidth), " */");
34
29
  },
35
30
  enum: (e, _, acc, opts) => {
36
31
  const res = [];
@@ -38,7 +33,7 @@ import { Pointer, ${__stringImpl(opts)}, WasmTypeBase, WasmTypeConstructor } fro
38
33
  for (let v of e.values) {
39
34
  let line;
40
35
  if (!isString(v)) {
41
- v.doc && gen.doc(v.doc, res);
36
+ v.doc && gen.doc(v.doc, res, opts);
42
37
  line = enumName(opts, v.name);
43
38
  if (v.value != null)
44
39
  line += ` = ${v.value}`;
@@ -60,7 +55,7 @@ import { Pointer, ${__stringImpl(opts)}, WasmTypeBase, WasmTypeConstructor } fro
60
55
  for (let f of struct.fields) {
61
56
  if (isPadding(f))
62
57
  continue;
63
- f.doc && gen.doc(f.doc, lines);
58
+ f.doc && gen.doc(f.doc, lines, opts);
64
59
  const ftype = __fieldType(f, opts);
65
60
  fieldTypes[f.name] = ftype;
66
61
  lines.push(`${f.name}: ${ftype};`);
@@ -21,13 +21,15 @@ export declare const isWasmPrim: (x: string) => x is WasmPrim;
21
21
  export declare const isWasmString: (x: string) => x is "string";
22
22
  export declare const isPadding: (f: StructField) => boolean;
23
23
  /**
24
- * Takes an array of strings or splits given string into lines, prefixes each
25
- * line with given `prefix` and then returns rejoined result.
24
+ * Takes an array of strings or splits given string into lines, word wraps and
25
+ * then prefixes each line with given `width` and `prefix`. Returns array of new
26
+ * lines.
26
27
  *
27
28
  * @param prefix
28
29
  * @param str
30
+ * @param width
29
31
  */
30
- export declare const prefixLines: (prefix: string, str: string | string[]) => string;
32
+ export declare const prefixLines: (prefix: string, str: string | string[], width: number) => string[];
31
33
  /**
32
34
  * Returns true if `type` is "slice".
33
35
  *
package/codegen/utils.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { isString } from "@thi.ng/checks/is-string";
2
+ import { wordWrapLine, wordWrapLines } from "@thi.ng/strings/word-wrap";
2
3
  /**
3
4
  * Returns true iff `x` is a {@link WasmPrim32}.
4
5
  *
@@ -20,15 +21,17 @@ export const isWasmPrim = (x) => isNumeric(x) || isBigNumeric(x);
20
21
  export const isWasmString = (x) => x === "string";
21
22
  export const isPadding = (f) => f.pad != null && f.pad > 0;
22
23
  /**
23
- * Takes an array of strings or splits given string into lines, prefixes each
24
- * line with given `prefix` and then returns rejoined result.
24
+ * Takes an array of strings or splits given string into lines, word wraps and
25
+ * then prefixes each line with given `width` and `prefix`. Returns array of new
26
+ * lines.
25
27
  *
26
28
  * @param prefix
27
29
  * @param str
30
+ * @param width
28
31
  */
29
- export const prefixLines = (prefix, str) => (isString(str) ? str.split("\n") : str)
30
- .map((line) => prefix + line)
31
- .join("\n");
32
+ export const prefixLines = (prefix, str, width) => (isString(str)
33
+ ? wordWrapLines(str, { width: width - prefix.length })
34
+ : str.flatMap((x) => wordWrapLine(x, { width: width - prefix.length }))).map((line) => prefix + line);
32
35
  /**
33
36
  * Returns true if `type` is "slice".
34
37
  *