@thi.ng/wasm-api 0.14.0 → 0.16.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,7 +1,8 @@
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
- import { PKG_NAME, USIZE, USIZE_SIZE, } from "../api.js";
4
- import { enumName, isBigNumeric, isNumeric, isPadding, isStringSlice, isWasmPrim, isWasmString, pointerFields, prefixLines, stringFields, withIndentation, } from "./utils.js";
3
+ import { PKG_NAME, } from "../api.js";
4
+ import { ensureLines, enumName, isBigNumeric, isNumeric, isPadding, isStringSlice, isWasmPrim, isWasmString, pointerFields, prefixLines, stringFields, withIndentation, } from "./utils.js";
5
+ import { fieldType as zigFieldType } from "./zig.js";
5
6
  /**
6
7
  * TypeScript code generator. Call with options and then pass to
7
8
  * {@link generateTypes} (see its docs for further usage).
@@ -22,7 +23,7 @@ export const TYPESCRIPT = (opts = {}) => {
22
23
  const SCOPES = [/\{$/, /\}\)?[;,]?$/];
23
24
  const gen = {
24
25
  pre: (opts) => `// @ts-ignore possibly includes unused imports
25
- import { Pointer, ${__stringImpl(opts)}, WasmTypeBase, WasmTypeConstructor } from "${PKG_NAME}";${opts.pre ? `\n${opts.pre}` : ""}`,
26
+ import { MemorySlice, Pointer, ${__stringImpl(opts)}, WasmTypeBase, WasmTypeConstructor } from "${PKG_NAME}";${opts.pre ? `\n${opts.pre}` : ""}`,
26
27
  post: () => opts.post || "",
27
28
  doc: (doc, acc, opts) => {
28
29
  acc.push("/**", ...prefixLines(" * ", doc, opts.lineWidth), " */");
@@ -55,11 +56,15 @@ import { Pointer, ${__stringImpl(opts)}, WasmTypeBase, WasmTypeConstructor } fro
55
56
  for (let f of struct.fields) {
56
57
  if (isPadding(f))
57
58
  continue;
58
- f.doc && gen.doc(f.doc, lines, opts);
59
+ const doc = __docType(struct, f, opts);
60
+ doc && gen.doc(doc, lines, opts);
59
61
  const ftype = __fieldType(f, opts);
60
62
  fieldTypes[f.name] = ftype;
61
63
  lines.push(`${f.name}: ${ftype};`);
62
64
  }
65
+ if (struct.body?.ts) {
66
+ lines.push("", ...ensureLines(struct.body.ts, "decl"));
67
+ }
63
68
  lines.push("}", "");
64
69
  const pointerDecls = pointerFields(struct.fields).map((x) => {
65
70
  return `let $${x.name}: ${fieldTypes[x.name]} | null = null;`;
@@ -91,7 +96,7 @@ import { Pointer, ${__stringImpl(opts)}, WasmTypeBase, WasmTypeConstructor } fro
91
96
  const fn = f.len
92
97
  ? [
93
98
  `(addr) => {`,
94
- ...__mapStringArray("buf", $stringImpl, f.len, isConst, true),
99
+ ...__mapStringArray(opts.target, "buf", $stringImpl, f.len, isConst, true),
95
100
  `}`,
96
101
  ]
97
102
  : [
@@ -107,15 +112,15 @@ import { Pointer, ${__stringImpl(opts)}, WasmTypeBase, WasmTypeConstructor } fro
107
112
  }
108
113
  }
109
114
  else if (f.tag === "slice") {
110
- lines.push(`const len = ${__ptr(offset + 4)};`);
115
+ lines.push(`const len = ${__ptr(opts.target, offset + 4)};`);
111
116
  if (isPrim) {
112
- lines.push(`const addr = ${__ptrShift(offset, f.type)};`, `return mem.${f.type}.subarray(addr, addr + len);`);
117
+ lines.push(`const addr = ${__ptrShift(opts.target, offset, f.type)};`, `return mem.${f.type}.subarray(addr, addr + len);`);
113
118
  }
114
119
  else if (isStr) {
115
- lines.push(`const addr = ${__ptr(offset)};`, ...__mapStringArray("buf", $stringImpl, "len", isConst));
120
+ lines.push(`const addr = ${__ptr(opts.target, offset)};`, ...__mapStringArray(opts.target, "buf", $stringImpl, "len", isConst));
116
121
  }
117
122
  else {
118
- lines.push(`const addr = ${__ptr(offset)};`, ...__mapArray(f));
123
+ lines.push(`const addr = ${__ptr(opts.target, offset)};`, ...__mapArray(f));
119
124
  }
120
125
  }
121
126
  else if (f.tag === "array" || f.tag === "vec") {
@@ -123,7 +128,7 @@ import { Pointer, ${__stringImpl(opts)}, WasmTypeBase, WasmTypeConstructor } fro
123
128
  lines.push(`const addr = ${__addrShift(offset, f.type)};`, `return mem.${f.type}.subarray(addr, addr + ${f.len});`);
124
129
  }
125
130
  else if (isStr) {
126
- lines.push(`if ($${f.name}) return $${f.name};`, `const addr = ${__addr(offset)};`, ...__mapStringArray(f.name, $stringImpl, f.len, isConst));
131
+ lines.push(`if ($${f.name}) return $${f.name};`, `const addr = ${__addr(offset)};`, ...__mapStringArray(opts.target, f.name, $stringImpl, f.len, isConst));
127
132
  }
128
133
  else {
129
134
  lines.push(`const addr = ${__addr(offset)};`, ...__mapArray(f, f.len));
@@ -159,9 +164,15 @@ import { Pointer, ${__stringImpl(opts)}, WasmTypeBase, WasmTypeConstructor } fro
159
164
  // close field accessor
160
165
  lines.push(`},`);
161
166
  }
167
+ if (struct.body?.ts) {
168
+ lines.push("", ...ensureLines(struct.body.ts, "impl"), "");
169
+ }
162
170
  lines.push("};", "}", "});", "");
163
171
  acc.push(...withIndentation(lines, indent, ...SCOPES));
164
172
  },
173
+ union: (type, coll, acc, opts) => {
174
+ gen.struct(type, coll, acc, opts);
175
+ },
165
176
  };
166
177
  return gen;
167
178
  };
@@ -201,9 +212,9 @@ const __addrShift = (offset, shift) => {
201
212
  return __addr(offset) + (bits ? " >>> " + bits : "");
202
213
  };
203
214
  /** @internal */
204
- const __ptr = (offset) => `mem.${USIZE}[${__addrShift(offset, USIZE)}]`;
215
+ const __ptr = (target, offset) => `mem.${target.usize}[${__addrShift(offset, target.usize)}]`;
205
216
  /** @internal */
206
- const __ptrShift = (offset, shift) => __ptr(offset) + " >>> " + __shift(shift);
217
+ const __ptrShift = (target, offset, shift) => __ptr(target, offset) + " >>> " + __shift(shift);
207
218
  const __mem = (type, offset) => `mem.${type}[${__addrShift(offset, type)}]`;
208
219
  /** @internal */
209
220
  const __mapArray = (f, len = "len") => [
@@ -213,8 +224,17 @@ const __mapArray = (f, len = "len") => [
213
224
  `return slice;`,
214
225
  ];
215
226
  /** @internal */
216
- const __mapStringArray = (name, type, len, isConst, isLocal = false) => [
227
+ const __mapStringArray = (target, name, type, len, isConst, isLocal = false) => [
217
228
  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}));`,
229
+ `for(let i = 0; i < ${len}; i++) $${name}.push(new ${type}(mem, addr + i * ${target.usizeBytes * (type === "WasmStringSlice" ? 2 : 1)}, ${isConst}));`,
219
230
  `return $${name};`,
220
231
  ];
232
+ const __docType = (parent, f, opts) => {
233
+ const doc = [...ensureLines(f.doc || [])];
234
+ if (isWasmPrim(f.type)) {
235
+ if (doc.length)
236
+ doc.push("");
237
+ doc.push(`WASM type: ${zigFieldType(parent, f, opts).type}`);
238
+ }
239
+ return doc.length ? doc : undefined;
240
+ };
@@ -1,5 +1,5 @@
1
1
  import type { BigType } from "@thi.ng/api";
2
- import type { CodeGenOpts, StructField, WasmPrim, WasmPrim32 } from "../api.js";
2
+ import type { CodeGenOpts, Field, InjectedBody, WasmPrim, WasmPrim32 } from "../api.js";
3
3
  /**
4
4
  * Returns true iff `x` is a {@link WasmPrim32}.
5
5
  *
@@ -19,17 +19,15 @@ 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
+ export declare const isPadding: (f: Field) => boolean;
23
+ export declare const isPointer: (f: Field) => boolean;
24
+ export declare const isSlice: (f: Field) => boolean;
23
25
  /**
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.
26
+ * Returns true iff the struct field is a pointer, slice or "string" type
27
27
  *
28
- * @param prefix
29
- * @param str
30
- * @param width
28
+ * @param f
31
29
  */
32
- export declare const prefixLines: (prefix: string, str: string | string[], width: number) => string[];
30
+ export declare const isPointerLike: (f: Field) => boolean;
33
31
  /**
34
32
  * Returns true if `type` is "slice".
35
33
  *
@@ -43,7 +41,7 @@ export declare const isStringSlice: (type: CodeGenOpts["stringType"]) => type is
43
41
  *
44
42
  * @internal
45
43
  */
46
- export declare const pointerFields: (fields: StructField[]) => StructField[];
44
+ export declare const pointerFields: (fields: Field[]) => Field[];
47
45
  /**
48
46
  * Returns filtered array of struct fields of only "string" fields.
49
47
  *
@@ -51,7 +49,7 @@ export declare const pointerFields: (fields: StructField[]) => StructField[];
51
49
  *
52
50
  * @internal
53
51
  */
54
- export declare const stringFields: (fields: StructField[]) => StructField[];
52
+ export declare const stringFields: (fields: Field[]) => Field[];
55
53
  /**
56
54
  * Returns enum identifier formatted according to given opts.
57
55
  *
@@ -61,6 +59,17 @@ export declare const stringFields: (fields: StructField[]) => StructField[];
61
59
  * @internal
62
60
  */
63
61
  export declare const enumName: (opts: CodeGenOpts, name: string) => string;
62
+ /**
63
+ * Takes an array of strings or splits given string into lines, word wraps and
64
+ * then prefixes each line with given `width` and `prefix`. Returns array of new
65
+ * lines.
66
+ *
67
+ * @param prefix
68
+ * @param str
69
+ * @param width
70
+ */
71
+ export declare const prefixLines: (prefix: string, str: string | string[], width: number) => string[];
72
+ export declare const ensureLines: (src: string | string[] | InjectedBody, key?: keyof InjectedBody) => Iterable<string>;
64
73
  /**
65
74
  * Yields iterator of given lines, each with applied indentation based on given
66
75
  * scope regexp's which are applied to each line to increase or decrease
package/codegen/utils.js CHANGED
@@ -1,4 +1,6 @@
1
+ import { isArray } from "@thi.ng/checks/is-array";
1
2
  import { isString } from "@thi.ng/checks/is-string";
3
+ import { split } from "@thi.ng/strings/split";
2
4
  import { wordWrapLine, wordWrapLines } from "@thi.ng/strings/word-wrap";
3
5
  /**
4
6
  * Returns true iff `x` is a {@link WasmPrim32}.
@@ -20,18 +22,14 @@ export const isBigNumeric = (x) => /^[iu]64$/.test(x);
20
22
  export const isWasmPrim = (x) => isNumeric(x) || isBigNumeric(x);
21
23
  export const isWasmString = (x) => x === "string";
22
24
  export const isPadding = (f) => f.pad != null && f.pad > 0;
25
+ export const isPointer = (f) => f.tag === "ptr";
26
+ export const isSlice = (f) => f.tag === "slice";
23
27
  /**
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.
28
+ * Returns true iff the struct field is a pointer, slice or "string" type
27
29
  *
28
- * @param prefix
29
- * @param str
30
- * @param width
30
+ * @param f
31
31
  */
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);
32
+ export const isPointerLike = (f) => isPointer(f) || isSlice(f) || isWasmString(f.type);
35
33
  /**
36
34
  * Returns true if `type` is "slice".
37
35
  *
@@ -63,6 +61,27 @@ export const stringFields = (fields) => fields.filter((f) => isWasmString(f.type
63
61
  * @internal
64
62
  */
65
63
  export const enumName = (opts, name) => opts.uppercaseEnums ? name.toUpperCase() : name;
64
+ /**
65
+ * Takes an array of strings or splits given string into lines, word wraps and
66
+ * then prefixes each line with given `width` and `prefix`. Returns array of new
67
+ * lines.
68
+ *
69
+ * @param prefix
70
+ * @param str
71
+ * @param width
72
+ */
73
+ export const prefixLines = (prefix, str, width) => (isString(str)
74
+ ? wordWrapLines(str, { width: width - prefix.length })
75
+ : str.flatMap((x) => wordWrapLine(x, { width: width - prefix.length }))).map((line) => prefix + line);
76
+ export const ensureLines = (src, key) => isString(src)
77
+ ? split(src)
78
+ : isArray(src)
79
+ ? src
80
+ : key
81
+ ? src[key]
82
+ ? ensureLines(src[key], key)
83
+ : []
84
+ : [];
66
85
  /**
67
86
  * Yields iterator of given lines, each with applied indentation based on given
68
87
  * scope regexp's which are applied to each line to increase or decrease
package/codegen/zig.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { CodeGenOptsBase, ICodeGen } from "../api.js";
1
+ import type { CodeGenOpts, CodeGenOptsBase, Field, ICodeGen, Struct, Union } from "../api.js";
2
2
  /**
3
3
  * Zig code generator options.
4
4
  */
@@ -15,4 +15,9 @@ export interface ZigOpts extends CodeGenOptsBase {
15
15
  * @param opts
16
16
  */
17
17
  export declare const ZIG: (opts?: Partial<ZigOpts>) => ICodeGen;
18
+ /** @internal */
19
+ export declare const fieldType: (parent: Struct | Union, f: Field, opts: CodeGenOpts) => {
20
+ type: string;
21
+ defaultVal: string;
22
+ };
18
23
  //# sourceMappingURL=zig.d.ts.map
package/codegen/zig.js CHANGED
@@ -1,8 +1,7 @@
1
1
  import { isNumber } from "@thi.ng/checks/is-number";
2
2
  import { isString } from "@thi.ng/checks/is-string";
3
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";
4
+ import { ensureLines, enumName, isPadding, isStringSlice, isWasmString, prefixLines, withIndentation, } from "./utils.js";
6
5
  /**
7
6
  * Zig code generator. Call with options and then pass to {@link generateTypes}
8
7
  * (see its docs for further usage).
@@ -39,81 +38,110 @@ export const ZIG = (opts = {}) => {
39
38
  lines.push(line + ",");
40
39
  }
41
40
  if (e.body?.zig) {
42
- lines.push("", ...split(e.body.zig), "");
41
+ lines.push("", ...ensureLines(e.body.zig, "impl"), "");
43
42
  }
44
43
  lines.push("};", "");
45
44
  acc.push(...withIndentation(lines, INDENT, ...SCOPES));
46
45
  },
47
46
  struct: (struct, _, acc, opts) => {
48
- const name = struct.name;
49
- const res = [];
50
- res.push(`pub const ${name} = ${struct.tag ? struct.tag + " " : ""}struct {`);
51
- const ftypes = {};
52
- let padID = 0;
53
- for (let f of struct.fields) {
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 = "";
70
- switch (f.tag) {
71
- case "array":
72
- ftype =
73
- f.sentinel !== undefined
74
- ? `[${f.len}:${f.sentinel}]${ftype}`
75
- : `[${f.len}]${ftype}`;
76
- break;
77
- case "slice":
78
- ftype = `[${f.sentinel !== undefined ? ":" + f.sentinel : ""}]${f.const ? "const " : ""}${ftype}`;
79
- break;
80
- case "vec":
81
- ftype = `@Vector(${f.len}, ${ftype})`;
82
- break;
83
- case "ptr":
84
- ftype = `*${f.const ? "const " : ""}${f.len ? `[${f.len}]` : ""}${ftype}`;
85
- break;
86
- case "scalar":
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
- }
94
- }
95
- ftypes[f.name] = ftype;
96
- res.push(`${f.name}: ${ftype}${defaultVal},`);
97
- }
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
- }
113
- }
114
- res.push("");
115
- acc.push(...withIndentation(res, INDENT, ...SCOPES));
47
+ acc.push(...withIndentation([
48
+ `pub const ${struct.name} = ${struct.tag ? struct.tag + " " : ""}struct {`,
49
+ ...__generateFields(gen, struct, opts),
50
+ ], INDENT, ...SCOPES));
51
+ },
52
+ union: (union, _, acc, opts) => {
53
+ acc.push(...withIndentation([
54
+ `pub const ${union.name} = ${union.tag ? union.tag + " " : ""}union {`,
55
+ ...__generateFields(gen, union, opts),
56
+ ], INDENT, ...SCOPES));
116
57
  },
117
58
  };
118
59
  return gen;
119
60
  };
61
+ const __generateFields = (gen, parent, opts) => {
62
+ const res = [];
63
+ const ftypes = {};
64
+ const isUnion = parent.type === "union";
65
+ const name = parent.name;
66
+ let padID = 0;
67
+ for (let f of parent.fields) {
68
+ // autolabel explicit padding fields
69
+ if (isPadding(f)) {
70
+ parent.tag === "packed"
71
+ ? __packedPadding(padID, f.pad, res)
72
+ : res.push(`__pad${padID}: [${f.pad}]u8,`);
73
+ padID++;
74
+ continue;
75
+ }
76
+ f.doc && gen.doc(f.doc, res, opts);
77
+ const { type, defaultVal } = fieldType(parent, f, opts);
78
+ ftypes[f.name] = type;
79
+ res.push(`${f.name}: ${type}${defaultVal},`);
80
+ }
81
+ if (parent.body?.zig) {
82
+ res.push("", ...ensureLines(parent.body.zig, "impl"), "");
83
+ }
84
+ res.push("};");
85
+ if (opts.debug) {
86
+ const fn = (fname, body) => res.push("", `export fn ${name}_${fname}() usize {`, `return ${body};`, `}`);
87
+ fn("align", `@alignOf(${name})`);
88
+ fn("size", `@sizeOf(${name})`);
89
+ for (let f of parent.fields) {
90
+ if (isPadding(f))
91
+ continue;
92
+ fn(f.name + "_align", `@alignOf(${ftypes[f.name]})`);
93
+ !isUnion &&
94
+ fn(f.name + "_offset", `@offsetOf(${name}, "${f.name}")`);
95
+ fn(f.name + "_size", `@sizeOf(${ftypes[f.name]})`);
96
+ }
97
+ }
98
+ res.push("");
99
+ return res;
100
+ };
101
+ /** @internal */
102
+ export const fieldType = (parent, f, opts) => {
103
+ let type = isWasmString(f.type)
104
+ ? isStringSlice(opts.stringType)
105
+ ? f.const !== false
106
+ ? "[]const u8"
107
+ : "[]u8"
108
+ : f.const !== false
109
+ ? "[*:0]const u8"
110
+ : "[*:0]u8"
111
+ : f.type;
112
+ let defaultVal = "";
113
+ switch (f.tag) {
114
+ case "array":
115
+ type =
116
+ f.sentinel !== undefined
117
+ ? `[${f.len}:${f.sentinel}]${type}`
118
+ : `[${f.len}]${type}`;
119
+ break;
120
+ case "slice":
121
+ type = `[${f.sentinel !== undefined ? ":" + f.sentinel : ""}]${f.const ? "const " : ""}${type}`;
122
+ break;
123
+ case "vec":
124
+ type = `@Vector(${f.len}, ${type})`;
125
+ break;
126
+ case "ptr":
127
+ type = `*${f.const ? "const " : ""}${f.len ? `[${f.len}]` : ""}${type}`;
128
+ break;
129
+ case "scalar":
130
+ default:
131
+ if (f.default != undefined) {
132
+ if (!(isString(f.default) || isNumber(f.default))) {
133
+ unsupported(`wrong default value for ${parent.name}.${f.name} (${f.default})`);
134
+ }
135
+ defaultVal = ` = ${JSON.stringify(f.default)}`;
136
+ }
137
+ }
138
+ return { type, defaultVal };
139
+ };
140
+ const __packedPadding = (id, n, res) => {
141
+ let i = 0;
142
+ n <<= 3;
143
+ for (; n >= 128; n -= 128, i++) {
144
+ res.push(`__pad${id}_${i}: u128,`);
145
+ }
146
+ res.push(`__pad${id}_${i}: u${n},`);
147
+ };
package/codegen.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { CodeGenOpts, ICodeGen, TypeColl } from "./api.js";
2
+ export declare const DEFAULT_CODEGEN_OPTS: CodeGenOpts;
2
3
  /**
3
4
  * Takes a type collection and analyzes each analyzed to compute individual
4
5
  * alignments and sizes.
@@ -11,7 +12,7 @@ import { CodeGenOpts, ICodeGen, TypeColl } from "./api.js";
11
12
  *
12
13
  * @internal
13
14
  */
14
- export declare const prepareTypes: (types: TypeColl, opts: CodeGenOpts) => void;
15
+ export declare const prepareTypes: (types: TypeColl, opts: CodeGenOpts) => TypeColl;
15
16
  /**
16
17
  * Code generator main entry point. Takes an object of {@link TopLevelType}
17
18
  * definitions, an actual code generator implementation for a single target