UNPKG

tstosc

Version:

A transpiler that convert TypeScript to SuperCollider's SCLang.

96 lines (93 loc) 4.14 kB
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 };