tstosc
Version:
A transpiler that convert TypeScript to SuperCollider's SCLang.
96 lines (93 loc) • 4.14 kB
JavaScript
import ts from 'typescript';
import { default_generator_context } from '../context.js';
import { convertTSStatementToSC } from './stmt_conv.js';
import { solveRecurBinding, generateVarConstDeclarationAndDefinition } from '../name_decl_def_hoist.js';
import { isSelfIndecrExpression } from './expr_conv.js';
import { memorised } from '../../../util/util.js';
import { Result } from '../../../util/Result.js';
import { convertTSClassToSCWithClassName } from './class_conv.js';
const default_convert_file_option = {
convert_file: true,
convert_class: true
};
function convertTSSourceFileToSC(source_file, generator_context = default_generator_context, option_from_user = default_convert_file_option) {
const option = { ...default_convert_file_option, ...option_from_user };
const preprocessing_pack = findPreprocessingNeededByCached(source_file);
generator_context = { ...generator_context, ...preprocessing_pack.exceptional_syntax };
generator_context.is_standalone_statement = true;
let result = { file: "", class: "" };
try {
if (option.convert_file) {
const def_decl_stmts = generateVarConstDeclarationAndDefinition(source_file);
result.file = def_decl_stmts + (def_decl_stmts.length > 0 ? "\n" : "") + generateHelperEnvironment(preprocessing_pack.exceptional_syntax, generator_context) + source_file.statements.map((s) => convertTSStatementToSC(s, generator_context)).filter((s) => s.length > 0).join("\n");
}
if (option.convert_class) {
result.class = preprocessing_pack.class_definitions.map(
({ name, definition }) => convertTSClassToSCWithClassName(definition, name, generator_context)
).join("\n\n");
}
} catch (err) {
return Result.createErr(err);
}
return Result.createOk(result);
}
const findPreprocessingNeededByCached = memorised(
findPreprocessingNeeded,
function makeKey(source_file) {
return source_file.fileName;
}
);
function findPreprocessingNeeded(source_file) {
let exceptional_syntax = {
has_unhandled_self_indecr_operator: false
};
let class_definitions = [];
function traverse(node) {
if (!exceptional_syntax.has_unhandled_self_indecr_operator && isSelfIndecrExpression(node)) {
exceptional_syntax.has_unhandled_self_indecr_operator = true;
} else if (ts.isClassDeclaration(node)) {
class_definitions.push({
name: escapeToSCClassName(node.name?.text ?? `${source_file.fileName.replace(/^[\.\/]|\.ts$/, "")}__default_export`),
definition: node
});
} else if (ts.isClassExpression(node)) {
let getVariableDeclarationParent2 = function(n) {
if (ts.isVariableDeclaration(n)) {
return n;
} else {
return getVariableDeclarationParent2(n.parent);
}
};
const { name: pattern, initializer: initialiser } = getVariableDeclarationParent2(node);
const def_decl_collection = solveRecurBinding(pattern, initialiser);
class_definitions.push(
...def_decl_collection.filter((d) => d[1] != void 0 && ts.isClassExpression(d[1])).map(([name, definition]) => ({ name, definition }))
);
}
{
node.forEachChild(traverse);
}
}
traverse(source_file);
return { exceptional_syntax, class_definitions };
}
function escapeToSCClassName(name) {
if (name.length == 0) {
throw TypeError(`The class name to escape is empty.`);
}
name = name.replace(/[^\w]/g, "_");
return name[0].toUpperCase() + name.slice(1);
}
function generateHelperEnvironment(exceptional_syntax, generator_context) {
let result = [];
if (exceptional_syntax.has_unhandled_self_indecr_operator) {
result.push(...[
"~tstosc__pre_incr = {|r|r.value=r.value+1;r.value} ;",
"~tstosc__post_incr = {|r|var t=r.value;r.value=r.value+1;t} ;",
"~tstosc__pre_decr = {|r|r.value=r.value-1;r.value} ;",
"~tstosc__post_decr = {|r|var t=r.value;r.value=r.value-1;t} ;"
]);
}
return result.length > 0 ? "/* Helper Environment */\n" + result.join("\n") + "\n\n" : "";
}
export { convertTSSourceFileToSC, default_convert_file_option, findPreprocessingNeeded, findPreprocessingNeededByCached };