@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/CHANGELOG.md +77 -1
- package/README.md +248 -170
- package/api.d.ts +98 -14
- package/bin/wasm-api.mjs_ +17 -0
- package/bridge.d.ts +11 -1
- package/bridge.js +15 -10
- package/cli.js +51 -9
- package/codegen/c.d.ts +33 -0
- package/codegen/c.js +100 -0
- package/codegen/c11.d.ts +22 -0
- package/codegen/c11.js +125 -0
- package/codegen/typescript.d.ts +2 -12
- package/codegen/typescript.js +119 -88
- package/codegen/utils.d.ts +53 -4
- package/codegen/utils.js +63 -5
- package/codegen/zig.d.ts +2 -9
- package/codegen/zig.js +74 -29
- package/codegen.d.ts +1 -23
- package/codegen.js +35 -8
- package/include/wasmapi.h +34 -20
- package/include/wasmapi.zig +53 -46
- package/index.d.ts +3 -0
- package/index.js +3 -0
- package/package.json +32 -19
- package/pointer.d.ts +23 -0
- package/pointer.js +27 -0
- package/string.d.ts +96 -0
- package/string.js +145 -0
package/api.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { BigType, FloatType, Fn,
|
|
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
|
-
|
|
136
|
-
|
|
137
|
-
|
|
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
|
-
*
|
|
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
|
-
*
|
|
299
|
+
* Currently only supported for {@link ZIG} scalar & string values,
|
|
300
|
+
* otherwise ignored!
|
|
273
301
|
*/
|
|
274
|
-
default?:
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
|
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
|
-
|
|
44
|
-
|
|
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.
|
|
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.
|
|
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 {
|
|
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 (!(
|
|
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
|
-
|
|
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
|
+
};
|
package/codegen/c11.d.ts
ADDED
|
@@ -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; }`;
|