@thi.ng/wasm-api 0.9.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/api.d.ts CHANGED
@@ -1,6 +1,7 @@
1
- import type { BigType, FloatType, Fn, Fn2 } from "@thi.ng/api";
1
+ import type { BigType, FloatType, Fn } from "@thi.ng/api";
2
2
  import type { WasmBridge } from "./bridge.js";
3
3
  export declare const PKG_NAME = "@thi.ng/wasm-api";
4
+ export declare const EVENT_MEMORY_CHANGED = "memory-changed";
4
5
  export declare type BigIntArray = bigint[] | BigInt64Array | BigUint64Array;
5
6
  /**
6
7
  * Common interface for WASM/JS child APIs which will be used in combination
@@ -27,9 +28,9 @@ export interface IWasmAPI<T extends WasmExports = WasmExports> {
27
28
  getImports(): WebAssembly.ModuleImports;
28
29
  }
29
30
  /**
30
- * Base interface of exports declared by the WASM module. At the very least, the
31
- * module needs to export its memory and the functions defined in this
32
- * interface.
31
+ * Base interface of exports declared by the WASM module. At the very least, a
32
+ * compatible module needs to export its memory and the functions defined in
33
+ * this interface.
33
34
  *
34
35
  * @remarks
35
36
  * This interface is supposed to be extended with the concrete exports defined
@@ -45,21 +46,34 @@ export interface WasmExports {
45
46
  */
46
47
  memory: WebAssembly.Memory;
47
48
  /**
48
- * Implementation specific memory allocation function (likely heap-based).
49
- * If successful returns address of new memory block, or zero if
50
- * unsuccessful.
49
+ * Implementation specific WASM memory allocation function. If successful
50
+ * returns address of new memory block, or zero if unsuccessful.
51
51
  *
52
52
  * @remarks
53
- * In the supplied Zig bindings (see `/zig/core.zig`), by default this is
54
- * using the `std.heap.GeneralPurposeAllocator` (which also automatically
55
- * handles growing the WASM memory), however as mentioned the underlying
56
- * mechanism is purposefully left to the actual WASM-side implementation. In
57
- * a C program, this would likely use `malloc()` or similar...
53
+ * #### Zig
54
+ *
55
+ * Using the supplied Zig bindings (see `/include/wasmapi.zig`), it's the
56
+ * user's responsibility to define a public `WASM_ALLOCATOR` in the root
57
+ * source file to enable allocations, e.g. using the
58
+ * [`std.heap.GeneralPurposeAllocator`](https://ziglang.org/documentation/master/#Choosing-an-Allocator)
59
+ * (which also automatically handles growing the WASM memory). However, as
60
+ * mentioned, the underlying mechanism is purposefully left to the actual
61
+ * WASM-side implementation. If no allocator is defined this function
62
+ * returns zero, which in turn will cause {@link WasmBridge.allocate} to
63
+ * throw an error.
64
+ *
65
+ * #### C/C++
66
+ *
67
+ * Using the supplied C bindings (see `/include/wasmapi.h`), it's the user's
68
+ * responsibility to enable allocation support by defining the
69
+ * `WASMAPI_MALLOC` symbol (and compiling the WASM module with a malloc
70
+ * implementation).
58
71
  */
59
72
  _wasm_allocate(numBytes: number): number;
