267 lines
6.4 KiB
JavaScript
267 lines
6.4 KiB
JavaScript
import { parseArgs } from "util";
|
|
|
|
let types = {
|
|
logical: {
|
|
c: "char",
|
|
js: "boolean",
|
|
sql: "INTEGER",
|
|
size: 1,
|
|
},
|
|
u8: {
|
|
c: "unsigned char",
|
|
js: "Uint8",
|
|
sql: "INTEGER",
|
|
size: 1,
|
|
},
|
|
i8: {
|
|
c: "char",
|
|
js: "Int8",
|
|
sql: "INTEGER",
|
|
size: 1,
|
|
},
|
|
u16: {
|
|
c: "unsigned short",
|
|
js: "Uint16",
|
|
sql: "INTEGER",
|
|
size: 2,
|
|
},
|
|
i16: {
|
|
c: "short",
|
|
js: "Int16",
|
|
sql: "INTEGER",
|
|
size: 2,
|
|
},
|
|
u32: {
|
|
c: "unisgned int",
|
|
js: "Uint32",
|
|
sql: "INTEGER",
|
|
size: 4,
|
|
},
|
|
i32: {
|
|
c: "int",
|
|
js: "Int32",
|
|
sql: "INTEGER",
|
|
size: 4,
|
|
},
|
|
i64: {
|
|
c: "long",
|
|
js: "BigInt64",
|
|
sql: "INTEGER"
|
|
},
|
|
u64: {
|
|
c: "unsigned long",
|
|
js: "BigUint64",
|
|
sql: "INTEGER"
|
|
},
|
|
f32: {
|
|
c: "float",
|
|
js: "Float32",
|
|
sql: "REAL",
|
|
size: 4,
|
|
},
|
|
f64: {
|
|
c: "double",
|
|
js: "Float64",
|
|
sql: "REAL",
|
|
size: 8,
|
|
},
|
|
};
|
|
|
|
const { values } = parseArgs({
|
|
args: Bun.argv,
|
|
options: {
|
|
schema: {
|
|
type: "string",
|
|
short: "S",
|
|
},
|
|
javascript_out: {
|
|
type: "string",
|
|
short: "j",
|
|
},
|
|
c_out: {
|
|
type: "string",
|
|
short: "c",
|
|
},
|
|
sqlite_out: {
|
|
type: "string",
|
|
short: "s"
|
|
}
|
|
},
|
|
strict: true,
|
|
allowPositionals: true,
|
|
});
|
|
|
|
function jsStructConstructor(size, containsString, type, args) {
|
|
return `
|
|
/**
|
|
* Get the struct representation of the object
|
|
* @return {Uint8Array} u8 array of the C struct.
|
|
*/
|
|
get bytes() {
|
|
return new Uint8Array(this._ptr);
|
|
}
|
|
/**
|
|
* Constructs a new ${type}
|
|
*
|
|
* @param ${args} init The arguments to construct the object.
|
|
* @param {ArrayBuffer} ptr The pointer to the C struct.
|
|
*/
|
|
constructor(init = {}, ptr = undefined) {
|
|
this._size = ${size};
|
|
this._ptr = ptr?.buffer || new ArrayBuffer(this._size);
|
|
this._data = new DataView(this._ptr);
|
|
${
|
|
containsString
|
|
? `
|
|
this._encoder = new TextEncoder();
|
|
this._decoder = new TextDecoder();`
|
|
: ""
|
|
}
|
|
for (const key of Object.keys(init)) {
|
|
this[key] = init[key];
|
|
}
|
|
}`;
|
|
}
|
|
|
|
const sFile = Bun.file(values.schema);
|
|
const schema = await sFile.json();
|
|
let cData = "";
|
|
let sqlData = "";
|
|
|
|
for (const type of Object.keys(schema)) {
|
|
let containsString = false;
|
|
let offset = 0;
|
|
let size = 0;
|
|
let importStatements = "";
|
|
let jsData = "";
|
|
let foreignKeys = "";
|
|
const props = schema[type].members;
|
|
let typeDef = `/**\n * @typedef {Object} ${type} ${schema[type].comment}\n`;
|
|
let args = `{{`;
|
|
sqlData += `CREATE TABLE ${type} (id INTEGER PRIMARY KEY AUTOINCREMENT`;
|
|
cData += `typedef struct ${type} {`;
|
|
jsData += `class ${type} {`;
|
|
for (const prop of Object.keys(props)) {
|
|
const propType = props[prop].type;
|
|
const kind = props[prop].kind;
|
|
const comment = props[prop].comment;
|
|
let typeSize = parseInt(types[propType]?.size);
|
|
|
|
switch (kind) {
|
|
case "string":
|
|
containsString = true;
|
|
typeSize = props[prop].size;
|
|
const iSize = parseInt(offset) + parseInt(typeSize);
|
|
jsData += `
|
|
/**
|
|
* ${comment}
|
|
* @return {string} gets the value of ${prop}
|
|
*/
|
|
get ${prop}() {
|
|
return this._decoder.decode(new Uint8Array(this._ptr.slice(${parseInt(
|
|
offset
|
|
)}, ${iSize})));
|
|
}
|
|
/**
|
|
* ${comment}
|
|
* @param {string} v sets the value of ${prop}
|
|
*/
|
|
set ${prop}(v) {
|
|
this._data.set(this._encoder.encode(v), ${parseInt(offset)});
|
|
}`;
|
|
sqlData += `, ${prop} TEXT`;
|
|
cData += `
|
|
char ${prop}[${iSize}]; // ${comment}`;
|
|
args += `${prop}: string, `;
|
|
typeDef += ` * @property {string} ${prop} ${comment}\n`;
|
|
break;
|
|
case "scalar":
|
|
typeSize = types[propType].size;
|
|
jsData += `
|
|
/**
|
|
* ${comment}
|
|
* @return {${types[propType].js}} gets the value of ${prop}
|
|
*/
|
|
get ${prop}() {
|
|
return this._data.get${types[propType].js}(${parseInt(offset)}, true);
|
|
}
|
|
/**
|
|
* ${comment}
|
|
* @param {${types[propType].js}} sets the value of ${prop}
|
|
*/
|
|
set ${prop}(v) {
|
|
return this._data.set${types[propType].js}(${parseInt(
|
|
offset
|
|
)}, v, true);
|
|
}`;
|
|
args += `${prop}: ${types[propType].js}, `;
|
|
typeDef += ` * @property ${types[propType].js} ${propType} ${comment}\n`;
|
|
sqlData += `, ${prop} ${types[propType].sql}`;
|
|
cData += `
|
|
${types[propType].c} ${prop}; // ${comment}`;
|
|
|
|
break;
|
|
case "struct":
|
|
const jsSize = parseInt(offset) + parseInt(types[propType].size);
|
|
jsData += `
|
|
/**
|
|
* ${comment}
|
|
* @return {${types[propType].js}} gets the value of ${prop}
|
|
*/
|
|
get ${prop}() {
|
|
return new ${propType}({}, new Uint8Array(this._ptr.slice(${offset}, ${jsSize})));
|
|
}
|
|
/**
|
|
* ${comment}
|
|
* @param {${types[propType].js}} sets the value of ${prop}
|
|
*/
|
|
set ${prop}(v) {
|
|
this._data.set(v.bytes(), ${offset});
|
|
}`;
|
|
const importS = `import ${propType} from "./${propType}"\n`;
|
|
if (!importStatements.includes(importS)) {
|
|
importStatements += importS;
|
|
}
|
|
const localKey = `${prop.toLowerCase()}_id`;
|
|
args += `${prop}: ${types[propType].js}, `;
|
|
typeDef += ` * @property ${types[propType].js} ${propType} ${comment}\n`;
|
|
sqlData += `, ${localKey} INTEGER`;
|
|
foreignKeys += `\n, FOREIGN KEY(${localKey}) REFERENCES ${propType}(id)`
|
|
cData += `\n\t\t${types[propType].c} ${prop}; // ${comment}`;
|
|
break;
|
|
case "array":
|
|
throw new Error("Not Implemented!");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
size += parseInt(typeSize);
|
|
offset += parseInt(typeSize);
|
|
}
|
|
|
|
/**
|
|
* add the new type to the list so we can use it for structs later.
|
|
*/
|
|
types[type] = {
|
|
c: type,
|
|
js: type,
|
|
size: parseInt(size),
|
|
};
|
|
|
|
typeDef += " */\n"
|
|
args += `}}`
|
|
jsData = typeDef + jsData;
|
|
jsData += jsStructConstructor(size, containsString, type, args);
|
|
jsData += `\n}\n\nexport default ${type}`;
|
|
cData += `\n} ${type};\n\n`;
|
|
sqlData += `${foreignKeys});\n\n`;
|
|
|
|
await Bun.write(
|
|
Bun.file(values.javascript_out + type + ".js"),
|
|
importStatements + jsData
|
|
);
|
|
}
|
|
await Bun.write(Bun.file(values.c_out + "types.h"), cData);
|
|
await Bun.write(Bun.file(values.sqlite_out + "types.sql"), sqlData);
|