UNPKG

tstosc

Version:

A transpiler that convert TypeScript to SuperCollider's SCLang.

442 lines (436 loc) 24.5 kB
'use strict'; var ts = require('typescript'); var context_cjs = require('../context.cjs'); var expr_conv_cjs = require('./expr_conv.cjs'); var code_block_conv_cjs = require('./code_block_conv.cjs'); var literal_conv_cjs = require('./literal_conv.cjs'); var error_cjs = require('../../../util/error.cjs'); var name_decl_def_hoist_cjs = require('../name_decl_def_hoist.cjs'); var util_cjs = require('../../../util/util.cjs'); var ts_cjs = require('../../../util/ts.cjs'); function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; } var ts__default = /*#__PURE__*/_interopDefault(ts); const supported_statement_syntax_kind = [ ts__default.default.SyntaxKind.VariableStatement, ts__default.default.SyntaxKind.ExpressionStatement, ts__default.default.SyntaxKind.IfStatement, ts__default.default.SyntaxKind.DoStatement, ts__default.default.SyntaxKind.WhileStatement, ts__default.default.SyntaxKind.ForStatement, ts__default.default.SyntaxKind.ForInStatement, ts__default.default.SyntaxKind.ForOfStatement, ts__default.default.SyntaxKind.ContinueStatement, ts__default.default.SyntaxKind.BreakStatement, ts__default.default.SyntaxKind.ReturnStatement, // Not recommended and Not supported in this project: ts.SyntaxKind.WithStatement, ts__default.default.SyntaxKind.SwitchStatement, ts__default.default.SyntaxKind.LabeledStatement, ts__default.default.SyntaxKind.ThrowStatement, ts__default.default.SyntaxKind.TryStatement, // Might be implemented in the future: ts.SyntaxKind.DebuggerStatement, ts__default.default.SyntaxKind.EmptyStatement ]; function convertTSStatementToSC(stmt, generator_context = context_cjs.default_generator_context) { const { indent_level, is_standalone_statement } = generator_context; switch (stmt.kind) { // `import` statement. // TODO: If there is any need to process this kind of statement. case ts__default.default.SyntaxKind.ImportDeclaration: return ""; // Convert only after-while-initialiser. See function `isAfterwhileInitInitialiser`. case ts__default.default.SyntaxKind.VariableStatement: return restyleTSVariableStatementToSC(stmt, generator_context).indent(is_standalone_statement ? indent_level : 0); // Might be code block used as scope in `statements`. case ts__default.default.SyntaxKind.Block: return code_block_conv_cjs.convertTSCodeBlockToSC(stmt, generator_context); case ts__default.default.SyntaxKind.FunctionDeclaration: return converTSFunctionDeclarationToSC(stmt, generator_context); case ts__default.default.SyntaxKind.ExpressionStatement: return expr_conv_cjs.convertTSExpressionToSC( stmt.expression, generator_context.isStandalongStatement() ).indent(is_standalone_statement ? indent_level : 0) + " ;"; case ts__default.default.SyntaxKind.IfStatement: return convertTSIfStatementToSC(stmt, generator_context).indent(is_standalone_statement ? indent_level : 0); case ts__default.default.SyntaxKind.DoStatement: return convertTSDoStatementToSC(stmt, generator_context).indent(is_standalone_statement ? indent_level : 0); case ts__default.default.SyntaxKind.LabeledStatement: return convertTSLabeledStatementToSC(stmt, generator_context); case ts__default.default.SyntaxKind.WhileStatement: return convertTSWhileStatementToSC(stmt, generator_context).indent(is_standalone_statement ? indent_level : 0); case ts__default.default.SyntaxKind.ForStatement: return convertTSForStatementToSC(stmt, generator_context).indent(is_standalone_statement ? indent_level : 0); case ts__default.default.SyntaxKind.ForInStatement: return convertTSForInStatementToSC(stmt, generator_context).indent(is_standalone_statement ? indent_level : 0); case ts__default.default.SyntaxKind.ForOfStatement: return convertTSForOfStatementToSC(stmt, generator_context).indent(is_standalone_statement ? indent_level : 0); case ts__default.default.SyntaxKind.ContinueStatement: return translateTSContinueStatementToSC(stmt, generator_context).indent(is_standalone_statement ? indent_level : 0); case ts__default.default.SyntaxKind.BreakStatement: return translateTSBreakStatementToSC(stmt, generator_context).indent(is_standalone_statement ? indent_level : 0); case ts__default.default.SyntaxKind.ReturnStatement: return translateTSReturnStatementToSC(stmt, generator_context).indent(is_standalone_statement ? indent_level : 0); case ts__default.default.SyntaxKind.SwitchStatement: return convertTSSwitchStatementToSC(stmt, generator_context).indent(is_standalone_statement ? indent_level : 0); case ts__default.default.SyntaxKind.ThrowStatement: return convertTSThrowStatementToSC(stmt, generator_context).indent(is_standalone_statement ? indent_level : 0); case ts__default.default.SyntaxKind.TryStatement: return convertTSTryStatementToSC(stmt, generator_context).indent(is_standalone_statement ? indent_level : 0); // Solved by class definition generation (to User Extension Dir). case ts__default.default.SyntaxKind.ClassDeclaration: // SCLang does not check type. case ts__default.default.SyntaxKind.InterfaceDeclaration: case ts__default.default.SyntaxKind.TypeAliasDeclaration: // No need to transpile. case ts__default.default.SyntaxKind.EmptyStatement: return ""; case ts__default.default.SyntaxKind.WithStatement: case ts__default.default.SyntaxKind.DebuggerStatement: default: throw error_cjs.UnsupportedTypeError.forNodeWithSyntaxKind(stmt, "statement"); } } function restyleTSVariableStatementToSC(s, generator_context = context_cjs.default_generator_context, force_conversion = false) { let collection = []; for (const d of s.declarationList.declarations) { if (ts__default.default.isIdentifier(d.name)) { collection.push([d.name.text, d.initializer]); } else { collection.push(...name_decl_def_hoist_cjs.solveRecurBinding(d.name, d.initializer)); } } const hint = name_decl_def_hoist_cjs.isLetDeclarationList(s.declarationList) ? "let" : "const"; return collection.filter( (p) => p[1] != void 0 && (force_conversion || name_decl_def_hoist_cjs.isAfterwhileInitInitialiser(p[1])) ).map(([n, v]) => { const converted_stmts = expr_conv_cjs.convertTSExpressionToSC(v, generator_context).split(";\n"); const conv_stmt_until_last = converted_stmts.slice(0, -1).join(";\n"); return conv_stmt_until_last + (conv_stmt_until_last.length > 0 ? ";\n" : "") + `/* ${hint} */ ${n} = ${converted_stmts.at(-1)} ;`; }).join("\n"); } function converTSFunctionDeclarationToSC(s, generator_context = context_cjs.default_generator_context) { if (s.name == void 0 || s.body == void 0) { return ""; } else { return `${s.name.text} = ` + literal_conv_cjs.convertTSFunctionToSC(s, generator_context.atValuePosition()) + " ;"; } } function convertTSLabeledStatementToSC(s, generator_context = context_cjs.default_generator_context) { return convertTSStatementToSC(s.statement, generator_context.withStatementLabel(s.label.text)); } function convertIncrementor(incrementor, generator_context) { if (expr_conv_cjs.isSelfIndecrExpression(incrementor)) { const operator = incrementor.operator == ts__default.default.SyntaxKind.PlusPlusToken ? "+" : "-"; const target = expr_conv_cjs.convertTSExpressionToSC(incrementor.operand, generator_context.atValuePosition()); return `/* increment */ ${target} = ${target} ${operator} 1 `; } else { return expr_conv_cjs.convertTSExpressionToSC(incrementor, generator_context.isStandalongStatement()); } } function convertControlFlowBody(b, generator_context, incrementor, is_a_loop = false) { if (b == void 0) { return "{ }"; } const { statement_label, is_nested_loop } = generator_context; let generator_context_new = generator_context.withStatementLabel("").isNestedLoop().clearedStatementLabel(); if (generator_context_new.with_loop_interrupt == null) { if (util_cjs.isArrayLike(b)) { generator_context_new.with_loop_interrupt = false; for (let i = 0; i < b.length; i++) { if (ts__default.default.isBreakOrContinueStatement(b[i])) { generator_context_new.with_loop_interrupt = true; break; } } } else { generator_context_new.with_loop_interrupt = ts__default.default.isBlock(b) ? b.forEachChild( function traverse(n) { if (ts__default.default.isBreakOrContinueStatement(n)) { return true; } else { return n.forEachChild(traverse) ?? false; } } ) ?? false : ts__default.default.isBreakOrContinueStatement(b); } } let result = ""; if (util_cjs.isArrayLike(b)) { let converted = []; for (let i = 0; i < b.length; i++) { converted.push(convertTSStatementToSC(b[i], generator_context_new)); } result = converted.join("\n"); } else if (ts__default.default.isBlock(b)) { result = code_block_conv_cjs.convertTSCodeBlockToSC(b, generator_context_new); } else if (!ts__default.default.isEmptyStatement(b)) { result = convertTSStatementToSC(b, generator_context_new.isStandalongStatement(false)); } if (generator_context_new.with_loop_interrupt && is_a_loop) { result = "/* loop body */\nvar tstosc__loop_should_break = block { |loop_end|\n" + // If there is also a label, put it before all statement. ((statement_label != "" ? `var /* label */ tstosc__label__${statement_label} = loop_end ; ` : "") + result + "\n/* Dafault value for tstosc__loop_should_break */\nfalse ;").indent(1) + ` } ; /* Loop-Should-Break Checkpoint */ if (tstosc__loop_should_break) { ${is_nested_loop ? "loop_end.value(true) ;" : "^nil ;"} } ;`; } result += incrementor != void 0 ? "\n" + convertIncrementor(incrementor, generator_context_new) + " ;" : ""; const stmt_len = result.split("\n").length; if (stmt_len > 1) { return "{\n" + result.indent(stmt_len > 1 ? 1 : 0) + "\n}"; } else if (stmt_len == 1) { return "{ " + result + " }"; } else { return "{ }"; } } function convertTSIfStatementToSC(s, generator_context = context_cjs.default_generator_context) { const { is_standalone_statement, indent_level, statement_label } = generator_context; const generator_context_new = generator_context.clearedStatementLabel(); const sep = is_standalone_statement ? "\n" : " "; let result = "if(" + (is_standalone_statement ? "\n" : "") + expr_conv_cjs.convertTSExpressionToSC(s.expression, generator_context_new).indent(indent_level + 1) + "," + sep + convertControlFlowBody(s.thenStatement, generator_context_new).indent(indent_level + 1) + "," + sep + convertControlFlowBody(s.elseStatement, generator_context_new).indent(indent_level + 1) + (is_standalone_statement ? "\n)" : ")") + " ;"; result = wrapIfLabelExist(result, statement_label); return result; } function convertTSDoStatementToSC(s, generator_context = context_cjs.default_generator_context) { const iter_body_name = "~tstosc__do_body__" + util_cjs.hash(s.statement).slice(0, 8); const iter_body_run = iter_body_name + ".()"; return iter_body_name + ` = ${convertControlFlowBody(s.statement, generator_context.makeBreakMeans("stop_a_loop"), void 0, true)} ; ` + iter_body_run + ` ; while({ ${expr_conv_cjs.convertTSExpressionToSC(s.expression, generator_context)} }, { ${iter_body_run} }) ;`; } function convertTSWhileStatementToSC(s, generator_context = context_cjs.default_generator_context) { const { is_standalone_statement, indent_level } = generator_context; const raw_condition = expr_conv_cjs.convertTSExpressionToSC(s.expression, generator_context); const condition = raw_condition.includes("\n") ? "{\n" + raw_condition.indent(1) + "\n}" : "{ " + raw_condition + " },"; const body = convertControlFlowBody(s.statement, generator_context.makeBreakMeans("stop_a_loop"), void 0, true); return "while(" + (is_standalone_statement ? "\n" : "") + condition.indent(indent_level + 1) + (is_standalone_statement ? "\n" : " ") + body.indent(indent_level + 1) + (is_standalone_statement ? "\n)" : ")") + " ;"; } function convertTSForStatementToSC(s, generator_context = context_cjs.default_generator_context) { const { is_standalone_statement, indent_level } = generator_context; const generator_context_new = generator_context.makeBreakMeans("stop_a_loop"); const sep = is_standalone_statement ? "\n" : " "; function convertForInitialiser(init_part2) { if (init_part2 == void 0) { return ""; } if (ts__default.default.isVariableDeclarationList(init_part2)) { return restyleTSVariableStatementToSC( ts__default.default.factory.createVariableStatement(void 0, init_part2), generator_context_new, true // Forcing the conversion. ); } else if (ts__default.default.isExpression(init_part2)) { return expr_conv_cjs.convertTSExpressionToSC(init_part2, generator_context_new); } else { throw error_cjs.UnsupportedTypeError.forNodeWithSyntaxKind(init_part2, "for statement initialiser"); } } function convertForCondition(cond) { if (cond == void 0) { return ""; } else { return `{ ${expr_conv_cjs.convertTSExpressionToSC(cond, generator_context_new)} }`; } } const init_part = convertForInitialiser(s.initializer); return init_part + (init_part.length > 0 ? "\n" : "") + "while(" + (is_standalone_statement ? "\n " : "") + convertForCondition(s.condition) + "," + sep + convertControlFlowBody(s.statement, generator_context_new, s.incrementor, true).indent(indent_level + 1) + (is_standalone_statement ? "\n)" : ")") + " ;"; } function convertTSForInOrForOfStatement__Impl(s, kind, generator_context = context_cjs.default_generator_context) { const body = convertControlFlowBody( s.statement, generator_context.outdent(), /* incrementor */ void 0, /* is_a_loop */ true ).replace(/^{\s?|\s?}$/g, ""); const is_multi_line_body = body.includes("\n"); const sep = is_multi_line_body ? "\n" : " "; const is_complex_initialiser = !(ts__default.default.isVariableDeclarationList(s.initializer) && ts__default.default.isIdentifier( s.initializer.declarations[0].name )) && !ts__default.default.isIdentifier(s.initializer); const initialiser_arg = is_complex_initialiser ? "tstosc__loopvar" : expr_conv_cjs.escapeForSCVarIfNeeded((ts__default.default.isVariableDeclarationList(s.initializer) ? s.initializer.declarations[0].name : s.initializer).text); const loopvar_pattern_solving = is_complex_initialiser ? name_decl_def_hoist_cjs.getDefDeclOutput("loopvar", name_decl_def_hoist_cjs.solveRecurBinding( s.initializer.declarations[0].name, { ...ts__default.default.factory.createIdentifier("tstosc__loopvar"), kind: ts__default.default.SyntaxKind.Identifier, text: "tstosc__loopvar", forEachChild: () => void 0 } )) : ""; return expr_conv_cjs.convertTSExpressionToSC(s.expression, generator_context.clearedStatementLabel()) + ".do" + sep + (kind == "for-in" ? "{ /* for-in */ |tstosc__drop_arg, " + initialiser_arg + "|" + sep : "{ /* for-of */ |" + initialiser_arg + ", tstosc__drop_arg|" + sep) + (loopvar_pattern_solving != "" ? loopvar_pattern_solving.indent(1) + sep : "") + body.indent(is_multi_line_body ? 1 : 0) + sep + "}"; } function convertTSForInStatementToSC(s, generator_context = context_cjs.default_generator_context) { return convertTSForInOrForOfStatement__Impl(s, "for-in", generator_context); } function convertTSForOfStatementToSC(s, generator_context = context_cjs.default_generator_context) { return convertTSForInOrForOfStatement__Impl(s, "for-of", generator_context); } function translateTSContinueStatementToSC(s, generator_context = context_cjs.default_generator_context) { return "/* continue */ " + (s.label != void 0 ? `tstosc__label__${s.label.text}` : "loop_end") + ".value(false) ;"; } function translateTSBreakStatementToSC(s, generator_context = context_cjs.default_generator_context) { const break_means = s.label == void 0 ? generator_context.break_means : "end_label"; switch (break_means) { // But if the `break` comes with a label, that means breaking case "end_label": case "stop_a_loop": return "/* break */ " + (s.label != void 0 ? `tstosc__label__${s.label.text}` : "loop_end") + ".value(true) ;"; // A early break of a switch's case. case "end_switch_case": return "/* break */ tstosc__switch_break.value(nil) ;"; case "nothing": throw TypeError("`break` here is not associated with switch/loop."); } } function translateTSReturnStatementToSC(s, generator_context = context_cjs.default_generator_context) { const return_expr = s.expression != void 0 ? expr_conv_cjs.convertTSExpressionToSC(s.expression, generator_context.atValuePosition()) : "nil /* no-expr return */"; if (generator_context.is_generating_method) { return "^" + return_expr; } else { return (generator_context.with_early_return ? `return_with.value(${return_expr})` : return_expr) + " ;"; } } function convertTSSwitchStatementToSC(s, generator_context = context_cjs.default_generator_context) { const { statement_label } = generator_context; const generator_context_new = generator_context.makeBreakMeans("end_switch_case").clearedStatementLabel(); const case_clause_pack = s.caseBlock.clauses.map( function(c) { const break_stmt_index = c.statements.findIndex( (s2) => ( // TODO: still problematic. ts__default.default.isBreakStatement(s2) || s2.forEachChild( function findEarlyBreak(n) { if (ts__default.default.isBreakStatement(s2)) { return true; } else { return n.forEachChild(findEarlyBreak) ?? false; } } ) ) ); const has_early_break = break_stmt_index >= 0 && break_stmt_index < c.statements.length - 1; const is_case_clause = ts__default.default.isCaseClause(c); const last_stmt = c.statements.at(-1); const has_trailing_break = last_stmt != void 0 && ts__default.default.isBreakStatement(last_stmt) && last_stmt.label == void 0; const is_fallthrough = is_case_clause && !has_early_break && !has_trailing_break && c.statements.length == 0; return { c, /* Check if there is a `break` statement that ends case earlier. */ has_early_break, has_trailing_break, is_fallthrough, is_case_clause, condition_value: is_case_clause ? expr_conv_cjs.convertTSExpressionToSC(c.expression, generator_context_new) : "/* otherwise */ 0/0", // Since `NaN` does not equal to itself. Useful for fallthrough to `default`. fallthrough_jump_value: null }; } ).reduceRight( function(accu, curr) { if (curr.is_case_clause && curr.is_fallthrough) { const prev = accu.at(-1); curr.fallthrough_jump_value = prev == void 0 ? curr.condition_value : prev.is_fallthrough ? prev.fallthrough_jump_value : prev.condition_value; } accu.push(curr); return accu; }, [] ).reverse(); function convertCase({ c, has_early_break, has_trailing_break, is_case_clause, condition_value, is_fallthrough, fallthrough_jump_value }, index, clauses_pack_array) { const hint = is_case_clause ? "/* case */ " : "/* otherwise */ "; const cond = is_case_clause ? "{ " + condition_value + " }," : ""; const body_raw = is_fallthrough ? `{ /* fallthough */ thisFunction.value(${fallthrough_jump_value}); }` : convertControlFlowBody(has_trailing_break ? c.statements.slice(0, -1) : c.statements, generator_context_new); const body = has_early_break ? `{ block { |tstosc__switch_break| ${body_raw.replace(/^{\s|\s}$/g, "")} } }` : body_raw; const sep = body.includes("\n") ? "\n" : " "; return (hint + cond + sep + body).indent(1); } const value_to_test = expr_conv_cjs.convertTSExpressionToSC(s.expression, generator_context_new); const converted_clauses = case_clause_pack.map((c, index, arr) => convertCase(c)).join(",\n"); let result = ""; if (case_clause_pack.some((p) => p.is_fallthrough)) { result = `{ |tstosc__test_value| /* switch(${value_to_test}) */ ` + ("switch( tstosc__test_value,\n" + converted_clauses + "\n) ;").indent(1) + ` }.value(${value_to_test}) ;`; } else { result = "switch( " + value_to_test + ",\n" + converted_clauses + "\n) ;"; } result = wrapIfLabelExist(result, statement_label); return result; } function wrapIfLabelExist(converted, label) { if (label == "" || label == void 0) { return converted; } else { return `block { |tstosc__label__${label}| ${converted.indent(1)} } ;`; } } function convertTSThrowStatementToSC(s, generator_context = context_cjs.default_generator_context) { const expr_type = ts_cjs.getTypeOfTSNode(generator_context.compiler_program, s.expression); if ((expr_type.getBaseTypes()?.map((t) => t.symbol.name).includes("Error") ?? false) || expr_type.symbol.name == "Error") { return `${expr_conv_cjs.convertTSExpressionToSC(s.expression, generator_context)}.throw() ;`; } else { return `Error(${expr_conv_cjs.convertTSExpressionToSC(s.expression, generator_context)}).throw() ;`; } } function convertTSTryStatementToSC(s, generator_context = context_cjs.default_generator_context) { const { statement_label } = generator_context; const generator_context_new = generator_context.clearedStatementLabel(); const try_block = convertControlFlowBody(s.tryBlock, generator_context_new); const has_catch_block = s.catchClause != void 0; const has_catch_var = s.catchClause?.variableDeclaration != void 0; const is_catch_var_identifier = has_catch_var && ts__default.default.isIdentifier(s.catchClause.variableDeclaration.name); const catch_block_raw = has_catch_block ? convertControlFlowBody(s.catchClause.block, generator_context_new) : ""; const catch_var = has_catch_block && has_catch_var ? is_catch_var_identifier ? expr_conv_cjs.escapeForSCVarIfNeeded(s.catchClause.variableDeclaration.name.text) : "tstosc__catchvar" : ""; const catch_var_solving_part = has_catch_block && has_catch_var && !is_catch_var_identifier ? "\n" + name_decl_def_hoist_cjs.getDefDeclOutput("catchvar", name_decl_def_hoist_cjs.solveRecurBinding( s.catchClause.variableDeclaration.name, { ...ts__default.default.factory.createIdentifier("tstosc__catchvar"), kind: ts__default.default.SyntaxKind.Identifier, text: "tstosc__catchvar", forEachChild: () => void 0 } ), generator_context_new) : ""; const catch_block = has_catch_block ? catch_block_raw.replace(/^{/g, `{ |${catch_var}|${catch_var_solving_part}`) : ""; let result = "try" + (try_block.includes("\n") ? "\n" : " ") + try_block + "\n/* catch */" + (catch_block.includes("\n") ? "\n" : " ") + (has_catch_block ? catch_block : " { }"); if (s.finallyBlock != void 0) { const finally_block = code_block_conv_cjs.convertTSCodeBlockToSC(s.finallyBlock, generator_context_new); result = "protect\n{" + result.indent(1) + "}\n/* finally */\n{" + finally_block.indent(1) + "} ;"; } result = wrapIfLabelExist(result, statement_label); return result; } exports.converTSFunctionDeclarationToSC = converTSFunctionDeclarationToSC; exports.convertTSDoStatementToSC = convertTSDoStatementToSC; exports.convertTSForInStatementToSC = convertTSForInStatementToSC; exports.convertTSForOfStatementToSC = convertTSForOfStatementToSC; exports.convertTSForStatementToSC = convertTSForStatementToSC; exports.convertTSIfStatementToSC = convertTSIfStatementToSC; exports.convertTSLabeledStatementToSC = convertTSLabeledStatementToSC; exports.convertTSStatementToSC = convertTSStatementToSC; exports.convertTSSwitchStatementToSC = convertTSSwitchStatementToSC; exports.convertTSThrowStatementToSC = convertTSThrowStatementToSC; exports.convertTSTryStatementToSC = convertTSTryStatementToSC; exports.convertTSWhileStatementToSC = convertTSWhileStatementToSC; exports.restyleTSVariableStatementToSC = restyleTSVariableStatementToSC; exports.supported_statement_syntax_kind = supported_statement_syntax_kind; exports.translateTSBreakStatementToSC = translateTSBreakStatementToSC; exports.translateTSContinueStatementToSC = translateTSContinueStatementToSC; exports.translateTSReturnStatementToSC = translateTSReturnStatementToSC;