new bindgen
This commit is contained in:
parent
094e597416
commit
20df39ee4e
1 changed files with 219 additions and 9 deletions
|
|
@ -1,6 +1,7 @@
|
|||
import { XMLParser } from "fast-xml-parser";
|
||||
import { createWriteStream } from "node:fs";
|
||||
import { createWriteStream, existsSync, mkdirSync, statSync } from "node:fs";
|
||||
import { createRequire } from "node:module";
|
||||
import { join } from "node:path";
|
||||
|
||||
const { upstream } = createRequire(import.meta.url)("../package.json");
|
||||
const source =
|
||||
|
|
@ -10,6 +11,7 @@ const source =
|
|||
const structsOut = createWriteStream("lib/structs.ts");
|
||||
const enumsOut = createWriteStream("lib/enums.ts");
|
||||
const constantsOut = createWriteStream("lib/constants.ts");
|
||||
const widgetOut = createWriteStream("lib/widget.ts");
|
||||
|
||||
function toCamelCase(input) {
|
||||
const split = input.split("_");
|
||||
|
|
@ -33,6 +35,8 @@ function typeToTypescript(type) {
|
|||
return "number";
|
||||
case "string":
|
||||
return "string";
|
||||
case "pixmap":
|
||||
return "Pixmap";
|
||||
default:
|
||||
"any";
|
||||
}
|
||||
|
|
@ -42,7 +46,6 @@ function scanStructs(root) {
|
|||
const list = root["structs"]["struct"];
|
||||
|
||||
for (const struct of list) {
|
||||
console.log(struct);
|
||||
const structName = struct["@_name"];
|
||||
structsOut.write("export interface " + structName + " {\n");
|
||||
|
||||
|
|
@ -51,8 +54,14 @@ function scanStructs(root) {
|
|||
if (!type.startsWith("@_") && Array.isArray(children)) {
|
||||
for (const child of children) {
|
||||
const attributeName = toCamelCase(child["@_name"]);
|
||||
let attributeType = typeToTypescript(type);
|
||||
|
||||
if (type == "struct") {
|
||||
attributeType = child["@_defname"];
|
||||
}
|
||||
|
||||
structsOut.write(
|
||||
attributeName + ": " + typeToTypescript(type) + ";\n"
|
||||
attributeName + ": " + attributeType + ";\n"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -115,11 +124,207 @@ function scanConstants(root) {
|
|||
constantsOut.end();
|
||||
}
|
||||
|
||||
function getPropertyInfo(root, query) {
|
||||
const properties = root["properties"];
|
||||
|
||||
for (const [type, children] of Object.entries(properties)) {
|
||||
const match = children.find((k) => k["@_name"] == query);
|
||||
if (match) {
|
||||
return {
|
||||
boolean: match["@_boolean"] == "yes",
|
||||
defName: match["@_defname"],
|
||||
type,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getPropertySignature(inputName, inputType) {
|
||||
let prefix = null,
|
||||
setter = null,
|
||||
getter = null;
|
||||
|
||||
switch (inputType) {
|
||||
case "string":
|
||||
prefix = "S";
|
||||
setter = "setString";
|
||||
getter = "getString";
|
||||
break;
|
||||
case "integer":
|
||||
prefix = "I";
|
||||
setter = "setInteger";
|
||||
getter = "getInteger";
|
||||
break;
|
||||
case "handler":
|
||||
prefix = "C";
|
||||
setter = "addUserHandler";
|
||||
break;
|
||||
default:
|
||||
prefix = "V";
|
||||
setter = "setVoid";
|
||||
getter = "getVoid";
|
||||
break;
|
||||
}
|
||||
|
||||
return { prefix, setter, getter };
|
||||
}
|
||||
|
||||
function createPropertyMethods(name, type, isBoolean, defName = "unknown") {
|
||||
const output = [];
|
||||
|
||||
// fluent setter
|
||||
const { prefix, setter, getter } = getPropertySignature(name, type);
|
||||
const methodName = toCamelCase(
|
||||
"set" + name.charAt(0).toUpperCase() + name.slice(1));
|
||||
const propertyId = prefix + name;
|
||||
|
||||
let finalType = isBoolean ? "boolean" : typeToTypescript(type);
|
||||
const finalValue = isBoolean ? "+value" : "value";
|
||||
if (type == "struct") finalType = defName;
|
||||
|
||||
output.push(
|
||||
// signature
|
||||
"public " + methodName + "(value: " + finalType + "): this {\n" +
|
||||
|
||||
// call internal method
|
||||
"return this." + setter + "(\"" + propertyId + "\"," +
|
||||
finalValue + ");\n}\n\n"
|
||||
);
|
||||
|
||||
// property setter and getter
|
||||
const getterName = toCamelCase(name);
|
||||
let cast = type == "pixmap" ? " as Pixmap" : "";
|
||||
|
||||
if (isBoolean) cast = " != 0"
|
||||
else if (type == "struct") cast = "as " + defName
|
||||
|
||||
output.push(
|
||||
// getter
|
||||
"get " + getterName + "(): " + finalType + " {\n" +
|
||||
"return this." + getter + "(\"" + propertyId +
|
||||
"\")" + cast + ";\n}\n\n" +
|
||||
|
||||
// setter
|
||||
"set " + getterName + "(value: " + finalType + ") {\n" +
|
||||
"this." + setter + "(\"" + propertyId + "\", " + finalValue + ");\n}\n\n"
|
||||
);
|
||||
|
||||
return output.join("");
|
||||
}
|
||||
|
||||
function scanProperties(root) {
|
||||
const properties = root["properties"];
|
||||
|
||||
widgetOut.write(
|
||||
"import { BaseWidget } from \"./milsko\"\n" +
|
||||
"import { Pixmap } from \"./types\"\n\n" +
|
||||
"export class Widget extends BaseWidget {\n"
|
||||
);
|
||||
|
||||
for (const [type, children] of Object.entries(properties)) {
|
||||
for (const prop of children) {
|
||||
if (prop["@_common"] != "yes") continue;
|
||||
|
||||
const name = prop["@_name"];
|
||||
const isBoolean = prop["@_boolean"] == "yes";
|
||||
|
||||
widgetOut.write(createPropertyMethods(name, type, isBoolean));
|
||||
}
|
||||
}
|
||||
|
||||
widgetOut.write("\n}");
|
||||
widgetOut.close();
|
||||
}
|
||||
|
||||
function scanWidgets(root) {
|
||||
const widgets = root["widgets"]["widget"];
|
||||
const unsupported = ["OpenGL", "Vulkan"];
|
||||
|
||||
// first loop through widgets to detect for imports and print lines
|
||||
for (const widget of widgets) {
|
||||
const widgetName = widget["@_name"];
|
||||
if (unsupported.includes(widgetName)) continue;
|
||||
|
||||
const typeImports = ["WidgetOptions"];
|
||||
const structImports = [];
|
||||
const lines = [];
|
||||
|
||||
const widgetFile = createWriteStream(
|
||||
join("lib/widgets/", toCamelCase(widgetName) + ".ts")
|
||||
);
|
||||
|
||||
// scan properties
|
||||
if (widget["properties"]) {
|
||||
const widgetProperties = widget["properties"]["property"];
|
||||
|
||||
for (const prop of widgetProperties) {
|
||||
const name = prop["@_name"];
|
||||
let { boolean, defName, type } = getPropertyInfo(root, name);
|
||||
|
||||
if (type == "struct") {
|
||||
if (!structImports.includes(defName))
|
||||
structImports.push(defName);
|
||||
}
|
||||
|
||||
if (type == "pixmap") {
|
||||
if (!typeImports.includes("Pixmap"))
|
||||
typeImports.push("Pixmap");
|
||||
}
|
||||
|
||||
// create methods
|
||||
lines.push(createPropertyMethods(name, type, boolean, defName))
|
||||
}
|
||||
}
|
||||
|
||||
lines.push("}\n");
|
||||
|
||||
// after looping write the contents in the correct order
|
||||
widgetFile.write(
|
||||
"import { NativeClasses } from \"../milsko\"\n" +
|
||||
"import { Widget } from \"../widget\"\n"
|
||||
);
|
||||
|
||||
if (structImports.length) {
|
||||
widgetFile.write(
|
||||
"import { " + structImports.join(", ") +
|
||||
" } from \"../structs\"\n"
|
||||
);
|
||||
}
|
||||
|
||||
widgetFile.write(
|
||||
"import { " + typeImports.join(", ") +
|
||||
" } from \"../types\"\n"
|
||||
);
|
||||
|
||||
// init class
|
||||
widgetFile.write("\nexport class " + widgetName + " extends Widget {\n");
|
||||
|
||||
// constructor
|
||||
widgetFile.write(
|
||||
"constructor(options: WidgetOptions) {\n" +
|
||||
"super(NativeClasses." + widgetName + ", options);" +
|
||||
"}\n\n"
|
||||
);
|
||||
|
||||
for (const line of lines) {
|
||||
widgetFile.write(line);
|
||||
}
|
||||
|
||||
widgetFile.close();
|
||||
}
|
||||
}
|
||||
|
||||
function isArray(tagName) {
|
||||
if (tagName == "property" || tagName == "struct")
|
||||
return true;
|
||||
}
|
||||
|
||||
async function bindgen() {
|
||||
const parser = new XMLParser({
|
||||
attributeNamePrefix: "@_",
|
||||
ignoreAttributes: false,
|
||||
parseAttributeValue: true
|
||||
parseAttributeValue: true,
|
||||
isArray
|
||||
});
|
||||
|
||||
const response = await fetch(source);
|
||||
|
|
@ -135,14 +340,17 @@ async function bindgen() {
|
|||
const match = tags.find((k) => k.name == upstream);
|
||||
|
||||
if (!match) {
|
||||
throw new Error(
|
||||
"Could not find a tag that matches the upstream version " + upstream
|
||||
console.warn(
|
||||
"Could not find a tag that matches the upstream version " + upstream +
|
||||
", using as commit hash instead."
|
||||
);
|
||||
}
|
||||
|
||||
const commitHash = match["id"]
|
||||
const specURL = "https://forgejo.nishi.boats/pyrite-dev/milsko/raw/commit/" +
|
||||
commitHash + "/milsko.xml";
|
||||
const commitHash = match ? match["id"] : upstream;
|
||||
const specURL =
|
||||
"https://forgejo.nishi.boats/pyrite-dev/milsko/raw/commit/" +
|
||||
commitHash +
|
||||
"/milsko.xml";
|
||||
|
||||
const spac = await fetch(specURL);
|
||||
if (!spac.ok) {
|
||||
|
|
@ -158,6 +366,8 @@ async function bindgen() {
|
|||
scanStructs(root);
|
||||
scanEnumerations(root);
|
||||
scanConstants(root);
|
||||
scanProperties(root);
|
||||
scanWidgets(root);
|
||||
}
|
||||
|
||||
bindgen();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue