Skip to content

Commit

Permalink
feat(wasm-api): update/fix codegens & config
Browse files Browse the repository at this point in the history
- add global `CodeGenOpts.uppercaseEnums` option (migrate from TSOpts)
- set `i32` as enum default tag (for C compatibility)
- update C11 codegen, add `typePrefix` option
- fix C11 enum codegen (use `typedef enum`)
- add internal enumName() helper
- minor updates Zig codegen
  • Loading branch information
postspectacular committed Sep 9, 2022
1 parent b05cfcb commit 87def23
Show file tree
Hide file tree
Showing 6 changed files with 72 additions and 27 deletions.
11 changes: 10 additions & 1 deletion packages/wasm-api/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,10 @@ export interface StructField extends TypeInfo {
export interface Enum extends TopLevelType {
type: "enum";
/**
* No i64/u64 support, due to Typescript not supporting bigint enum values
* No i64/u64 support, due to Typescript not supporting bigint enum values.
* For C compatibility only i32 or u32 is allowed.
*
* @defaultValue "i32"
*/
tag: Exclude<WasmPrim32, FloatType>;
/**
Expand Down Expand Up @@ -366,6 +369,12 @@ export interface CodeGenOpts {
* @defaultValue "slice"
*/
stringType: "slice" | "ptr";
/**
* If true (default), forces uppercase enum identifiers
*
* @defaultValue true
*/
uppercaseEnums: boolean;
/**
* Unless set to false, the generated output will be prefixed with a header
* line comment of generator meta data
Expand Down
5 changes: 4 additions & 1 deletion packages/wasm-api/src/codegen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,9 @@ const alignOf = defmulti<TopLevelType | StructField, TypeColl, number>(
return align;
},

enum: (e) => {
enum: (type) => {
const e = <Enum>type;
if (!e.tag) e.tag = "i32";
return (e.__align = SIZEOF[(<Enum>e).tag]);
},

Expand Down Expand Up @@ -174,6 +176,7 @@ export const generateTypes = (
const $opts = <CodeGenOpts>{
header: true,
stringType: "slice",
uppercaseEnums: true,
...opts,
};
prepareTypes(types, $opts);
Expand Down
35 changes: 26 additions & 9 deletions packages/wasm-api/src/codegen/c11.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { isString } from "@thi.ng/checks/is-string";
import { unsupported } from "@thi.ng/errors/unsupported";
import type { ICodeGen, WasmPrim } from "../api.js";
import {
enumName,
isPadding,
isStringSlice,
prefixLines,
Expand Down Expand Up @@ -32,6 +34,10 @@ export interface C11Opts {
* Optional postfix (inserted after the generated code)
*/
post: string;
/**
* Optional name prefix for generated types, e.g. `WASM_`.
*/
typePrefix: string;
}

/**
Expand All @@ -45,8 +51,12 @@ export interface C11Opts {
* @param opts
*/
export const C11 = (opts: Partial<C11Opts> = {}) => {
const { typePrefix } = {
typePrefix: "",
...opts,
};
const INDENT = " ";
const SCOPES: [RegExp, RegExp] = [/\{$/, /\}\)?[;,]?$/];
const SCOPES: [RegExp, RegExp] = [/\{$/, /^\}[ A-Za-z0-9_]*[;,]?$/];

const gen: ICodeGen = {
pre: (opts) => `#pragma once
Expand All @@ -60,26 +70,32 @@ ${opts.debug ? "\n#include <stdalign.h>" : ""}
acc.push(prefixLines("// ", doc));
},

enum: (e, _, acc) => {
enum: (e, _, acc, opts) => {
if (!(e.tag === "i32" || e.tag === "u32")) {
unsupported(
`enum ${e.name} must be a i32/u32 in C, but got '${e.tag}'`
);
}
const name = typePrefix + e.name;
const lines: string[] = [];
lines.push(`enum {`);
lines.push(`typedef enum {`);
for (let v of e.values) {
let line: string;
if (!isString(v)) {
v.doc && gen.doc(v.doc, lines);
line = `${e.name}_${v.name}`;
line = enumName(opts, v.name);
if (v.value != null) line += ` = ${v.value}`;
} else {
line = v;
line = enumName(opts, v);
}
lines.push(line + ",");
}
lines.push("};", "");
lines.push(`} ${name};`, "");
acc.push(...withIndentation(lines, INDENT, ...SCOPES));
},

struct: (struct, _, acc, opts) => {
const name = struct.name;
struct: (struct, coll, acc, opts) => {
const name = typePrefix + struct.name;
const res: string[] = [];
res.push(`typedef struct ${name} ${name};`, `struct ${name} {`);
const ftypes: Record<string, string> = {};
Expand All @@ -98,6 +114,7 @@ ${opts.debug ? "\n#include <stdalign.h>" : ""}
? __slice("char", fconst)
: `${f.const !== false ? "const " : ""}char*`
: PRIM_ALIASES[<WasmPrim>f.type] || f.type;
if (coll[ftype]) ftype = typePrefix + ftype;
switch (f.tag) {
case "array":
case "vec":
Expand All @@ -121,9 +138,9 @@ ${opts.debug ? "\n#include <stdalign.h>" : ""}
res.push("};");

if (opts.debug) {
res.push("");
const fn = (fname: string, body: string) =>
res.push(
"",
`size_t __attribute__((used)) ${name}_${fname}() {`,
`return ${body};`,
`}`
Expand Down
18 changes: 6 additions & 12 deletions packages/wasm-api/src/codegen/typescript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
WasmPrim,
} from "../api.js";
import {
enumName,
isBigNumeric,
isNumeric,
isPadding,
Expand All @@ -40,12 +41,6 @@ export interface TSOpts {
* @defaultValue "\t"
*/
indent: string;
/**
* If true (default), forces uppercase enums
*
* @defaultValue true
*/
uppercaseEnums: boolean;
/**
* Optional prelude (inserted after the main TS prelude)
*/
Expand All @@ -68,10 +63,9 @@ export interface TSOpts {
* @param opts
*/
export const TYPESCRIPT = (opts: Partial<TSOpts> = {}) => {
const { indent, uppercaseEnums } = <TSOpts>{
const { indent } = <TSOpts>{
indent: "\t",
stringType: "slice",
uppercaseEnums: true,
...opts,
};

Expand All @@ -95,17 +89,17 @@ import { Pointer, ${__stringImpl(
}
},

enum: (e, _, acc) => {
enum: (e, _, acc, opts) => {
const res: string[] = [];
res.push(`export enum ${e.name} {`);
for (let v of e.values) {
let line = indent;
let line: string;
if (!isString(v)) {
v.doc && gen.doc(v.doc, res);
line += uppercaseEnums ? v.name.toUpperCase() : v.name;
line = enumName(opts, v.name);
if (v.value != null) line += ` = ${v.value}`;
} else {
line += uppercaseEnums ? v.toUpperCase() : v;
line = enumName(opts, v);
}
res.push(line + ",");
}
Expand Down
21 changes: 21 additions & 0 deletions packages/wasm-api/src/codegen/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,23 +43,44 @@ export const prefixLines = (prefix: string, str: string | string[]) =>

/**
* Returns true if `type` is "slice".
*
* @param type
*/
export const isStringSlice = (
type: CodeGenOpts["stringType"]
): type is "slice" => type === "slice";

/**
* Returns filtered array of struct fields of with "ptr" tag.
*
* @param fields
*
* @internal
*/
export const pointerFields = (fields: StructField[]) =>
fields.filter((f) => f.tag === "ptr");

/**
* Returns filtered array of struct fields of only "string" fields.
*
* @param fields
*
* @internal
*/
export const stringFields = (fields: StructField[]) =>
fields.filter((f) => isWasmString(f.type) && f.tag !== "ptr");

/**
* Returns enum identifier formatted according to given opts.
*
* @param opts
* @param name
*
* @internal
*/
export const enumName = (opts: CodeGenOpts, name: string) =>
opts.uppercaseEnums ? name.toUpperCase() : name;

/**
* Yields iterator of given lines, each with applied indentation based on given
* scope regexp's which are applied to each line to increase or decrease
Expand Down
9 changes: 5 additions & 4 deletions packages/wasm-api/src/codegen/zig.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { isString } from "@thi.ng/checks/is-string";
import type { ICodeGen } from "../api.js";
import {
enumName,
isPadding,
isStringSlice,
prefixLines,
Expand Down Expand Up @@ -44,17 +45,17 @@ export const ZIG = (opts: Partial<ZigOpts> = {}) => {
acc.push(prefixLines(topLevel ? "//! " : "/// ", doc));
},

enum: (e, _, acc) => {
enum: (e, _, acc, opts) => {
const lines: string[] = [];
lines.push(`pub const ${e.name} = enum(${e.tag}) {`);
for (let v of e.values) {
let line: string;
if (!isString(v)) {
v.doc && gen.doc(v.doc, lines);
line = v.name;
line = enumName(opts, v.name);
if (v.value != null) line += ` = ${v.value}`;
} else {
line = v;
line = enumName(opts, v);
}
lines.push(line + ",");
}
Expand Down Expand Up @@ -113,9 +114,9 @@ export const ZIG = (opts: Partial<ZigOpts> = {}) => {
res.push("};");

if (opts.debug) {
res.push("");
const fn = (fname: string, body: string) =>
res.push(
"",
`export fn ${name}_${fname}() usize {`,
`return ${body};`,
`}`
Expand Down

0 comments on commit 87def23

Please sign in to comment.