60
73
  /**
61
74
  * Implementation specific function to free a previously allocated chunk of
62
- * of WASM memory (allocated via {@link WasmExports._wasm_allocate}).
75
+ * of WASM memory (allocated via {@link WasmExports._wasm_allocate}, also
76
+ * see remarks for that function).
63
77
  *
64
78
  * @param addr
65
79
  * @param numBytes
@@ -118,9 +132,9 @@ export interface CoreAPI extends WebAssembly.ModuleImports {
118
132
  printI32: Fn<number, void>;
119
133
  printU32: Fn<number, void>;
120
134
  printU32Hex: Fn<number, void>;
121
- _printI64: Fn2<number, number, void>;
122
- _printU64: Fn2<number, number, void>;
123
- _printU64Hex: Fn2<number, number, void>;
135
+ printI64: Fn<bigint, void>;
136
+ printU64: Fn<bigint, void>;
137
+ printU64Hex: Fn<bigint, void>;
124
138
  printF32: Fn<number, void>;
125
139
  printF64: Fn<number, void>;
126
140
  _printI8Array: (addr: number, len: number) => void;
@@ -136,6 +150,9 @@ export interface CoreAPI extends WebAssembly.ModuleImports {
136
150
  _printStr0: (addr: number) => void;
137
151
  _printStr: (addr: number, len: number) => void;
138
152
  debug: () => void;
153
+ _panic: (addr: number, len: number) => void;
154
+ timer: () => number;
155
+ epoch: () => bigint;
139
156
  }
140
157
  export interface WasmTypeBase {
141
158
  /**
@@ -203,11 +220,18 @@ export interface Struct extends TopLevelType {
203
220
  /**
204
221
  * If true, struct fields will be re-ordered in descending order based on
205
222
  * their {@link TypeInfo.__align} size. This might result in overall smaller
206
- * structs due to minimizing inter-field padding.
223
+ * structs due to minimizing implicit inter-field padding caused by
224
+ * alignment requirements. **If this option is enabled, then the struct MUST
225
+ * NOT contain any padding fields!**
207
226
  *
208
227
  * @defaultValue false
209
228
  */
210
229
  auto?: boolean;
230
+ /**
231
+ * Optional qualifier for the kind of struct to be emitted (codegen specific
232
+ * interpretation, currently only used by {@link ZIG}).
233
+ */
234
+ tag?: "extern" | "packed";
211
235
  }
