@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.
@@ -1,24 +1,14 @@
1
- import { ICodeGen } from "../api.js";
1
+ import { CodeGenOptsBase, ICodeGen } from "../api.js";
2
2
  /**
3
3
  * TypeScript code generator options.
4
4
  */
5
- export interface TSOpts {
5
+ export interface TSOpts extends CodeGenOptsBase {
6
6
  /**
7
7
  * Indentation string
8
8
  *
9
9
  * @defaultValue "\t"
10
10
  */
11
11
  indent: string;
12
- /**
13
- * If true (default), forces uppercase enums
14
- *
15
- * @defaultValue true
16
- */
17
- uppercaseEnums: boolean;
18
- /**
19
- * Same as {@link CodeGenOpts.stringType}.
20
- */
21
- stringType: "slice" | "ptr";
22
12
  }
23
13
  /**
24
14
  * TypeScript code generator. Call with options and then pass to
@@ -1,7 +1,7 @@
1
1
  import { BIGINT_ARRAY_CTORS, BIT_SHIFTS, TYPEDARRAY_CTORS, } from "@thi.ng/api/typedarray";
2
2
  import { isString } from "@thi.ng/checks/is-string";
3
3
  import { PKG_NAME, USIZE, USIZE_SIZE, } from "../api.js";
4
- import { isBigNumeric, isNumeric, isWasmPrim, isWasmString, prefixLines, } from "./utils.js";
4
+ import { enumName, isBigNumeric, isNumeric, isPadding, isStringSlice, isWasmPrim, isWasmString, pointerFields, prefixLines, stringFields, withIndentation, } from "./utils.js";
5
5
  /**
6
6
  * TypeScript code generator. Call with options and then pass to
7
7
  * {@link generateTypes} (see its docs for further usage).
@@ -13,156 +13,185 @@ import { isBigNumeric, isNumeric, isWasmPrim, isWasmString, prefixLines, } from
13
13
  *
14
14
  * @param opts
15
15
  */
16
- export const TYPESCRIPT = (opts) => {
17
- const { indent, stringType, uppercaseEnums } = {
16
+ export const TYPESCRIPT = (opts = {}) => {
17
+ const { indent } = {
18
18
  indent: "\t",
19
19
  stringType: "slice",
20
- uppercaseEnums: true,
21
20
  ...opts,
22
21
  };
23
- const I = indent;
24
- const I2 = I + I;
25
- const I3 = I2 + I;
22
+ const SCOPES = [/\{$/, /\}\)?[;,]?$/];
26
23
  const gen = {
27
- pre: `import type { WasmTypeBase, WasmTypeConstructor } from "${PKG_NAME}";`,
28
- doc: (doc, indent, acc) => {
29
- if (doc.indexOf("\n") !== -1) {
30
- acc.push(indent + "/**", prefixLines(indent + " * ", doc), indent + " */");
31
- }
32
- else {
33
- acc.push(`${indent}/** ${doc} */`);
34
- }
24
+ pre: (opts) => `// @ts-ignore possibly includes unused imports
25
+ import { Pointer, ${__stringImpl(opts)}, WasmTypeBase, WasmTypeConstructor } from "${PKG_NAME}";${opts.pre ? `\n${opts.pre}` : ""}`,
26
+ post: () => opts.post || "",
27
+ doc: (doc, acc, opts) => {
28
+ acc.push("/**", ...prefixLines(" * ", doc, opts.lineWidth), " */");
35
29
  },
36
- enum: (type, _, acc) => {
37
- const e = type;
38
- acc.push(`export enum ${e.name} {`);
30
+ enum: (e, _, acc, opts) => {
31
+ const res = [];
32
+ res.push(`export enum ${e.name} {`);
39
33
  for (let v of e.values) {
40
- let line = indent;
34
+ let line;
41
35
  if (!isString(v)) {
42
- v.doc && gen.doc(v.doc, indent, acc);
43
- line += uppercaseEnums ? v.name.toUpperCase() : v.name;
36
+ v.doc && gen.doc(v.doc, res, opts);
37
+ line = enumName(opts, v.name);
44
38
  if (v.value != null)
45
39
  line += ` = ${v.value}`;
46
40
  }
47
41
  else {
48
- line += uppercaseEnums ? v.toUpperCase() : v;
42
+ line = enumName(opts, v);
49
43
  }
50
- acc.push(line + ",");
44
+ res.push(line + ",");
51
45
  }
52
- acc.push("}\n");
53
- return acc;
46
+ res.push("}", "");
47
+ acc.push(...withIndentation(res, indent, ...SCOPES));
54
48
  },
55
- struct: (type, types, acc) => {
56
- const struct = type;
57
- const returnTypes = {};
49
+ struct: (struct, types, acc, opts) => {
50
+ const fieldTypes = {};
51
+ const $stringImpl = __stringImpl(opts);
52
+ const lines = [];
58
53
  // interface definition
59
- acc.push(`export interface ${struct.name} extends WasmTypeBase {`);
54
+ lines.push(`export interface ${struct.name} extends WasmTypeBase {`);
60
55
  for (let f of struct.fields) {
61
- f.doc && gen.doc(f.doc, indent, acc);
62
- let line = `${indent}${f.name}: `;
63
- let rtype = "";
64
- if (f.tag == "array" || f.tag == "slice" || f.tag === "vec") {
65
- rtype = isNumeric(f.type)
66
- ? TYPEDARRAY_CTORS[f.type].name
67
- : isBigNumeric(f.type)
68
- ? BIGINT_ARRAY_CTORS[f.type].name
69
- : f.type + "[]";
70
- }
71
- else if (!f.tag || f.tag === "scalar" || f.tag === "ptr") {
72
- rtype = isBigNumeric(f.type)
73
- ? "bigint"
74
- : isNumeric(f.type)
75
- ? "number"
76
- : f.type;
77
- }
78
- returnTypes[f.name] = rtype;
79
- acc.push(line + rtype + ";");
56
+ if (isPadding(f))
57
+ continue;
58
+ f.doc && gen.doc(f.doc, lines, opts);
59
+ const ftype = __fieldType(f, opts);
60
+ fieldTypes[f.name] = ftype;
61
+ lines.push(`${f.name}: ${ftype};`);
80
62
  }
81
- acc.push("}\n");
63
+ lines.push("}", "");
64
+ const pointerDecls = pointerFields(struct.fields).map((x) => {
65
+ return `let $${x.name}: ${fieldTypes[x.name]} | null = null;`;
66
+ });
67
+ const stringDecls = stringFields(struct.fields).map((x) => {
68
+ const suffix = x.tag === "array" || x.tag === "slice" ? "[]" : "";
69
+ return `let $${x.name}: ${$stringImpl}${suffix} | null = null;`;
70
+ });
82
71
  // type implementation
83
- acc.push(`export const $${struct.name}: WasmTypeConstructor<${struct.name}> = (mem) => ({`, `${I}get align() { return ${struct.__align}; },`, `${I}get size() { return ${struct.__size}; },`, `${I}instance: (base) => ({`, `${I2}get __base() { return base; },`, `${I2}get __bytes() { return mem.u8.subarray(base, base + ${struct.__size}); },`);
72
+ lines.push(`export const $${struct.name}: WasmTypeConstructor<${struct.name}> = (mem) => ({`, `get align() {`, `return ${struct.__align};`, `},`, `get size() {`, `return ${struct.__size};`, `},`, `instance: (base) => {`, ...pointerDecls, ...stringDecls, `return {`, `get __base() {`, `return base;`, `},`, `get __bytes() {`, `return mem.u8.subarray(base, base + ${struct.__size});`, `},`);
84
73
  for (let f of struct.fields) {
74
+ // skip explicit padding fields
75
+ if (isPadding(f))
76
+ continue;
77
+ const ftype = fieldTypes[f.name];
85
78
  const offset = f.__offset || 0;
86
- acc.push(`${I2}get ${f.name}(): ${returnTypes[f.name]} {`);
79
+ lines.push(`get ${f.name}(): ${ftype} {`);
87
80
  const isPrim = isWasmPrim(f.type);
88
81
  const isStr = isWasmString(f.type);
82
+ const isConst = isStr ? f.const !== false : !!f.const;
89
83
  if (f.tag === "ptr") {
90
84
  if (isPrim) {
91
- acc.push(`${I3}return mem.${f.type}[${__ptrShift(offset, f.type)}];`);
85
+ const fn = f.len
86
+ ? `(base) => mem.${f.type}.subarray(${__addrShift(0, f.type)}, (${__addrShift(0, f.type)}) + ${f.len})`
87
+ : `(base) => mem.${f.type}[${__addrShift(0, f.type)}]`;
88
+ lines.push(`return $${f.name} || ($${f.name} = new ${ftype}(mem, ${__addr(offset)}, ${fn}));`);
92
89
  }
93
90
  else if (isStr) {
94
- acc.push(
95
- // double deref
96
- stringType === "slice"
97
- ? `${I3}return mem.getString(mem.${USIZE}[${__ptr(offset)} >>> ${USIZE_SIZE}])`
98
- : `${I3}return mem.getString(${__ptr(offset)})`);
91
+ const fn = f.len
92
+ ? [
93
+ `(addr) => {`,
94
+ ...__mapStringArray("buf", $stringImpl, f.len, isConst, true),
95
+ `}`,
96
+ ]
97
+ : [
98
+ `(addr) => new ${$stringImpl}(mem, addr, ${isConst})`,
99
+ ];
100
+ lines.push(`return $${f.name} || ($${f.name} = new ${ftype}(mem, ${__addr(offset)},`, ...fn, `));`);
99
101
  }
100
102
  else {
101
- acc.push(`${I3}return $${f.type}.instance(${__ptr(offset)});`);
103
+ const fn = f.len
104
+ ? [`(addr) => {`, ...__mapArray(f, f.len), `}`]
105
+ : [`(addr) => new $${f.type}.instance(addr)`];
106
+ lines.push(`return $${f.name} || ($${f.name} = new ${ftype}(mem, ${__addr(offset)},`, ...fn, `));`);
102
107
  }
103
108
  }
104
109
  else if (f.tag === "slice") {
105
- acc.push(`${I3}const len = ${__ptr(offset + 4)};`);
110
+ lines.push(`const len = ${__ptr(offset + 4)};`);
106
111
  if (isPrim) {
107
- acc.push(`${I3}const addr = ${__ptrShift(offset, f.type)};`, `${I3}return mem.${f.type}.subarray(addr, addr + len);`);
112
+ lines.push(`const addr = ${__ptrShift(offset, f.type)};`, `return mem.${f.type}.subarray(addr, addr + len);`);
108
113
  }
109
114
  else if (isStr) {
110
- acc.push(`${I3}const addr = ${__ptr(offset)};`, __mapStringArray(I3, stringType));
115
+ lines.push(`const addr = ${__ptr(offset)};`, ...__mapStringArray("buf", $stringImpl, "len", isConst));
111
116
  }
112
117
  else {
113
- acc.push(`${I3}const addr = ${__ptr(offset)};`, __mapArray(f, I3));
118
+ lines.push(`const addr = ${__ptr(offset)};`, ...__mapArray(f));
114
119
  }
115
120
  }
116
121
  else if (f.tag === "array" || f.tag === "vec") {
117
122
  if (isPrim) {
118
- acc.push(`${I3}const addr = ${__addrShift(offset, f.type)};`, `${I3}return mem.${f.type}.subarray(addr, addr + ${f.len});`);
123
+ lines.push(`const addr = ${__addrShift(offset, f.type)};`, `return mem.${f.type}.subarray(addr, addr + ${f.len});`);
119
124
  }
120
125
  else if (isStr) {
121
- acc.push(`${I3}const addr = ${__addr(offset)};`, __mapStringArray(I3, stringType, f.len));
126
+ lines.push(`if ($${f.name}) return $${f.name};`, `const addr = ${__addr(offset)};`, ...__mapStringArray(f.name, $stringImpl, f.len, isConst));
122
127
  }
123
128
  else {
124
- acc.push(`${I3}const addr = ${__addr(offset)};`, __mapArray(f, I3, f.len));
129
+ lines.push(`const addr = ${__addr(offset)};`, ...__mapArray(f, f.len));
125
130
  }
126
131
  }
127
132
  else {
128
133
  let setter;
129
134
  if (isPrim) {
130
135
  const addr = __mem(f.type, f.__offset);
131
- acc.push(`${I3}return ${addr};`);
136
+ lines.push(`return ${addr};`);
132
137
  setter = `${addr} = x`;
133
138
  }
134
139
  else if (isStr) {
135
- acc.push(`${I3}return mem.getString(${__ptr(offset)})`);
136
- setter =
137
- stringType === "slice"
138
- ? `mem.setString(x, ${__ptr(offset)}, ${__ptr(offset + 4)} + 1, true);`
139
- : `throw new Error("unsupported for raw string pointers")`;
140
+ lines.push(`return $${f.name} || ($${f.name} = new ${$stringImpl}(mem, ${__addr(offset)}, ${isConst}));`);
140
141
  }
141
142
  else if (types[f.type].type === "enum") {
142
143
  const tag = types[f.type].tag;
143
144
  const addr = __mem(tag, f.__offset);
144
- acc.push(`${I3}return ${addr};`);
145
+ lines.push(`return ${addr};`);
145
146
  setter = `${addr} = x`;
146
147
  }
147
148
  else {
148
- acc.push(`${I3}return $${f.type}(mem).instance(${__addr(offset)});`);
149
+ lines.push(`return $${f.type}(mem).instance(${__addr(offset)});`);
149
150
  setter = `mem.u8.set(x.__bytes, ${__addr(offset)})`;
150
151
  }
151
- // close getter
152
- acc.push(`${I2}},`);
153
152
  // setter
154
- acc.push(`${I2}set ${f.name}(x: ${returnTypes[f.name]}) {`, `${I3}${setter};`);
153
+ if (setter) {
154
+ lines.push(
155
+ // close getter
156
+ `},`, `set ${f.name}(x: ${fieldTypes[f.name]}) {`, `${setter};`);
157
+ }
155
158
  }
156
159
  // close field accessor
157
- acc.push(`${I2}},`);
160
+ lines.push(`},`);
158
161
  }
159
- acc.push(`${I}})\n});\n`);
160
- return acc;
162
+ lines.push("};", "}", "});", "");
163
+ acc.push(...withIndentation(lines, indent, ...SCOPES));
161
164
  },
162
165
  };
163
166
  return gen;
164
167
  };
165
168
  /** @internal */
169
+ const __stringImpl = (opts) => isStringSlice(opts.stringType) ? "WasmStringSlice" : "WasmStringPtr";
170
+ /** @internal */
171
+ const __fieldType = (f, opts) => {
172
+ const ftype = f.tag == "array" ||
173
+ f.tag == "slice" ||
174
+ f.tag === "vec" ||
175
+ (f.tag === "ptr" && f.len)
176
+ ? isNumeric(f.type)
177
+ ? TYPEDARRAY_CTORS[f.type].name
178
+ : isBigNumeric(f.type)
179
+ ? BIGINT_ARRAY_CTORS[f.type].name
180
+ : isWasmString(f.type)
181
+ ? __stringImpl(opts) + "[]"
182
+ : f.type + "[]"
183
+ : !f.tag || f.tag === "scalar" || f.tag === "ptr"
184
+ ? isBigNumeric(f.type)
185
+ ? "bigint"
186
+ : isNumeric(f.type)
187
+ ? "number"
188
+ : isWasmString(f.type)
189
+ ? __stringImpl(opts)
190
+ : f.type
191
+ : f.type;
192
+ return f.tag === "ptr" ? `Pointer<${ftype}>` : ftype;
193
+ };
194
+ /** @internal */
166
195
  const __shift = (type) => BIT_SHIFTS[type];
167
196
  /** @internal */
168
197
  const __addr = (offset) => (offset > 0 ? `(base + ${offset})` : "base");
@@ -177,13 +206,15 @@ const __ptr = (offset) => `mem.${USIZE}[${__addrShift(offset, USIZE)}]`;
177
206
  const __ptrShift = (offset, shift) => __ptr(offset) + " >>> " + __shift(shift);
178
207
  const __mem = (type, offset) => `mem.${type}[${__addrShift(offset, type)}]`;
179
208
  /** @internal */
180
- const __mapArray = (f, indent, len = "len") => prefixLines(indent, `const inst = $${f.type}(mem);
181
- const slice: ${f.type}[] = [];
182
- for(let i = 0; i < ${len}; i++) slice.push(inst.instance(addr + i * ${f.__size}));
183
- return slice;`);
209
+ const __mapArray = (f, len = "len") => [
210
+ `const inst = $${f.type}(mem);`,
211
+ `const slice: ${f.type}[] = [];`,
212
+ `for(let i = 0; i < ${len}; i++) slice.push(inst.instance(addr + i * ${f.__size}));`,
213
+ `return slice;`,
214
+ ];
184
215
  /** @internal */
185
- const __mapStringArray = (indent, type, len = "len") => prefixLines(indent, [
186
- "const slice: string[] = [];",
187
- `for(let i = 0; i < ${len}; i++) slice.push(mem.getString(mem.${USIZE}[(addr + i * ${USIZE_SIZE * (type === "slice" ? 2 : 1)}) >>> ${__shift(USIZE)}]));`,
188
- "return slice;",
189
- ]);
216
+ const __mapStringArray = (name, type, len, isConst, isLocal = false) => [
217
+ isLocal ? `const $${name}: ${type}[] = [];` : `$${name} = [];`,
218
+ `for(let i = 0; i < ${len}; i++) $${name}.push(new ${type}(mem, addr + i * ${USIZE_SIZE * (type === "WasmStringSlice" ? 2 : 1)}, ${isConst}));`,
219
+ `return $${name};`,
220
+ ];
@@ -1,5 +1,5 @@
1
1
  import type { BigType } from "@thi.ng/api";
2
- import type { WasmPrim, WasmPrim32 } from "../api.js";
2
+ import type { CodeGenOpts, StructField, WasmPrim, WasmPrim32 } from "../api.js";
3
3
  /**
4
4
  * Returns true iff `x` is a {@link WasmPrim32}.
5
5
  *
@@ -19,12 +19,61 @@ export declare const isBigNumeric: (x: string) => x is BigType;
19
19
  */
20
20
  export declare const isWasmPrim: (x: string) => x is WasmPrim;
21
21
  export declare const isWasmString: (x: string) => x is "string";
22
+ export declare const isPadding: (f: StructField) => boolean;
22
23
  /**
23
- * Takes an array of strings or splits given string into lines, prefixes each
24
- * line with given `prefix` and then returns rejoined result.
24
+ * Takes an array of strings or splits given string into lines, word wraps and
25
+ * then prefixes each line with given `width` and `prefix`. Returns array of new
26
+ * lines.
25
27
  *
26
28
  * @param prefix
27
29
  * @param str
30
+ * @param width
28
31
  */
29
- export declare const prefixLines: (prefix: string, str: string | string[]) => string;
32
+ export declare const prefixLines: (prefix: string, str: string | string[], width: number) => string[];
33
+ /**
34
+ * Returns true if `type` is "slice".
35
+ *
36
+ * @param type
37
+ */
38
+ export declare const isStringSlice: (type: CodeGenOpts["stringType"]) => type is "slice";
39
+ /**
40
+ * Returns filtered array of struct fields of with "ptr" tag.
41
+ *
42
+ * @param fields
43
+ *
44
+ * @internal
45
+ */
46
+ export declare const pointerFields: (fields: StructField[]) => StructField[];
47
+ /**
48
+ * Returns filtered array of struct fields of only "string" fields.
49
+ *
50
+ * @param fields
51
+ *
52
+ * @internal
53
+ */
54
+ export declare const stringFields: (fields: StructField[]) => StructField[];
55
+ /**
56
+ * Returns enum identifier formatted according to given opts.
57
+ *
58
+ * @param opts
59
+ * @param name
60
+ *
61
+ * @internal
62
+ */
63
+ export declare const enumName: (opts: CodeGenOpts, name: string) => string;
64
+ /**
65
+ * Yields iterator of given lines, each with applied indentation based on given
66
+ * scope regexp's which are applied to each line to increase or decrease
67
+ * indentation level (the initial indentation level can be specified via
68
+ * optional `level` arg, default 0). If `scopeStart` succeeds, the indent is
69
+ * increased for the _next_ line. If `scopeEnd` succeeds the level is decreased
70
+ * for the _current_ line. ...
71
+ *
72
+ * @param lines
73
+ * @param indent
74
+ * @param scopeStart
75
+ * @param scopeEnd
76
+ * @param level
77
+ */
78
+ export declare function withIndentation(lines: string[], indent: string, scopeStart: RegExp, scopeEnd: RegExp, level?: number): Generator<string, void, unknown>;
30
79
  //# sourceMappingURL=utils.d.ts.map
package/codegen/utils.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { isString } from "@thi.ng/checks/is-string";
2
+ import { wordWrapLine, wordWrapLines } from "@thi.ng/strings/word-wrap";
2
3
  /**
3
4
  * Returns true iff `x` is a {@link WasmPrim32}.
4
5
  *
@@ -18,13 +19,70 @@ export const isBigNumeric = (x) => /^[iu]64$/.test(x);
18
19
  */
19
20
  export const isWasmPrim = (x) => isNumeric(x) || isBigNumeric(x);
20
21
  export const isWasmString = (x) => x === "string";
22
+ export const isPadding = (f) => f.pad != null && f.pad > 0;
21
23
  /**
22
- * Takes an array of strings or splits given string into lines, prefixes each
23
- * line with given `prefix` and then returns rejoined result.
24
+ * Takes an array of strings or splits given string into lines, word wraps and
25
+ * then prefixes each line with given `width` and `prefix`. Returns array of new
26
+ * lines.
24
27
  *
25
28
  * @param prefix
26
29
  * @param str
30
+ * @param width
27
31
  */
28
- export const prefixLines = (prefix, str) => (isString(str) ? str.split("\n") : str)
29
- .map((line) => prefix + line)
30
- .join("\n");
32
+ export const prefixLines = (prefix, str, width) => (isString(str)
33
+ ? wordWrapLines(str, { width: width - prefix.length })
34
+ : str.flatMap((x) => wordWrapLine(x, { width: width - prefix.length }))).map((line) => prefix + line);
35
+ /**
36
+ * Returns true if `type` is "slice".
37
+ *
38
+ * @param type
39
+ */
40
+ export const isStringSlice = (type) => type === "slice";
41
+ /**
42
+ * Returns filtered array of struct fields of with "ptr" tag.
43
+ *
44
+ * @param fields
45
+ *
46
+ * @internal
47
+ */
48
+ export const pointerFields = (fields) => fields.filter((f) => f.tag === "ptr");
49
+ /**
50
+ * Returns filtered array of struct fields of only "string" fields.
51
+ *
52
+ * @param fields
53
+ *
54
+ * @internal
55
+ */
56
+ export const stringFields = (fields) => fields.filter((f) => isWasmString(f.type) && f.tag !== "ptr");
57
+ /**
58
+ * Returns enum identifier formatted according to given opts.
59
+ *
60
+ * @param opts
61
+ * @param name
62
+ *
63
+ * @internal
64
+ */
65
+ export const enumName = (opts, name) => opts.uppercaseEnums ? name.toUpperCase() : name;
66
+ /**
67
+ * Yields iterator of given lines, each with applied indentation based on given
68
+ * scope regexp's which are applied to each line to increase or decrease
69
+ * indentation level (the initial indentation level can be specified via
70
+ * optional `level` arg, default 0). If `scopeStart` succeeds, the indent is
71
+ * increased for the _next_ line. If `scopeEnd` succeeds the level is decreased
72
+ * for the _current_ line. ...
73
+ *
74
+ * @param lines
75
+ * @param indent
76
+ * @param scopeStart
77
+ * @param scopeEnd
78
+ * @param level
79
+ */
80
+ export function* withIndentation(lines, indent, scopeStart, scopeEnd, level = 0) {
81
+ const stack = new Array(level).fill(indent);
82
+ for (let l of lines) {
83
+ scopeEnd.test(l) && stack.pop();
84
+ const curr = stack.length ? stack[stack.length - 1] : "";
85
+ yield curr + l;
86
+ scopeStart.test(l) && stack.push(curr + indent);
87
+ }
88
+ }
package/codegen/zig.d.ts CHANGED
@@ -1,15 +1,8 @@
1
- import type { ICodeGen } from "../api.js";
1
+ import type { CodeGenOptsBase, ICodeGen } from "../api.js";
2
2
  /**
3
3
  * Zig code generator options.
4
4
  */
5
- export interface ZigOpts {
6
- /**
7
- * If true, generates various struct & struct field analysis functions
8
- * (sizes, alignment, offsets etc.).
9
- *
10
- * @defaultValue false
11
- */
12
- debug: boolean;
5
+ export interface ZigOpts extends CodeGenOptsBase {
13
6
  }
14
7
  /**
15
8
  * Zig code generator. Call with options and then pass to {@link generateTypes}
package/codegen/zig.js CHANGED
@@ -1,5 +1,8 @@
1
+ import { isNumber } from "@thi.ng/checks/is-number";
1
2
  import { isString } from "@thi.ng/checks/is-string";
2
- import { prefixLines } from "./utils.js";
3
+ import { unsupported } from "@thi.ng/errors/unsupported";
4
+ import { split } from "@thi.ng/strings/split";
5
+ import { enumName, isPadding, isStringSlice, prefixLines, withIndentation, } from "./utils.js";
3
6
  /**
4
7
  * Zig code generator. Call with options and then pass to {@link generateTypes}
5
8
  * (see its docs for further usage).
@@ -10,64 +13,106 @@ import { prefixLines } from "./utils.js";
10
13
  *
11
14
  * @param opts
12
15
  */
13
- export const ZIG = (opts) => {
14
- const { debug } = { debug: false, ...opts };
16
+ export const ZIG = (opts = {}) => {
17
+ const INDENT = " ";
18
+ const SCOPES = [/\{$/, /\}\)?[;,]?$/];
15
19
  const gen = {
16
- doc: (doc, indent, acc, topLevel = false) => {
17
- acc.push(prefixLines(topLevel ? "//! " : indent + "/// ", doc));
20
+ pre: () => `const std = @import("std");${opts.pre ? `\n${opts.pre}` : ""}`,
21
+ post: () => opts.post || "",
22
+ doc: (doc, acc, opts, topLevel = false) => {
23
+ acc.push(...prefixLines(topLevel ? "//! " : "/// ", doc, opts.lineWidth));
18
24
  },
19
- enum: (e, _, acc) => {
20
- acc.push(`pub const ${e.name} = enum(${e.tag}) {`);
25
+ enum: (e, _, acc, opts) => {
26
+ const lines = [];
27
+ lines.push(`pub const ${e.name} = enum(${e.tag}) {`);
21
28
  for (let v of e.values) {
22
- let line = ` `;
29
+ let line;
23
30
  if (!isString(v)) {
24
- v.doc && gen.doc(v.doc, " ", acc);
25
- line += v.name;
31
+ v.doc && gen.doc(v.doc, lines, opts);
32
+ line = enumName(opts, v.name);
26
33
  if (v.value != null)
27
34
  line += ` = ${v.value}`;
28
35
  }
29
36
  else {
30
- line += v;
37
+ line = enumName(opts, v);
31
38
  }
32
- acc.push(line + ",");
39
+ lines.push(line + ",");
33
40
  }
34
- acc.push("};\n");
41
+ if (e.body?.zig) {
42
+ lines.push("", ...split(e.body.zig), "");
43
+ }
44
+ lines.push("};", "");
45
+ acc.push(...withIndentation(lines, INDENT, ...SCOPES));
35
46
  },
36
- struct: (struct, _, acc) => {
47
+ struct: (struct, _, acc, opts) => {
37
48
  const name = struct.name;
38
- acc.push(`pub const ${name} = struct {`);
49
+ const res = [];
50
+ res.push(`pub const ${name} = ${struct.tag ? struct.tag + " " : ""}struct {`);
39
51
  const ftypes = {};
52
+ let padID = 0;
40
53
  for (let f of struct.fields) {
41
- f.doc && gen.doc(f.doc, " ", acc);
42
- let ftype = f.type === "string" ? "[]const u8" : f.type;
54
+ // autolabel explicit padding fields
55
+ if (isPadding(f)) {
56
+ res.push(`__pad${padID++}: [${f.pad}]u8,`);
57
+ continue;
58
+ }
59
+ f.doc && gen.doc(f.doc, res, opts);
60
+ let ftype = f.type === "string"
61
+ ? isStringSlice(opts.stringType)
62
+ ? f.const !== false
63
+ ? "[]const u8"
64
+ : "[]u8"
65
+ : f.const !== false
66
+ ? "[*:0]const u8"
67
+ : "[*:0]u8"
68
+ : f.type;
69
+ let defaultVal = "";
43
70
  switch (f.tag) {
44
71
  case "array":
45
- ftype = `[${f.len}]${ftype}`;
72
+ ftype =
73
+ f.sentinel !== undefined
74
+ ? `[${f.len}:${f.sentinel}]${ftype}`
75
+ : `[${f.len}]${ftype}`;
46
76
  break;
47
77
  case "slice":
48
- ftype = `[]${ftype}`;
78
+ ftype = `[${f.sentinel !== undefined ? ":" + f.sentinel : ""}]${f.const ? "const " : ""}${ftype}`;
49
79
  break;
50
80
  case "vec":
51
81
  ftype = `@Vector(${f.len}, ${ftype})`;
52
82
  break;
53
83
  case "ptr":
54
- ftype = `*${f.len ? `[${f.len}]` : ""}${ftype}`;
84
+ ftype = `*${f.const ? "const " : ""}${f.len ? `[${f.len}]` : ""}${ftype}`;
55
85
  break;
56
86
  case "scalar":
57
87
  default:
88
+ if (f.default != undefined) {
89
+ if (!(isString(f.default) || isNumber(f.default))) {
90
+ unsupported(`wrong default value for ${name}.${f.name} (${f.default})`);
91
+ }
92
+ defaultVal = ` = ${JSON.stringify(f.default)}`;
93
+ }
58
94
  }
59
95
  ftypes[f.name] = ftype;
60
- acc.push(` ${f.name}: ${ftype},`);
96
+ res.push(`${f.name}: ${ftype}${defaultVal},`);
61
97
  }
62
- acc.push("};\n");
63
- if (!debug)
64
- return;
65
- const fn = (fname, body) => `export fn ${name}_${fname}() usize { return ${body}; }`;
66
- acc.push(fn("align", `@alignOf(${name})`), fn("size", `@sizeOf(${name})`));
67
- for (let f of struct.fields) {
68
- acc.push(fn(f.name + "_align", `@alignOf(${ftypes[f.name]})`), fn(f.name + "_offset", `@offsetOf(${name}, "${f.name}")`), fn(f.name + "_size", `@sizeOf(${ftypes[f.name]})`));
98
+ if (struct.body?.zig) {
99
+ res.push("", ...split(struct.body.zig), "");
100
+ }
101
+ res.push("};");
102
+ if (opts.debug) {
103
+ const fn = (fname, body) => res.push("", `export fn ${name}_${fname}() usize {`, `return ${body};`, `}`);
104
+ fn("align", `@alignOf(${name})`);
105
+ fn("size", `@sizeOf(${name})`);
106
+ for (let f of struct.fields) {
107
+ if (isPadding(f))
108
+ continue;
109
+ fn(f.name + "_align", `@alignOf(${ftypes[f.name]})`);
110
+ fn(f.name + "_offset", `@offsetOf(${name}, "${f.name}")`);
111
+ fn(f.name + "_size", `@sizeOf(${ftypes[f.name]})`);
112
+ }
69
113
  }
70
- acc.push("");
114
+ res.push("");
115
+ acc.push(...withIndentation(res, INDENT, ...SCOPES));
71
116
  },
72
117
  };
73
118
  return gen;