@thi.ng/wasm-api 0.10.0 → 0.12.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,4 +1,4 @@
1
- import type { BigType, FloatType, Fn, Fn2 } 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";
@@ -132,9 +132,9 @@ export interface CoreAPI extends WebAssembly.ModuleImports {
132
132
  printI32: Fn<number, void>;
133
133
  printU32: Fn<number, void>;
134
134
  printU32Hex: Fn<number, void>;
135
- _printI64: Fn2<number, number, void>;
136
- _printU64: Fn2<number, number, void>;
137
- _printU64Hex: Fn2<number, number, void>;
135
+ printI64: Fn<bigint, void>;
136
+ printU64: Fn<bigint, void>;
137
+ printU64Hex: Fn<bigint, void>;
138
138
  printF32: Fn<number, void>;
139
139
  printF64: Fn<number, void>;
140
140
  _printI8Array: (addr: number, len: number) => void;
@@ -150,6 +150,9 @@ export interface CoreAPI extends WebAssembly.ModuleImports {
150
150
  _printStr0: (addr: number) => void;
151
151
  _printStr: (addr: number, len: number) => void;
152
152
  debug: () => void;
153
+ _panic: (addr: number, len: number) => void;
154
+ timer: () => number;
155
+ epoch: () => bigint;
153
156
  }
154
157
  export interface WasmTypeBase {
155
158
  /**
@@ -172,6 +175,9 @@ export declare type WasmUint = "u8" | "u16" | "u32" | "u64";
172
175
  export declare type WasmFloat = FloatType;
173
176
  export declare type WasmPrim = WasmInt | WasmUint | WasmFloat;
174
177
  export declare type WasmPrim32 = Exclude<WasmPrim, BigType>;
178
+ export declare type ReadonlyWasmString = IDeref<string> & ILength & {
179
+ readonly addr: number;
180
+ };
175
181
  export declare type TypeColl = Record<string, TopLevelType>;
176
182
  export interface TypeInfo {
177
183
  /**
@@ -206,6 +212,14 @@ export interface TopLevelType extends TypeInfo {
206
212
  * Type / kind
207
213
  */
208
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>;
209
223
  }
210
224
  export interface Struct extends TopLevelType {
211
225
  type: "struct";
@@ -217,11 +231,18 @@ export interface Struct extends TopLevelType {
217
231
  /**
218
232
  * If true, struct fields will be re-ordered in descending order based on
219
233
  * their {@link TypeInfo.__align} size. This might result in overall smaller
220
- * structs due to minimizing inter-field padding.
234
+ * structs due to minimizing implicit inter-field padding caused by
235
+ * alignment requirements. **If this option is enabled, then the struct MUST
236
+ * NOT contain any padding fields!**
221
237
  *
222
238
  * @defaultValue false
223
239
  */
224
240
  auto?: boolean;
241
+ /**
242
+ * Optional qualifier for the kind of struct to be emitted (codegen specific
243
+ * interpretation, currently only used by {@link ZIG}).
244
+ */
245
+ tag?: "extern" | "packed";
225
246
  }
226
247
  export interface StructField extends TypeInfo {
227
248
  /**
@@ -261,7 +282,13 @@ export interface StructField extends TypeInfo {
261
282
  */
262
283
  type: WasmPrim | "string" | "opaque" | string;
263
284
  /**
264
- * TODO currently unsupported & ignored!
285
+ * Const qualifier (default is true for `string`, false for all other
286
+ * types). Only used for pointers or slices.
287
+ */
288
+ const?: boolean;
289
+ /**
290
+ * Currently only supported for {@link ZIG} arrays & slices, otherwise
291
+ * ignored!
265
292
  */
266
293
  sentinel?: number;
267
294
  /**
@@ -269,14 +296,23 @@ export interface StructField extends TypeInfo {
269
296
  */
270
297
  len?: number;
271
298
  /**
272
- * TODO currently unsupported & ignored!
299
+ * Currently only supported for {@link ZIG} scalar & string values,
300
+ * otherwise ignored!
273
301
  */
274
- default?: any;
302
+ default?: number;
303
+ /**
304
+ * If defined and > 0, the field will be considered for padding purposes only and
305
+ * the value provided is the number of bytes used.
306
+ */
307
+ pad?: number;
275
308
  }
276
309
  export interface Enum extends TopLevelType {
277
310
  type: "enum";
278
311
  /**
279
- * No i64/u64 support, due to Typescript not supporting bigint enum values
312
+ * No i64/u64 support, due to Typescript not supporting bigint enum values.
313
+ * For C compatibility only i32 or u32 is allowed.
314
+ *
315
+ * @defaultValue "i32"
280
316
  */
281
317
  tag: Exclude<WasmPrim32, FloatType>;
282
318
  /**
@@ -299,27 +335,75 @@ export interface EnumValue {
299
335
  */
300
336
  doc?: string;
301
337
  }
338
+ export interface CodeGenOptsBase {
339
+ /**
340
+ * Optional string to be injected before generated type defs (but after
341
+ * codegen's own prelude, if any)
342
+ */
343
+ pre: string;
344
+ /**
345
+ * Optional string to be injected after generated type defs (but before
346
+ * codegen's own epilogue, if any)
347
+ */
348
+ post: string;
349
+ }
350
+ /**
351
+ * Global/shared code generator options.
352
+ */
353
+ export interface CodeGenOpts extends CodeGenOptsBase {
354
+ /**
355
+ * Identifier how strings are stored on WASM side, e.g. in Zig string
356
+ * literals are slices (8 bytes), in C just plain pointers (4 bytes).
357
+ *
358
+ * @defaultValue "slice"
359
+ */
360
+ stringType: "slice" | "ptr";
361
+ /**
362
+ * If true (default), forces uppercase enum identifiers
363
+ *
364
+ * @defaultValue true
365
+ */
366
+ uppercaseEnums: boolean;
367
+ /**
368
+ * Unless set to false, the generated output will be prefixed with a header
369
+ * line comment of generator meta data
370
+ */
371
+ header: boolean;
372
+ /**
373
+ * If true, codegens MAY generate various additional struct & struct field
374
+ * analysis functions (sizes, alignment, offsets etc.).
375
+ *
376
+ * @defaultValue false
377
+ */
378
+ debug: boolean;
379
+ /**
380
+ * Target line width for word wrapping doc strings
381
+ *
382
+ * @defaultValue 80
383
+ */
384
+ lineWidth: number;
385
+ }
302
386
  export interface ICodeGen {
303
387
  /**
304
388
  * Optional prelude source, to be prepended before any generated type defs.
305
389
  */
306
- pre?: string;
390
+ pre?: Fn<CodeGenOpts, string>;
307
391
  /**
308
392
  * Optional source code to be appended after any generated type defs.
309
393
  */
310
- post?: string;
394
+ post?: Fn<CodeGenOpts, string>;
311
395
  /**
312
396
  * Docstring codegen
313
397
  */
314
- doc: (doc: string, indent: string, acc: string[], topLevel?: boolean) => void;
398
+ doc: (doc: string, acc: string[], opts: CodeGenOpts, topLevel?: boolean) => void;
315
399
  /**
316
400
  * Codegen for enum types.
317
401
  */
318
- enum: (type: Enum, types: TypeColl, acc: string[]) => void;
402
+ enum: (type: Enum, types: TypeColl, acc: string[], opts: CodeGenOpts) => void;
319
403
  /**
320
404
  * Codegen for struct types.
321
405
  */
322
- struct: (type: Struct, types: TypeColl, acc: string[]) => void;
406
+ struct: (type: Struct, types: TypeColl, acc: string[], opts: CodeGenOpts) => void;
323
407
  }
324
408
  /**
325
409
  * WASM usize type. Assuming wasm32 until wasm64 surfaces, then need an option.
@@ -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.d.ts CHANGED
@@ -1,7 +1,17 @@
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, 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;
package/bridge.js CHANGED
@@ -2,10 +2,10 @@ import { __decorate } from "tslib";
2
2
  import { INotifyMixin } from "@thi.ng/api/mixins/inotify";
3
3
  import { defError } from "@thi.ng/errors/deferror";
4
4
  import { illegalArgs } from "@thi.ng/errors/illegal-arguments";
5
- import { U16, U32, U64HL, U8 } from "@thi.ng/hex";
5
+ import { U16, U32, U64BIG, U8 } from "@thi.ng/hex";
6
6
  import { ConsoleLogger } from "@thi.ng/logger/console";
7
7
  import { EVENT_MEMORY_CHANGED, } from "./api.js";
8
- const B32 = BigInt(32);
8
+ export const Panic = defError(() => "Panic");
9
9
  export const OutOfMemoryError = defError(() => "Out of memory");
10
10
  /**
11
11
  * The main interop API bridge between the JS host environment and a WebAssembly
@@ -34,18 +34,18 @@ let WasmBridge = class WasmBridge {
34
34
  this.api = {
35
35
  printI8: logN,
36
36
  printU8: logN,
37
- printU8Hex: (x) => this.logger.debug(`0x${U8(x)}`),
38
37
  printI16: logN,
39
38
  printU16: logN,
40
- printU16Hex: (x) => this.logger.debug(`0x${U16(x)}`),
41
39
  printI32: logN,
42
40
  printU32: (x) => this.logger.debug(x >>> 0),
43
- printU32Hex: (x) => this.logger.debug(`0x${U32(x)}`),
44
- _printI64: (hi, lo) => this.logger.debug((BigInt(hi) << B32) | BigInt(lo)),
45
- _printU64: (hi, lo) => this.logger.debug((BigInt(hi >>> 0) << B32) | BigInt(lo >>> 0)),
46
- _printU64Hex: (hi, lo) => this.logger.debug(`0x${U64HL(hi, lo)}`),
41
+ printI64: (x) => this.logger.debug(x),
42
+ printU64: (x) => this.logger.debug(x),
47
43
  printF32: logN,
48
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)}`),
49
49
  _printI8Array: logA(this.getI8Array.bind(this)),
50
50
  _printU8Array: logA(this.getU8Array.bind(this)),
51
51
  _printI16Array: logA(this.getI16Array.bind(this)),
@@ -61,6 +61,11 @@ let WasmBridge = class WasmBridge {
61
61
  debug: () => {
62
62
  debugger;
63
63
  },
64
+ _panic: (addr, len) => {
65
+ throw new Panic(this.getString(addr, len));
66
+ },
67
+ timer: () => performance.now(),
68
+ epoch: () => BigInt(Date.now()),
64
69
  };
65
70
  }
66
71
  /**
@@ -210,7 +215,7 @@ let WasmBridge = class WasmBridge {
210
215
  const addr = this.exports._wasm_allocate(numBytes);
211
216
  if (!addr)
212
217
  throw new OutOfMemoryError(`unable to allocate: ${numBytes}`);
213
- 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)}`);
214
219
  this.ensureMemory();
215
220
  clear && this.u8.fill(0, addr, addr + numBytes);
216
221
  return addr;
@@ -229,7 +234,7 @@ let WasmBridge = class WasmBridge {
229
234
  * @param numBytes
230
235
  */
231
236
  free(addr, numBytes) {
232
- 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)}`);
233
238
  this.exports._wasm_free(addr, numBytes);
234
239
  }
235
240
  getI8(addr) {
package/cli.js CHANGED
@@ -1,15 +1,22 @@
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 { readJSON, readText, writeJSON, writeText } from "@thi.ng/file-io";
5
5
  import { ConsoleLogger } from "@thi.ng/logger";
6
- import { resolve } from "path";
6
+ import { mutIn } from "@thi.ng/paths";
7
+ import { dirname, 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 = {
15
+ analytics: string({
16
+ alias: "a",
17
+ hint: "FILE",
18
+ desc: "output file path for raw codegen analytics",
19
+ }),
13
20
  config: string({
14
21
  alias: "c",
15
22
  hint: "FILE",
@@ -27,6 +34,11 @@ const argOpts = {
27
34
  delim: ",",
28
35
  }),
29
36
  out: strings({ alias: "o", hint: "FILE", desc: "output file path" }),
37
+ string: oneOf(["slice", "ptr"], {
38
+ alias: "s",
39
+ hint: "TYPE",
40
+ desc: "Force string type implementation",
41
+ }),
30
42
  };
31
43
  export const INSTALL_DIR = resolve(`${process.argv[2]}/..`);
32
44
  export const PKG = readJSON(`${INSTALL_DIR}/package.json`);
@@ -63,6 +75,16 @@ const addTypeSpec = (ctx, path, coll, spec) => {
63
75
  invalidSpec(path, `${spec.name} type: ${spec.type}`);
64
76
  if (coll[spec.name])
65
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
+ }
66
88
  ctx.logger.debug(`registering ${spec.type}: ${spec.name}`);
67
89
  coll[spec.name] = spec;
68
90
  spec.__path = path;
@@ -72,7 +94,10 @@ const validateTypeRefs = (coll) => {
72
94
  if (spec.type !== "struct")
73
95
  continue;
74
96
  for (let f of spec.fields) {
75
- if (!(isWasmPrim(f.type) || isWasmString(f.type) || coll[f.type])) {
97
+ if (!(isPadding(f) ||
98
+ isWasmPrim(f.type) ||
99
+ isWasmString(f.type) ||
100
+ coll[f.type])) {
76
101
  invalidSpec(spec.__path, `structfield ${spec.name}.${f.name} of unknown type: ${f.type}`);
77
102
  }
78
103
  }
@@ -127,13 +152,30 @@ try {
127
152
  }
128
153
  const ctx = {
129
154
  logger: new ConsoleLogger("wasm-api", opts.debug ? "DEBUG" : "INFO"),
130
- config: {},
155
+ config: { global: {} },
131
156
  opts,
132
157
  };
133
158
  if (opts.config) {
134
- ctx.config = readJSON(resolve(opts.config), ctx.logger);
159
+ opts.config = resolve(opts.config);
160
+ ctx.config = readJSON(opts.config, ctx.logger);
161
+ for (let id in ctx.config) {
162
+ const conf = ctx.config[id];
163
+ if (conf.pre && conf.pre[0] === "@") {
164
+ conf.pre = readText(resolve(dirname(opts.config), conf.pre.substring(1)), ctx.logger);
165
+ }
166
+ if (conf.post && conf.post[0] === "@") {
167
+ conf.post = readText(resolve(dirname(opts.config), conf.post.substring(1)), ctx.logger);
168
+ }
169
+ }
170
+ }
171
+ opts.debug && mutIn(ctx, ["config", "global", "debug"], true);
172
+ opts.string && mutIn(ctx, ["config", "global", "stringType"], opts.string);
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);
135
178
  }
136
- generateOutputs(ctx, parseTypeSpecs(ctx, rest));
137
179
  }
138
180
  catch (e) {
139
181
  if (!(e instanceof ParseError))
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
package/codegen/c11.js ADDED
@@ -0,0 +1,125 @@
1
+ import { isString } from "@thi.ng/checks/is-string";
2
+ import { unsupported } from "@thi.ng/errors/unsupported";
3
+ import { enumName, isPadding, isStringSlice, prefixLines, withIndentation, } from "./utils.js";
4
+ const PRIM_ALIASES = {
5
+ i8: "int8_t",
6
+ u8: "uint8_t",
7
+ i16: "int16_t",
8
+ u16: "uint16_t",
9
+ i32: "int32_t",
10
+ u32: "uint32_t",
11
+ i64: "int64_t",
12
+ u64: "uint64_t",
13
+ f32: "float",
14
+ f64: "double",
15
+ };
16
+ /**
17
+ * Zig code generator. Call with options and then pass to {@link generateTypes}
18
+ * (see its docs for further usage).
19
+ *
20
+ * @remarks
21
+ * This codegen generates struct and enum definitions for a {@link TypeColl}
22
+ * given to {@link generateTypes}.
23
+ *
24
+ * @param opts
25
+ */
26
+ export const C11 = (opts = {}) => {
27
+ const { typePrefix } = {
28
+ typePrefix: "",
29
+ ...opts,
30
+ };
31
+ const INDENT = " ";
32
+ const SCOPES = [/\{$/, /^\}[ A-Za-z0-9_]*[;,]?$/];
33
+ const gen = {
34
+ pre: (opts) => `#pragma once
35
+ ${opts.debug ? "\n#include <stdalign.h>" : ""}
36
+ #include <stddef.h>
37
+ #include <stdint.h>${opts.pre ? `\n${opts.pre}` : ""}`,
38
+ post: () => opts.post || "",
39
+ doc: (doc, acc, opts) => {
40
+ acc.push(...prefixLines("// ", doc, opts.lineWidth));
41
+ },
42
+ enum: (e, _, acc, opts) => {
43
+ if (!(e.tag === "i32" || e.tag === "u32")) {
44
+ unsupported(`enum ${e.name} must be a i32/u32 in C, but got '${e.tag}'`);
45
+ }
46
+ const name = typePrefix + e.name;
47
+ const lines = [];
48
+ lines.push(`typedef enum {`);
49
+ for (let v of e.values) {
50
+ let line;
51
+ if (!isString(v)) {
52
+ v.doc && gen.doc(v.doc, lines, opts);
53
+ line = enumName(opts, v.name);
54
+ if (v.value != null)
55
+ line += ` = ${v.value}`;
56
+ }
57
+ else {
58
+ line = enumName(opts, v);
59
+ }
60
+ lines.push(line + ",");
61
+ }
62
+ lines.push(`} ${name};`, "");
63
+ acc.push(...withIndentation(lines, INDENT, ...SCOPES));
64
+ },
65
+ struct: (struct, coll, acc, opts) => {
66
+ const name = typePrefix + struct.name;
67
+ const res = [];
68
+ res.push(`typedef struct ${name} ${name};`, `struct ${name} {`);
69
+ const ftypes = {};
70
+ let padID = 0;
71
+ for (let f of struct.fields) {
72
+ // autolabel explicit padding fields
73
+ if (isPadding(f)) {
74
+ res.push(`uint8_t __pad${padID++}[${f.pad}];`);
75
+ continue;
76
+ }
77
+ f.doc && gen.doc(f.doc, res, opts);
78
+ const fconst = f.const ? "const " : "";
79
+ let ftype = f.type === "string"
80
+ ? isStringSlice(opts.stringType)
81
+ ? __slice("char", fconst)
82
+ : `${f.const !== false ? "const " : ""}char*`
83
+ : PRIM_ALIASES[f.type] || f.type;
84
+ if (coll[ftype])
85
+ ftype = typePrefix + ftype;
86
+ switch (f.tag) {
87
+ case "array":
88
+ case "vec":
89
+ res.push(`${fconst}${ftype} ${f.name}[${f.len}];`);
90
+ ftype = `${ftype}[${f.len}]`;
91
+ break;
92
+ case "slice":
93
+ ftype = __slice(ftype, fconst);
94
+ res.push(`${ftype} ${f.name};`);
95
+ break;
96
+ case "ptr":
97
+ ftype = `${fconst}${ftype}*`;
98
+ res.push(`${ftype} ${f.name};`);
99
+ break;
100
+ case "scalar":
101
+ default:
102
+ res.push(`${ftype} ${f.name};`);
103
+ }
104
+ ftypes[f.name] = ftype;
105
+ }
106
+ res.push("};");
107
+ if (opts.debug) {
108
+ const fn = (fname, body) => res.push("", `size_t __attribute__((used)) ${name}_${fname}() {`, `return ${body};`, `}`);
109
+ fn("align", `alignof(${name})`);
110
+ fn("size", `sizeof(${name})`);
111
+ for (let f of struct.fields) {
112
+ if (isPadding(f))
113
+ continue;
114
+ fn(f.name + "_align", `alignof(${ftypes[f.name]})`);
115
+ fn(f.name + "_offset", `offsetof(${name}, ${f.name})`);
116
+ fn(f.name + "_size", `sizeof(${ftypes[f.name]})`);
117
+ }
118
+ }
119
+ res.push("");
120
+ acc.push(...withIndentation(res, INDENT, ...SCOPES));
121
+ },
122
+ };
123
+ return gen;
124
+ };
125
+ const __slice = (type, $const) => `struct { ${$const}${type} *ptr; size_t len; }`;