UNPKG

tstosc

Version:

A transpiler that convert TypeScript to SuperCollider's SCLang.

179 lines (176 loc) 7.37 kB
import ts from 'typescript'; import { bin, zip, ZipOption } from '../../util/util.js'; import { hasSelfIndecrExpression, escapeForSCVarIfNeeded, convertTSExpressionToSC } from './ts_to_sc_convert/expr_conv.js'; import { default_generator_context } from './context.js'; function generateVarConstDeclarationAndDefinition(node, generator_context = default_generator_context) { const { lets, consts, vars, loopvars, functions } = extractVarConstDeclarationAndDefinition(node, generator_context); const lets_output = getDefDeclOutput("let", lets, generator_context); const consts_output = getDefDeclOutput("const", consts, generator_context); const vars_output = getDefDeclOutput("var", vars, generator_context); const loopvars_output = getDefDeclOutput("loopvar", loopvars, generator_context); const functions_output = getDefDeclOutput("function", functions, generator_context); const all_output = [lets_output, consts_output, vars_output, loopvars_output, functions_output].filter((s) => s.length > 0).join("\n"); return all_output + (all_output.length > 0 ? "\n" : ""); } function extractVarConstDeclarationAndDefinition(node, generator_context = default_generator_context) { let lets = []; let consts = []; let vars = []; let loopvars = []; let functions = node.statements.filter((s) => ts.isFunctionDeclaration(s)).filter((s) => s.name != void 0).map((f) => [f.name.text, void 0]); const decl_lists = node.statements.filter((s) => ts.isVariableStatement(s)).flatMap((s) => s.declarationList); const loopvar_lists = node.statements.flatMap((s) => ts.isLabeledStatement(s) ? s.statement : s).filter((s) => isLoopvarIncludingStatement(s)); for (const l of decl_lists) { let collection; if (isLetDeclarationList(l)) { collection = lets; } else if (isConstDeclarationList(l)) { collection = consts; } else if (isVarDeclarationList(l)) { collection = vars; } else { throw TypeError(`Cannot solve the flag ${l.flags} (${bin(l.flags)}).`); } for (const d of l.declarations) { if (ts.isIdentifier(d.name)) { collection.push([d.name.text, d.initializer]); } else { collection.push(...solveRecurBinding(d.name, d.initializer)); } } } for (const s of loopvar_lists) { const initialiser = s.initializer; if (initialiser != void 0 && ts.isVariableDeclarationList(initialiser)) { for (const d of initialiser.declarations) { if (ts.isIdentifier(d.name)) { loopvars.push([d.name.text, d.initializer]); } else { loopvars.push(...solveRecurBinding(d.name, d.initializer)); } } } } return { lets, consts, vars, loopvars, functions }; } function isLoopvarIncludingStatement(s) { return ts.isForStatement(s) || ts.isForOfStatement(s) || ts.isForInStatement(s); } function getDefDeclOutput(type, collection, generator_context = default_generator_context) { const { indent_level, is_standalone_statement } = generator_context; if (collection.length > 0) { let convert2 = function([n, v]) { const name = escapeForSCVarIfNeeded(n.toString()); const init_part = v == void 0 || isAfterwhileInitInitialiser(v) ? "" : ` = ${convertTSExpressionToSC(v, generator_context.atValuePosition())}`; return `${name}${init_part}`; }; return `var /* ${type} */ ${collection.map((c) => convert2(c)).join(", ")} ;`.indent(is_standalone_statement ? indent_level : 0); } else { return ""; } } function isAfterwhileInitInitialiser(v) { return hasSelfIndecrExpression(v); } function extractNameOnly(pattern) { let result = []; function traverse(n) { if (ts.isIdentifier(n)) { result.push(n.text); } n.forEachChild(traverse); } traverse(pattern); return []; } function solveRecurBinding(pattern, initialiser) { if (initialiser == void 0) { return extractNameOnly(pattern).map((n) => [n, void 0]); } let result = []; if (ts.isArrayBindingPattern(pattern)) { if (ts.isArrayLiteralExpression(initialiser)) { const names_and_init_values = zip(pattern.elements, initialiser.elements, ZipOption.minimal_zip); for (const [n, v] of names_and_init_values) { if (ts.isBindingElement(n)) { if (ts.isIdentifier(n.name)) { result.push([n.name.text, v]); } else { result.push(...solveRecurBinding(n.name, v)); } } } } else if (ts.isIdentifier(initialiser) || ts.isExpression(initialiser)) { const names_and_init_values = zip( pattern.elements, [...new Array(pattern.elements.length)].map((_, index) => index), ZipOption.minimal_zip ); for (const [n, i] of names_and_init_values) { if (ts.isBindingElement(n)) { if (ts.isIdentifier(n.name)) { result.push([ n.name.text, // Be careful that `n` might have default parameter (`n.initializer`). n.initializer == void 0 ? ts.factory.createElementAccessExpression(initialiser, i) : ts.factory.createBinaryExpression( ts.factory.createElementAccessExpression(initialiser, i), ts.SyntaxKind.QuestionQuestionToken, n.initializer ) ]); } else { result.push(...solveRecurBinding( n.name, ts.factory.createElementAccessExpression(initialiser, i) )); } } } } } else if (ts.isObjectBindingPattern(pattern)) { let init_part_is_literal; if (ts.isObjectLiteralExpression(initialiser)) { init_part_is_literal = true; } else if (ts.isIdentifier(initialiser) || ts.isExpression(initialiser)) { init_part_is_literal = false; } else { throw TypeError(`Unknown type of initialiser ${JSON.stringify(initialiser)}.`); } for (const n of pattern.elements) { if (ts.isIdentifier(n.name)) { result.push([ n.name.text, init_part_is_literal ? initialiser.properties.find( (p) => ts.isPropertyAssignment(p) && ts.isIdentifier(p.name) && p.name.text == n.name.text ).initializer : ts.factory.createPropertyAccessExpression(initialiser, n.name.text) ]); } else if (ts.isObjectBindingPattern(n.name) && ts.isIdentifier(n.propertyName)) { result.push(...solveRecurBinding( n.name, init_part_is_literal ? initialiser.properties.find( (p) => ts.isPropertyAssignment(p) && ts.isIdentifier(p.name) && p.name.text == n.propertyName.text ).initializer : ts.factory.createPropertyAccessExpression(initialiser, n.propertyName) )); } } } else if (ts.isIdentifier(pattern)) { result.push([pattern.text, initialiser]); } return result; } function isLetDeclarationList(decl_list) { return (decl_list.flags & ts.NodeFlags.Let) > 0; } function isConstDeclarationList(decl_list) { return (decl_list.flags & ts.NodeFlags.Const) > 0; } function isVarDeclarationList(decl_list) { return decl_list.flags == ts.NodeFlags.None; } export { extractVarConstDeclarationAndDefinition, generateVarConstDeclarationAndDefinition, getDefDeclOutput, isAfterwhileInitInitialiser, isConstDeclarationList, isLetDeclarationList, isVarDeclarationList, solveRecurBinding };