tstosc
Version:
A transpiler that convert TypeScript to SuperCollider's SCLang.
179 lines (176 loc) • 7.37 kB
JavaScript
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 };