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
203 lines • 10.5 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 utility_1 = require("../utility");
function fallThroughConditionsRequireIfStatement(fallThroughConditions, fallThroughVar) {
return (fallThroughConditions.length > 0 &&
(fallThroughConditions.length !== 1 || fallThroughConditions[0] !== fallThroughVar));
}
function compileRemainingConditions(state, result, fallThroughConditions, anyFallThrough, previousCaseFallsThrough, fallThroughVar) {
if (fallThroughVar && previousCaseFallsThrough && fallThroughConditions[0] !== fallThroughVar) {
fallThroughConditions.unshift(fallThroughVar);
}
if (anyFallThrough) {
return result + state.indent + `${fallThroughVar} = ${fallThroughConditions.join(" or ")};\n`;
}
else {
return result + state.indent + `if ${fallThroughConditions.join(" or ")} then\n${state.indent}end;\n`;
}
}
function compileSwitchStatement(state, node) {
let preResult = "";
let expStr;
const expression = utility_1.skipNodesDownwards(node.getExpression());
state.enterPrecedingStatementContext();
const rawExpStr = _1.compileExpression(state, expression);
const expressionContext = state.exitPrecedingStatementContext();
const hasPrecedingStatements = expressionContext.length > 0;
if (hasPrecedingStatements) {
preResult += expressionContext.join("");
}
if ((hasPrecedingStatements && expressionContext.isPushed) ||
(ts.TypeGuards.isIdentifier(expression) && _1.isIdentifierDefinedInConst(expression))) {
expStr = rawExpStr;
}
else {
expStr = state.getNewId();
preResult += state.indent + `local ${expStr} = ${rawExpStr};\n`;
}
preResult += state.indent + `repeat\n`;
state.pushIndent();
state.pushIdStack();
state.hoistStack.push(new Set());
let fallThroughVar;
const clauses = node.getCaseBlock().getClauses();
let anyFallThrough = false;
let result = "";
let previousCaseFallsThrough = false;
const lastClauseIndex = clauses.length - 1;
const lastClause = clauses[lastClauseIndex];
const lastNonDefaultClauseIndex = clauses.length - 1 - [...clauses].reverse().findIndex(clause => ts.TypeGuards.isCaseClause(clause));
if (lastClause) {
const hasDefault = !ts.TypeGuards.isCaseClause(lastClause);
let fallThroughConditions = new Array();
for (let i = 0; i < clauses.length; i++) {
const clause = clauses[i];
const statements = clause.getStatements();
let writeThatWeFellThrough = true;
let lastStatement = statements[statements.length - 1];
let blockStatements = statements;
while (lastStatement && ts.TypeGuards.isBlock(lastStatement)) {
blockStatements = lastStatement.getStatements();
lastStatement = blockStatements[blockStatements.length - 1];
}
// Returns/Breaks are not always the last statement. Unreachable code is valid TS
const endsInReturnOrBreakStatement = blockStatements.find(statement => ts.TypeGuards.isBreakStatement(statement) || ts.TypeGuards.isReturnStatement(statement));
const hasStatements = statements.length > 0;
const currentCaseFallsThrough = !endsInReturnOrBreakStatement && (hasDefault ? lastClauseIndex - 1 : lastClauseIndex) > i;
const shouldPushFallThroughVar = currentCaseFallsThrough && hasStatements && i !== lastNonDefaultClauseIndex;
// add if statement if the clause is non-default
let isNonDefault = false;
if (ts.TypeGuards.isCaseClause(clause)) {
isNonDefault = true;
state.enterPrecedingStatementContext();
const clauseExp = clause.getExpression();
let clauseExpStr = _1.compileExpression(state, clauseExp);
if (_1.shouldWrapExpression(clauseExp, false)) {
clauseExpStr = `(${clauseExpStr})`;
}
let context = state.exitPrecedingStatementContext();
const hasContext = context.length > 0;
let condition = `${expStr} == ${clauseExpStr}`;
const fellThroughFirstHere = !anyFallThrough;
let wroteFallThrough = false;
/*
God, grant me the serenity to accept the things I cannot change,
The courage to change the things I can,
And wisdom to know the difference.
*/
if (!anyFallThrough &&
(((!hasStatements || previousCaseFallsThrough) && hasContext) ||
(hasStatements && currentCaseFallsThrough && i !== lastNonDefaultClauseIndex))) {
fallThroughVar = state.getNewId();
anyFallThrough = true;
if (hasStatements ||
(hasContext &&
(fallThroughConditionsRequireIfStatement(fallThroughConditions, fallThroughVar) ||
previousCaseFallsThrough))) {
result += state.indent + `local ${fallThroughVar} = false;\n`;
wroteFallThrough = true;
}
}
if (!hasStatements || previousCaseFallsThrough) {
if (fallThroughVar && hasContext) {
let indent = 1;
if (fallThroughConditionsRequireIfStatement(fallThroughConditions, fallThroughVar)) {
if (fallThroughVar &&
!wroteFallThrough &&
previousCaseFallsThrough &&
fallThroughVar !== condition &&
fallThroughConditions[0] !== fallThroughVar) {
fallThroughConditions.unshift(fallThroughVar);
}
result += state.indent + `if ${fallThroughConditions.join(" or ")} then\n`;
result += state.indent + `\t${fallThroughVar} = true;\n`;
result += state.indent + "else\n";
state.pushIndent();
}
else if (previousCaseFallsThrough) {
result += state.indent + `if not ${fallThroughVar} then\n`;
state.pushIndent();
}
else {
indent = 0;
}
result += utility_1.joinIndentedLines(context, indent);
result +=
state.indent +
`${fellThroughFirstHere && !wroteFallThrough ? "local " : ""}${fallThroughVar} = ${condition};\n`;
if (indent === 1) {
state.popIndent();
result += state.indent + `end;\n`;
}
condition = fallThroughVar;
fallThroughConditions = [];
context = undefined;
}
}
fallThroughConditions.push(condition);
if (hasStatements) {
if (fallThroughVar &&
!wroteFallThrough &&
previousCaseFallsThrough &&
fallThroughVar !== condition &&
fallThroughConditions[0] !== fallThroughVar) {
fallThroughConditions.unshift(fallThroughVar);
}
if (context) {
result += utility_1.joinIndentedLines(context, 0);
}
if (fallThroughConditions.length === 1 && fallThroughConditions[0] === fallThroughVar) {
writeThatWeFellThrough = false;
}
result += state.indent + `if ${fallThroughConditions.join(" or ")} then\n`;
state.pushIndent();
fallThroughConditions = new Array();
}
else {
previousCaseFallsThrough = true;
continue;
}
}
else if (i !== lastClauseIndex) {
throw new CompilerError_1.CompilerError("Default case must be the last case in a switch statement!", clause, CompilerError_1.CompilerErrorType.BadSwitchDefaultPosition);
}
else {
// empty remaining conditions
if (fallThroughConditionsRequireIfStatement(fallThroughConditions, fallThroughVar)) {
result = compileRemainingConditions(state, result, fallThroughConditions, anyFallThrough, previousCaseFallsThrough, fallThroughVar);
fallThroughConditions = [];
}
}
result += _1.compileStatementedNode(state, clause);
if (writeThatWeFellThrough && shouldPushFallThroughVar) {
result += state.indent + `${fallThroughVar} = true;\n`;
}
if (isNonDefault) {
state.popIndent();
result += state.indent + `end;\n`;
}
previousCaseFallsThrough = currentCaseFallsThrough;
}
// empty remaining conditions
if (fallThroughConditionsRequireIfStatement(fallThroughConditions, fallThroughVar)) {
result = compileRemainingConditions(state, result, fallThroughConditions, anyFallThrough, previousCaseFallsThrough, fallThroughVar);
}
}
result = state.popHoistStack(result);
state.popIdStack();
state.popIndent();
result += state.indent + `until true;\n`;
return preResult + result;
}
exports.compileSwitchStatement = compileSwitchStatement;
//# sourceMappingURL=switch.js.map