@thi.ng/wasm-api 0.18.0 → 1.0.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/cli.js DELETED
@@ -1,188 +0,0 @@
1
- import { flag, oneOf, oneOfMulti, parse, ParseError, string, strings, usage, } from "@thi.ng/args";
2
- import { isArray, isPlainObject, isString } from "@thi.ng/checks";
3
- import { illegalArgs } from "@thi.ng/errors";
4
- import { readJSON, readText, writeJSON, writeText } from "@thi.ng/file-io";
5
- import { ConsoleLogger } from "@thi.ng/logger";
6
- import { mutIn } from "@thi.ng/paths";
7
- import { dirname, resolve } from "path";
8
- import { generateTypes } from "./codegen.js";
9
- import { C11 } from "./codegen/c11.js";
10
- import { TYPESCRIPT } from "./codegen/typescript.js";
11
- import { isPadding, isWasmPrim, isWasmString } from "./codegen/utils.js";
12
- import { ZIG } from "./codegen/zig.js";
13
- const GENERATORS = { c11: C11, ts: TYPESCRIPT, zig: ZIG };
14
- const argOpts = {
15
- analytics: string({
16
- alias: "a",
17
- hint: "FILE",
18
- desc: "output file path for raw codegen analytics",
19
- }),
20
- config: string({
21
- alias: "c",
22
- hint: "FILE",
23
- desc: "JSON config file with codegen options",
24
- }),
25
- debug: flag({
26
- alias: "d",
27
- default: false,
28
- desc: "enable debug output & functions",
29
- }),
30
- dryRun: flag({
31
- default: false,
32
- desc: "enable dry run (don't overwrite files)",
33
- }),
34
- lang: oneOfMulti(Object.keys(GENERATORS), {
35
- alias: "l",
36
- desc: "target language",
37
- default: ["ts", "zig"],
38
- delim: ",",
39
- }),
40
- out: strings({ alias: "o", hint: "FILE", desc: "output file path" }),
41
- string: oneOf(["slice", "ptr"], {
42
- alias: "s",
43
- hint: "TYPE",
44
- desc: "Force string type implementation",
45
- }),
46
- };
47
- export const INSTALL_DIR = resolve(`${process.argv[2]}/..`);
48
- export const PKG = readJSON(`${INSTALL_DIR}/package.json`);
49
- export const APP_NAME = PKG.name.split("/")[1];
50
- export const HEADER = `
51
- █ █ █ │
52
- ██ █ │
53
- █ █ █ █ █ █ █ █ │ ${PKG.name} ${PKG.version}
54
- █ █ █ █ █ █ █ █ █ │ Multi-language data bindings code generator
55
- █ │
56
- █ █ │
57
- `;
58
- const usageOpts = {
59
- lineWidth: process.stdout.columns,
60
- prefix: `${HEADER}
61
- usage: ${APP_NAME} [OPTS] JSON-INPUT-FILE(S) ...
62
- ${APP_NAME} --help
63
-
64
- `,
65
- showGroupNames: true,
66
- paramWidth: 32,
67
- };
68
- const showUsage = () => {
69
- process.stderr.write(usage(argOpts, usageOpts));
70
- process.exit(1);
71
- };
72
- const invalidSpec = (path, msg) => {
73
- throw new Error(`invalid typedef: ${path}${msg ? ` (${msg})` : ""}`);
74
- };
75
- const addTypeSpec = (ctx, path, coll, spec) => {
76
- if (!(spec.name && spec.type))
77
- invalidSpec(path);
78
- if (!["enum", "struct", "union"].includes(spec.type))
79
- invalidSpec(path, `${spec.name} type: ${spec.type}`);
80
- if (coll[spec.name])
81
- invalidSpec(path, `duplicate name: ${spec.name}`);
82
- if (spec.body) {
83
- if (!isPlainObject(spec.body))
84
- invalidSpec(path, `${spec.name}.body must be an object`);
85
- for (let lang in spec.body) {
86
- const src = spec.body[lang];
87
- if (isString(src) && src[0] === "@") {
88
- spec.body[lang] = readText(src.substring(1), ctx.logger);
89
- }
90
- }
91
- }
92
- ctx.logger.debug(`registering ${spec.type}: ${spec.name}`);
93
- coll[spec.name] = spec;
94
- spec.__path = path;
95
- };
96
- const validateTypeRefs = (coll) => {
97
- for (let spec of Object.values(coll)) {
98
- if (spec.type !== "struct")
99
- continue;
100
- for (let f of spec.fields) {
101
- if (!(isPadding(f) ||
102
- isWasmPrim(f.type) ||
103
- isWasmString(f.type) ||
104
- coll[f.type])) {
105
- invalidSpec(spec.__path, `structfield ${spec.name}.${f.name} of unknown type: ${f.type}`);
106
- }
107
- }
108
- }
109
- };
110
- const parseTypeSpecs = (ctx, inputs) => {
111
- const coll = {};
112
- for (let path of inputs) {
113
- try {
114
- const spec = readJSON(resolve(path), ctx.logger);
115
- if (isArray(spec)) {
116
- for (let s of spec)
117
- addTypeSpec(ctx, path, coll, s);
118
- }
119
- else if (isPlainObject(spec)) {
120
- addTypeSpec(ctx, path, coll, spec);
121
- }
122
- else {
123
- invalidSpec(path);
124
- }
125
- }
126
- catch (e) {
127
- process.stderr.write(e.message);
128
- process.exit(1);
129
- }
130
- }
131
- validateTypeRefs(coll);
132
- return coll;
133
- };
134
- const generateOutputs = ({ config, logger, opts }, coll) => {
135
- for (let i = 0; i < opts.lang.length; i++) {
136
- const lang = opts.lang[i];
137
- logger.debug(`generating ${lang.toUpperCase()} output...`);
138
- const src = generateTypes(coll, GENERATORS[lang](config[lang]), config.global);
139
- if (opts.out) {
140
- writeText(resolve(opts.out[i]), src, logger, opts.dryRun);
141
- }
142
- else {
143
- process.stdout.write(src + "\n");
144
- }
145
- }
146
- };
147
- try {
148
- const result = parse(argOpts, process.argv, { start: 3, usageOpts });
149
- if (!result)
150
- process.exit(1);
151
- const { result: opts, rest } = result;
152
- if (!rest.length)
153
- showUsage();
154
- if (opts.out && opts.lang.length != opts.out.length) {
155
- illegalArgs(`expected ${opts.lang.length} outputs, but got ${opts.out.length}`);
156
- }
157
- const ctx = {
158
- logger: new ConsoleLogger("wasm-api", opts.debug ? "DEBUG" : "INFO"),
159
- config: { global: {} },
160
- opts,
161
- };
162
- if (opts.config) {
163
- opts.config = resolve(opts.config);
164
- ctx.config = readJSON(opts.config, ctx.logger);
165
- for (let id in ctx.config) {
166
- const conf = ctx.config[id];
167
- if (conf.pre && conf.pre[0] === "@") {
168
- conf.pre = readText(resolve(dirname(opts.config), conf.pre.substring(1)), ctx.logger);
169
- }
170
- if (conf.post && conf.post[0] === "@") {
171
- conf.post = readText(resolve(dirname(opts.config), conf.post.substring(1)), ctx.logger);
172
- }
173
- }
174
- }
175
- opts.debug && mutIn(ctx, ["config", "global", "debug"], true);
176
- opts.string && mutIn(ctx, ["config", "global", "stringType"], opts.string);
177
- const types = parseTypeSpecs(ctx, rest);
178
- generateOutputs(ctx, types);
179
- if (ctx.opts.analytics) {
180
- // always write analytics, even if dry run
181
- writeJSON(resolve(ctx.opts.analytics), types, undefined, "\t", ctx.logger);
182
- }
183
- }
184
- catch (e) {
185
- if (!(e instanceof ParseError))
186
- process.stderr.write(e.message);
187
- process.exit(1);
188
- }
@@ -1,14 +0,0 @@
1
- import type { AlignStrategy, TopLevelType } from "../api.js";
2
- /**
3
- * C ABI compatible alignment
4
- */
5
- export declare const ALIGN_C: AlignStrategy;
6
- export declare const ALIGN_PACKED: AlignStrategy;
7
- /**
8
- * Returns a suitable alignment strategy for given type, either via user
9
- * supplied impl defined for the type or derived via a struct's tag.
10
- *
11
- * @param type
12
- */
13
- export declare const selectAlignment: (type: TopLevelType) => AlignStrategy;
14
- //# sourceMappingURL=align.d.ts.map
package/codegen/align.js DELETED
@@ -1,35 +0,0 @@
1
- import { SIZEOF } from "@thi.ng/api/typedarray";
2
- import { align as $align } from "@thi.ng/binary/align";
3
- import { ceilPow2 } from "@thi.ng/binary/pow";
4
- /**
5
- * C ABI compatible alignment
6
- */
7
- export const ALIGN_C = {
8
- align: (field) => {
9
- let align = SIZEOF[field.type];
10
- if (field.tag === "vec") {
11
- align *= ceilPow2(field.len);
12
- }
13
- return align;
14
- },
15
- size: (size, align) => $align(size, align),
16
- offset: (offset, align) => $align(offset, align),
17
- };
18
- export const ALIGN_PACKED = {
19
- align: () => 1,
20
- size: (size) => size,
21
- offset: (offset) => offset,
22
- };
23
- /**
24
- * Returns a suitable alignment strategy for given type, either via user
25
- * supplied impl defined for the type or derived via a struct's tag.
26
- *
27
- * @param type
28
- */
29
- export const selectAlignment = (type) => {
30
- if (type.type === "struct" || type.type === "union") {
31
- let $type = type;
32
- return $type.align || ($type.tag === "packed" ? ALIGN_PACKED : ALIGN_C);
33
- }
34
- return ALIGN_C;
35
- };
package/codegen/c.d.ts DELETED
@@ -1,33 +0,0 @@
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 DELETED
@@ -1,100 +0,0 @@
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 DELETED
@@ -1,22 +0,0 @@
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 DELETED
@@ -1,145 +0,0 @@
1
- import { isString } from "@thi.ng/checks/is-string";
2
- import { unsupported } from "@thi.ng/errors/unsupported";
3
- import { enumName, isPadding, isStringSlice, isWasmString, 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
-
36
- #ifdef __cplusplus
37
- extern "C" {
38
- #endif
39
- ${opts.debug ? "\n#include <stdalign.h>" : ""}
40
- #include <stddef.h>
41
- #include <stdint.h>${opts.pre ? `\n${opts.pre}` : ""}`,
42
- post: () => `${opts.post ? `${opts.post}\n` : ""}#ifdef __cplusplus\n}\n#endif\n`,
43
- doc: (doc, acc, opts) => {
44
- acc.push(...prefixLines("// ", doc, opts.lineWidth));
45
- },
46
- enum: (e, _, acc, opts) => {
47
- if (!(e.tag === "i32" || e.tag === "u32")) {
48
- unsupported(`enum ${e.name} must be a i32/u32 in C, but got '${e.tag}'`);
49
- }
50
- const name = typePrefix + e.name;
51
- const lines = [];
52
- lines.push(`typedef enum {`);
53
- for (let v of e.values) {
54
- let line;
55
- if (!isString(v)) {
56
- v.doc && gen.doc(v.doc, lines, opts);
57
- line = enumName(opts, v.name);
58
- if (v.value != null)
59
- line += ` = ${v.value}`;
60
- }
61
- else {
62
- line = enumName(opts, v);
63
- }
64
- lines.push(line + ",");
65
- }
66
- lines.push(`} ${name};`, "");
67
- acc.push(...withIndentation(lines, INDENT, ...SCOPES));
68
- },
69
- struct: (struct, coll, acc, opts) => {
70
- const name = typePrefix + struct.name;
71
- acc.push(...withIndentation([
72
- `typedef struct ${name} ${name};`,
73
- `struct ${name} {`,
74
- ...__generateFields(gen, struct, coll, opts, typePrefix),
75
- ], INDENT, ...SCOPES));
76
- },
77
- union: (union, coll, acc, opts) => {
78
- const name = typePrefix + union.name;
79
- acc.push(...withIndentation([
80
- `typedef union ${name} ${name};`,
81
- `union ${name} {`,
82
- ...__generateFields(gen, union, coll, opts, typePrefix),
83
- ], INDENT, ...SCOPES));
84
- },
85
- };
86
- return gen;
87
- };
88
- const __generateFields = (gen, parent, coll, opts, typePrefix) => {
89
- const res = [];
90
- const ftypes = {};
91
- const isUnion = parent.type === "union";
92
- const name = typePrefix + parent.name;
93
- let padID = 0;
94
- for (let f of parent.fields) {
95
- // autolabel explicit padding fields
96
- if (isPadding(f)) {
97
- res.push(`uint8_t __pad${padID++}[${f.pad}];`);
98
- continue;
99
- }
100
- f.doc && gen.doc(f.doc, res, opts);
101
- const fconst = f.const ? "const " : "";
102
- let ftype = isWasmString(f.type)
103
- ? isStringSlice(opts.stringType)
104
- ? __slice("char", fconst)
105
- : `${f.const !== false ? "const " : ""}char*`
106
- : PRIM_ALIASES[f.type] || f.type;
107
- if (coll[ftype])
108
- ftype = typePrefix + ftype;
109
- switch (f.tag) {
110
- case "array":
111
- case "vec":
112
- res.push(`${fconst}${ftype} ${f.name}[${f.len}];`);
113
- ftype = `${ftype}[${f.len}]`;
114
- break;
115
- case "slice":
116
- ftype = __slice(ftype, fconst);
117
- res.push(`${ftype} ${f.name};`);
118
- break;
119
- case "ptr":
120
- ftype = `${fconst}${ftype}*`;
121
- res.push(`${ftype} ${f.name};`);
122
- break;
123
- case "scalar":
124
- default:
125
- res.push(`${ftype} ${f.name};`);
126
- }
127
- ftypes[f.name] = ftype;
128
- }
129
- res.push("};");
130
- if (opts.debug) {
131
- const fn = (fname, body) => res.push("", `size_t __attribute__((used)) ${name}_${fname}() {`, `return ${body};`, `}`);
132
- fn("align", `alignof(${name})`);
133
- fn("size", `sizeof(${name})`);
134
- for (let f of parent.fields) {
135
- if (isPadding(f))
136
- continue;
137
- fn(f.name + "_align", `alignof(${ftypes[f.name]})`);
138
- !isUnion && fn(f.name + "_offset", `offsetof(${name}, ${f.name})`);
139
- fn(f.name + "_size", `sizeof(${ftypes[f.name]})`);
140
- }
141
- }
142
- res.push("");
143
- return res;
144
- };
145
- const __slice = (type, $const) => `struct { ${$const}${type} *ptr; size_t len; }`;
@@ -1,25 +0,0 @@
1
- import { CodeGenOptsBase, ICodeGen } from "../api.js";
2
- /**
3
- * TypeScript code generator options.
4
- */
5
- export interface TSOpts extends CodeGenOptsBase {
6
- /**
7
- * Indentation string
8
- *
9
- * @defaultValue "\t"
10
- */
11
- indent: string;
12
- }
13
- /**
14
- * TypeScript code generator. Call with options and then pass to
15
- * {@link generateTypes} (see its docs for further usage).
16
- *
17
- * @remarks
18
- * This codegen generates interface and enum definitions for a {@link TypeColl}
19
- * given to {@link generateTypes}. For structs it will also generate memory
20
- * mapped wrappers with fully typed accessors.
21
- *
22
- * @param opts
23
- */
24
- export declare const TYPESCRIPT: (opts?: Partial<TSOpts>) => ICodeGen;
25
- //# sourceMappingURL=typescript.d.ts.map