UNPKG

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

289 lines 14.4 kB
"use strict"; 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 typeUtilities_1 = require("../typeUtilities"); const utility_1 = require("../utility"); function isConstantNumberVariableOrLiteral(condValue) { return (ts.TypeGuards.isNumericLiteral(condValue) || (typeUtilities_1.isNumberType(typeUtilities_1.getType(condValue)) && ts.TypeGuards.isIdentifier(condValue) && condValue.getDefinitions().every(a => { const declNode = a.getDeclarationNode(); if (declNode && ts.TypeGuards.isVariableDeclaration(declNode)) { const declParent = declNode.getParent(); if (ts.TypeGuards.isVariableDeclarationList(declParent)) { return declParent.getDeclarationKind() === ts.VariableDeclarationKind.Const; } } return false; }))); } function isExpressionConstantNumbers(node) { const children = node.getChildren().map(child => utility_1.skipNodesDownwards(child)); return (children.length > 0 && children.every(child => isConstantNumberVariableOrLiteral(child) || child.getKind() === ts.SyntaxKind.PlusToken || child.getKind() === ts.SyntaxKind.MinusToken || child.getKind() === ts.SyntaxKind.AsteriskToken || child.getKind() === ts.SyntaxKind.SlashToken || child.getKind() === ts.SyntaxKind.AsteriskAsteriskToken || child.getKind() === ts.SyntaxKind.PercentToken || (ts.TypeGuards.isPrefixUnaryExpression(child) && child.getOperatorToken() === ts.SyntaxKind.MinusToken) || (ts.TypeGuards.isBinaryExpression(child) && isExpressionConstantNumbers(child)))); } function getSignAndIncrementorForStatement(state, incrementor, id) { let forIntervalStr = ""; let sign = ""; if (ts.TypeGuards.isBinaryExpression(incrementor)) { // const lhs = skipNodesDownwards(incrementor.getLeft()); let rhs = utility_1.skipNodesDownwards(incrementor.getRight()); const operator = utility_1.skipNodesDownwards(incrementor.getOperatorToken()); if (typeUtilities_1.isNumberType(typeUtilities_1.getType(rhs)) && operator.getKind() === ts.SyntaxKind.EqualsToken && ts.TypeGuards.isBinaryExpression(rhs)) { const rhsLhs = utility_1.skipNodesDownwards(rhs.getLeft()); const rhsOp = utility_1.skipNodesDownwards(rhs.getOperatorToken()); if (ts.TypeGuards.isIdentifier(rhsLhs) && utility_1.isIdentifierWhoseDefinitionMatchesNode(rhsLhs, id)) { switch (rhsOp.getKind()) { case ts.SyntaxKind.MinusToken: sign = "-"; break; case ts.SyntaxKind.PlusToken: sign = "+"; break; } } } else { switch (operator.getKind()) { case ts.SyntaxKind.PlusEqualsToken: sign = "+"; break; case ts.SyntaxKind.MinusEqualsToken: sign = "-"; break; } } if (rhs && ts.TypeGuards.isPrefixUnaryExpression(rhs)) { if (rhs.getOperatorToken() === ts.SyntaxKind.MinusToken) { switch (sign) { case "+": sign = "-"; break; case "-": sign = "+"; break; case "": return ["", ""]; } rhs = rhs.getOperand(); } } if (ts.TypeGuards.isNumericLiteral(rhs)) { forIntervalStr = _1.compileExpression(state, rhs); } } else if (incrementor.getOperatorToken() === ts.SyntaxKind.MinusMinusToken) { forIntervalStr = "1"; sign = "-"; } else { forIntervalStr = "1"; sign = "+"; } return [sign, forIntervalStr]; } function getLimitInForStatement(state, condition, id) { if (ts.TypeGuards.isBinaryExpression(condition)) { const lhs = utility_1.skipNodesDownwards(condition.getLeft()); const operator = condition.getOperatorToken(); const rhs = utility_1.skipNodesDownwards(condition.getRight()); if (utility_1.isIdentifierWhoseDefinitionMatchesNode(lhs, id)) { switch (operator.getKind()) { case ts.SyntaxKind.GreaterThanEqualsToken: // >= return [">=", rhs]; case ts.SyntaxKind.LessThanEqualsToken: // <= return ["<=", rhs]; } } else if (utility_1.isIdentifierWhoseDefinitionMatchesNode(rhs, id)) { switch (operator.getKind()) { case ts.SyntaxKind.GreaterThanEqualsToken: // >= return ["<=", lhs]; case ts.SyntaxKind.LessThanEqualsToken: // <= return [">=", lhs]; } } } return ["", undefined]; } function safelyHandleExpressionsInForStatement(state, incrementor, incrementorStr, context, indentation) { return (utility_1.joinIndentedLines(context, indentation) + state.indent + (context.isPushed ? incrementorStr : _1.placeIncrementorInStatementIfExpression(state, incrementor, incrementorStr))); } function getSimpleForLoopString(state, initializer, forLoopVars, statement) { let result = ""; state.popIndent(); const first = _1.compileVariableDeclarationList(state, initializer) .trim() .replace(/^local /, "") .replace(/;$/, ""); result = state.indent + `for ${first}, ${forLoopVars} do\n`; state.pushIndent(); result += _1.compileLoopBody(state, statement); state.popIndent(); result += state.indent + `end;\n`; return result; } function compileForStatement(state, node) { state.pushIdStack(); const statement = node.getStatement(); const condition = utility_1.skipNodesDownwards(node.getCondition()); const incrementor = utility_1.skipNodesDownwards(node.getIncrementor()); const localizations = new Array(); const cleanups = new Array(); let result = state.indent + "do\n"; state.pushIndent(); const initializer = utility_1.skipNodesDownwards(node.getInitializer()); let conditionStr; let incrementorStr; let conditionContext; let incrementorContext; let initializerContext; if (initializer) { if (ts.TypeGuards.isVariableDeclarationList(initializer)) { const declarations = initializer.getDeclarations(); const statementDescendants = statement.getDescendants(); if (initializer.getDeclarationKind() === ts.VariableDeclarationKind.Let) { if (declarations.length === 1) { const [declaration] = declarations; const lhs = utility_1.skipNodesDownwards(declaration.getNameNode()); const rhs = utility_1.skipNodesDownwards(declaration.getInitializer()); if (ts.TypeGuards.isIdentifier(lhs) && !statementDescendants.some(statementDescendant => _1.expressionModifiesVariable(statementDescendant, lhs)) && incrementor && condition && rhs) { // check if we can convert to a simple for loop // IF there aren't any in-loop modifications to the let variable // AND the let variable is a single numeric variable // AND the incrementor is a simple +/- expression of the let var // AND the conditional expression is a binary expression // with one of these operators: <= >= < > // AND the conditional expression compares the let var to a numeric literal // OR the conditional expression compares the let var to an unchanging number const rhsType = typeUtilities_1.getType(rhs); if (typeUtilities_1.isNumberType(rhsType)) { if (_1.expressionModifiesVariable(incrementor, lhs)) { const [incrSign, incrValue] = getSignAndIncrementorForStatement(state, incrementor, lhs); if (incrSign && incrValue) { const [condSign, condValue] = getLimitInForStatement(state, condition, lhs); // numeric literals, or constant number identifiers are safe if (condValue && ts.TypeGuards.isExpression(condValue) && (isConstantNumberVariableOrLiteral(condValue) || isExpressionConstantNumbers(condValue))) { if ((incrSign === "+" && condSign === "<=") || (incrSign === "-" && condSign === ">=")) { const incrStr = incrSign === "-" ? incrSign + incrValue : incrValue; return getSimpleForLoopString(state, initializer, _1.compileExpression(state, condValue) + (incrStr === "1" ? "" : ", " + incrStr), statement); } } } } } } } } state.enterPrecedingStatementContext(); const expStr = _1.compileVariableDeclarationList(state, initializer); result += state.exitPrecedingStatementContextAndJoin() + expStr; // + ";\n"; // if we can't convert to a simple for loop: // if it has any internal function declarations, make sure to locally scope variables if (statementDescendants.some(_1.nodeHasParameters)) { state.enterPrecedingStatementContext(); conditionStr = condition ? _1.compileExpression(state, condition) : "true"; conditionContext = state.exitPrecedingStatementContext(); state.enterPrecedingStatementContext(); incrementorStr = incrementor ? _1.compileExpression(state, incrementor) + ";\n" : undefined; incrementorContext = state.exitPrecedingStatementContext(); declarations.forEach(declaration => { const lhs = declaration.getChildAtIndex(0); if (ts.TypeGuards.isIdentifier(lhs)) { const name = lhs.getText(); const isLoopVarModified = statementDescendants.some(statementDescendant => _1.expressionModifiesVariable(statementDescendant, lhs)); const alias = state.getNewId(); state.pushIndent(); localizations.push(`local ${alias} = ${name};\n`); state.popIndent(); // don't leak const previous = state.variableAliases.get(name); cleanups.push(() => { if (previous) { state.variableAliases.set(name, previous); } else { state.variableAliases.delete(name); } if (isLoopVarModified) { result += state.indent + `${name} = ${alias};\n`; } }); state.variableAliases.set(name, alias); } }); } } else if (ts.TypeGuards.isExpression(initializer)) { state.enterPrecedingStatementContext(); const expStr = _1.compileExpression(state, initializer); initializerContext = state.exitPrecedingStatementContext(); result += safelyHandleExpressionsInForStatement(state, initializer, expStr, initializerContext, 0) + ";\n"; } } // order matters if (conditionStr === undefined) { state.enterPrecedingStatementContext(); conditionStr = condition ? _1.compileExpression(state, condition) : "true"; conditionContext = state.exitPrecedingStatementContext(); state.enterPrecedingStatementContext(); incrementorStr = incrementor ? _1.compileExpression(state, incrementor) + ";\n" : undefined; incrementorContext = state.exitPrecedingStatementContext(); } const conditionContextHasStatements = conditionContext && conditionContext.length > 0; result += state.indent + `while ${conditionContextHasStatements ? "true" : conditionStr} do\n`; state.pushIndent(); if (conditionContextHasStatements) { result += utility_1.joinIndentedLines(conditionContext, 1); result += state.indent + `if not (${conditionStr}) then break; end;\n`; } for (const localization of localizations) { result += state.indent + localization; } result += _1.compileLoopBody(state, statement); cleanups.forEach(cleanup => cleanup()); if (incrementor && incrementorStr) { result += safelyHandleExpressionsInForStatement(state, incrementor, incrementorStr, incrementorContext, 1); } state.popIndent(); result += state.indent + "end;\n"; state.popIndent(); result += state.indent + `end;\n`; state.popIdStack(); return result; } exports.compileForStatement = compileForStatement; //# sourceMappingURL=for.js.map