X Tutup
Skip to content
Merged
402 changes: 402 additions & 0 deletions language-extensions/index.d.ts

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions src/transformation/utils/diagnostics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,10 @@ export const unsupportedMultiFunctionAssignment = createErrorDiagnosticFactory(
"Omitted expressions and BindingElements are expected here."
);

export const invalidOperatorMappingUse = createErrorDiagnosticFactory(
"This function must always be directly called and cannot be referred to."
);

export const annotationDeprecated = createWarningDiagnosticFactory(
(kind: AnnotationKind) =>
`'@${kind}' is deprecated and will be removed in a future update. Please update your code before upgrading to the next release, otherwise your project will no longer compile. ` +
Expand Down
83 changes: 81 additions & 2 deletions src/transformation/utils/language-extensions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,84 @@ import * as path from "path";
export enum ExtensionKind {
MultiFunction = "MultiFunction",
MultiType = "MultiType",
AdditionOperatorType = "AdditionOperatorType",
AdditionOperatorMethodType = "AdditionOperatorMethodType",
SubtractionOperatorType = "SubtractionOperatorType",
SubtractionOperatorMethodType = "SubtractionOperatorMethodType",
MultiplicationOperatorType = "MultiplicationOperatorType",
MultiplicationOperatorMethodType = "MultiplicationOperatorMethodType",
DivisionOperatorType = "DivisionOperatorType",
DivisionOperatorMethodType = "DivisionOperatorMethodType",
ModuloOperatorType = "ModuloOperatorType",
ModuloOperatorMethodType = "ModuloOperatorMethodType",
PowerOperatorType = "PowerOperatorType",
PowerOperatorMethodType = "PowerOperatorMethodType",
FloorDivisionOperatorType = "FloorDivisionOperatorType",
FloorDivisionOperatorMethodType = "FloorDivisionOperatorMethodType",
BitwiseAndOperatorType = "BitwiseAndOperatorType",
BitwiseAndOperatorMethodType = "BitwiseAndOperatorMethodType",
BitwiseOrOperatorType = "BitwiseOrOperatorType",
BitwiseOrOperatorMethodType = "BitwiseOrOperatorMethodType",
BitwiseExclusiveOrOperatorType = "BitwiseExclusiveOrOperatorType",
BitwiseExclusiveOrOperatorMethodType = "BitwiseExclusiveOrOperatorMethodType",
BitwiseLeftShiftOperatorType = "BitwiseLeftShiftOperatorType",
BitwiseLeftShiftOperatorMethodType = "BitwiseLeftShiftOperatorMethodType",
BitwiseRightShiftOperatorType = "BitwiseRightShiftOperatorType",
BitwiseRightShiftOperatorMethodType = "BitwiseRightShiftOperatorMethodType",
ConcatOperatorType = "ConcatOperatorType",
ConcatOperatorMethodType = "ConcatOperatorMethodType",
LessThanOperatorType = "LessThanOperatorType",
LessThanOperatorMethodType = "LessThanOperatorMethodType",
GreaterThanOperatorType = "GreaterThanOperatorType",
GreaterThanOperatorMethodType = "GreaterThanOperatorMethodType",
NegationOperatorType = "NegationOperatorType",
NegationOperatorMethodType = "NegationOperatorMethodType",
BitwiseNotOperatorType = "BitwiseNotOperatorType",
BitwiseNotOperatorMethodType = "BitwiseNotOperatorMethodType",
LengthOperatorType = "LengthOperatorType",
LengthOperatorMethodType = "LengthOperatorMethodType",
}

const typeNameToExtensionKind: { [name: string]: ExtensionKind } = {
MultiReturn: ExtensionKind.MultiType,
LuaAddition: ExtensionKind.AdditionOperatorType,
LuaAdditionMethod: ExtensionKind.AdditionOperatorMethodType,
LuaSubtraction: ExtensionKind.SubtractionOperatorType,
LuaSubtractionMethod: ExtensionKind.SubtractionOperatorMethodType,
LuaMultiplication: ExtensionKind.MultiplicationOperatorType,
LuaMultiplicationMethod: ExtensionKind.MultiplicationOperatorMethodType,
LuaDivision: ExtensionKind.DivisionOperatorType,
LuaDivisionMethod: ExtensionKind.DivisionOperatorMethodType,
LuaModulo: ExtensionKind.ModuloOperatorType,
LuaModuloMethod: ExtensionKind.ModuloOperatorMethodType,
LuaPower: ExtensionKind.PowerOperatorType,
LuaPowerMethod: ExtensionKind.PowerOperatorMethodType,
LuaFloorDivision: ExtensionKind.FloorDivisionOperatorType,
LuaFloorDivisionMethod: ExtensionKind.FloorDivisionOperatorMethodType,
LuaBitwiseAnd: ExtensionKind.BitwiseAndOperatorType,
LuaBitwiseAndMethod: ExtensionKind.BitwiseAndOperatorMethodType,
LuaBitwiseOr: ExtensionKind.BitwiseOrOperatorType,
LuaBitwiseOrMethod: ExtensionKind.BitwiseOrOperatorMethodType,
LuaBitwiseExclusiveOr: ExtensionKind.BitwiseExclusiveOrOperatorType,
LuaBitwiseExclusiveOrMethod: ExtensionKind.BitwiseExclusiveOrOperatorMethodType,
LuaBitwiseLeftShift: ExtensionKind.BitwiseLeftShiftOperatorType,
LuaBitwiseLeftShiftMethod: ExtensionKind.BitwiseLeftShiftOperatorMethodType,
LuaBitwiseRightShift: ExtensionKind.BitwiseRightShiftOperatorType,
LuaBitwiseRightShiftMethod: ExtensionKind.BitwiseRightShiftOperatorMethodType,
LuaConcat: ExtensionKind.ConcatOperatorType,
LuaConcatMethod: ExtensionKind.ConcatOperatorMethodType,
LuaLessThan: ExtensionKind.LessThanOperatorType,
LuaLessThanMethod: ExtensionKind.LessThanOperatorMethodType,
LuaGreaterThan: ExtensionKind.GreaterThanOperatorType,
LuaGreaterThanMethod: ExtensionKind.GreaterThanOperatorMethodType,
LuaNegation: ExtensionKind.NegationOperatorType,
LuaNegationMethod: ExtensionKind.NegationOperatorMethodType,
LuaBitwiseNot: ExtensionKind.BitwiseNotOperatorType,
LuaBitwiseNotMethod: ExtensionKind.BitwiseNotOperatorMethodType,
LuaLength: ExtensionKind.LengthOperatorType,
LuaLengthMethod: ExtensionKind.LengthOperatorMethodType,
};

function isSourceFileFromLanguageExtensions(sourceFile: ts.SourceFile): boolean {
const extensionDirectory = path.resolve(__dirname, "../../../language-extensions");
const sourceFileDirectory = path.dirname(path.normalize(sourceFile.fileName));
Expand All @@ -19,8 +95,11 @@ export function getExtensionKind(declaration: ts.Declaration): ExtensionKind | u
return ExtensionKind.MultiFunction;
}

if (ts.isTypeAliasDeclaration(declaration) && declaration.name.text === "MultiReturn") {
return ExtensionKind.MultiType;
if (ts.isTypeAliasDeclaration(declaration)) {
const extensionKind = typeNameToExtensionKind[declaration.name.text];
if (extensionKind) {
return extensionKind;
}
}

throw new Error("Unknown extension kind");
Expand Down
5 changes: 5 additions & 0 deletions src/transformation/visitors/call.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { isArrayType, isExpressionWithEvaluationEffect, isInDestructingAssignmen
import { transformElementAccessArgument } from "./access";
import { transformLuaTableCallExpression } from "./lua-table";
import { returnsMultiType } from "./language-extensions/multi";
import { isOperatorMapping, transformOperatorMappingExpression } from "./language-extensions/operators";

export type PropertyCallExpression = ts.CallExpression & { expression: ts.PropertyAccessExpression };

Expand Down Expand Up @@ -208,6 +209,10 @@ export const transformCallExpression: FunctionVisitor<ts.CallExpression> = (node
return wrapResult ? wrapInTable(builtinResult) : builtinResult;
}

if (isOperatorMapping(context, node)) {
return transformOperatorMappingExpression(context, node);
}

if (ts.isPropertyAccessExpression(node.expression)) {
const result = transformPropertyCall(context, node as PropertyCallExpression);
return wrapResult ? wrapInTable(result) : result;
Expand Down
7 changes: 6 additions & 1 deletion src/transformation/visitors/identifier.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,24 @@ import * as lua from "../../LuaAST";
import { transformBuiltinIdentifierExpression } from "../builtins";
import { FunctionVisitor, TransformationContext } from "../context";
import { isForRangeType } from "../utils/annotations";
import { invalidForRangeCall, invalidMultiFunctionUse } from "../utils/diagnostics";
import { invalidForRangeCall, invalidMultiFunctionUse, invalidOperatorMappingUse } from "../utils/diagnostics";
import { createExportedIdentifier, getSymbolExportScope } from "../utils/export";
import { createSafeName, hasUnsafeIdentifierName } from "../utils/safe-names";
import { getIdentifierSymbolId } from "../utils/symbols";
import { findFirstNodeAbove } from "../utils/typescript";
import { isMultiFunctionNode } from "./language-extensions/multi";
import { isOperatorMapping } from "./language-extensions/operators";

export function transformIdentifier(context: TransformationContext, identifier: ts.Identifier): lua.Identifier {
if (isMultiFunctionNode(context, identifier)) {
context.diagnostics.push(invalidMultiFunctionUse(identifier));
return lua.createAnonymousIdentifier(identifier);
}

if (isOperatorMapping(context, identifier)) {
context.diagnostics.push(invalidOperatorMappingUse(identifier));
}

if (isForRangeType(context, identifier)) {
const callExpression = findFirstNodeAbove(identifier, ts.isCallExpression);
if (!callExpression || !callExpression.parent || !ts.isForOfStatement(callExpression.parent)) {
Expand Down
177 changes: 177 additions & 0 deletions src/transformation/visitors/language-extensions/operators.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
import * as ts from "typescript";
import * as lua from "../../../LuaAST";
import { TransformationContext } from "../../context";
import * as extensions from "../../utils/language-extensions";
import { assert } from "../../../utils";
import { findFirstNodeAbove } from "../../utils/typescript";
import { LuaTarget } from "../../../CompilerOptions";
import { unsupportedForTarget } from "../../utils/diagnostics";

const binaryOperatorMappings = new Map<extensions.ExtensionKind, lua.BinaryOperator>([
[extensions.ExtensionKind.AdditionOperatorType, lua.SyntaxKind.AdditionOperator],
[extensions.ExtensionKind.AdditionOperatorMethodType, lua.SyntaxKind.AdditionOperator],
[extensions.ExtensionKind.SubtractionOperatorType, lua.SyntaxKind.SubtractionOperator],
[extensions.ExtensionKind.SubtractionOperatorMethodType, lua.SyntaxKind.SubtractionOperator],
[extensions.ExtensionKind.MultiplicationOperatorType, lua.SyntaxKind.MultiplicationOperator],
[extensions.ExtensionKind.MultiplicationOperatorMethodType, lua.SyntaxKind.MultiplicationOperator],
[extensions.ExtensionKind.DivisionOperatorType, lua.SyntaxKind.DivisionOperator],
[extensions.ExtensionKind.DivisionOperatorMethodType, lua.SyntaxKind.DivisionOperator],
[extensions.ExtensionKind.ModuloOperatorType, lua.SyntaxKind.ModuloOperator],
[extensions.ExtensionKind.ModuloOperatorMethodType, lua.SyntaxKind.ModuloOperator],
[extensions.ExtensionKind.PowerOperatorType, lua.SyntaxKind.PowerOperator],
[extensions.ExtensionKind.PowerOperatorMethodType, lua.SyntaxKind.PowerOperator],
[extensions.ExtensionKind.FloorDivisionOperatorType, lua.SyntaxKind.FloorDivisionOperator],
[extensions.ExtensionKind.FloorDivisionOperatorMethodType, lua.SyntaxKind.FloorDivisionOperator],
[extensions.ExtensionKind.BitwiseAndOperatorType, lua.SyntaxKind.BitwiseAndOperator],
[extensions.ExtensionKind.BitwiseAndOperatorMethodType, lua.SyntaxKind.BitwiseAndOperator],
[extensions.ExtensionKind.BitwiseOrOperatorType, lua.SyntaxKind.BitwiseOrOperator],
[extensions.ExtensionKind.BitwiseOrOperatorMethodType, lua.SyntaxKind.BitwiseOrOperator],
[extensions.ExtensionKind.BitwiseExclusiveOrOperatorType, lua.SyntaxKind.BitwiseExclusiveOrOperator],
[extensions.ExtensionKind.BitwiseExclusiveOrOperatorMethodType, lua.SyntaxKind.BitwiseExclusiveOrOperator],
[extensions.ExtensionKind.BitwiseLeftShiftOperatorType, lua.SyntaxKind.BitwiseLeftShiftOperator],
[extensions.ExtensionKind.BitwiseLeftShiftOperatorMethodType, lua.SyntaxKind.BitwiseLeftShiftOperator],
[extensions.ExtensionKind.BitwiseRightShiftOperatorType, lua.SyntaxKind.BitwiseRightShiftOperator],
[extensions.ExtensionKind.BitwiseRightShiftOperatorMethodType, lua.SyntaxKind.BitwiseRightShiftOperator],
[extensions.ExtensionKind.ConcatOperatorType, lua.SyntaxKind.ConcatOperator],
[extensions.ExtensionKind.ConcatOperatorMethodType, lua.SyntaxKind.ConcatOperator],
[extensions.ExtensionKind.LessThanOperatorType, lua.SyntaxKind.LessThanOperator],
[extensions.ExtensionKind.LessThanOperatorMethodType, lua.SyntaxKind.LessThanOperator],
[extensions.ExtensionKind.GreaterThanOperatorType, lua.SyntaxKind.GreaterThanOperator],
[extensions.ExtensionKind.GreaterThanOperatorMethodType, lua.SyntaxKind.GreaterThanOperator],
]);

const unaryOperatorMappings = new Map<extensions.ExtensionKind, lua.UnaryOperator>([
[extensions.ExtensionKind.NegationOperatorType, lua.SyntaxKind.NegationOperator],
[extensions.ExtensionKind.NegationOperatorMethodType, lua.SyntaxKind.NegationOperator],
[extensions.ExtensionKind.BitwiseNotOperatorType, lua.SyntaxKind.BitwiseNotOperator],
[extensions.ExtensionKind.BitwiseNotOperatorMethodType, lua.SyntaxKind.BitwiseNotOperator],
[extensions.ExtensionKind.LengthOperatorType, lua.SyntaxKind.LengthOperator],
[extensions.ExtensionKind.LengthOperatorMethodType, lua.SyntaxKind.LengthOperator],
]);

const operatorMapExtensions = new Set<extensions.ExtensionKind>([
...binaryOperatorMappings.keys(),
...unaryOperatorMappings.keys(),
]);

const bitwiseOperatorMapExtensions = new Set<extensions.ExtensionKind>([
extensions.ExtensionKind.BitwiseAndOperatorType,
extensions.ExtensionKind.BitwiseAndOperatorMethodType,
extensions.ExtensionKind.BitwiseOrOperatorType,
extensions.ExtensionKind.BitwiseOrOperatorMethodType,
extensions.ExtensionKind.BitwiseExclusiveOrOperatorType,
extensions.ExtensionKind.BitwiseExclusiveOrOperatorMethodType,
extensions.ExtensionKind.BitwiseLeftShiftOperatorType,
extensions.ExtensionKind.BitwiseLeftShiftOperatorMethodType,
extensions.ExtensionKind.BitwiseRightShiftOperatorType,
extensions.ExtensionKind.BitwiseRightShiftOperatorMethodType,
extensions.ExtensionKind.BitwiseNotOperatorType,
extensions.ExtensionKind.BitwiseNotOperatorMethodType,
]);

function getTypeDeclaration(declaration: ts.Declaration) {
return ts.isTypeAliasDeclaration(declaration)
? declaration
: findFirstNodeAbove(declaration, ts.isTypeAliasDeclaration);
}

function getOperatorMapExtensionKindForCall(context: TransformationContext, node: ts.CallExpression) {
const signature = context.checker.getResolvedSignature(node);
if (!signature || !signature.declaration) {
return;
}
const typeDeclaration = getTypeDeclaration(signature.declaration);
if (!typeDeclaration) {
return;
}
const mapping = extensions.getExtensionKind(typeDeclaration);
if (mapping !== undefined && operatorMapExtensions.has(mapping)) {
return mapping;
}
}

function isOperatorMapDeclaration(declaration: ts.Declaration) {
const typeDeclaration = getTypeDeclaration(declaration);
if (typeDeclaration) {
const extensionKind = extensions.getExtensionKind(typeDeclaration);
return extensionKind !== undefined ? operatorMapExtensions.has(extensionKind) : false;
}
}

function isOperatorMapType(context: TransformationContext, type: ts.Type): boolean {
if (type.isUnionOrIntersection()) {
return type.types.some(t => isOperatorMapType(context, t));
} else {
return type.symbol?.declarations?.some(isOperatorMapDeclaration);
}
}

function isOperatorMapIdentifier(context: TransformationContext, node: ts.Identifier) {
const type = context.checker.getTypeAtLocation(node);
return isOperatorMapType(context, type);
}

export function isOperatorMapping(context: TransformationContext, node: ts.CallExpression | ts.Identifier) {
if (ts.isCallExpression(node)) {
return getOperatorMapExtensionKindForCall(context, node) !== undefined;
} else {
return isOperatorMapIdentifier(context, node);
}
}

export function transformOperatorMappingExpression(
context: TransformationContext,
node: ts.CallExpression
): lua.Expression {
const extensionKind = getOperatorMapExtensionKindForCall(context, node);
assert(extensionKind);

const isBefore53 =
context.luaTarget === LuaTarget.Lua51 ||
context.luaTarget === LuaTarget.Lua52 ||
context.luaTarget === LuaTarget.LuaJIT ||
context.luaTarget === LuaTarget.Universal;
if (isBefore53) {
const luaTarget = context.luaTarget === LuaTarget.Universal ? LuaTarget.Lua51 : context.luaTarget;
if (bitwiseOperatorMapExtensions.has(extensionKind)) {
context.diagnostics.push(unsupportedForTarget(node, "Native bitwise operations", luaTarget));
} else if (
extensionKind === extensions.ExtensionKind.FloorDivisionOperatorType ||
extensionKind === extensions.ExtensionKind.FloorDivisionOperatorMethodType
) {
context.diagnostics.push(unsupportedForTarget(node, "Floor division operator", luaTarget));
}
}

const args = node.arguments.slice();
if (binaryOperatorMappings.has(extensionKind)) {
if (
args.length === 1 &&
(ts.isPropertyAccessExpression(node.expression) || ts.isElementAccessExpression(node.expression))
) {
args.unshift(node.expression.expression);
}

const luaOperator = binaryOperatorMappings.get(extensionKind);
assert(luaOperator);
return lua.createBinaryExpression(
context.transformExpression(args[0]),
context.transformExpression(args[1]),
luaOperator
);
} else {
let arg: ts.Expression;
if (
args.length === 0 &&
(ts.isPropertyAccessExpression(node.expression) || ts.isElementAccessExpression(node.expression))
) {
arg = node.expression.expression;
} else {
arg = args[0];
}

const luaOperator = unaryOperatorMappings.get(extensionKind);
assert(luaOperator);
return lua.createUnaryExpression(context.transformExpression(arg), luaOperator);
}
}
Loading
X Tutup