tstosc
Version:
A transpiler that convert TypeScript to SuperCollider's SCLang.
427 lines (421 loc) • 22.6 kB
JavaScript
;
var ts = require('typescript');
var literal_conv_cjs = require('./literal_conv.cjs');
var error_cjs = require('../../../util/error.cjs');
var context_cjs = require('../context.cjs');
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
var ts__default = /*#__PURE__*/_interopDefault(ts);
const supported_expression_syntax_kind = [
ts__default.default.SyntaxKind.ArrayLiteralExpression,
// Considered and processed as literal.
ts__default.default.SyntaxKind.ObjectLiteralExpression,
// Considered and processed as literal.
ts__default.default.SyntaxKind.PropertyAccessExpression,
ts__default.default.SyntaxKind.ElementAccessExpression,
ts__default.default.SyntaxKind.CallExpression,
ts__default.default.SyntaxKind.NewExpression,
ts__default.default.SyntaxKind.TaggedTemplateExpression,
ts__default.default.SyntaxKind.TypeAssertionExpression,
ts__default.default.SyntaxKind.ParenthesizedExpression,
ts__default.default.SyntaxKind.FunctionExpression,
// Considered and processed as literal.
ts__default.default.SyntaxKind.ArrowFunction,
// Considered and processed as literal.
// ts.SyntaxKind.DeleteExpression, // Low impl possibility.
// ts.SyntaxKind.TypeOfExpression, // TODO.
// ts.SyntaxKind.VoidExpression, // Meaningless when writing program for SuperCollider.
// ts.SyntaxKind.AwaitExpression, // Might be impl.
ts__default.default.SyntaxKind.PrefixUnaryExpression,
ts__default.default.SyntaxKind.PostfixUnaryExpression,
ts__default.default.SyntaxKind.BinaryExpression,
ts__default.default.SyntaxKind.ConditionalExpression,
ts__default.default.SyntaxKind.TemplateExpression,
// Considered and processed as literal.
// ts.SyntaxKind.YieldExpression, // Might be supported in the future.
ts__default.default.SyntaxKind.SpreadElement,
ts__default.default.SyntaxKind.ClassExpression,
ts__default.default.SyntaxKind.OmittedExpression,
ts__default.default.SyntaxKind.ExpressionWithTypeArguments,
ts__default.default.SyntaxKind.AsExpression,
ts__default.default.SyntaxKind.NonNullExpression,
ts__default.default.SyntaxKind.MetaProperty,
// ts.SyntaxKind.SyntheticExpression, // Internal, not possible to be seen in AST.
ts__default.default.SyntaxKind.SatisfiesExpression
];
function convertTSExpressionToSC(expr, generator_context = context_cjs.default_generator_context) {
if (literal_conv_cjs.isTSLiteral(expr)) {
return literal_conv_cjs.convertTSLiteralToSC(expr, generator_context);
}
if (ts__default.default.isIdentifier(expr)) {
return (generator_context.is_generating_class_name ? escapeForSCClassIfNeeded : escapeForSCVarIfNeeded)(expr.text);
}
if (generator_context.has_unhandled_self_indecr_operator) {
const expr_to_wrap = tryFindSelfIndecrOperator(expr);
if (expr_to_wrap.length > 0) {
let getName2 = function(e) {
const id = e.getText().replace(/[^\w]/g, "_");
return "~tstosc__temp__" + (id.length == 0 ? count++ : id);
};
let count = 0;
const expr_and_names = expr_to_wrap.map((e) => [e, getName2(e)]);
const name_of_expr = `~tstosc__temp_result__${count++}`;
expr_and_names.forEach(([e, n]) => generator_context.expression_temporise_dict.set(e, n));
const result = [
// This generate things like `~tstosc__temp_a = Ref.new(a) ;`.
expr_and_names.map(([e, n]) => `${n} = \`(${convertTSExpressionToSC(e, generator_context)}) ;`).deduplicated().join("\n"),
name_of_expr + " = " + convertTSExpressionToSC(expr, {
...generator_context,
has_unhandled_self_indecr_operator: false
}) + " ;",
expr_and_names.map(([e, n]) => `${convertTSExpressionToSC(e, generator_context)} = ${n}.value ;`).deduplicated().join("\n"),
name_of_expr
].join("\n");
expr_and_names.forEach(([e, _]) => generator_context.expression_temporise_dict.delete(e));
return result;
}
}
switch (expr.kind) {
case ts__default.default.SyntaxKind.PropertyAccessExpression:
return convertTSPropertyAccessExpressionToSC(expr, generator_context);
case ts__default.default.SyntaxKind.ElementAccessExpression:
return convertTSElementAccessExpressionToSC(expr, generator_context);
case ts__default.default.SyntaxKind.CallExpression:
return convertTSCallExpressionToSC(expr, generator_context);
case ts__default.default.SyntaxKind.NewExpression:
return convertTSNewExpressionToSC(expr, generator_context);
case ts__default.default.SyntaxKind.TaggedTemplateExpression:
return convertTSTaggedTemplateExpressionToSC(expr, generator_context);
case ts__default.default.SyntaxKind.ParenthesizedExpression:
return "(" + convertTSExpressionToSC(expr.expression, generator_context) + ")";
case ts__default.default.SyntaxKind.PrefixUnaryExpression:
return translateTSPrefixUnaryExpressionToSC(expr, generator_context);
case ts__default.default.SyntaxKind.PostfixUnaryExpression:
return translateTSPostfixUnaryExpressionToSC(expr, generator_context);
case ts__default.default.SyntaxKind.BinaryExpression:
return convertTSBinaryExpressionToSC(expr, generator_context);
case ts__default.default.SyntaxKind.ConditionalExpression:
return convertTSConditionalExpressionToSC(expr, generator_context);
case ts__default.default.SyntaxKind.ThisKeyword: {
switch (generator_context.this_coming_from) {
case "object_literal_parameter":
return "tstosc__this_param";
case "built_instance":
return "tstosc__built_instance";
case "itself":
return "this";
case "nothing":
default:
throw TypeError("`this` here is not associated with outside.");
}
}
case ts__default.default.SyntaxKind.SuperKeyword: {
switch (generator_context.super_means) {
// If in a constructor, the first super call should be turn into `super.new`.
case "constructor":
return "super.new";
// If in a static method, calling `super` is equal to use super class's name.
case "class_name":
return generator_context.class_info.super_class_name;
// If it means same as `this` (in this condition, `super` is interchangable with `this`).
case "as_it_is":
return "super";
case "nothing":
default:
throw TypeError("`super` here is not associated with a class method/constructor.");
}
}
case ts__default.default.SyntaxKind.ClassExpression:
// The `...` in `[1, 2, ...[3, 4]]`.
case ts__default.default.SyntaxKind.SpreadElement:
// Handled in array transforming.
// The missing part like `let [a0, , a2] = [0, 1, 2]`, the place that should be `a1`.
case ts__default.default.SyntaxKind.OmittedExpression:
return "";
// Since sclang does not have static-typing, ignore these statement.
case ts__default.default.SyntaxKind.TypeAssertionExpression:
case ts__default.default.SyntaxKind.ExpressionWithTypeArguments:
// Like `<string>` in `Array<string>`.
case ts__default.default.SyntaxKind.AsExpression:
case ts__default.default.SyntaxKind.NonNullExpression:
case ts__default.default.SyntaxKind.SatisfiesExpression:
return convertTSExpressionToSC(expr.expression, generator_context);
case ts__default.default.SyntaxKind.TypeOfExpression:
// TODO: Will be implemented soon.
case ts__default.default.SyntaxKind.AwaitExpression:
// Might be supported in the future.
case ts__default.default.SyntaxKind.YieldExpression:
// Might be supported in the future.
case ts__default.default.SyntaxKind.MetaProperty:
// Low possibility to be implemented...
case ts__default.default.SyntaxKind.DeleteExpression:
// Low possibility to be implemented...
default:
throw error_cjs.UnsupportedTypeError.forNodeWithSyntaxKind(expr, "expression");
}
}
function isSelfIndecrExpression(e) {
return (ts__default.default.isPrefixUnaryExpression(e) || ts__default.default.isPostfixUnaryExpression(e)) && (e.operator == ts__default.default.SyntaxKind.PlusPlusToken || e.operator == ts__default.default.SyntaxKind.MinusMinusToken);
}
function hasSelfIndecrExpression(e) {
if (ts__default.default.isExpression(e) && isSelfIndecrExpression(e)) {
return true;
} else {
return e.forEachChild(hasSelfIndecrExpression) ?? false;
}
}
function tryFindSelfIndecrOperator(e) {
let result = [];
function traverse(node) {
if (ts__default.default.isExpression(node) && isSelfIndecrExpression(node)) {
result.push(
ts__default.default.isParenthesizedExpression(node.operand) ? node.operand.expression : node.operand
);
}
node.forEachChild(traverse);
}
traverse(e);
return result;
}
function isStoringObjectLiteral(node, generator_context) {
if (node.kind == ts__default.default.SyntaxKind.ThisKeyword || node.kind == ts__default.default.SyntaxKind.SuperKeyword) {
return false;
}
if (ts__default.default.isIdentifier(node) && node.text.startsWith("tstosc_dvar_")) {
return true;
}
const node_type = generator_context.compiler_program.getTypeChecker().getTypeAtLocation(node);
return "symbol" in node_type && (node_type.symbol.escapedName == "__object" || (node_type.symbol.flags & ts__default.default.SymbolFlags.ObjectLiteral) != 0);
}
function convertTSPropertyAccessExpressionToSC(e, generator_context = context_cjs.default_generator_context) {
return isStoringObjectLiteral(e.expression, generator_context) || generator_context.this_coming_from == "object_literal_parameter" ? `${convertTSExpressionToSC(e.expression, generator_context)}["${escapeForSCVarIfNeeded(e.name.text)}"]` : `${convertTSExpressionToSC(e.expression, generator_context)}.${escapeForSCVarIfNeeded(e.name.text)}`;
}
function convertTSElementAccessExpressionToSC(e, generator_context = context_cjs.default_generator_context) {
return convertTSExpressionToSC(e.expression, generator_context) + "[" + convertTSExpressionToSC(e.argumentExpression, generator_context) + "]";
}
function isMethod(node, generator_context) {
const node_type = generator_context.compiler_program.getTypeChecker().getTypeAtLocation(node);
return "symbol" in node_type && (node_type.symbol.flags & ts__default.default.SymbolFlags.Method) != 0;
}
function convertTSCallExpressionToSC(e, generator_context = context_cjs.default_generator_context) {
const is_left_side_a_constructor_super = e.expression.kind == ts__default.default.SyntaxKind.SuperKeyword && generator_context.is_generating_constructor;
const left_side = is_left_side_a_constructor_super ? convertTSExpressionToSC(e.expression, generator_context.makeSuperMeans("constructor")) : convertTSExpressionToSC(e.expression, generator_context);
const dot_or_not = ts__default.default.isPropertyAccessExpression(e.expression) || isMethod(e.expression, generator_context) || is_left_side_a_constructor_super ? "" : ".";
const result = left_side + dot_or_not + "(" + e.arguments.map((a) => convertTSExpressionToSC(a, generator_context)).join(", ") + ")";
return is_left_side_a_constructor_super ? "(tstosc__built_instance = " + result + ")" : result;
}
function convertTSNewExpressionToSC(e, generator_context = context_cjs.default_generator_context) {
return convertTSExpressionToSC(e.expression, generator_context.willGenerateClassName()) + ".new(" + (e.arguments?.map((a) => convertTSExpressionToSC(a, generator_context)).join(", ") ?? "") + ")";
}
function convertTSTaggedTemplateExpressionToSC(e, generator_context = context_cjs.default_generator_context) {
const { template, tag } = e;
if (ts__default.default.isNoSubstitutionTemplateLiteral(template)) {
return convertTSCallExpressionToSC(ts__default.default.factory.createCallExpression(
tag,
/* type_arguments */
void 0,
[
// The tag-function's first argument is the plain-text in the template.
ts__default.default.factory.createArrayLiteralExpression([ts__default.default.factory.createStringLiteral(template.text)])
]
), generator_context);
} else {
return convertTSCallExpressionToSC(ts__default.default.factory.createCallExpression(
tag,
/* type_arguments */
void 0,
[
// The tag-function's first argument is the plain-text in the template.
ts__default.default.factory.createArrayLiteralExpression([
template.head,
...template.templateSpans.map((s) => s.literal)
].map((s) => ts__default.default.factory.createStringLiteral(s.text))),
// The following argument is the template's `${}` expression part.
...template.templateSpans.map((s) => s.expression)
]
), generator_context);
}
}
function returnOrThrowIfNotInTemporiseDict(e, generator_context) {
const temp_name = generator_context.expression_temporise_dict.get(e);
if (temp_name == void 0) {
throw new error_cjs.RuntimeError(
`Expression ("${e.getText()}") should be in expression_temporise_dict, but not found.`
);
}
return temp_name;
}
function translateTSPrefixUnaryExpressionToSC(e, generator_context = context_cjs.default_generator_context) {
switch (e.operator) {
case ts__default.default.SyntaxKind.MinusToken:
return "-" + convertTSExpressionToSC(e.operand, generator_context);
case ts__default.default.SyntaxKind.ExclamationToken:
return "not(" + convertTSExpressionToSC(e.operand, generator_context) + ")";
case ts__default.default.SyntaxKind.PlusPlusToken:
return "~tstosc__pre_incr.(" + returnOrThrowIfNotInTemporiseDict(e.operand, generator_context) + ")";
case ts__default.default.SyntaxKind.MinusMinusToken:
return "~tstosc__pre_decr.(" + returnOrThrowIfNotInTemporiseDict(e.operand, generator_context) + ")";
case ts__default.default.SyntaxKind.TildeToken:
return "bitNot(" + convertTSExpressionToSC(e.operand, generator_context) + ")";
case ts__default.default.SyntaxKind.PlusToken:
return convertTSExpressionToSC(e.operand, generator_context);
default:
throw error_cjs.UnsupportedTypeError.ofSyntaxKind(
e.operator,
"unary prefix operator",
e
);
}
}
function translateTSPostfixUnaryExpressionToSC(e, generator_context = context_cjs.default_generator_context) {
switch (e.operator) {
case ts__default.default.SyntaxKind.PlusPlusToken:
return "~tstosc__post_incr.(" + returnOrThrowIfNotInTemporiseDict(e.operand, generator_context) + ")";
case ts__default.default.SyntaxKind.MinusMinusToken:
return "~tstosc__post_decr.(" + returnOrThrowIfNotInTemporiseDict(e.operand, generator_context) + ")";
default:
throw error_cjs.UnsupportedTypeError.ofSyntaxKind(
e.operator,
"unary postfix operator",
e
);
}
}
function convertTSConditionalExpressionToSC(e, generator_context = context_cjs.default_generator_context) {
return "if(" + convertTSExpressionToSC(e.condition, generator_context.atValuePosition()) + ", " + convertTSExpressionToSC(e.whenTrue, generator_context.atValuePosition()) + ", " + convertTSExpressionToSC(e.whenFalse, generator_context.atValuePosition()) + ")";
}
const ts_infix_becoming_binary_operator_to_sc_dict = /* @__PURE__ */ new Map([
[ts__default.default.SyntaxKind.PlusToken, "+"],
[ts__default.default.SyntaxKind.MinusToken, "-"],
[ts__default.default.SyntaxKind.AsteriskToken, "*"],
[ts__default.default.SyntaxKind.SlashToken, "/"],
[ts__default.default.SyntaxKind.PercentToken, "%"],
[ts__default.default.SyntaxKind.AsteriskAsteriskToken, "**"],
[ts__default.default.SyntaxKind.EqualsToken, "="],
[ts__default.default.SyntaxKind.EqualsEqualsToken, "=="],
[ts__default.default.SyntaxKind.ExclamationEqualsToken, "!="],
[ts__default.default.SyntaxKind.EqualsEqualsEqualsToken, "=="],
[ts__default.default.SyntaxKind.LessThanToken, "<"],
[ts__default.default.SyntaxKind.GreaterThanToken, ">"],
[ts__default.default.SyntaxKind.LessThanEqualsToken, "<="],
[ts__default.default.SyntaxKind.GreaterThanEqualsToken, ">="],
[ts__default.default.SyntaxKind.AmpersandAmpersandToken, "&&"],
[ts__default.default.SyntaxKind.BarBarToken, "||"],
[ts__default.default.SyntaxKind.QuestionQuestionToken, "??"]
]);
const ts_function_call_becoming_operator_to_sc_dict = /* @__PURE__ */ new Map([
// Unary
[ts__default.default.SyntaxKind.TildeToken, "bitNot"],
[ts__default.default.SyntaxKind.ExclamationToken, "not"],
// Binary
[ts__default.default.SyntaxKind.AmpersandToken, "bitAnd"],
[ts__default.default.SyntaxKind.BarToken, "bitOr"],
[ts__default.default.SyntaxKind.CaretToken, "bitXor"],
[ts__default.default.SyntaxKind.LessThanLessThanToken, "leftShift"],
[ts__default.default.SyntaxKind.GreaterThanGreaterThanToken, "rightShift"],
[ts__default.default.SyntaxKind.GreaterThanGreaterThanGreaterThanToken, "unsignedRightShift"]
]);
const ts_shorthand_assignment_operator_to_ts_operator_dict = /* @__PURE__ */ new Map([
// Arithmetic
[ts__default.default.SyntaxKind.PlusEqualsToken, ts__default.default.SyntaxKind.PlusToken],
[ts__default.default.SyntaxKind.MinusEqualsToken, ts__default.default.SyntaxKind.MinusToken],
[ts__default.default.SyntaxKind.AsteriskEqualsToken, ts__default.default.SyntaxKind.AsteriskToken],
[ts__default.default.SyntaxKind.SlashEqualsToken, ts__default.default.SyntaxKind.SlashToken],
[ts__default.default.SyntaxKind.PercentEqualsToken, ts__default.default.SyntaxKind.PercentToken],
[ts__default.default.SyntaxKind.AsteriskAsteriskEqualsToken, ts__default.default.SyntaxKind.AsteriskAsteriskToken],
// Bitwise
[ts__default.default.SyntaxKind.AmpersandEqualsToken, ts__default.default.SyntaxKind.AmpersandToken],
[ts__default.default.SyntaxKind.BarEqualsToken, ts__default.default.SyntaxKind.BarToken],
[ts__default.default.SyntaxKind.CaretEqualsToken, ts__default.default.SyntaxKind.CaretToken],
[ts__default.default.SyntaxKind.LessThanLessThanEqualsToken, ts__default.default.SyntaxKind.LessThanLessThanToken],
[ts__default.default.SyntaxKind.GreaterThanGreaterThanEqualsToken, ts__default.default.SyntaxKind.GreaterThanGreaterThanToken],
[ts__default.default.SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken, ts__default.default.SyntaxKind.GreaterThanGreaterThanGreaterThanToken]
]);
function convertTSBinaryExpressionToSC(e, generator_context = context_cjs.default_generator_context) {
const [left_expr, op_syntax_kind, right_expr] = [e.left, e.operatorToken.kind, e.right];
{
const op_conved = ts_infix_becoming_binary_operator_to_sc_dict.get(op_syntax_kind);
if (op_conved != void 0) {
return convertTSExpressionToSC(e.left, generator_context.atValuePosition()) + " " + op_conved + " " + convertTSExpressionToSC(e.right, generator_context.atValuePosition());
}
}
{
const op_conved = ts_function_call_becoming_operator_to_sc_dict.get(op_syntax_kind);
if (op_conved != void 0) {
return op_conved + "(" + convertTSExpressionToSC(left_expr, generator_context.atValuePosition()) + ", " + convertTSExpressionToSC(right_expr, generator_context.atValuePosition()) + ")";
}
}
{
const ts_op_conved = ts_shorthand_assignment_operator_to_ts_operator_dict.get(op_syntax_kind);
if (ts_op_conved != void 0) {
return convertTSExpressionToSC(ts__default.default.factory.createAssignment(
// Make convert such as `a += 1` to `a = a + 1`.
left_expr,
ts__default.default.factory.createBinaryExpression(left_expr, ts_op_conved, right_expr)
), generator_context);
}
}
switch (op_syntax_kind) {
case ts__default.default.SyntaxKind.InstanceOfKeyword:
return convertTSExpressionToSC(left_expr, generator_context.atValuePosition()) + ".isKindOf(" + right_expr.text + ")";
case ts__default.default.SyntaxKind.InKeyword:
return right_expr.text + ".respondsTo(" + literal_conv_cjs.convertToSCSymbol(left_expr) + ")";
default:
throw error_cjs.UnsupportedTypeError.forNodeWithSyntaxKind(e.operatorToken, "operator");
}
}
function isLegalSCIdentifier(name) {
return /^[A-Za-z]\w*$/g.test(name);
}
function isLegalSCVar(name) {
return isLegalSCIdentifier(name) && "a" <= name[0] && name[0] <= "z";
}
function isLegalSCClass(name) {
return isLegalSCIdentifier(name) && "A" <= name[0] && name[0] <= "Z";
}
function escapeForSCVarIfNeeded(name, esc_seq = "escvar_") {
if (isLegalSCVar(name)) {
return name;
} else {
const escaped_name = esc_seq + name.replace(/\W/g, "_");
console.warn(
`The variable name "${name}" is illegal in SCLang, and will be replaced to "${escaped_name}".`
);
return escaped_name;
}
}
function escapeForSCFunctionIfNeeded(name, esc_seq = "escfunction_") {
return escapeForSCVarIfNeeded(name, esc_seq);
}
function escapeForSCClassIfNeeded(name, esc_seq = "ESCCLASS_") {
if (isLegalSCClass(name)) {
return name;
} else {
const escaped_name = esc_seq + name.replace(/\W/g, "_");
console.warn(
`The class name "${name}" is illegal in SCLang, and will be replaced to "${escaped_name}".`
);
return escaped_name;
}
}
exports.convertTSBinaryExpressionToSC = convertTSBinaryExpressionToSC;
exports.convertTSCallExpressionToSC = convertTSCallExpressionToSC;
exports.convertTSConditionalExpressionToSC = convertTSConditionalExpressionToSC;
exports.convertTSElementAccessExpressionToSC = convertTSElementAccessExpressionToSC;
exports.convertTSExpressionToSC = convertTSExpressionToSC;
exports.convertTSNewExpressionToSC = convertTSNewExpressionToSC;
exports.convertTSPropertyAccessExpressionToSC = convertTSPropertyAccessExpressionToSC;
exports.convertTSTaggedTemplateExpressionToSC = convertTSTaggedTemplateExpressionToSC;
exports.escapeForSCClassIfNeeded = escapeForSCClassIfNeeded;
exports.escapeForSCFunctionIfNeeded = escapeForSCFunctionIfNeeded;
exports.escapeForSCVarIfNeeded = escapeForSCVarIfNeeded;
exports.hasSelfIndecrExpression = hasSelfIndecrExpression;
exports.isLegalSCClass = isLegalSCClass;
exports.isLegalSCIdentifier = isLegalSCIdentifier;
exports.isLegalSCVar = isLegalSCVar;
exports.isMethod = isMethod;
exports.isSelfIndecrExpression = isSelfIndecrExpression;
exports.isStoringObjectLiteral = isStoringObjectLiteral;
exports.supported_expression_syntax_kind = supported_expression_syntax_kind;
exports.translateTSPostfixUnaryExpressionToSC = translateTSPostfixUnaryExpressionToSC;
exports.translateTSPrefixUnaryExpressionToSC = translateTSPrefixUnaryExpressionToSC;
exports.tryFindSelfIndecrOperator = tryFindSelfIndecrOperator;