Compare commits
6 Commits
38038d710b
...
6e281c40aa
Author | SHA1 | Date |
---|---|---|
Jill | 6e281c40aa | |
Jill | ead6aa7066 | |
Jill | 84320e5aab | |
Jill | a43df3e28b | |
Jill | c0b1105ee4 | |
Jill | ff84493fac |
178
index.js
178
index.js
|
@ -8,6 +8,56 @@ const outPath = path.resolve(__dirname, './out/');
|
|||
|
||||
let classes = {};
|
||||
|
||||
let tsToLuaTypes = {
|
||||
undefined: 'nil',
|
||||
unknown: 'any',
|
||||
}
|
||||
|
||||
function transpileType(type) {
|
||||
if (!type) return 'what';
|
||||
type = type.trim();
|
||||
|
||||
if (type.startsWith('(') && type.endsWith(')')) type = type.slice(1, -1);
|
||||
|
||||
if (tsToLuaTypes[type]) {
|
||||
return tsToLuaTypes[type];
|
||||
}
|
||||
|
||||
if (type.endsWith('[]')) {
|
||||
return transpileType(type.slice(0, -2)) + '[]';
|
||||
}
|
||||
|
||||
if (type.includes('|')) {
|
||||
return type.split('|').map(t => transpileType(t)).join(' | ');
|
||||
}
|
||||
|
||||
if (type.startsWith('"')) {
|
||||
return 'string';
|
||||
}
|
||||
|
||||
if (type.startsWith('Record<') && type.endsWith('>')) {
|
||||
return 'table<' + transpileType(type.split(',')[0].replace('Record<','')) + ', ' + transpileType(type.split(',')[1].replace('>','')) + '>';
|
||||
}
|
||||
if (type.startsWith('LuaTable<') && type.endsWith('>')) {
|
||||
return 'table<' + transpileType(type.split(',')[0].replace('LuaTable<','')) + ', ' + transpileType(type.split(',')[1].replace('>','')) + '>';
|
||||
}
|
||||
|
||||
let match;
|
||||
|
||||
if (match = /\((.*?)\) ?=> ?(.*)/.exec(type)) { // functions
|
||||
return `fun(${match[1] !== '' ? match[1].split(',').map(c => c.split(':')[0] + ':' + transpileType(c.split(':')[1])).join(',') : ''}): ${transpileType(match[2])}`;
|
||||
}
|
||||
|
||||
if (match = /(?:Array)|(?:Map)<(.*?)>/.exec(type)) {
|
||||
return transpileType(match[1]) + '[]';
|
||||
}
|
||||
|
||||
if (match = /(.*?)<(.*?)>/.exec(type)) { // any other weird type we just discard
|
||||
return transpileType(match[2]);
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
function transpile(parsed, indent, prefix) {
|
||||
let global = false;
|
||||
indent = indent || 0;
|
||||
|
@ -29,11 +79,11 @@ function transpile(parsed, indent, prefix) {
|
|||
}
|
||||
|
||||
classes[d.name] = contents;
|
||||
return `---@class ${d.name}${n}${contents.filter(e => e[0] === 'const').map(c => `---@field public ${c[1].name} ${c[1].type}${n}`).join('')}${global ? '' : '__class_'}${d.name} = {}${n}${contents.filter(e => e[0] !== 'const').map(c => transpile(c, indent, `${global ? '' : '__class_'}${d.name}`)).join(n)}\n`;
|
||||
return `---@class ${d.name}${n}${contents.filter(e => e[0] === 'const').map(c => `---@field public ${c[1].name} ${transpileType(c[1].type)}${n}`).join('')}${global ? '' : '__class_'}${d.name} = {}${n}${contents.filter(e => e[0] !== 'const').map(c => transpile(c, indent, `${global ? '' : '__class_'}${d.name}`)).join(n)}\n`;
|
||||
case 'function':
|
||||
return `${d.arguments.map(p => `---@param ${p.name.replace('?', '')} ${p.type}${n}`).join('')}---@return ${d.returns}${n}function ${prefix ? prefix + ':' : ''}${d.name}(${d.arguments.map(a => a.name.replace('?', '')).join(', ')}) end`;
|
||||
return `${d.arguments.map(p => `---@param ${p.name.replace('?', '')} ${transpileType(p.type)}${n}`).join('')}---@return ${transpileType(d.returns)}${n}function ${prefix ? prefix + ':' : ''}${d.name}(${d.arguments.map(a => a.name.replace('?', '')).join(', ')}) end`;
|
||||
case 'const':
|
||||
return `---@type ${d.type}${n}${prefix ? prefix + '.' : ''}${d.name} = nil`;
|
||||
return `---@type ${transpileType(d.type)}${n}${prefix ? prefix + '.' : ''}${d.name} = nil`;
|
||||
case 'enum':
|
||||
return `${prefix ? prefix + '.' : ''}${d.name} = {${n} ${d.contents.map(c => `${c.name} = ${c.value}`).join(`,${n} `)}${n}}`;
|
||||
}
|
||||
|
@ -48,6 +98,8 @@ function removeComments(code) {
|
|||
return code;
|
||||
}
|
||||
|
||||
let parsedElements = 0;
|
||||
|
||||
/**
|
||||
* @param {string} code
|
||||
*/
|
||||
|
@ -61,12 +113,14 @@ function parse(code) {
|
|||
// sanitize stuff
|
||||
code = removeComments(code);
|
||||
|
||||
const elements = [];
|
||||
let match;
|
||||
let elements = [];
|
||||
|
||||
// look for interfaces
|
||||
let interfaceFreeCode = code;
|
||||
const interface = /interface (\w+)(?: extends (\w+))? ?{([^}]*)}/g;
|
||||
while ((match = interface.exec(code)) !== null) {
|
||||
parsedElements++;
|
||||
interfaceFreeCode = interfaceFreeCode.replace(match[0], '');
|
||||
elements.push(['interface', {name: match[1], extends: match[2], contents: parse(match[3])}]);
|
||||
}
|
||||
|
@ -76,6 +130,7 @@ function parse(code) {
|
|||
let namespaceFreeCode = code;
|
||||
const namespace = /namespace (\w+) ?{([^}]*)}/g;
|
||||
while ((match = namespace.exec(code)) !== null) {
|
||||
parsedElements++;
|
||||
namespaceFreeCode = namespaceFreeCode.replace(match[0], '');
|
||||
elements.push(['namespace', {name: match[1], contents: parse(match[2])}]);
|
||||
}
|
||||
|
@ -83,8 +138,9 @@ function parse(code) {
|
|||
|
||||
// look for functions
|
||||
let functionFreeCode = code;
|
||||
const functions = /(function)?\s(\w+)\s*\(([^;]*)\)(\s*:\s*([\w\[\]]+))?/g; // wow this sucks
|
||||
const functions = /(function)?\s(\w+)\s*\(([^;]*)\)(\s*:\s*([\w\[\]\s|<>,]+))?/g; // wow this sucks
|
||||
while ((match = functions.exec(code)) !== null) {
|
||||
parsedElements++;
|
||||
functionFreeCode = functionFreeCode.replace(match[0], '');
|
||||
let arguments = match[3].split(',').map(s => {
|
||||
const split = s.split(':').map(s => s.trim());
|
||||
|
@ -98,6 +154,7 @@ function parse(code) {
|
|||
let enumFreeCode = code;
|
||||
const enums = /enum (\w+) ?{([^}]*)}/g;
|
||||
while ((match = enums.exec(code)) !== null) {
|
||||
parsedElements++;
|
||||
enumFreeCode = enumFreeCode.replace(match[0], '');
|
||||
elements.push(['enum', {name: match[1], contents: match[2].split(',').filter(c => c.split('=').length === 2).map(c => {return {name: c.split('=')[0].trim(), value: c.split('=')[1].trim()}})}]);
|
||||
}
|
||||
|
@ -107,14 +164,33 @@ function parse(code) {
|
|||
let constFreeCode = code;
|
||||
const consts = /(\w+)\s*:\s*(\w+)/g;
|
||||
while ((match = consts.exec(code)) !== null) {
|
||||
parsedElements++;
|
||||
constFreeCode = constFreeCode.replace(match[0], '');
|
||||
elements.push(['const', {name: match[1], type: match[2]}]);
|
||||
}
|
||||
code = constFreeCode;
|
||||
|
||||
elements = elements.sort((a, b) => b[1].extends ? 1 : 0 - a[1].extends ? 1 : 0).reverse(); // for extending bullshit
|
||||
|
||||
return elements;
|
||||
}
|
||||
|
||||
async function recursiveReaddir(rpath, p, fileNames) {
|
||||
p = p || '';
|
||||
fileNames = (fileNames || []).slice();
|
||||
const files = await fs.readdir(rpath);
|
||||
for (const f of files) {
|
||||
const stat = await fs.lstat(path.join(rpath, f));
|
||||
if (stat.isDirectory() || stat.isSymbolicLink()) {
|
||||
fileNames.push(...await recursiveReaddir(path.join(rpath, f), p + f + '/', fileNames));
|
||||
} else {
|
||||
fileNames.push(p + f);
|
||||
}
|
||||
}
|
||||
|
||||
return fileNames;
|
||||
}
|
||||
|
||||
(async () => {
|
||||
let timeParsing = 0;
|
||||
let timeTotal = 0;
|
||||
|
@ -134,64 +210,68 @@ function parse(code) {
|
|||
let errored = [];
|
||||
let errorReason = {};
|
||||
|
||||
const files = await fs.readdir(declarationsPath);
|
||||
const files = await recursiveReaddir(declarationsPath);
|
||||
const filesAmt = files.length;
|
||||
let i = 0;
|
||||
for (const f of files) {
|
||||
i++;
|
||||
const filePath = path.resolve(declarationsPath, f);
|
||||
|
||||
const stat = await fs.lstat(filePath);
|
||||
if (stat.isFile()) {
|
||||
const file = await fs.readFile(filePath, 'utf8');
|
||||
console.log(` ${chalk.cyanBright(f)}`);
|
||||
const file = await fs.readFile(filePath, 'utf8');
|
||||
|
||||
let startParse = Date.now();
|
||||
const parsed = parse(file);
|
||||
console.log(` parsed ${chalk.magentaBright(parsed.length)} elements from ${chalk.cyanBright(f)}`);
|
||||
timeParsing += Date.now() - startParse;
|
||||
let startParse = Date.now();
|
||||
parsedElements = 0;
|
||||
const parsed = parse(file);
|
||||
console.log(` parsed ${chalk.magentaBright(parsedElements)} objects from ${chalk.cyanBright(f)}`);
|
||||
timeParsing += Date.now() - startParse;
|
||||
|
||||
if (parsed.length === 0) {
|
||||
console.log(`${chalk.redBright('!')} no elements parsed, ${chalk.gray('not writing anything')}`);
|
||||
continue;
|
||||
if (parsed.length === 0) {
|
||||
console.log(`${chalk.redBright('!')} no elements parsed, ${chalk.gray('not writing anything')}`);
|
||||
continue;
|
||||
}
|
||||
if (parsed.length > 10) {
|
||||
console.log(`${chalk.redBright('!')} over 10 top-level objects were parsed - this may be a bad idea`);
|
||||
}
|
||||
|
||||
let transpiled;
|
||||
try {
|
||||
transpiled = parsed.map(p => transpile(p)).join('\n').trim();
|
||||
errored = errored.filter(e => e !== f);
|
||||
} catch(err) {
|
||||
errorReason[f] = err;
|
||||
if (errored.length > files.length - i) {
|
||||
console.error(`${chalk.redBright('!')} failed resolving extends!`);
|
||||
errored = [...new Set(errored)];
|
||||
console.error(`${chalk.redBright('!')} abandoned ${chalk.magentaBright(errored.length)} files:`);
|
||||
let longest = errored.reduce((a, b) => Math.max(a.length || a || 0, b.length));
|
||||
errored.forEach(e => console.error(` - ${chalk.cyanBright(e)}${' '.repeat(longest - e.length + 2)}${chalk.gray(' → ' + errorReason[e].message.replace('Class not found in class storage: ', ''))}`));
|
||||
break;
|
||||
}
|
||||
|
||||
let transpiled;
|
||||
try {
|
||||
transpiled = parsed.map(p => transpile(p)).join('\n').trim();
|
||||
errored = errored.filter(e => e !== f);
|
||||
} catch(err) {
|
||||
errorReason[f] = err;
|
||||
if (errored.length > files.length - i) {
|
||||
console.error(`${chalk.redBright('!')} failed resolving extends!`);
|
||||
console.error(`${chalk.redBright('!')} abandoned ${chalk.magentaBright(errored.length)} files:`);
|
||||
let longest = errored.reduce((a, b) => Math.max(a.length || a || 0, b.length));
|
||||
errored.forEach(e => console.error(` - ${chalk.cyanBright(e)}${' '.repeat(longest - e.length + 2)}${chalk.gray(' -> ' + errorReason[e].message.replace('Class not found in class storage: ', ''))}`));
|
||||
break;
|
||||
}
|
||||
console.error(`${chalk.yellowBright('!')} ${err}`);
|
||||
console.error(`${chalk.yellowBright('!')} pushing to end of queue`);
|
||||
files.push(f);
|
||||
errored.push(f);
|
||||
|
||||
console.error(`${chalk.yellowBright('!')} ${err}`);
|
||||
console.error(`${chalk.yellowBright('!')} pushing to end of queue`);
|
||||
files.push(f);
|
||||
errored.push(f);
|
||||
|
||||
timeTotal += Date.now() - startParse;
|
||||
|
||||
continue;
|
||||
}
|
||||
console.log(` transpiled w/ final length of ${chalk.magentaBright(transpiled.length + ' chars')}`);
|
||||
timeTotal += Date.now() - startParse;
|
||||
|
||||
if (transpiled.length === 0) {
|
||||
console.log(`${chalk.redBright('!')} nothing transpiled, ${chalk.gray('not writing anything')}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
//console.log(inspect(parsed, false, 10));
|
||||
const luaFilename = f.replace('.d.ts', '.lua');
|
||||
await fs.writeFile(path.resolve(outPath, luaFilename), transpiled);
|
||||
console.log(` wrote ${chalk.cyanBright(luaFilename)}`);
|
||||
continue;
|
||||
}
|
||||
console.log(` transpiled w/ final length of ${chalk.magentaBright(transpiled.length + ' chars')}`);
|
||||
timeTotal += Date.now() - startParse;
|
||||
|
||||
if (transpiled.length === 0) {
|
||||
console.log(`${chalk.redBright('!')} nothing transpiled, ${chalk.gray('not writing anything')}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
//console.log(inspect(parsed, false, 10));
|
||||
const luaFilename = f.replace('.d.ts', '.lua').split('/').pop();
|
||||
await fs.writeFile(path.resolve(outPath, luaFilename), transpiled);
|
||||
//console.log(` wrote ${chalk.cyanBright(luaFilename)}`);
|
||||
}
|
||||
|
||||
console.log(`\nfinished transpiling in ${chalk.magentaBright(timeTotal + 'ms')} ${chalk.gray(`(${Math.floor(timeParsing/timeTotal * 1000 + 0.5) / 10}% spent parsing)`)}`);
|
||||
console.log(`\nfinished transpiling ${chalk.magentaBright(filesAmt)} ${chalk.gray(`+ ${files.length - filesAmt}`)} files in ${chalk.magentaBright(timeTotal + 'ms')} ${chalk.gray(`(${Math.floor(timeParsing/timeTotal * 1000 + 0.5) / 10}% spent parsing)`)}`);
|
||||
console.log(`check ${chalk.cyanBright(outPath)}`);
|
||||
})();
|
Loading…
Reference in New Issue