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

271 lines 11.9 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 getVariableName(state, lhs, names, values, preStatements, postStatements) { if (lhs) { let varName = ""; if (ts.TypeGuards.isArrayBindingPattern(lhs) || ts.TypeGuards.isObjectBindingPattern(lhs)) { varName = state.getNewId(); _1.getBindingData(state, names, values, preStatements, postStatements, lhs, varName); } else if (ts.TypeGuards.isIdentifier(lhs)) { varName = lhs.getText(); _1.checkReserved(varName, lhs); } if (varName) { return varName; } } throw new CompilerError_1.CompilerError("Unexpected for..of initializer", lhs, CompilerError_1.CompilerErrorType.BadForOfInitializer, true); } function asDeclarationListOrThrow(initializer) { if (!ts.TypeGuards.isVariableDeclarationList(initializer)) { const initKindName = initializer.getKindName(); throw new CompilerError_1.CompilerError(`ForOf Loop has an unexpected initializer! (${initKindName})`, initializer, CompilerError_1.CompilerErrorType.UnexpectedInitializer, true); } return initializer; } function getSingleDeclarationOrThrow(initializer) { const declarations = initializer.getDeclarations(); if (declarations.length !== 1) { throw new CompilerError_1.CompilerError("Expected a single declaration in ForOf loop", initializer, CompilerError_1.CompilerErrorType.BadForOfInitializer, true); } return declarations[0]; } var ForOfLoopType; (function (ForOfLoopType) { ForOfLoopType[ForOfLoopType["Keys"] = 0] = "Keys"; ForOfLoopType[ForOfLoopType["Values"] = 1] = "Values"; ForOfLoopType[ForOfLoopType["Entries"] = 2] = "Entries"; ForOfLoopType[ForOfLoopType["Array"] = 3] = "Array"; ForOfLoopType[ForOfLoopType["ArrayEntries"] = 4] = "ArrayEntries"; ForOfLoopType[ForOfLoopType["String"] = 5] = "String"; ForOfLoopType[ForOfLoopType["IterableFunction"] = 6] = "IterableFunction"; ForOfLoopType[ForOfLoopType["Symbol_iterator"] = 7] = "Symbol_iterator"; })(ForOfLoopType || (ForOfLoopType = {})); function* propertyAccessExpressionTypeIter(state, exp) { while (ts.TypeGuards.isCallExpression(exp)) { const subExp = utility_1.skipNodesDownwards(exp.getExpression()); if (!ts.TypeGuards.isPropertyAccessExpression(subExp)) { break; } yield { exp: subExp, type: _1.getPropertyAccessExpressionType(state, subExp) }; exp = utility_1.skipNodesDownwards(subExp.getExpression()); } } function getLoopType(state, node, reversed = false, backwards = false) { const exp = utility_1.skipNodesDownwards(node.getExpression()); const expType = typeUtilities_1.getType(exp); const iter = propertyAccessExpressionTypeIter(state, exp); let data = iter.next(); if (!data.done) { let subExp = data.value.exp; switch (data.value.type) { case 7 /* ObjectConstructor */: { const iterExp = utility_1.skipNodesDownwards(exp.getArguments()[0]); switch (subExp.getName()) { case "keys": return [iterExp, ForOfLoopType.Keys, reversed, backwards]; case "entries": return [iterExp, ForOfLoopType.Entries, reversed, backwards]; case "values": return [iterExp, ForOfLoopType.Values, reversed, backwards]; } break; } case 0 /* Array */: { switch (subExp.getName()) { case "entries": { do { subExp = data.value.exp; reversed = !reversed; data = iter.next(); } while (!data.done && (data.value.type === 0 /* Array */ && data.value.exp.getName() === "reverse")); return [ utility_1.skipNodesDownwards(subExp.getExpression()), ForOfLoopType.ArrayEntries, !reversed, backwards, ]; } case "reverse": { const [lowerExp, lowerLoopExpType, isReversed, isBackwards] = getLoopType(state, subExp, reversed, !backwards); switch (lowerLoopExpType) { case ForOfLoopType.Array: case ForOfLoopType.ArrayEntries: return [lowerExp, lowerLoopExpType, isReversed, isBackwards]; } return [utility_1.skipNodesDownwards(subExp.getExpression()), ForOfLoopType.Array, reversed, !backwards]; } } break; } } } if (typeUtilities_1.isMapType(expType)) { return [exp, ForOfLoopType.Entries, reversed, backwards]; } else if (typeUtilities_1.isSetType(expType)) { return [exp, ForOfLoopType.Keys, reversed, backwards]; } else if (typeUtilities_1.isArrayType(expType)) { return [exp, ForOfLoopType.Array, reversed, backwards]; } else if (typeUtilities_1.isStringType(expType)) { return [exp, ForOfLoopType.String, reversed, backwards]; } else if (typeUtilities_1.isIterableFunction(expType)) { return [exp, ForOfLoopType.IterableFunction, reversed, backwards]; } else { return [exp, ForOfLoopType.Symbol_iterator, reversed, backwards]; } } function compileForOfStatement(state, node) { state.enterPrecedingStatementContext(); const initializer = asDeclarationListOrThrow(node.getInitializer()); const declaration = getSingleDeclarationOrThrow(initializer); const statement = node.getStatement(); const [exp, loopType, isReversed, isBackwards] = getLoopType(state, node); const lhs = declaration.getNameNode(); let varName; /** The key to be used in the for loop. If it is the empty string, it is irrelevant. */ let key = ""; /** The value to be used in the for loop. If it is the empty string, it is irrelevant. */ let value = ""; /** The expression to iterate through */ let expStr = _1.compileExpression(state, exp); let result = ""; const names = new Array(); const values = new Array(); const preStatements = new Array(); const postStatements = new Array(); /** Whether we should iterate as a simple for loop (defaults to a for..in loop) */ let isNumericForLoop = false; if (loopType === ForOfLoopType.Entries || loopType === ForOfLoopType.ArrayEntries) { if (loopType === ForOfLoopType.ArrayEntries) { expStr = _1.getReadableExpressionName(state, exp, expStr); isNumericForLoop = true; } else { expStr = `pairs(${expStr})`; } if (ts.TypeGuards.isArrayBindingPattern(lhs)) { const [first, second] = lhs.getElements(); if (first && ts.TypeGuards.isBindingElement(first)) { key = getVariableName(state, first.getNameNode(), names, values, preStatements, postStatements); } if (second && ts.TypeGuards.isBindingElement(second)) { value = getVariableName(state, second.getNameNode(), names, values, preStatements, postStatements); } } else { if (!ts.TypeGuards.isIdentifier(lhs)) { throw new CompilerError_1.CompilerError("Unexpected for..of initializer", lhs, CompilerError_1.CompilerErrorType.BadForOfInitializer, true); } key = state.getNewId(); value = state.getNewId(); varName = getVariableName(state, lhs, names, values, preStatements, postStatements); preStatements.push(`local ${varName} = {${key}, ${value}};`); } } else { varName = getVariableName(state, lhs, names, values, preStatements, postStatements); switch (loopType) { case ForOfLoopType.Keys: key = varName; expStr = `pairs(${expStr})`; break; case ForOfLoopType.Values: value = varName; expStr = `pairs(${expStr})`; break; case ForOfLoopType.Array: value = varName; expStr = _1.getReadableExpressionName(state, exp, expStr); isNumericForLoop = true; break; case ForOfLoopType.String: key = varName; expStr = `(${expStr}):gmatch(".")`; break; case ForOfLoopType.IterableFunction: key = varName; break; case ForOfLoopType.Symbol_iterator: { if (!typeUtilities_1.isIterableIterator(typeUtilities_1.getType(exp), exp)) { expStr = _1.getReadableExpressionName(state, exp, expStr); expStr = `${expStr}[TS.Symbol_iterator](${expStr})`; } const loopVar = state.getNewId(); key = loopVar; expStr = `${expStr}.next`; preStatements.push(`if ${loopVar}.done then break end;`); preStatements.push(`local ${varName} = ${loopVar}.value;`); break; } } } if (isNumericForLoop) { let accessor; let loopEndValue = `#${expStr}`; if (key) { accessor = value && isReversed ? `${(loopEndValue = state.pushPrecedingStatementToNewId(node, loopEndValue))} - ${key}` : `${key} + 1`; if (isBackwards) { result += state.indent + `for ${key} = ${loopEndValue} - 1, 0, -1 do\n`; } else { result += state.indent + `for ${key} = 0, ${loopEndValue} - 1 do\n`; } } else { accessor = state.getNewId(); if (isReversed ? !isBackwards : isBackwards) { result += state.indent + `for ${accessor} = ${loopEndValue}, 1, -1 do\n`; } else { result += state.indent + `for ${accessor} = 1, ${loopEndValue} do\n`; } } state.pushIndent(); if (value) { result += state.indent + `local ${value} = ${expStr}[${accessor}];\n`; } } else { result += state.indent + `for ${key || "_"}${value ? `, ${value}` : ""} in ${expStr} do\n`; state.pushIndent(); } for (const myStatement of preStatements) { result += state.indent + myStatement + "\n"; } _1.concatNamesAndValues(state, names, values, true, str => { result += str; }); for (const myStatement of postStatements) { result += state.indent + myStatement + "\n"; } state.pushIdStack(); result += _1.compileLoopBody(state, statement); state.popIndent(); result += state.indent + `end;\n`; state.popIdStack(); return state.exitPrecedingStatementContextAndJoin() + result; } exports.compileForOfStatement = compileForOfStatement; //# sourceMappingURL=forOf.js.map