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
JavaScript
;
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