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

200 lines 10.1 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 isIdentifierDefinedInConst(exp) { // I have no idea why, but getDefinitionNodes() cannot replace this for (const def of exp.getDefinitions()) { const definition = def.getNode().getFirstAncestorByKind(ts.SyntaxKind.VariableStatement); if (definition && definition.getDeclarationKind() === ts.VariableDeclarationKind.Const) { return true; } } return false; } exports.isIdentifierDefinedInConst = isIdentifierDefinedInConst; function isIdentifierDefinedInExportLet(exp) { // I have no idea why, but getDefinitionNodes() cannot replace this for (const def of exp.getDefinitions()) { const definition = def.getNode().getFirstAncestorByKind(ts.SyntaxKind.VariableStatement); if (definition && definition.hasExportKeyword() && definition.getDeclarationKind() === ts.VariableDeclarationKind.Let) { return true; } } return false; } exports.isIdentifierDefinedInExportLet = isIdentifierDefinedInExportLet; /** * Gets the writable operand name, meaning the code should be able to do `returnValue = x;` * The rule in this case is that if there is a depth of 3 or more, e.g. `Foo.Bar.i`, we push `Foo.Bar` */ function getWritableOperandName(state, operand, doNotCompileAccess = false) { if (ts.TypeGuards.isPropertyAccessExpression(operand) || ts.TypeGuards.isElementAccessExpression(operand)) { const child = utility_1.skipNodesDownwards(operand.getExpression()); if (!ts.TypeGuards.isThisExpression(child) && !ts.TypeGuards.isSuperExpression(child) && (!ts.TypeGuards.isIdentifier(child) || isIdentifierDefinedInExportLet(child))) { const id = state.pushPrecedingStatementToReuseableId(operand, _1.compileExpression(state, child)); let propertyStr; if (doNotCompileAccess) { propertyStr = ""; } else if (ts.TypeGuards.isPropertyAccessExpression(operand)) { propertyStr = "." + _1.compileExpression(state, operand.getNameNode()); } else { const access = getComputedPropertyAccess(state, utility_1.skipNodesDownwards(operand.getArgumentExpressionOrThrow()), utility_1.skipNodesDownwards(operand.getExpression())); propertyStr = `[${access}]`; } return { expStr: id + propertyStr, isIdentifier: false }; } else if (doNotCompileAccess) { return { expStr: _1.compileExpression(state, child), isIdentifier: false }; } } return { expStr: _1.compileExpression(state, operand), isIdentifier: ts.TypeGuards.isIdentifier(operand) && !isIdentifierDefinedInExportLet(operand), }; } exports.getWritableOperandName = getWritableOperandName; /** * Similar to getWritableOperandName, but should push anything with any depth. This includes export let vars. */ function getReadableExpressionName(state, exp, expStr = _1.compileExpression(state, exp)) { const nonNullExp = utility_1.skipNodesDownwards(exp); if (expStr.match(/^\(*_\d+\)*$/) || (ts.TypeGuards.isIdentifier(nonNullExp) && !isIdentifierDefinedInExportLet(nonNullExp)) || ts.TypeGuards.isThisExpression(nonNullExp) || ts.TypeGuards.isSuperExpression(nonNullExp) || // We know that new Sets and Maps are already ALWAYS pushed (ts.TypeGuards.isNewExpression(nonNullExp) && (typeUtilities_1.isSetType(typeUtilities_1.getType(exp)) || typeUtilities_1.isMapType(typeUtilities_1.getType(exp))))) { return expStr; } else { return state.pushPrecedingStatementToReuseableId(nonNullExp, expStr); } } exports.getReadableExpressionName = getReadableExpressionName; function compilePropertyAccessExpression(state, node) { const exp = utility_1.skipNodesDownwards(node.getExpression()); const propertyStr = node.getName(); const expType = typeUtilities_1.getType(exp); const propertyAccessExpressionType = _1.getPropertyAccessExpressionType(state, node); if (typeUtilities_1.getCompilerDirectiveWithLaxConstraint(expType, "array" /* Array */, t => t.isTuple()) && propertyStr === "length") { throw new CompilerError_1.CompilerError(`Cannot access the \`length\` property of a tuple! Instead use \`${exp.getText()}.size()\``, node, CompilerError_1.CompilerErrorType.TupleLength); } else if (propertyAccessExpressionType !== -1 /* None */) { throw new CompilerError_1.CompilerError(`Invalid property access! Cannot index non-member "${propertyStr}" (a roblox-ts macro function)`, node, CompilerError_1.CompilerErrorType.InvalidMacroIndex); } const nameNode = node.getNameNode(); _1.checkApiAccess(state, nameNode); _1.checkNonAny(exp); _1.checkNonAny(nameNode); if (ts.TypeGuards.isSuperExpression(exp)) { return utility_1.safeLuaIndex("self", propertyStr); } const symbol = expType.getSymbol(); if (symbol) { const valDec = symbol.getValueDeclaration(); if (valDec) { if (ts.TypeGuards.isFunctionDeclaration(valDec) || ts.TypeGuards.isArrowFunction(valDec) || ts.TypeGuards.isFunctionExpression(valDec) || ts.TypeGuards.isMethodDeclaration(valDec)) { throw new CompilerError_1.CompilerError("Cannot index a function value!", node, CompilerError_1.CompilerErrorType.NoFunctionIndex); } else if (ts.TypeGuards.isEnumDeclaration(valDec)) { if (valDec.isConstEnum()) { const value = valDec.getMemberOrThrow(propertyStr).getValue(); if (typeof value === "number") { return `${value}`; } else if (typeof value === "string") { return `"${value}"`; } } } else if (ts.TypeGuards.isClassDeclaration(valDec)) { if (propertyStr === "prototype") { throw new CompilerError_1.CompilerError("Class prototypes are not supported!", node, CompilerError_1.CompilerErrorType.NoClassPrototype); } } } } let expStr = _1.compileExpression(state, exp); if (_1.shouldWrapExpression(exp, false)) { expStr = `(${expStr})`; } return expStr === "TS.Symbol" ? `${expStr}_${propertyStr}` : `${expStr}.${propertyStr}`; } exports.compilePropertyAccessExpression = compilePropertyAccessExpression; function addOneToArrayIndex(valueStr) { if (valueStr.indexOf("e") === -1 && valueStr.indexOf("E") === -1) { const valueNumber = Number(valueStr); if (!Number.isNaN(valueNumber)) { return (valueNumber + 1).toString(); } } return valueStr + " + 1"; } exports.addOneToArrayIndex = addOneToArrayIndex; function getComputedPropertyAccess(state, exp, fromNode) { const expType = typeUtilities_1.getType(exp); let expStr = _1.compileExpression(state, exp); const fromType = ts.TypeGuards.isCallExpression(fromNode) ? fromNode.getReturnType() : typeUtilities_1.getType(fromNode); if (typeUtilities_1.isArrayType(fromType)) { if (typeUtilities_1.isNumberTypeStrict(expType)) { expStr = addOneToArrayIndex(expStr); } else { throw new CompilerError_1.CompilerError(`Invalid indexing of ${fromType.getText()}. Got ${expType.getText()}, expected number`, exp, CompilerError_1.CompilerErrorType.InvalidComputedIndex); } } else if (typeUtilities_1.isSetType(fromType) || typeUtilities_1.isMapType(fromType) || typeUtilities_1.isStringType(fromType) || typeUtilities_1.isArrayTypeLax(fromType)) { throw new CompilerError_1.CompilerError(`Invalid index type: ${expType.getText()}.` + ` Type ${fromType.getText()} is not indexable.`, exp, CompilerError_1.CompilerErrorType.InvalidComputedIndex); } return expStr; } exports.getComputedPropertyAccess = getComputedPropertyAccess; function compileElementAccessBracketExpression(state, node) { return getComputedPropertyAccess(state, utility_1.skipNodesDownwards(node.getArgumentExpressionOrThrow()), utility_1.skipNodesDownwards(node.getExpression())); } exports.compileElementAccessBracketExpression = compileElementAccessBracketExpression; function compileElementAccessDataTypeExpression(state, node, expStr = "") { const expNode = utility_1.skipNodesDownwards(_1.checkNonAny(node.getExpression())); if (expStr === "") { if (ts.TypeGuards.isCallExpression(expNode) && typeUtilities_1.isTupleReturnTypeCall(expNode)) { expStr = _1.compileCallExpression(state, expNode, true); return (argExpStr) => (argExpStr === "1" ? `(${expStr})` : `(select(${argExpStr}, ${expStr}))`); } else { expStr = _1.compileExpression(state, expNode); } } if (_1.shouldWrapExpression(expNode, false)) { return (argExpStr) => `(${expStr})[${argExpStr}]`; } else { return (argExpStr) => `${expStr}[${argExpStr}]`; } } exports.compileElementAccessDataTypeExpression = compileElementAccessDataTypeExpression; function compileElementAccessExpression(state, node) { return compileElementAccessDataTypeExpression(state, node)(compileElementAccessBracketExpression(state, node)); } exports.compileElementAccessExpression = compileElementAccessExpression; //# sourceMappingURL=indexed.js.map