212
236
  export interface StructField extends TypeInfo {
213
237
  /**
@@ -246,6 +270,11 @@ export interface StructField extends TypeInfo {
246
270
  * TODO `opaque` currently unsupported.
247
271
  */
248
272
  type: WasmPrim | "string" | "opaque" | string;
273
+ /**
274
+ * Const qualifier (default is true for `string`, false for all other
275
+ * types). Only used for pointers or slices.
276
+ */
277
+ const?: boolean;
249
278
  /**
250
279
  * TODO currently unsupported & ignored!
251
280
  */
@@ -258,11 +287,19 @@ export interface StructField extends TypeInfo {
258
287
  * TODO currently unsupported & ignored!
259
288
  */
260
289
  default?: any;
290
+ /**
291
+ * If defined and > 0, the field will be considered for padding purposes only and
292
+ * the value provided is the number of bytes used.
293
+ */
294
+ pad?: number;
261
295
  }
262
296
  export interface Enum extends TopLevelType {
263
297
  type: "enum";
264
298
  /**
265
- * No i64/u64 support, due to Typescript not supporting bigint enum values
299
+ * No i64/u64 support, due to Typescript not supporting bigint enum values.
300
+ * For C compatibility only i32 or u32 is allowed.
301
+ *
302
+ * @defaultValue "i32"
266
303
  */
267
304
  tag: Exclude<WasmPrim32, FloatType>;
268
305
  /**
@@ -285,27 +322,69 @@ export interface EnumValue {
285
322
  */
286
323
  doc?: string;
287
324
  }
325
+ export interface CodeGenOptsBase {
326
+ /**
327
+ * Optional string to be injected before generated type defs (but after
328
+ * codegen's own prelude, if any)
329
+ */
330
+ pre: string;
331
+ /**
332
+ * Optional string to be injected after generated type defs (but before
333
+ * codegen's own epilogue, if any)
334
+ */
335
+ post: string;
336
+ }
337
+ /**
338
+ * Global/shared code generator options.
339
+ */
340
+ export interface CodeGenOpts extends CodeGenOptsBase {
341
+ /**
342
+ * Identifier how strings are stored on WASM side, e.g. in Zig string
343
+ * literals are slices (8 bytes), in C just plain pointers (4 bytes).
344
+ *
345
+ * @defaultValue "slice"
346
+ */
347
+ stringType: "slice" | "ptr";
348
+ /**
349
+ * If true (default), forces uppercase enum identifiers
350
+ *
351
+ * @defaultValue true
352
+ */
353
+ uppercaseEnums: boolean;
354
+ /**
355
+ * Unless set to false, the generated output will be prefixed with a header
356
+ * line comment of generator meta data
357
+ */
358
+ header: boolean;
359
+ /**
360
+ * If true, codegens MAY generate various additional struct & struct field
361
+ * analysis functions (sizes, alignment, offsets etc.).
362
+ *
363
+ * @defaultValue false
364
+ */
365
+ debug: boolean;
366
+ }
288
367
  export interface ICodeGen {
289
368
  /**
290
369
  * Optional prelude source, to be prepended before any generated type defs.
291
370
  */
292
- pre?: string;
371
+ pre?: Fn<CodeGenOpts, string>;
293
372
  /**
294
373
  * Optional source code to be appended after any generated type defs.
295
374
  */
296
- post?: string;
375
+ post?: Fn<CodeGenOpts, string>;
297
376
  /**
298
377
  * Docstring codegen
299
378
  */
300
- doc: (doc: string, indent: string, acc: string[], topLevel?: boolean) => void;
379
+ doc: (doc: string, acc: string[], topLevel?: boolean) => void;
301
380
  /**
302
381
  * Codegen for enum types.
303
382
  */
304
- enum: (type: Enum, types: TypeColl, acc: string[]) => void;
383
+ enum: (type: Enum, types: TypeColl, acc: string[], opts: CodeGenOpts) => void;
305
384
  /**
306
385
  * Codegen for struct types.
307
386
  */
308
- struct: (type: Struct, types: TypeColl, acc: string[]) => void;
387
+ struct: (type: Struct, types: TypeColl, acc: string[], opts: CodeGenOpts) => void;
309
388
  }
310
389
  /**
311
390
  * WASM usize type. Assuming wasm32 until wasm64 surfaces, then need an option.
package/api.js CHANGED
@@ -1,4 +1,5 @@
1
1
  export const PKG_NAME = "@thi.ng/wasm-api";
2
+ export const EVENT_MEMORY_CHANGED = "memory-changed";
2
3
  /**
3
4
  * WASM usize type. Assuming wasm32 until wasm64 surfaces, then need an option.
4
5
  */
package/bridge.d.ts CHANGED
@@ -1,7 +1,17 @@
1
1
  /// <reference types="node" />
2
- import type { NumericArray } from "@thi.ng/api";
2
+ import type { Event, INotify, Listener, NumericArray } from "@thi.ng/api";
3
3
  import type { ILogger } from "@thi.ng/logger";
4
- import type { BigIntArray, CoreAPI, IWasmAPI, WasmExports, IWasmMemoryAccess } from "./api.js";
4
+ import { BigIntArray, CoreAPI, IWasmAPI, IWasmMemoryAccess, WasmExports } from "./api.js";
5
+ export declare const Panic: {
6
+ new (msg?: string | undefined): {
7
+ name: string;
8
+ message: string;
9
+ stack?: string | undefined;
10
+ };
11
+ captureStackTrace(targetObject: object, constructorOpt?: Function | undefined): void;
12
+ prepareStackTrace?: ((err: Error, stackTraces: NodeJS.CallSite[]) => any) | undefined;
13
+ stackTraceLimit: number;
14
+ };
5
15
  export declare const OutOfMemoryError: {
6
16
  new (msg?: string | undefined): {
7
17
  name: string;
@@ -28,7 +38,7 @@ export declare const OutOfMemoryError: {
28
38
  * 64bit integers are handled via JS `BigInt` and hence require the host env to
29
39
  * support it. No polyfill is provided.
30
40
  */
31
- export declare class WasmBridge<T extends WasmExports = WasmExports> implements IWasmMemoryAccess {
41
+ export declare class WasmBridge<T extends WasmExports = WasmExports> implements IWasmMemoryAccess, INotify {
32
42
  modules: Record<string, IWasmAPI<T>>;
33
43
  logger: ILogger;
34
44
  i8: Int8Array;
@@ -66,14 +76,23 @@ export declare class WasmBridge<T extends WasmExports = WasmExports> implements
66
76
  * then initializes all declared bridge child API modules. Returns false if
67
77
  * any of the module initializations failed.
68
78
  *
79
+ * @remarks
80
+ * Emits the {@link EVENT_MEMORY_CHANGED} event just before returning (and
81
+ * AFTER all child API modules have been initialized).
82
+ *
69
83
  * @param exports
70
84
  */
71
85
  init(exports: T): Promise<boolean>;
72
86
  /**
73
- * Called automatically. Initializes and/or updates the various typed WASM
74
- * memory views (e.g. after growing the WASM memory).
87
+ * Called automatically during initialization. Initializes and/or updates
88
+ * the various typed WASM memory views (e.g. after growing the WASM memory
89
+ * and the previous buffer becoming detached). Unless `notify` is false,
90
+ * the {@link EVENT_MEMORY_CHANGED} event will be emitted if the memory
91
+ * views had to be updated.
92
+ *
93
+ * @param notify
75
94
  */
76
- ensureMemory(): void;
95
+ ensureMemory(notify?: boolean): void;
77
96
  /**
78
97
  * Required use for WASM module instantiation to provide JS imports to the
79
98
  * module. Returns an object of all WASM imports declared by the bridge core
@@ -137,6 +156,10 @@ export declare class WasmBridge<T extends WasmExports = WasmExports> implements
137
156
  * `numBytes` value must be the same as previously given to
138
157
  * {@link WasmBridge.allocate}.
139
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
+ *
140
163
  * @param addr
141
164
  * @param numBytes
142
165
  */
@@ -208,5 +231,11 @@ export declare class WasmBridge<T extends WasmExports = WasmExports> implements
208
231
  */
209
232
  setString(str: string, addr: number, maxBytes: number, terminate?: boolean): number;
210
233
  getElementById(addr: number, len?: number): HTMLElement;
234
+ /** {@inheritDoc @thi.ng/api#INotify.addListener} */
235
+ addListener(id: string, fn: Listener, scope?: any): boolean;
236
+ /** {@inheritDoc @thi.ng/api#INotify.removeListener} */
237
+ removeListener(id: string, fn: Listener, scope?: any): boolean;
238
+ /** {@inheritDoc @thi.ng/api#INotify.notify} */
239
+ notify(event: Event): void;
211
240
  }
212
241
  //# sourceMappingURL=bridge.d.ts.map
package/bridge.js CHANGED
@@ -1,8 +1,11 @@
1
+ import { __decorate } from "tslib";
2
+ import { INotifyMixin } from "@thi.ng/api/mixins/inotify";
1
3
  import { defError } from "@thi.ng/errors/deferror";
2
4
  import { illegalArgs } from "@thi.ng/errors/illegal-arguments";
3
- import { U16, U32, U64HL, U8 } from "@thi.ng/hex";
5
+ import { U16, U32, U64BIG, U8 } from "@thi.ng/hex";
4
6
  import { ConsoleLogger } from "@thi.ng/logger/console";
5
- const B32 = BigInt(32);
7
+ import { EVENT_MEMORY_CHANGED, } from "./api.js";
8
+ export const Panic = defError(() => "Panic");
6
9
  export const OutOfMemoryError = defError(() => "Out of memory");
7
10
  /**
8
11
  * The main interop API bridge between the JS host environment and a WebAssembly
@@ -20,7 +23,7 @@ export const OutOfMemoryError = defError(() => "Out of memory");
20
23
  * 64bit integers are handled via JS `BigInt` and hence require the host env to
21
24
  * support it. No polyfill is provided.
22
25
  */
23
- export class WasmBridge {
26
+ let WasmBridge = class WasmBridge {
24
27
  constructor(modules = {}, logger = new ConsoleLogger("wasm")) {
25
28
  this.modules = modules;
26
29
  this.logger = logger;
@@ -31,18 +34,18 @@ export class WasmBridge {
31
34
  this.api = {
32
35
  printI8: logN,
33
36
  printU8: logN,
34
- printU8Hex: (x) => this.logger.debug(`0x${U8(x)}`),
35
37
  printI16: logN,
36
38
  printU16: logN,
37
- printU16Hex: (x) => this.logger.debug(`0x${U16(x)}`),
38
39
  printI32: logN,
39
40
  printU32: (x) => this.logger.debug(x >>> 0),
40
- printU32Hex: (x) => this.logger.debug(`0x${U32(x)}`),
41
- _printI64: (hi, lo) => this.logger.debug((BigInt(hi) << B32) | BigInt(lo)),
42
- _printU64: (hi, lo) => this.logger.debug((BigInt(hi >>> 0) << B32) | BigInt(lo >>> 0)),
43
- _printU64Hex: (hi, lo) => this.logger.debug(`0x${U64HL(hi, lo)}`),
41
+ printI64: (x) => this.logger.debug(x),
42
+ printU64: (x) => this.logger.debug(x),
44
43
  printF32: logN,
45
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)}`),
46
49
  _printI8Array: logA(this.getI8Array.bind(this)),
47
50
  _printU8Array: logA(this.getU8Array.bind(this)),
48
51
  _printI16Array: logA(this.getI16Array.bind(this)),
@@ -58,6 +61,11 @@ export class WasmBridge {
58
61
  debug: () => {
59
62
  debugger;
60
63
  },
64
+ _panic: (addr, len) => {
65
+ throw new Panic(this.getString(addr, len));
66
+ },
67
+ timer: () => performance.now(),
68
+ epoch: () => BigInt(Date.now()),
61
69
  };
62
70
  }
63
71
  /**
@@ -86,24 +94,34 @@ export class WasmBridge {
86
94
  * then initializes all declared bridge child API modules. Returns false if
87
95
  * any of the module initializations failed.
88
96
  *
97
+ * @remarks
98
+ * Emits the {@link EVENT_MEMORY_CHANGED} event just before returning (and
99
+ * AFTER all child API modules have been initialized).
100
+ *
89
101
  * @param exports
90
102
  */
91
103
  async init(exports) {
92
104
  this.exports = exports;
93
- this.ensureMemory();
105
+ this.ensureMemory(false);
94
106
  for (let id in this.modules) {
95
107
  this.logger.debug(`initializing API module: ${id}`);
96
108
  const status = await this.modules[id].init(this);
97
109
  if (!status)
98
110
  return false;
99
111
  }
112
+ this.notify({ id: EVENT_MEMORY_CHANGED, value: this.exports.memory });
100
113
  return true;
101
114
  }
102
115
  /**
103
- * Called automatically. Initializes and/or updates the various typed WASM
104
- * memory views (e.g. after growing the WASM memory).
116
+ * Called automatically during initialization. Initializes and/or updates
117
+ * the various typed WASM memory views (e.g. after growing the WASM memory
118
+ * and the previous buffer becoming detached). Unless `notify` is false,
119
+ * the {@link EVENT_MEMORY_CHANGED} event will be emitted if the memory
120
+ * views had to be updated.
121
+ *
122
+ * @param notify
105
123
  */
106
- ensureMemory() {
124
+ ensureMemory(notify = true) {
107
125
  const buf = this.exports.memory.buffer;
108
126
  if (this.u8 && this.u8.buffer === buf)
109
127
  return;
@@ -117,6 +135,11 @@ export class WasmBridge {
117
135
  this.u64 = new BigUint64Array(buf);
118
136
  this.f32 = new Float32Array(buf);
119
137
  this.f64 = new Float64Array(buf);
138
+ notify &&
139
+ this.notify({
140
+ id: EVENT_MEMORY_CHANGED,
141
+ value: this.exports.memory,
142
+ });
120
143
  }
121
144
  /**
122
145
  * Required use for WASM module instantiation to provide JS imports to the
@@ -192,7 +215,7 @@ export class WasmBridge {
192
215
  const addr = this.exports._wasm_allocate(numBytes);
193
216
  if (!addr)
194
217
  throw new OutOfMemoryError(`unable to allocate: ${numBytes}`);
195
- this.logger.debug(`allocated ${numBytes} bytes @ 0x${U32(addr)}`);
218
+ this.logger.debug(`allocated ${numBytes} bytes @ 0x${U32(addr)} .. 0x${U32(addr + numBytes - 1)}`);
196
219
  this.ensureMemory();
197
220
  clear && this.u8.fill(0, addr, addr + numBytes);
198
221
  return addr;
@@ -203,6 +226,10 @@ export class WasmBridge {
203
226
  * `numBytes` value must be the same as previously given to
204
227
  * {@link WasmBridge.allocate}.
205
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
+ *
206
233
  * @param addr
207
234
  * @param numBytes
208
235
  */
@@ -403,4 +430,17 @@ export class WasmBridge {
403
430
  el == null && illegalArgs(`missing DOM element #${id}`);
404
431
  return el;
405
432
  }
406
- }
433
+ /** {@inheritDoc @thi.ng/api#INotify.addListener} */
434
+ // @ts-ignore: mixin
435
+ addListener(id, fn, scope) { }
436
+ /** {@inheritDoc @thi.ng/api#INotify.removeListener} */
437
+ // @ts-ignore: mixin
438
+ removeListener(id, fn, scope) { }
439
+ /** {@inheritDoc @thi.ng/api#INotify.notify} */
440
+ // @ts-ignore: mixin
441
+ notify(event) { }
442
+ };
443
+ WasmBridge = __decorate([
444
+ INotifyMixin
445
+ ], WasmBridge);
446
+ export { WasmBridge };
package/cli.js CHANGED
@@ -1,14 +1,16 @@
1
- import { flag, oneOfMulti, parse, ParseError, string, strings, usage, } from "@thi.ng/args";
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 { readJSON, writeText } from "@thi.ng/file-io";
4
+ import { mutIn } from "@thi.ng/paths/mut-in";
5
+ import { readJSON, readText, writeText } from "@thi.ng/file-io";
5
6
  import { ConsoleLogger } from "@thi.ng/logger";
6
7
  import { resolve } from "path";
7
8
  import { generateTypes } from "./codegen.js";
9
+ import { C11 } from "./codegen/c11.js";
8
10
  import { TYPESCRIPT } from "./codegen/typescript.js";
9
- import { isWasmPrim, isWasmString } from "./codegen/utils.js";
11
+ import { isPadding, isWasmPrim, isWasmString } from "./codegen/utils.js";
10
12
  import { ZIG } from "./codegen/zig.js";
11
- const GENERATORS = { ts: TYPESCRIPT, zig: ZIG };
13
+ const GENERATORS = { c11: C11, ts: TYPESCRIPT, zig: ZIG };
12
14
  const argOpts = {
13
15
  config: string({
14
16
  alias: "c",
@@ -27,6 +29,11 @@ const argOpts = {
27
29
  delim: ",",
28
30
  }),
29
31
  out: strings({ alias: "o", hint: "FILE", desc: "output file path" }),
32
+ string: oneOf(["slice", "ptr"], {
33
+ alias: "s",
34
+ hint: "TYPE",
35
+ desc: "Force string type implementation",
36
+ }),
30
37
  };
31
38
  export const INSTALL_DIR = resolve(`${process.argv[2]}/..`);
32
39
  export const PKG = readJSON(`${INSTALL_DIR}/package.json`);
@@ -72,7 +79,10 @@ const validateTypeRefs = (coll) => {
72
79
  if (spec.type !== "struct")
73
80
  continue;
74
81
  for (let f of spec.fields) {
75
- if (!(isWasmPrim(f.type) || isWasmString(f.type) || coll[f.type])) {
82
+ if (!(isPadding(f) ||
83
+ isWasmPrim(f.type) ||
84
+ isWasmString(f.type) ||
85
+ coll[f.type])) {
76
86
  invalidSpec(spec.__path, `structfield ${spec.name}.${f.name} of unknown type: ${f.type}`);
77
87
  }
78
88
  }
@@ -127,12 +137,23 @@ try {
127
137
  }
128
138
  const ctx = {
129
139
  logger: new ConsoleLogger("wasm-api", opts.debug ? "DEBUG" : "INFO"),
130
- config: {},
140
+ config: { global: {} },
131
141
  opts,
132
142
  };
133
143
  if (opts.config) {
134
144
  ctx.config = readJSON(resolve(opts.config), ctx.logger);
145
+ for (let id in ctx.config) {
146
+ const conf = ctx.config[id];
147
+ if (conf.pre && conf.pre[0] === "@") {
148
+ conf.pre = readText(conf.pre.substring(1), ctx.logger);
149
+ }
150
+ if (conf.post && conf.post[0] === "@") {
151
+ conf.post = readText(conf.post.substring(1), ctx.logger);
152
+ }
153
+ }
135
154
  }
155
+ opts.debug && mutIn(ctx, ["config", "global", "debug"], true);
156
+ opts.string && mutIn(ctx, ["config", "global", "stringType"], opts.string);
136
157
  generateOutputs(ctx, parseTypeSpecs(ctx, rest));
137
158
  }
138
159
  catch (e) {
package/codegen/c.d.ts ADDED
@@ -0,0 +1,33 @@
1
+ import type { ICodeGen } from "../api.js";
2
+ /**
3
+ * Zig code generator options.
4
+ */
5
+ export interface COpts {
6
+ /**
7
+ * If true, generates various struct & struct field analysis functions
8
+ * (sizes, alignment, offsets etc.).
9
+ *
10
+ * @defaultValue false
11
+ */
12
+ debug: boolean;
13
+ /**
14
+ * Optional prelude
15
+ */
16
+ pre: string;
17
+ /**
18
+ * Optional postfix (inserted after the generated code)
19
+ */
20
+ post: string;
21
+ }
22
+ /**
23
+ * Zig code generator. Call with options and then pass to {@link generateTypes}
24
+ * (see its docs for further usage).
25
+ *
26
+ * @remarks
27
+ * This codegen generates struct and enum definitions for a {@link TypeColl}
28
+ * given to {@link generateTypes}.
29
+ *
30
+ * @param opts
31
+ */
32
+ export declare const C11: (opts?: Partial<COpts>) => ICodeGen;
33
+ //# sourceMappingURL=c.d.ts.map
package/codegen/c.js ADDED
@@ -0,0 +1,100 @@
1
+ import { isString } from "@thi.ng/checks/is-string";
2
+ import { isPadding, isStringSlice, prefixLines, withIndentation, } from "./utils.js";
3
+ /**
4
+ * Zig code generator. Call with options and then pass to {@link generateTypes}
5
+ * (see its docs for further usage).
6
+ *
7
+ * @remarks
8
+ * This codegen generates struct and enum definitions for a {@link TypeColl}
9
+ * given to {@link generateTypes}.
10
+ *
11
+ * @param opts
12
+ */
13
+ export const C11 = (opts = {}) => {
14
+ const { debug } = { debug: false, ...opts };
15
+ const INDENT = " ";
16
+ const SCOPES = [/\{$/, /\}\)?[;,]?$/];
17
+ const gen = {
18
+ pre: (opts) => `#pragma once
19
+ #include <stddef.h>
20
+ #include <stdint.h>${opts.pre ? `\n${opts.pre}` : ""}`,
21
+ post: () => opts.post || "",
22
+ doc: (doc, acc) => {
23
+ acc.push(prefixLines("// ", doc));
24
+ },
25
+ enum: (e, _, acc) => {
26
+ const lines = [];
27
+ lines.push(`enum {`);
28
+ for (let v of e.values) {
29
+ let line;
30
+ if (!isString(v)) {
31
+ v.doc && gen.doc(v.doc, lines);
32
+ line = `${e.name}_${v.name}`;
33
+ if (v.value != null)
34
+ line += ` = ${v.value}`;
35
+ }
36
+ else {
37
+ line = v;
38
+ }
39
+ lines.push(line + ",");
40
+ }
41
+ lines.push("};", "");
42
+ acc.push(...withIndentation(lines, INDENT, ...SCOPES));
43
+ },
44
+ struct: (struct, _, acc, opts) => {
45
+ const name = struct.name;
46
+ const res = [];
47
+ res.push(`typedef struct ${name} ${name};`, `struct ${name} {`);
48
+ const ftypes = {};
49
+ let padID = 0;
50
+ for (let f of struct.fields) {
51
+ // autolabel explicit padding fields
52
+ if (isPadding(f)) {
53
+ res.push(`__pad${padID++}: [${f.pad}]u8,`);
54
+ continue;
55
+ }
56
+ f.doc && gen.doc(f.doc, res);
57
+ let ftype = f.type === "string"
58
+ ? isStringSlice(opts.stringType)
59
+ ? f.const !== false
60
+ ? "[]const u8"
61
+ : "[]u8"
62
+ : f.const !== false
63
+ ? "[*:0]const u8"
64
+ : "[*:0]u8"
65
+ : f.type;
66
+ switch (f.tag) {
67
+ case "array":
68
+ case "vec":
69
+ ftype = `[${f.len}]${ftype}`;
70
+ break;
71
+ case "slice":
72
+ ftype = `[]${f.const ? "const " : ""}${ftype}`;
73
+ break;
74
+ case "ptr":
75
+ ftype = `*${f.const ? "const " : ""}${f.len ? `[${f.len}]` : ""}${ftype}`;
76
+ break;
77
+ case "scalar":
78
+ default:
79
+ }
80
+ ftypes[f.name] = ftype;
81
+ res.push(`${f.name}: ${ftype},`);
82
+ }
83
+ res.push("};");
84
+ if (debug) {
85
+ res.push("");
86
+ const fn = (fname, body) => res.push(`size_t __attribute__((used)) ${name}_${fname}() {`, `return ${body};`, `}`);
87
+ fn("align", `alignof(${name})`);
88
+ fn("size", `sizeof(${name})`);
89
+ for (let f of struct.fields) {
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
+ }
94
+ }
95
+ res.push("");
96
+ acc.push(...withIndentation(res, INDENT, ...SCOPES));
97
+ },
98
+ };
99
+ return gen;
100
+ };
@@ -0,0 +1,22 @@
1
+ import type { CodeGenOptsBase, ICodeGen } from "../api.js";
2
+ /**
3
+ * Zig code generator options.
4
+ */
5
+ export interface C11Opts extends CodeGenOptsBase {
6
+ /**
7
+ * Optional name prefix for generated types, e.g. `WASM_`.
8
+ */
9
+ typePrefix: string;
10
+ }
11
+ /**
12
+ * Zig code generator. Call with options and then pass to {@link generateTypes}
13
+ * (see its docs for further usage).
14
+ *
15
+ * @remarks
16
+ * This codegen generates struct and enum definitions for a {@link TypeColl}
17
+ * given to {@link generateTypes}.
18
+ *
19
+ * @param opts
20
+ */
21
+ export declare const C11: (opts?: Partial<C11Opts>) => ICodeGen;
22
+ //# sourceMappingURL=c11.d.ts.map