roblox-ts
Version:
<div align="center"><img width=25% src="https://i.imgur.com/yCjHmng.png"></div> <h1 align="center"><a href="https://roblox-ts.github.io/">roblox-ts</a></h1> <div align="center">A TypeScript-to-Lua Compiler for Roblox</div> <br> <div align="center"> <a hr
325 lines • 13.2 kB
JavaScript
;
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
const ts = __importStar(require("ts-morph"));
const _1 = require(".");
const CompilerError_1 = require("../errors/CompilerError");
const typeUtilities_1 = require("../typeUtilities");
const utility_1 = require("../utility");
exports.nodeHasParameters = (ancestor) => ts.TypeGuards.isFunctionExpression(ancestor) ||
ts.TypeGuards.isArrowFunction(ancestor) ||
ts.TypeGuards.isFunctionDeclaration(ancestor) ||
ts.TypeGuards.isConstructorDeclaration(ancestor) ||
ts.TypeGuards.isMethodDeclaration(ancestor);
function getReturnStrFromExpression(state, exp, func) {
exp = utility_1.skipNodesDownwards(exp);
if (func && typeUtilities_1.isTupleType(func.getReturnType())) {
if (ts.TypeGuards.isArrayLiteralExpression(exp)) {
let expStr = _1.compileExpression(state, exp);
expStr = expStr.substr(2, expStr.length - 4);
return `return ${expStr};`;
}
else if (ts.TypeGuards.isCallExpression(exp) && typeUtilities_1.isTupleType(exp.getReturnType())) {
const expStr = _1.compileCallExpression(state, exp, true);
return `return ${expStr};`;
}
else {
const expStr = _1.compileExpression(state, exp);
return `return unpack(${expStr});`;
}
}
{
state.declarationContext.set(exp, {
isIdentifier: false,
set: "return",
});
const expStr = _1.compileExpression(state, exp);
return state.declarationContext.delete(exp) && `return ${expStr};`;
}
}
function compileReturnStatement(state, node) {
const exp = utility_1.skipNodesDownwards(node.getExpression());
if (exp) {
state.enterPrecedingStatementContext();
const funcNode = node.getFirstAncestor(exports.nodeHasParameters);
const returnStr = getReturnStrFromExpression(state, exp, funcNode);
return state.exitPrecedingStatementContextAndJoin() + (returnStr ? state.indent + returnStr + "\n" : "");
}
else {
return state.indent + `return nil;\n`;
}
}
exports.compileReturnStatement = compileReturnStatement;
function isFunctionExpressionMethod(node) {
const parent = utility_1.skipNodesUpwards(node.getParent());
return ts.TypeGuards.isPropertyAssignment(parent) && ts.TypeGuards.isObjectLiteralExpression(parent.getParent());
}
exports.isFunctionExpressionMethod = isFunctionExpressionMethod;
function isMethodDeclaration(node) {
return (ts.TypeGuards.isMethodDeclaration(node) ||
(ts.TypeGuards.isFunctionExpression(node) && isFunctionExpressionMethod(node)));
}
exports.isMethodDeclaration = isMethodDeclaration;
function compileFunctionBody(state, body, node, initializers) {
const isBlock = ts.TypeGuards.isBlock(body);
const isExpression = ts.TypeGuards.isExpression(body);
let result = "";
if (isBlock || isExpression) {
result += "\n";
state.pushIndent();
initializers.forEach(initializer => (result += state.indent + initializer + "\n"));
if (isBlock) {
result += _1.compileBlock(state, body);
}
else {
state.enterPrecedingStatementContext();
const returnStr = getReturnStrFromExpression(state, body, node);
result += state.exitPrecedingStatementContextAndJoin() + (returnStr ? state.indent + returnStr + "\n" : "");
}
state.popIndent();
result += state.indent;
}
else {
/* istanbul ignore next */
throw new CompilerError_1.CompilerError(`Unexpected function body ( ${body.getKindName()} ) in compileFunctionBody`, node, CompilerError_1.CompilerErrorType.BadFunctionBody, true);
}
return result;
}
function canSugaryCompileFunction(node) {
if (node.getSourceFile().getExtension() === "tsx") {
return false;
}
else if (ts.TypeGuards.isConstructorDeclaration(node)) {
return true;
}
else if (ts.TypeGuards.isFunctionDeclaration(node) || ts.TypeGuards.isMethodDeclaration(node)) {
const nameNode = node.getNameNode();
if (nameNode && ts.TypeGuards.isIdentifier(nameNode)) {
return true;
}
}
return false;
}
function compileFunction(state, node, name, body, namePrefix = "") {
state.pushIdStack();
const paramNames = new Array();
const initializers = new Array();
_1.getParameterData(state, paramNames, initializers, node);
_1.checkReturnsNonAny(node);
let result;
let frontWrap = "";
let backWrap = "";
let prefix = "";
if (ts.TypeGuards.isFunctionDeclaration(node)) {
const nameNode = node.getNameNode();
if (nameNode && typeUtilities_1.shouldHoist(node, nameNode)) {
state.pushHoistStack(name);
}
else {
prefix = "local ";
}
}
let isGenerator = false;
if (!ts.TypeGuards.isConstructorDeclaration(node)) {
/* istanbul ignore next */
if (node.isAsync()) {
state.usesTSLibrary = true;
frontWrap = "TS.async(";
backWrap = ")" + backWrap;
}
isGenerator = !ts.TypeGuards.isArrowFunction(node) && node.isGenerator();
}
const sugarcoat = name !== "" && frontWrap === "" && canSugaryCompileFunction(node);
let namePrefixEndsInColon = namePrefix.endsWith(":");
if (name) {
if (sugarcoat) {
result = state.indent + prefix;
}
else {
if (namePrefix && namePrefixEndsInColon) {
namePrefix = namePrefix.slice(0, -1) + ".";
namePrefixEndsInColon = false;
}
result = state.indent + prefix + namePrefix + name + " = ";
}
backWrap += ";\n";
}
else {
result = "";
}
if (isMethodDeclaration(node) && !namePrefixEndsInColon) {
giveInitialSelfParameter(node, paramNames);
}
result += frontWrap + "function" + (sugarcoat ? " " + namePrefix + name : "") + "(" + paramNames.join(", ") + ")";
if (isGenerator) {
// will error if IterableIterator is nullable
typeUtilities_1.isIterableIterator(node.getReturnType(), node);
result += "\n";
state.pushIndent();
result += state.indent + `return {\n`;
state.pushIndent();
result += state.indent + `next = coroutine.wrap(function()`;
result += compileFunctionBody(state, body, node, initializers);
result += `\twhile true do coroutine.yield({ done = true }) end;\n`;
result += state.indent + `end);\n`;
state.popIndent();
result += state.indent + `};\n`;
state.popIndent();
result += state.indent;
}
else {
result += compileFunctionBody(state, body, node, initializers);
}
state.popIdStack();
return result + "end" + backWrap;
}
function giveInitialSelfParameter(node, paramNames) {
const parameters = node.getParameters();
let replacedThis = false;
if (parameters.length > 0) {
const child = parameters[0].getFirstChildByKind(ts.SyntaxKind.Identifier);
const classParent = node.getFirstAncestor((ancestor) => ts.TypeGuards.isClassDeclaration(ancestor) || ts.TypeGuards.isClassExpression(ancestor));
if (classParent &&
child &&
child.getText() === "this" &&
(typeUtilities_1.getType(child).getText() === "this" || typeUtilities_1.getType(child) === typeUtilities_1.getType(classParent))) {
paramNames[0] = "self";
replacedThis = true;
}
}
if (!replacedThis) {
const thisParam = node.getParameter("this");
if (!thisParam || typeUtilities_1.getType(thisParam).getText() !== "void") {
paramNames.unshift("self");
}
}
}
function compileFunctionDeclaration(state, node) {
const body = node.getBody();
let name = node.getName();
if (name) {
_1.checkReserved(name, node, true);
}
else {
name = state.getNewId();
}
if (body) {
state.pushExport(name, node);
return compileFunction(state, node, name, body);
}
else {
return "";
}
}
exports.compileFunctionDeclaration = compileFunctionDeclaration;
function compileMethodDeclaration(state, node, namePrefix) {
const nameNode = node.getNameNode();
let name;
if (ts.TypeGuards.isComputedPropertyName(nameNode)) {
namePrefix = namePrefix.slice(0, -1);
name = `[${_1.compileExpression(state, utility_1.skipNodesDownwards(nameNode.getExpression()))}]`;
}
else {
name = _1.compileExpression(state, nameNode);
_1.checkReserved(name, node);
}
return compileFunction(state, node, name, node.getBodyOrThrow(), namePrefix);
}
exports.compileMethodDeclaration = compileMethodDeclaration;
function containsSuperExpression(child) {
if (child && ts.TypeGuards.isExpressionStatement(child)) {
const exp = utility_1.skipNodesDownwards(child.getExpression());
if (ts.TypeGuards.isCallExpression(exp)) {
const superExp = utility_1.skipNodesDownwards(exp.getExpression());
if (ts.TypeGuards.isSuperExpression(superExp)) {
return true;
}
}
}
return false;
}
function compileConstructorDeclaration(state, classExp, className, node, extraInitializers, hasSuper, isRoact) {
if (isRoact && !node) {
return "";
}
const paramNames = new Array();
const initializers = new Array();
const defaults = new Array();
const inheritsFromArray = typeUtilities_1.classDeclarationInheritsFromArray(classExp, false);
state.pushIdStack();
if (node) {
_1.getParameterData(state, paramNames, initializers, node, defaults);
}
else if (!inheritsFromArray) {
paramNames.push("...");
}
const paramStr = paramNames.join(", ");
const methodName = isRoact ? "init" : "constructor";
let result = "";
result += state.indent + `function ${className}:${methodName}(${paramStr})\n`;
state.pushIndent();
if (node) {
const body = node.getBodyOrThrow();
if (ts.TypeGuards.isBlock(body)) {
defaults.forEach(initializer => (result += state.indent + initializer + "\n"));
const bodyStatements = body.getStatements();
let k = 0;
if (containsSuperExpression(bodyStatements[k])) {
result += _1.compileStatement(state, bodyStatements[k++]);
}
initializers.forEach(initializer => (result += state.indent + initializer + "\n"));
if (extraInitializers) {
extraInitializers.forEach(initializer => (result += initializer));
}
for (; k < bodyStatements.length; ++k) {
result += _1.compileStatement(state, bodyStatements[k]);
}
const returnStatement = node.getStatementByKind(ts.SyntaxKind.ReturnStatement);
if (returnStatement) {
throw new CompilerError_1.CompilerError(`Cannot use return statement in constructor for ${className}`, returnStatement, CompilerError_1.CompilerErrorType.NoConstructorReturn);
}
}
}
else {
if (hasSuper && !inheritsFromArray) {
result += state.indent + `super.constructor(self, ...);\n`;
}
if (extraInitializers) {
extraInitializers.forEach(initializer => (result += initializer));
}
}
state.popIndent();
state.popIdStack();
result += state.indent + "end;\n";
return result;
}
exports.compileConstructorDeclaration = compileConstructorDeclaration;
function compileFunctionExpression(state, node) {
const potentialNameNode = node.getChildAtIndex(1);
if (ts.TypeGuards.isFunctionExpression(node) &&
ts.TypeGuards.isIdentifier(potentialNameNode) &&
potentialNameNode.findReferences()[0].getReferences().length > 1) {
const name = _1.compileExpression(state, potentialNameNode);
const id = state.pushPrecedingStatementToNewId(node, "");
state.pushPrecedingStatements(node, state.indent + `do\n`);
state.pushIndent();
state.pushPrecedingStatements(node, state.indent + `local ${name};\n`);
state.pushPrecedingStatements(node, compileFunction(state, node, `${name}`, node.getBody()));
state.pushPrecedingStatements(node, state.indent + `${id} = ${name};\n`);
state.popIndent();
state.pushPrecedingStatements(node, state.indent + `end;\n`);
// this should not be classified as isPushed.
return id;
}
else {
return compileFunction(state, node, "", node.getBody());
}
}
exports.compileFunctionExpression = compileFunctionExpression;
//# sourceMappingURL=function.js.map