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

342 lines 16.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 compileParamDefault(state, exp, name) { const initializer = utility_1.skipNodesDownwards(exp); state.enterPrecedingStatementContext(); state.declarationContext.set(initializer, { isIdentifier: ts.TypeGuards.isIdentifier(initializer) && !_1.isIdentifierDefinedInExportLet(initializer), set: name, }); const expStr = _1.compileExpression(state, initializer); const context = state.exitPrecedingStatementContext(); state.pushIndent(); const declaration = state.declarationContext.delete(initializer) ? `${name} = ${expStr};` : ""; let newline; let indentation; let tab; let contextLines; if (context.length > (declaration ? 0 : 1)) { newline = "\n"; indentation = state.indent; tab = "\t"; contextLines = utility_1.joinIndentedLines(context, 2); } else { newline = " "; indentation = ""; tab = ""; contextLines = utility_1.joinIndentedLines(context, 0).replace(/\n/g, " "); } state.popIndent(); return "if ".concat(name, " == nil then", newline, contextLines, indentation, declaration ? tab + `${declaration}` + newline + indentation : "", "end;"); } function getParameterData(state, paramNames, initializers, node, defaults) { for (const param of node.getParameters()) { const child = param.getFirstChild(exp => ts.TypeGuards.isIdentifier(exp) || ts.TypeGuards.isArrayBindingPattern(exp) || ts.TypeGuards.isObjectBindingPattern(exp)); /* istanbul ignore next */ if (child === undefined) { throw new CompilerError_1.CompilerError("Child missing from parameter!", param, CompilerError_1.CompilerErrorType.ParameterChildMissing, true); } let name; if (ts.TypeGuards.isIdentifier(child)) { if (param.getName() === "this") { continue; } name = _1.compileExpression(state, child); } else { name = state.getNewId(); } if (param.isRestParameter()) { paramNames.push("..."); initializers.push(`local ${name} = { ... };`); } else { paramNames.push(name); } if (param.hasInitializer()) { (defaults ? defaults : initializers).push(compileParamDefault(state, param.getInitializer(), name)); } if (param.hasScopeKeyword() || param.isReadonly()) { const classDec = node.getParent(); if (ts.TypeGuards.isClassDeclaration(classDec) || ts.TypeGuards.isClassExpression(classDec)) { _1.checkPropertyCollision(classDec, param); } initializers.push(`self.${name} = ${name};`); } if (ts.TypeGuards.isArrayBindingPattern(child) || ts.TypeGuards.isObjectBindingPattern(child)) { const names = new Array(); const values = new Array(); const preStatements = new Array(); const postStatements = new Array(); getBindingData(state, names, values, preStatements, postStatements, child, name); preStatements.forEach(statement => initializers.push(statement)); concatNamesAndValues(state, names, values, true, declaration => initializers.push(declaration), false); postStatements.forEach(statement => initializers.push(statement)); } } } exports.getParameterData = getParameterData; function arrayAccessor(state, t, key) { return `${t}[${key}]`; } function objectAccessor(state, t, node, getAccessor, nameNode = node, aliasNode = node) { let name; if (ts.TypeGuards.isShorthandPropertyAssignment(nameNode)) { nameNode = nameNode.getNameNode(); } const rhs = node .getFirstAncestorOrThrow(ancestor => ts.TypeGuards.isObjectLiteralExpression(ancestor) || ts.TypeGuards.isObjectBindingPattern(ancestor)) .getParentOrThrow() .getLastChildOrThrow(() => true); if (ts.TypeGuards.isIdentifier(nameNode)) { name = _1.compileExpression(state, nameNode); } else if (ts.TypeGuards.isComputedPropertyName(nameNode)) { const exp = utility_1.skipNodesDownwards(nameNode.getExpression()); name = _1.getComputedPropertyAccess(state, exp, rhs); return `${t}[${name}]`; } else if (ts.TypeGuards.isNumericLiteral(nameNode) || ts.TypeGuards.isStringLiteral(nameNode)) { name = _1.compileExpression(state, nameNode); return `${t}[${name}]`; } else { throw new CompilerError_1.CompilerError(`Cannot index an object with type ${nameNode.getKindName()}.`, nameNode, CompilerError_1.CompilerErrorType.BadExpression, true); } if (getAccessor) { const type = typeUtilities_1.getType(aliasNode); if (typeUtilities_1.isArrayMethodType(type) || typeUtilities_1.isMapMethodType(type) || typeUtilities_1.isSetMethodType(type) || typeUtilities_1.isStringMethodType(type)) { throw new CompilerError_1.CompilerError(`Cannot index method ${name} (a roblox-ts internal)`, aliasNode, CompilerError_1.CompilerErrorType.BadDestructuringType); } } // We need this because length is built-in to the TS compiler, even if we removed it from our types if (typeUtilities_1.getCompilerDirectiveWithLaxConstraint(typeUtilities_1.getType(rhs), "array" /* Array */, r => r.isTuple()) && name === "length") { throw new CompilerError_1.CompilerError(`Cannot access the \`length\` property of a tuple! Instead use \`${rhs.getText()}.size()\``, node, CompilerError_1.CompilerErrorType.TupleLength); } return `${t}.${name}`; } function stringAccessor(state, t, key) { return `${t}:sub(${key}, ${key})`; } function setAccessor(state, t, key, preStatements, idStack) { const id = state.getNewId(); const lastId = idStack[idStack.length - 1]; if (lastId !== undefined) { preStatements.push(`local ${id} = next(${t}, ${lastId})`); } else { preStatements.push(`local ${id} = next(${t})`); } idStack.push(id); return id; } function mapAccessor(state, t, key, preStatements, idStack, isHole = false) { const keyId = state.getNewId(); const lastId = idStack[idStack.length - 1]; let valueId; let valueIdStr = ""; if (!isHole) { valueId = state.getNewId(); valueIdStr = `, ${valueId}`; } if (lastId !== undefined) { preStatements.push(`local ${keyId}${valueIdStr} = next(${t}, ${lastId})`); } else { preStatements.push(`local ${keyId}${valueIdStr} = next(${t})`); } idStack.push(keyId); return `{ ${keyId}${valueIdStr} }`; } function iterAccessor(state, t, key, preStatements, idStack, isHole = false) { if (isHole) { preStatements.push(`${t}.next()`); return ""; } else { const id = state.getNewId(); preStatements.push(`local ${id} = ${t}.next();`); return `${id}.value`; } } function iterableFunctionAccessor(state, t, key, preStatements, idStack, isHole = false) { if (isHole) { preStatements.push(`${t}()`); return ""; } else { return `${t}()`; } } function getAccessorForBindingPatternType(bindingPattern) { const bindingPatternType = typeUtilities_1.getType(bindingPattern); if (typeUtilities_1.isArrayType(bindingPatternType)) { return arrayAccessor; } else if (typeUtilities_1.isStringType(bindingPatternType)) { return stringAccessor; } else if (typeUtilities_1.isSetType(bindingPatternType)) { return setAccessor; } else if (typeUtilities_1.isMapType(bindingPatternType)) { return mapAccessor; } else if (typeUtilities_1.isIterableFunction(bindingPatternType)) { return iterableFunctionAccessor; } else if (typeUtilities_1.isIterableIterator(bindingPatternType, bindingPattern) || typeUtilities_1.isObjectType(bindingPatternType) || ts.TypeGuards.isThisExpression(bindingPattern) || ts.TypeGuards.isSuperExpression(bindingPattern)) { return iterAccessor; } else { if (bindingPattern.getKind() === ts.SyntaxKind.ObjectBindingPattern) { return null; } else { throw new CompilerError_1.CompilerError(`Cannot destructure an object of type ${bindingPatternType.getText()}`, bindingPattern, CompilerError_1.CompilerErrorType.BadDestructuringType); } } } exports.getAccessorForBindingPatternType = getAccessorForBindingPatternType; function concatNamesAndValues(state, names, values, isLocal, func, includeSpacing = true, includeSemicolon = true) { if (values.length > 0) { names[0] = names[0] || "_"; func(`${includeSpacing ? state.indent : ""}${isLocal ? "local " : ""}${names.join(", ")} = ${values.join(", ")}${includeSemicolon ? ";" : ""}${includeSpacing ? "\n" : ""}`); } } exports.concatNamesAndValues = concatNamesAndValues; function getBindingData(state, names, values, preStatements, postStatements, bindingPattern, parentId, getAccessor = getAccessorForBindingPatternType(bindingPattern)) { const idStack = new Array(); const strKeys = bindingPattern.getKind() === ts.SyntaxKind.ObjectBindingPattern; let childIndex = 1; for (const item of bindingPattern.getFirstChildByKindOrThrow(ts.SyntaxKind.SyntaxList).getChildren()) { if (ts.TypeGuards.isBindingElement(item)) { const [child, op, pattern] = item.getChildren(); if (child.getKind() === ts.SyntaxKind.DotDotDotToken) { throw new CompilerError_1.CompilerError("Operator ... is not supported for destructuring!", child, CompilerError_1.CompilerErrorType.SpreadDestructuring); } if (pattern && (ts.TypeGuards.isArrayBindingPattern(pattern) || ts.TypeGuards.isObjectBindingPattern(pattern))) { const childId = state.getNewId(); const accessor = strKeys ? objectAccessor(state, parentId, child, getAccessor) : getAccessor(state, parentId, childIndex, preStatements, idStack); preStatements.push(`local ${childId} = ${accessor};`); getBindingData(state, names, values, preStatements, postStatements, pattern, childId); } else if (ts.TypeGuards.isArrayBindingPattern(child)) { const childId = state.getNewId(); const accessor = strKeys ? objectAccessor(state, parentId, child, getAccessor) : getAccessor(state, parentId, childIndex, preStatements, idStack); preStatements.push(`local ${childId} = ${accessor};`); getBindingData(state, names, values, preStatements, postStatements, child, childId); } else if (ts.TypeGuards.isIdentifier(child)) { const idNode = pattern && ts.TypeGuards.isIdentifier(pattern) ? pattern : child; const id = _1.compileIdentifier(state, idNode, true); names.push(id); if (op && op.getKind() === ts.SyntaxKind.EqualsToken) { postStatements.push(compileParamDefault(state, pattern, id)); } const accessor = strKeys ? objectAccessor(state, parentId, child, getAccessor, child, idNode) : getAccessor(state, parentId, childIndex, preStatements, idStack); values.push(accessor); } else if (ts.TypeGuards.isObjectBindingPattern(child)) { const childId = state.getNewId(); const accessor = strKeys ? objectAccessor(state, parentId, child, getAccessor) : getAccessor(state, parentId, childIndex, preStatements, idStack); preStatements.push(`local ${childId} = ${accessor};`); getBindingData(state, names, values, preStatements, postStatements, child, childId); } else if (ts.TypeGuards.isComputedPropertyName(child)) { const expStr = _1.getComputedPropertyAccess(state, utility_1.skipNodesDownwards(child.getExpression()), bindingPattern); const accessor = `${parentId}[${expStr}]`; const childId = _1.compileExpression(state, pattern); preStatements.push(`local ${childId} = ${accessor};`); } else if (child.getKind() !== ts.SyntaxKind.CommaToken && !ts.TypeGuards.isOmittedExpression(child)) { throw new CompilerError_1.CompilerError(`Unexpected ${child.getKindName()} in getBindingData.`, child, CompilerError_1.CompilerErrorType.UnexpectedBindingPattern, true); } } else if (ts.TypeGuards.isIdentifier(item)) { const id = _1.compileExpression(state, item); names.push(id); values.push(getAccessor(state, parentId, childIndex, preStatements, idStack)); } else if (ts.TypeGuards.isPropertyAccessExpression(item)) { const id = _1.compileExpression(state, item); names.push(id); values.push(getAccessor(state, parentId, childIndex, preStatements, idStack)); } else if (ts.TypeGuards.isArrayLiteralExpression(item)) { const childId = state.getNewId(); preStatements.push(`local ${childId} = ${getAccessor(state, parentId, childIndex, preStatements, idStack)};`); getBindingData(state, names, values, preStatements, postStatements, item, childId); } else if (item.getKind() === ts.SyntaxKind.CommaToken) { childIndex--; } else if (ts.TypeGuards.isObjectLiteralExpression(item)) { const childId = state.getNewId(); preStatements.push(`local ${childId} = ${getAccessor(state, parentId, childIndex, preStatements, idStack)};`); getBindingData(state, names, values, preStatements, postStatements, item, childId); } else if (ts.TypeGuards.isShorthandPropertyAssignment(item)) { preStatements.push(`${item.getName()} = ${objectAccessor(state, parentId, item, getAccessor)};`); } else if (ts.TypeGuards.isPropertyAssignment(item)) { let alias; const nameNode = item.getNameNode(); if (item.hasInitializer()) { const initializer = utility_1.skipNodesDownwards(item.getInitializer()); if (ts.TypeGuards.isIdentifier(initializer) || ts.TypeGuards.isPropertyAccessExpression(initializer) || ts.TypeGuards.isElementAccessExpression(initializer)) { alias = _1.compileExpression(state, initializer); preStatements.push(`${alias} = ${objectAccessor(state, parentId, item, getAccessor, nameNode)};`); } else { alias = state.getNewId(); preStatements.push(`${alias} = ${objectAccessor(state, parentId, item, getAccessor, nameNode)};`); getBindingData(state, names, values, preStatements, postStatements, initializer, alias); } } else { alias = item.getName(); preStatements.push(`${alias} = ${objectAccessor(state, parentId, item, getAccessor, nameNode)};`); } } else if (ts.TypeGuards.isOmittedExpression(item)) { getAccessor(state, parentId, childIndex, preStatements, idStack, true); } else { throw new CompilerError_1.CompilerError(`Unexpected ${item.getKindName()} in getBindingData.`, item, CompilerError_1.CompilerErrorType.UnexpectedBindingPattern, true); } childIndex++; } } exports.getBindingData = getBindingData; //# sourceMappingURL=binding.js.map