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

218 lines 8.83 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 CompilerError_1 = require("./errors/CompilerError"); const utility_1 = require("./utility"); function canBePushedToReusableId(node) { if (ts.TypeGuards.isThisExpression(node) || ts.TypeGuards.isIdentifier(node) || ts.TypeGuards.isSuperExpression(node)) { return true; } else if (ts.TypeGuards.isPrefixUnaryExpression(node) || ts.TypeGuards.isPostfixUnaryExpression(node)) { return canBePushedToReusableId(node.getOperand()); } else if (ts.TypeGuards.isPropertyAccessExpression(node)) { return canBePushedToReusableId(node.getNameNode()); } else if (ts.TypeGuards.isElementAccessExpression(node)) { return canBePushedToReusableId(node.getArgumentExpressionOrThrow()); } else { return false; } } class CompilerState { constructor(rootDirPath, outDirPath, projectInfo, rojoProject, modulesDir, runtimeOverride) { this.rootDirPath = rootDirPath; this.outDirPath = outDirPath; this.projectInfo = projectInfo; this.rojoProject = rojoProject; this.modulesDir = modulesDir; this.runtimeOverride = runtimeOverride; this.declarationContext = new Map(); this.currentConditionalContext = ""; this.precedingStatementContexts = new Array(); // indent this.indent = ""; // id stack this.idStack = new Array(); // hoist stack this.hoistStack = new Array(); // export stack this.exportStack = new Array(); // in the form: { ORIGINAL_IDENTIFIER = REPLACEMENT_VALUE } // For example, this is used for exported/namespace values // which should be represented differently in Lua than they // can be represented in TS this.variableAliases = new Map(); this.namespaceStack = new Map(); this.continueId = -1; this.isModule = false; this.scriptContext = utility_1.ScriptContext.None; this.roactIndent = 0; this.hasRoactImport = false; this.usesTSLibrary = false; } pushToDeclarationOrNewId(node, expStr, condition = dec => dec.set !== "return") { const declaration = this.declarationContext.get(node); let id; const isReturn = declaration && declaration.set === "return"; if (declaration && condition(declaration)) { this.declarationContext.delete(node); ({ set: id } = declaration); const context = this.getCurrentPrecedingStatementContext(node); context.push(this.indent + `${declaration.needsLocalizing ? "local " : ""}${id}${expStr ? ` ${isReturn ? "" : "= "}${expStr}` : ""};\n`); context.isPushed = declaration.isIdentifier; } else { id = this.pushPrecedingStatementToReuseableId(node, expStr); } return id; } getCurrentPrecedingStatementContext(node) { const currentContext = this.precedingStatementContexts[this.precedingStatementContexts.length - 1]; if (!currentContext) { const kind = node.getKindName(); const ancestorName = node .getAncestors() .find(ancestor => ts.TypeGuards.isStatement(ancestor) || ts.TypeGuards.isStatementedNode(ancestor)) .getKindName(); throw new CompilerError_1.CompilerError(`roblox-ts does not support using a ${kind} which requires preceding statements in a ${ancestorName}.`, node, CompilerError_1.CompilerErrorType.BadExpression, true); } return currentContext; } enterPrecedingStatementContext(newContext = new Array()) { newContext.isPushed = false; this.precedingStatementContexts.push(newContext); return newContext; } exitPrecedingStatementContext() { return this.precedingStatementContexts.pop(); } exitPrecedingStatementContextAndJoin(numTabs = 0) { return utility_1.joinIndentedLines(this.exitPrecedingStatementContext(), numTabs); } pushPrecedingStatements(node, ...statements) { const context = this.getCurrentPrecedingStatementContext(node); context.isPushed = false; return context.push(...statements); } pushPrecedingStatementToNewId(node, compiledSource, newId = this.getNewId()) { const currentContext = this.getCurrentPrecedingStatementContext(node); currentContext.push(this.indent + `local ${newId}${compiledSource ? ` = ${compiledSource}` : ""};\n`); currentContext.isPushed = true; return newId; } pushPrecedingStatementToReuseableId(node, compiledSource, nextCachedStrs) { if (compiledSource === "" || !canBePushedToReusableId(node)) { return this.pushPrecedingStatementToNewId(node, compiledSource); } /** Gets the top PreStatement to compare to */ let previousTop; for (let i = this.precedingStatementContexts.length - 1; 0 <= i; i--) { const context = this.precedingStatementContexts[i]; const topPreStatement = context[context.length - 1]; if (topPreStatement) { previousTop = [...context].reverse(); break; } } for (const cache of [previousTop, nextCachedStrs]) { /** If we would write a duplicate `local _5 = i`, skip it */ if (cache) { for (const str of cache) { const matchesRegex = str.match(/^(\t*)local ([a-zA-Z_][a-zA-Z0-9_]*) = ([^;]+);\n$/); // iterate only through non-state changing pushed id statements if (!matchesRegex) { break; } const [, indentation, currentId, data] = matchesRegex; if (indentation === this.indent && data === compiledSource) { this.getCurrentPrecedingStatementContext(node).isPushed = true; return currentId; } } } } return this.pushPrecedingStatementToNewId(node, compiledSource); } pushIndent() { this.indent += "\t"; } popIndent() { this.indent = this.indent.substr(1); } pushIdStack() { this.idStack.push(0); } popIdStack() { this.idStack.pop(); } getNewId() { const sum = this.idStack.reduce((accum, value) => accum + value); this.idStack[this.idStack.length - 1]++; return `_${sum}`; } pushHoistStack(name) { this.hoistStack[this.hoistStack.length - 1].add(name); } popHoistStack(result) { const top = this.hoistStack.pop(); if (top) { const hoists = [...top]; const namedHoists = new Array(); const declareHoists = new Array(); hoists.forEach(v => (v.includes("=") ? declareHoists : namedHoists).push(v)); if (namedHoists && namedHoists.length > 0) { result = this.indent + `local ${namedHoists.join(", ")};\n` + result; } if (declareHoists && declareHoists.length > 0) { result = this.indent + `${declareHoists.join(";\n" + this.indent)};\n` + result; } } return result; } pushExport(name, node) { if (!node.hasExportKeyword()) { return; } const ancestorName = this.getExportContextName(node); const alias = node.hasDefaultKeyword() ? "_default" : name; this.exportStack[this.exportStack.length - 1].add(`${ancestorName}.${alias} = ${name};\n`); } getNameForContext(myNamespace) { let name; if (myNamespace) { name = myNamespace.getName(); name = this.namespaceStack.get(name) || name; } else { name = "_exports"; this.isModule = true; } return name; } getExportContextName(node) { return this.getNameForContext(node.getFirstAncestorByKind(ts.SyntaxKind.ModuleDeclaration)); } getAlias(name) { const alias = this.variableAliases.get(name); if (alias !== undefined) { return alias; } else { return name; } } } exports.CompilerState = CompilerState; //# sourceMappingURL=CompilerState.js.map