roblox-ts
Version:
A TypeScript-to-Luau Compiler for Roblox
103 lines • 5.3 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.transformLogical = transformLogical;
const luau_ast_1 = __importDefault(require("@roblox-ts/luau-ast"));
const assert_1 = require("../../Shared/util/assert");
const transformExpression_1 = require("./expressions/transformExpression");
const createTruthinessChecks_1 = require("../util/createTruthinessChecks");
const expressionChain_1 = require("../util/expressionChain");
const getKindName_1 = require("../util/getKindName");
const types_1 = require("../util/types");
const typescript_1 = __importDefault(require("typescript"));
function flattenByOperator(node, operatorKind) {
const result = new Array();
while (typescript_1.default.isBinaryExpression(node) && node.operatorToken.kind === operatorKind) {
result.unshift(node.right);
node = node.left;
}
result.unshift(node);
return result;
}
function getLogicalChain(state, binaryExp, binaryOperatorKind, enableInlining) {
return flattenByOperator(binaryExp, binaryOperatorKind).map((node, index, array) => {
const type = state.getType(node);
const [expression, statements] = state.capture(() => (0, transformExpression_1.transformExpression)(state, node));
let inline = false;
if (enableInlining) {
const willWrap = index < array.length - 1 && (0, createTruthinessChecks_1.willCreateTruthinessChecks)(type);
inline = luau_ast_1.default.list.isEmpty(statements) && !willWrap;
}
return { node, type, expression, statements, inline };
});
}
function buildLogicalChainPrereqs(state, chain, conditionId, buildCondition, index = 0) {
const expInfo = chain[index];
state.prereqList(expInfo.statements);
if (index === 0) {
state.prereq(luau_ast_1.default.create(luau_ast_1.default.SyntaxKind.VariableDeclaration, {
left: conditionId,
right: expInfo.expression,
}));
}
else {
state.prereq(luau_ast_1.default.create(luau_ast_1.default.SyntaxKind.Assignment, {
left: conditionId,
operator: "=",
right: expInfo.expression,
}));
}
if (index + 1 < chain.length) {
state.prereq(luau_ast_1.default.create(luau_ast_1.default.SyntaxKind.IfStatement, {
condition: buildCondition(conditionId, expInfo.node),
statements: state.capturePrereqs(() => buildLogicalChainPrereqs(state, chain, conditionId, buildCondition, index + 1)),
elseBody: luau_ast_1.default.list.make(),
}));
}
}
function mergeInlineExpressions(chain, binaryOperator) {
for (let i = 0; i < chain.length; i++) {
const info = chain[i];
if (info.inline) {
const exps = [info.expression];
const j = i + 1;
while (j < chain.length && chain[j].inline) {
exps.push(chain[j].expression);
chain.splice(j, 1);
}
info.expression = (0, expressionChain_1.binaryExpressionChain)(exps, binaryOperator);
}
}
}
function buildInlineConditionExpression(state, node, tsBinaryOperator, luaBinaryOperator, buildCondition) {
const chain = getLogicalChain(state, node, tsBinaryOperator, true);
mergeInlineExpressions(chain, luaBinaryOperator);
if (chain.length === 1 && chain[0].inline) {
return chain[0].expression;
}
const conditionId = luau_ast_1.default.tempId("condition");
buildLogicalChainPrereqs(state, chain, conditionId, buildCondition);
return conditionId;
}
function transformLogical(state, node) {
if (node.operatorToken.kind === typescript_1.default.SyntaxKind.AmpersandAmpersandToken) {
return buildInlineConditionExpression(state, node, node.operatorToken.kind, "and", (conditionId, node) => (0, createTruthinessChecks_1.createTruthinessChecks)(state, conditionId, node));
}
else if (node.operatorToken.kind === typescript_1.default.SyntaxKind.BarBarToken) {
return buildInlineConditionExpression(state, node, node.operatorToken.kind, "or", (conditionId, node) => luau_ast_1.default.unary("not", (0, createTruthinessChecks_1.createTruthinessChecks)(state, conditionId, node)));
}
else if (node.operatorToken.kind === typescript_1.default.SyntaxKind.QuestionQuestionToken) {
const conditionBuilder = (conditionId) => luau_ast_1.default.binary(conditionId, "==", luau_ast_1.default.nil());
if (!(0, types_1.isPossiblyType)(state.getType(node), (0, types_1.isBooleanLiteralType)(state, false))) {
return buildInlineConditionExpression(state, node, node.operatorToken.kind, "or", conditionBuilder);
}
const chain = getLogicalChain(state, node, typescript_1.default.SyntaxKind.QuestionQuestionToken, false);
const conditionId = luau_ast_1.default.tempId("condition");
buildLogicalChainPrereqs(state, chain, conditionId, conditionBuilder);
return conditionId;
}
(0, assert_1.assert)(false, `Operator not implemented: ${(0, getKindName_1.getKindName)(node.operatorToken.kind)}`);
}
//# sourceMappingURL=transformLogical.js.map