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

394 lines 18.6 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 CompilerError_1 = require("../errors/CompilerError"); const typeUtilities_1 = require("../typeUtilities"); const utility_1 = require("../utility"); function getLuaBarExpression(state, node, lhsStr, rhsStr) { state.usesTSLibrary = true; const rhs = utility_1.skipNodesDownwards(node.getRight()); if (ts.TypeGuards.isNumericLiteral(rhs) && rhs.getLiteralValue() === 0) { return `TS.bit_truncate(${lhsStr})`; } else { return `TS.bit_or(${lhsStr}, ${rhsStr})`; } } function getLuaBitExpression(state, lhsStr, rhsStr, name) { state.usesTSLibrary = true; return `TS.bit_${name}(${lhsStr}, ${rhsStr})`; } function getLuaAddExpression(node, lhsStr, rhsStr, wrap = false) { if (wrap) { rhsStr = `(${rhsStr})`; } const lhsType = typeUtilities_1.getType(node.getLeft()); const rhsType = typeUtilities_1.getType(node.getRight()); const lhsIsStr = typeUtilities_1.isStringType(lhsType); const lhsIsNum = typeUtilities_1.isNumberType(lhsType); const rhsIsStr = typeUtilities_1.isStringType(rhsType); const rhsIsNum = typeUtilities_1.isNumberType(rhsType); if (lhsIsStr || rhsIsStr) { if (!lhsIsStr && !lhsIsNum) { lhsStr = "tostring(" + lhsStr + ")"; } if (!rhsIsStr && !rhsIsNum) { rhsStr = "tostring(" + rhsStr + ")"; } return `${lhsStr} .. ${rhsStr}`; } else if (lhsIsNum && rhsIsNum) { return `${lhsStr} + ${rhsStr}`; } else { throw new CompilerError_1.CompilerError(`Unexpected types for addition: ${lhsType.getText()} + ${rhsType.getText()}`, node, CompilerError_1.CompilerErrorType.BadAddition, true); } } function isSetToken(opKind) { return (opKind === ts.SyntaxKind.EqualsToken || opKind === ts.SyntaxKind.BarEqualsToken || opKind === ts.SyntaxKind.AmpersandEqualsToken || opKind === ts.SyntaxKind.CaretEqualsToken || opKind === ts.SyntaxKind.LessThanLessThanEqualsToken || opKind === ts.SyntaxKind.GreaterThanGreaterThanEqualsToken || opKind === ts.SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken || opKind === ts.SyntaxKind.PlusEqualsToken || opKind === ts.SyntaxKind.MinusEqualsToken || opKind === ts.SyntaxKind.AsteriskEqualsToken || opKind === ts.SyntaxKind.SlashEqualsToken || opKind === ts.SyntaxKind.AsteriskAsteriskEqualsToken || opKind === ts.SyntaxKind.PercentEqualsToken); } exports.isSetToken = isSetToken; function compileBinaryLiteral(state, node, lhs, rhs) { const names = new Array(); const values = new Array(); const preStatements = new Array(); const postStatements = new Array(); let rootId; if ((ts.TypeGuards.isIdentifier(rhs) && !_1.isIdentifierDefinedInExportLet(rhs)) || ts.TypeGuards.isThisExpression(rhs) || ts.TypeGuards.isSuperExpression(rhs)) { rootId = _1.compileExpression(state, rhs); } else { rootId = state.getNewId(); preStatements.push(`local ${rootId} = ${_1.compileExpression(state, rhs)};`); } _1.getBindingData(state, names, values, preStatements, postStatements, lhs, rootId, _1.getAccessorForBindingPatternType(rhs)); const parent = utility_1.skipNodesUpwards(node.getParentOrThrow()); if (ts.TypeGuards.isExpressionStatement(parent) || ts.TypeGuards.isForStatement(parent)) { let result = ""; preStatements.forEach(statementStr => (result += state.indent + statementStr + "\n")); _1.concatNamesAndValues(state, names, values, false, declaration => (result += declaration)); postStatements.forEach(statementStr => (result += state.indent + statementStr + "\n")); return result.replace(/;\n$/, ""); // terrible hack } else { preStatements.forEach(statementStr => state.pushPrecedingStatements(rhs, state.indent + statementStr + "\n")); _1.concatNamesAndValues(state, names, values, false, declaration => state.pushPrecedingStatements(node, declaration)); postStatements.forEach(statementStr => state.pushPrecedingStatements(lhs, state.indent + statementStr + "\n")); return rootId; } } function compileBinaryExpression(state, node) { const nodeParent = utility_1.skipNodesUpwards(node.getParentOrThrow()); const parentKind = nodeParent.getKind(); const isStatement = parentKind === ts.SyntaxKind.ExpressionStatement || parentKind === ts.SyntaxKind.ForStatement; const opToken = node.getOperatorToken(); const opKind = opToken.getKind(); const isEqualsOperation = opKind === ts.SyntaxKind.EqualsToken; const rawRhs = node.getRight(); const lhs = utility_1.skipNodesDownwards(node.getLeft(), true); const rhs = utility_1.skipNodesDownwards(rawRhs, true); let lhsStr; let rhsStr; if (!isEqualsOperation) { _1.checkNonAny(lhs); _1.checkNonAny(rhs); } // binding patterns if (ts.TypeGuards.isArrayLiteralExpression(lhs)) { const isFlatBinding = lhs.getElements().every(v => ts.TypeGuards.isIdentifier(v)); if (isFlatBinding && rhs && ts.TypeGuards.isCallExpression(rhs) && typeUtilities_1.isTupleReturnTypeCall(rhs)) { // FIXME: Still broken for nested destructuring of non-arrays. // BUT this change makes it LESS broken than before. (try nested destructuring a string here) // e.g. [[[[[[[a]]]]]]] = func() where func() returns a LuaTuple<[string]> let result = ""; const preStatements = new Array(); const postStatements = new Array(); const names = lhs .getElements() .map(element => { if (ts.TypeGuards.isOmittedExpression(element)) { return "_"; } else if (ts.TypeGuards.isArrayLiteralExpression(element) || ts.TypeGuards.isObjectLiteralExpression(element)) { let rootId; rootId = state.getNewId(); _1.getBindingData(state, [], [], preStatements, postStatements, element, rootId, _1.getAccessorForBindingPatternType(rhs)); return rootId; } else { return _1.compileExpression(state, element); } }) .filter(s => s !== ""); const values = [_1.compileCallExpression(state, rhs, true)]; _1.concatNamesAndValues(state, names, values, false, declaration => (result += declaration + ";\n"), false, false); preStatements.forEach(statementStr => (result += state.indent + statementStr + "\n")); postStatements.forEach(statementStr => (result += state.indent + statementStr + "\n")); if (isStatement) { return result.replace(/;\n$/, ""); // terrible hack } else { throw new CompilerError_1.CompilerError("Cannot use a LuaTuple destructuring expression outside an ExpressionStatement!", node, CompilerError_1.CompilerErrorType.BadLuaTupleStatement); } } else { return compileBinaryLiteral(state, node, lhs, rhs); } } else if (ts.TypeGuards.isObjectLiteralExpression(lhs)) { return compileBinaryLiteral(state, node, lhs, rhs); } if (isSetToken(opKind)) { let isLhsIdentifier = ts.TypeGuards.isIdentifier(lhs) && !_1.isIdentifierDefinedInExportLet(lhs); let rhsStrContext; let hasOpenContext = false; let stashedInnerStr; const upperContext = state.getCurrentPrecedingStatementContext(node); if (ts.TypeGuards.isElementAccessExpression(lhs)) { const compileLhsStr = _1.compileElementAccessDataTypeExpression(state, lhs, _1.getWritableOperandName(state, lhs, true).expStr); let innerStr = _1.compileElementAccessBracketExpression(state, lhs); if (!typeUtilities_1.isConstantExpression(lhs.getArgumentExpressionOrThrow(), 0) && // Always push array values, even when isPushed, because we need to increment by 1 (!upperContext.isPushed || typeUtilities_1.isArrayType(typeUtilities_1.getType(lhs.getExpression())))) { const previousInner = innerStr; const previouslyPushed = upperContext.isPushed; stashedInnerStr = () => { // In this case, this will ALWAYS pop the `pushPrecedingStatementToNewId` immediately below upperContext.pop(); upperContext.isPushed = previouslyPushed; return compileLhsStr(previousInner); }; innerStr = state.pushPrecedingStatementToNewId(lhs, innerStr); } lhsStr = compileLhsStr(innerStr); } else if (!isEqualsOperation) { const newLhsStrData = _1.getWritableOperandName(state, lhs); lhsStr = newLhsStrData.expStr; isLhsIdentifier = newLhsStrData.isIdentifier; } else { lhsStr = _1.compileExpression(state, lhs); } const currentContext = state.enterPrecedingStatementContext(); if (isEqualsOperation) { if (isStatement) { state.declarationContext.set(rawRhs, { isIdentifier: isLhsIdentifier, set: lhsStr, }); hasOpenContext = true; } const previousLength = currentContext.length; rhsStr = _1.compileExpression(state, rhs); if (state.declarationContext.delete(rawRhs)) { hasOpenContext = false; } if (stashedInnerStr && currentContext.length - previousLength === 0) { lhsStr = stashedInnerStr(); } } else { rhsStr = _1.compileExpression(state, rhs); } rhsStrContext = state.exitPrecedingStatementContext(); const previouslhs = isEqualsOperation ? "" : isStatement && rhsStrContext.length === 0 ? lhsStr : state.pushPrecedingStatementToReuseableId(lhs, lhsStr, rhsStrContext); let { isPushed } = rhsStrContext; state.pushPrecedingStatements(rhs, ...rhsStrContext); if (opKind === ts.SyntaxKind.BarEqualsToken) { rhsStr = getLuaBarExpression(state, node, previouslhs, rhsStr); } else if (opKind === ts.SyntaxKind.AmpersandEqualsToken) { rhsStr = getLuaBitExpression(state, previouslhs, rhsStr, "and"); } else if (opKind === ts.SyntaxKind.CaretEqualsToken) { rhsStr = getLuaBitExpression(state, previouslhs, rhsStr, "xor"); } else if (opKind === ts.SyntaxKind.LessThanLessThanEqualsToken) { rhsStr = getLuaBitExpression(state, previouslhs, rhsStr, "lsh"); } else if (opKind === ts.SyntaxKind.GreaterThanGreaterThanEqualsToken) { rhsStr = getLuaBitExpression(state, previouslhs, rhsStr, "rsh"); } else if (opKind === ts.SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken) { rhsStr = getLuaBitExpression(state, previouslhs, rhsStr, "lrsh"); } else if (opKind === ts.SyntaxKind.PlusEqualsToken) { rhsStr = getLuaAddExpression(node, previouslhs, rhsStr, true); } else if (opKind === ts.SyntaxKind.MinusEqualsToken) { rhsStr = `${previouslhs} - (${rhsStr})`; } else if (opKind === ts.SyntaxKind.AsteriskEqualsToken) { rhsStr = `${previouslhs} * (${rhsStr})`; } else if (opKind === ts.SyntaxKind.SlashEqualsToken) { rhsStr = `${previouslhs} / (${rhsStr})`; } else if (opKind === ts.SyntaxKind.AsteriskAsteriskEqualsToken) { rhsStr = `${previouslhs} ^ (${rhsStr})`; } else if (opKind === ts.SyntaxKind.PercentEqualsToken) { rhsStr = `${previouslhs} % (${rhsStr})`; } else if (!isEqualsOperation) { throw new CompilerError_1.CompilerError(`Unexpected BinaryExpression ( ${opToken.getText()} ) in compileBinaryExpression #1`, opToken, CompilerError_1.CompilerErrorType.BadBinaryExpression, true); } const unUsedStatement = !isEqualsOperation || !hasOpenContext; if (isStatement) { return unUsedStatement ? `${lhsStr} = ${rhsStr}` : ""; } else { let returnStr = lhsStr; if (!isLhsIdentifier) { if (hasOpenContext) { if ((isPushed && isEqualsOperation) || typeUtilities_1.isConstantExpression(lhs, 0)) { returnStr = rhsStr = lhsStr; } else { returnStr = rhsStr = state.pushToDeclarationOrNewId(node, lhsStr, declaration => declaration.isIdentifier); ({ isPushed } = upperContext); } } else { if ((isPushed && isEqualsOperation) || typeUtilities_1.isConstantExpression(rhs, 0)) { returnStr = rhsStr; } else { returnStr = rhsStr = state.pushToDeclarationOrNewId(node, rhsStr, declaration => declaration.isIdentifier); ({ isPushed } = upperContext); } } } if (unUsedStatement) { if (!isPushed && !isStatement && !isEqualsOperation) { returnStr = rhsStr = state.pushToDeclarationOrNewId(node, rhsStr, declaration => declaration.isIdentifier); } // preserve isPushed upperContext.push(state.indent + `${lhsStr} = ${rhsStr};\n`); } return returnStr; } } else { state.enterPrecedingStatementContext(); lhsStr = _1.compileExpression(state, lhs); const lhsContext = state.exitPrecedingStatementContext(); state.enterPrecedingStatementContext(); rhsStr = _1.compileExpression(state, rhs); const rhsContext = state.exitPrecedingStatementContext(); state.pushPrecedingStatements(lhs, ...lhsContext); if (rhsContext.length > 0) { if (typeUtilities_1.shouldPushToPrecedingStatement(lhs, lhsStr, lhsContext)) { lhsStr = state.pushPrecedingStatementToReuseableId(lhs, lhsStr, rhsContext); } state.pushPrecedingStatements(rhs, ...rhsContext); } } if (opKind === ts.SyntaxKind.EqualsEqualsToken) { throw new CompilerError_1.CompilerError("operator '==' is not supported! Use '===' instead.", opToken, CompilerError_1.CompilerErrorType.NoEqualsEquals); } else if (opKind === ts.SyntaxKind.EqualsEqualsEqualsToken) { return `${lhsStr} == ${rhsStr}`; } else if (opKind === ts.SyntaxKind.ExclamationEqualsToken) { throw new CompilerError_1.CompilerError("operator '!=' is not supported! Use '!==' instead.", opToken, CompilerError_1.CompilerErrorType.NoExclamationEquals); } else if (opKind === ts.SyntaxKind.ExclamationEqualsEqualsToken) { return `${lhsStr} ~= ${rhsStr}`; } else if (opKind === ts.SyntaxKind.BarToken) { return getLuaBarExpression(state, node, lhsStr, rhsStr); } else if (opKind === ts.SyntaxKind.AmpersandToken) { return getLuaBitExpression(state, lhsStr, rhsStr, "and"); } else if (opKind === ts.SyntaxKind.CaretToken) { return getLuaBitExpression(state, lhsStr, rhsStr, "xor"); } else if (opKind === ts.SyntaxKind.LessThanLessThanToken) { return getLuaBitExpression(state, lhsStr, rhsStr, "lsh"); } else if (opKind === ts.SyntaxKind.GreaterThanGreaterThanToken) { return getLuaBitExpression(state, lhsStr, rhsStr, "rsh"); } else if (opKind === ts.SyntaxKind.GreaterThanGreaterThanGreaterThanToken) { return getLuaBitExpression(state, lhsStr, rhsStr, "lrsh"); } else if (opKind === ts.SyntaxKind.PlusToken) { return getLuaAddExpression(node, lhsStr, rhsStr); } else if (opKind === ts.SyntaxKind.MinusToken) { return `${lhsStr} - ${rhsStr}`; } else if (opKind === ts.SyntaxKind.AsteriskToken) { return `${lhsStr} * ${rhsStr}`; } else if (opKind === ts.SyntaxKind.SlashToken) { return `${lhsStr} / ${rhsStr}`; } else if (opKind === ts.SyntaxKind.AsteriskAsteriskToken) { return `${lhsStr} ^ ${rhsStr}`; } else if (opKind === ts.SyntaxKind.InKeyword) { // doesn't need parenthesis because In is restrictive return `${rhsStr}[${lhsStr}] ~= nil`; } else if (opKind === ts.SyntaxKind.AmpersandAmpersandToken) { return `${lhsStr} and ${rhsStr}`; } else if (opKind === ts.SyntaxKind.BarBarToken) { return `${lhsStr} or ${rhsStr}`; } else if (opKind === ts.SyntaxKind.GreaterThanToken) { return `${lhsStr} > ${rhsStr}`; } else if (opKind === ts.SyntaxKind.LessThanToken) { return `${lhsStr} < ${rhsStr}`; } else if (opKind === ts.SyntaxKind.GreaterThanEqualsToken) { return `${lhsStr} >= ${rhsStr}`; } else if (opKind === ts.SyntaxKind.LessThanEqualsToken) { return `${lhsStr} <= ${rhsStr}`; } else if (opKind === ts.SyntaxKind.PercentToken) { return `${lhsStr} % ${rhsStr}`; } else if (opKind === ts.SyntaxKind.InstanceOfKeyword) { state.usesTSLibrary = true; return `TS.instanceof(${lhsStr}, ${rhsStr})`; } else { throw new CompilerError_1.CompilerError(`Unexpected BinaryExpression (${node.getOperatorToken().getKindName()}) in compileBinaryExpression #2`, opToken, CompilerError_1.CompilerErrorType.BadBinaryExpression, true); } } exports.compileBinaryExpression = compileBinaryExpression; //# sourceMappingURL=binary.js.map