diff --git a/src/LuaAST.ts b/src/LuaAST.ts index 5085b2865..47323a7aa 100644 --- a/src/LuaAST.ts +++ b/src/LuaAST.ts @@ -71,20 +71,26 @@ export enum SyntaxKind { BitwiseOrOperator, BitwiseExclusiveOrOperator, BitwiseRightShiftOperator, + BitwiseArithmeticRightShift, BitwiseLeftShiftOperator, BitwiseNotOperator, // Unary } // TODO maybe name this PrefixUnary? not sure it makes sense to do so, because all unary ops in Lua are prefix -export type UnaryOperator = SyntaxKind.NegationOperator | SyntaxKind.LengthOperator | SyntaxKind.NotOperator | SyntaxKind.BitwiseNotOperator; +export type UnaryBitwiseOperator = SyntaxKind.BitwiseNotOperator; + +export type UnaryOperator = SyntaxKind.NegationOperator | SyntaxKind.LengthOperator | SyntaxKind.NotOperator | UnaryBitwiseOperator; + +export type BinaryBitwiseOperator = + SyntaxKind.BitwiseAndOperator | SyntaxKind.BitwiseOrOperator | SyntaxKind.BitwiseExclusiveOrOperator | + SyntaxKind.BitwiseRightShiftOperator | SyntaxKind.BitwiseArithmeticRightShift | SyntaxKind.BitwiseLeftShiftOperator; export type BinaryOperator = SyntaxKind.AdditionOperator | SyntaxKind.SubractionOperator | SyntaxKind.MultiplicationOperator | SyntaxKind.DivisionOperator | SyntaxKind.FloorDivisionOperator | SyntaxKind.ModuloOperator | SyntaxKind.PowerOperator | SyntaxKind.ConcatOperator | SyntaxKind.EqualityOperator | SyntaxKind.InequalityOperator | SyntaxKind.LessThanOperator | SyntaxKind.LessEqualOperator | SyntaxKind.GreaterThanOperator | SyntaxKind.GreaterEqualOperator | SyntaxKind.AndOperator | SyntaxKind.OrOperator | - SyntaxKind.BitwiseAndOperator | SyntaxKind.BitwiseOrOperator | SyntaxKind.BitwiseExclusiveOrOperator | - SyntaxKind.BitwiseRightShiftOperator | SyntaxKind.BitwiseLeftShiftOperator | SyntaxKind.BitwiseNotOperator; + BinaryBitwiseOperator; export type Operator = UnaryOperator | BinaryOperator; diff --git a/src/LuaPrinter.ts b/src/LuaPrinter.ts index 5e7734828..7c59f47e0 100644 --- a/src/LuaPrinter.ts +++ b/src/LuaPrinter.ts @@ -31,6 +31,7 @@ export class LuaPrinter { [tstl.SyntaxKind.BitwiseOrOperator]: "|", [tstl.SyntaxKind.BitwiseExclusiveOrOperator]: "~", [tstl.SyntaxKind.BitwiseRightShiftOperator]: ">>", + [tstl.SyntaxKind.BitwiseArithmeticRightShift]: ">>>", [tstl.SyntaxKind.BitwiseLeftShiftOperator]: "<<", [tstl.SyntaxKind.BitwiseNotOperator]: "~", }; @@ -57,7 +58,7 @@ export class LuaPrinter { || this.options.luaLibImport === LuaLibImportKind.Always) { header += `require("lualib_bundle");\n`; - } + } // Inline lualib features else if (this.options.luaLibImport === LuaLibImportKind.Inline && luaLibFeatures.size > 0) { diff --git a/src/LuaTransformer.ts b/src/LuaTransformer.ts index 269c8142b..32bbeca72 100644 --- a/src/LuaTransformer.ts +++ b/src/LuaTransformer.ts @@ -33,7 +33,7 @@ export class LuaTransformer { private isStrict = true; private checker: ts.TypeChecker; - private options: CompilerOptions; + protected options: CompilerOptions; private isModule: boolean; private currentSourceFile?: ts.SourceFile; @@ -878,7 +878,7 @@ export class LuaTransformer { const [isCompound, replacementOperator] = tsHelper.isBinaryAssignmentToken(expression.operatorToken.kind); if (isCompound) { // +=, -=, etc... - return this.transformCompoundAssignmentStatement(expression.left, expression.right, replacementOperator); + return this.transformCompoundAssignmentStatement(expression, expression.left, expression.right, replacementOperator); } else if (expression.operatorToken.kind === ts.SyntaxKind.EqualsToken) { // = assignment @@ -891,14 +891,14 @@ export class LuaTransformer { // ++i, --i const replacementOperator = expression.operator === ts.SyntaxKind.PlusPlusToken ? tstl.SyntaxKind.AdditionOperator : tstl.SyntaxKind.SubractionOperator; - return this.transformCompoundAssignmentStatement(expression.operand, ts.createLiteral(1), replacementOperator); + return this.transformCompoundAssignmentStatement(expression, expression.operand, ts.createLiteral(1), replacementOperator); } else if (ts.isPostfixUnaryExpression(expression)) { // i++, i-- const replacementOperator = expression.operator === ts.SyntaxKind.PlusPlusToken ? tstl.SyntaxKind.AdditionOperator : tstl.SyntaxKind.SubractionOperator; - return this.transformCompoundAssignmentStatement(expression.operand, ts.createLiteral(1), replacementOperator); + return this.transformCompoundAssignmentStatement(expression, expression.operand, ts.createLiteral(1), replacementOperator); } return tstl.createExpressionStatement(this.transformExpression(expression)); @@ -1297,73 +1297,102 @@ export class LuaTransformer { } } + public transformBinaryOperation( + node: ts.Node, + left: tstl.Expression, + right: tstl.Expression, + operator: tstl.BinaryOperator + ): tstl.Expression { + switch (operator) { + case tstl.SyntaxKind.BitwiseAndOperator: + case tstl.SyntaxKind.BitwiseOrOperator: + case tstl.SyntaxKind.BitwiseExclusiveOrOperator: + case tstl.SyntaxKind.BitwiseLeftShiftOperator: + case tstl.SyntaxKind.BitwiseRightShiftOperator: + case tstl.SyntaxKind.BitwiseArithmeticRightShift: + return this.transformBinaryBitOperation(node, left, right, operator); + + default: + return tstl.createBinaryExpression(left, right, operator, undefined, node); + } + } + public transformBinaryExpression(expression: ts.BinaryExpression): tstl.Expression { // Check if this is an assignment token, then handle accordingly const [isCompound, replacementOperator] = tsHelper.isBinaryAssignmentToken(expression.operatorToken.kind); if (isCompound) { - return this.transformCompoundAssignmentExpression(expression.left, expression.right, replacementOperator, false); + return this.transformCompoundAssignmentExpression(expression, expression.left, expression.right, replacementOperator, false); } const lhs = this.transformExpression(expression.left); const rhs = this.transformExpression(expression.right); - // Transpile Bitops + // Transpile operators switch (expression.operatorToken.kind) { case ts.SyntaxKind.AmpersandToken: + return this.transformBinaryOperation(expression, lhs, rhs, tstl.SyntaxKind.BitwiseAndOperator); case ts.SyntaxKind.BarToken: + return this.transformBinaryOperation(expression, lhs, rhs, tstl.SyntaxKind.BitwiseOrOperator); case ts.SyntaxKind.CaretToken: + return this.transformBinaryOperation(expression, lhs, rhs, tstl.SyntaxKind.BitwiseExclusiveOrOperator); case ts.SyntaxKind.LessThanLessThanToken: + return this.transformBinaryOperation(expression, lhs, rhs, tstl.SyntaxKind.BitwiseLeftShiftOperator); case ts.SyntaxKind.GreaterThanGreaterThanToken: + return this.transformBinaryOperation(expression, lhs, rhs, tstl.SyntaxKind.BitwiseRightShiftOperator); case ts.SyntaxKind.GreaterThanGreaterThanGreaterThanToken: - return this.transformBitOperation(expression, lhs, rhs); - } - - // Transpile operators - switch (expression.operatorToken.kind) { + return this.transformBinaryOperation(expression, lhs, rhs, tstl.SyntaxKind.BitwiseArithmeticRightShift); case ts.SyntaxKind.AmpersandAmpersandToken: - return tstl.createBinaryExpression(lhs, rhs, tstl.SyntaxKind.AndOperator); + return this.transformBinaryOperation(expression, lhs, rhs, tstl.SyntaxKind.AndOperator); case ts.SyntaxKind.BarBarToken: - return tstl.createBinaryExpression(lhs, rhs, tstl.SyntaxKind.OrOperator); + return this.transformBinaryOperation(expression, lhs, rhs, tstl.SyntaxKind.OrOperator); case ts.SyntaxKind.PlusToken: // Replace string + with .. const typeLeft = this.checker.getTypeAtLocation(expression.left); const typeRight = this.checker.getTypeAtLocation(expression.right); if (tsHelper.isStringType(typeLeft) || tsHelper.isStringType(typeRight)) { - return tstl.createBinaryExpression(lhs, rhs, tstl.SyntaxKind.ConcatOperator); + return this.transformBinaryOperation(expression, lhs, rhs, tstl.SyntaxKind.ConcatOperator); } - return tstl.createBinaryExpression(lhs, rhs, tstl.SyntaxKind.AdditionOperator); + return this.transformBinaryOperation(expression, lhs, rhs, tstl.SyntaxKind.AdditionOperator); case ts.SyntaxKind.MinusToken: - return tstl.createBinaryExpression(lhs, rhs, tstl.SyntaxKind.SubractionOperator); + return this.transformBinaryOperation(expression, lhs, rhs, tstl.SyntaxKind.SubractionOperator); case ts.SyntaxKind.AsteriskToken: - return tstl.createBinaryExpression(lhs, rhs, tstl.SyntaxKind.MultiplicationOperator); + return this.transformBinaryOperation(expression, lhs, rhs, tstl.SyntaxKind.MultiplicationOperator); case ts.SyntaxKind.AsteriskAsteriskToken: - return tstl.createBinaryExpression(lhs, rhs, tstl.SyntaxKind.PowerOperator); + return this.transformBinaryOperation(expression, lhs, rhs, tstl.SyntaxKind.PowerOperator); case ts.SyntaxKind.SlashToken: - return tstl.createBinaryExpression(lhs, rhs, tstl.SyntaxKind.DivisionOperator); + return this.transformBinaryOperation(expression, lhs, rhs, tstl.SyntaxKind.DivisionOperator); case ts.SyntaxKind.PercentToken: - return tstl.createBinaryExpression(lhs, rhs, tstl.SyntaxKind.ModuloOperator); + return this.transformBinaryOperation(expression, lhs, rhs, tstl.SyntaxKind.ModuloOperator); case ts.SyntaxKind.GreaterThanToken: - return tstl.createBinaryExpression(lhs, rhs, tstl.SyntaxKind.GreaterThanOperator); + return this.transformBinaryOperation(expression, lhs, rhs, tstl.SyntaxKind.GreaterThanOperator); case ts.SyntaxKind.GreaterThanEqualsToken: - return tstl.createBinaryExpression(lhs, rhs, tstl.SyntaxKind.GreaterEqualOperator); + return this.transformBinaryOperation(expression, lhs, rhs, tstl.SyntaxKind.GreaterEqualOperator); case ts.SyntaxKind.LessThanToken: - return tstl.createBinaryExpression(lhs, rhs, tstl.SyntaxKind.LessThanOperator); + return this.transformBinaryOperation(expression, lhs, rhs, tstl.SyntaxKind.LessThanOperator); case ts.SyntaxKind.LessThanEqualsToken: - return tstl.createBinaryExpression(lhs, rhs, tstl.SyntaxKind.LessEqualOperator); + return this.transformBinaryOperation(expression, lhs, rhs, tstl.SyntaxKind.LessEqualOperator); case ts.SyntaxKind.EqualsToken: return this.transformAssignmentExpression(expression); case ts.SyntaxKind.EqualsEqualsToken: case ts.SyntaxKind.EqualsEqualsEqualsToken: - return tstl.createBinaryExpression(lhs, rhs, tstl.SyntaxKind.EqualityOperator); + return this.transformBinaryOperation(expression, lhs, rhs, tstl.SyntaxKind.EqualityOperator); case ts.SyntaxKind.ExclamationEqualsToken: case ts.SyntaxKind.ExclamationEqualsEqualsToken: - return tstl.createBinaryExpression(lhs, rhs, tstl.SyntaxKind.InequalityOperator); + return this.transformBinaryOperation(expression, lhs, rhs, tstl.SyntaxKind.InequalityOperator); case ts.SyntaxKind.InKeyword: const indexExpression = tstl.createTableIndexExpression(rhs, lhs); - return tstl.createBinaryExpression(indexExpression, tstl.createNilLiteral(), tstl.SyntaxKind.InequalityOperator); + return tstl.createBinaryExpression( + indexExpression, + tstl.createNilLiteral(), + tstl.SyntaxKind.InequalityOperator, + undefined, + expression + ); + case ts.SyntaxKind.InstanceOfKeyword: return this.transformLuaLibFunction(LuaLibFeature.InstanceOf, lhs, rhs); + default: throw TSTLErrors.UnsupportedKind("binary operator", expression.operatorToken.kind, expression); } @@ -1371,9 +1400,14 @@ export class LuaTransformer { public transformAssignment(lhs: ts.Expression, right: tstl.Expression): tstl.Statement { if (ts.isPropertyAccessExpression(lhs) && tsHelper.hasSetAccessor(lhs, this.checker)) { - return tstl.createExpressionStatement(this.transformSetAccessor(lhs, right)); + return tstl.createExpressionStatement(this.transformSetAccessor(lhs, right), undefined, lhs.parent); } else { - return tstl.createAssignmentStatement(this.transformExpression(lhs) as tstl.IdentifierOrTableIndexExpression, right); + return tstl.createAssignmentStatement( + this.transformExpression(lhs) as tstl.IdentifierOrTableIndexExpression, + right, + undefined, + lhs.parent + ); } } @@ -1383,11 +1417,6 @@ export class LuaTransformer { const leftType = this.checker.getTypeAtLocation(expression.left); this.validateFunctionAssignment(expression.right, rightType, leftType); - if (ts.isPropertyAccessExpression(expression.left) && tsHelper.hasSetAccessor(expression.left, this.checker)) { - const right = this.transformExpression(expression.right); - return tstl.createExpressionStatement(this.transformSetAccessor(expression.left, right)); - } - if (ts.isArrayLiteralExpression(expression.left)) { // Destructuring assignment const left = expression.left.elements.map(e => this.transformExpression(e)); @@ -1399,7 +1428,7 @@ export class LuaTransformer { } else { right = [this.createUnpackCall(this.transformExpression(expression.right))]; } - return tstl.createAssignmentStatement(left as tstl.IdentifierOrTableIndexExpression[], right); + return tstl.createAssignmentStatement(left as tstl.IdentifierOrTableIndexExpression[], right, undefined, expression); } else { // Simple assignment return this.transformAssignment(expression.left, this.transformExpression(expression.right)); @@ -1472,6 +1501,7 @@ export class LuaTransformer { } public transformCompoundAssignmentExpression( + expression: ts.Expression, lhs: ts.Expression, rhs: ts.Expression, replacementOperator: tstl.BinaryOperator, @@ -1506,12 +1536,12 @@ export class LuaTransformer { // local ____TS_tmp = ____TS_obj[____TS_index]; // ____TS_obj[____TS_index] = ____TS_tmp ${replacementOperator} ${right}; tmpDeclaration = tstl.createVariableDeclarationStatement(tmp, accessExpression); - const operatorExpression = tstl.createBinaryExpression(tmp, right, replacementOperator); + const operatorExpression = this.transformBinaryOperation(expression, tmp, right, replacementOperator); assignStatement = tstl.createAssignmentStatement(accessExpression, operatorExpression); } else { // local ____TS_tmp = ____TS_obj[____TS_index] ${replacementOperator} ${right}; // ____TS_obj[____TS_index] = ____TS_tmp; - const operatorExpression = tstl.createBinaryExpression(accessExpression, right, replacementOperator); + const operatorExpression = this.transformBinaryOperation(expression, accessExpression, right, replacementOperator); tmpDeclaration = tstl.createVariableDeclarationStatement(tmp, operatorExpression); assignStatement = tstl.createAssignmentStatement(accessExpression, tmp); } @@ -1525,7 +1555,7 @@ export class LuaTransformer { // return ____TS_tmp const tmpIdentifier = tstl.createIdentifier("____TS_tmp"); const tmpDeclaration = tstl.createVariableDeclarationStatement(tmpIdentifier, left); - const operatorExpression = tstl.createBinaryExpression(tmpIdentifier, right, replacementOperator); + const operatorExpression = this.transformBinaryOperation(expression, tmpIdentifier, right, replacementOperator); const assignStatement = this.transformAssignment(lhs, operatorExpression); return this.createImmediatelyInvokedFunctionExpression([tmpDeclaration, assignStatement], tmpIdentifier); @@ -1535,7 +1565,7 @@ export class LuaTransformer { // ${left} = ____TS_tmp; // return ____TS_tmp const tmpIdentifier = tstl.createIdentifier("____TS_tmp"); - const operatorExpression = tstl.createBinaryExpression(left, right, replacementOperator); + const operatorExpression = this.transformBinaryOperation(lhs.parent, left, right, replacementOperator); const tmpDeclaration = tstl.createVariableDeclarationStatement(tmpIdentifier, operatorExpression); const assignStatement = this.transformAssignment(lhs, tmpIdentifier); return this.createImmediatelyInvokedFunctionExpression([tmpDeclaration, assignStatement], tmpIdentifier); @@ -1543,14 +1573,18 @@ export class LuaTransformer { } else { // Simple expressions // ${left} = ${right}; return ${right} - const operatorExpression = tstl.createBinaryExpression(left, right, replacementOperator); + const operatorExpression = this.transformBinaryOperation(lhs.parent, left, right, replacementOperator); const assignStatement = this.transformAssignment(lhs, operatorExpression); return this.createImmediatelyInvokedFunctionExpression([assignStatement], left); } } - public transformCompoundAssignmentStatement(lhs: ts.Expression, rhs: ts.Expression, replacementOperator: tstl.BinaryOperator): - tstl.Statement { + public transformCompoundAssignmentStatement( + node: ts.Node, + lhs: ts.Expression, + rhs: ts.Expression, + replacementOperator: tstl.BinaryOperator + ): tstl.Statement { if (replacementOperator === tstl.SyntaxKind.AdditionOperator) { // Check is we need to use string concat operator const typeLeft = this.checker.getTypeAtLocation(lhs); @@ -1573,21 +1607,125 @@ export class LuaTransformer { const objAndIndexDeclaration = tstl.createVariableDeclarationStatement( [obj, index], [this.transformExpression(objExpression), this.transformExpression(indexExpression)]); const accessExpression = tstl.createTableIndexExpression(obj, index); - const operatorExpression = - tstl.createBinaryExpression(accessExpression, tstl.createParenthesizedExpression(right), replacementOperator); + const operatorExpression = this.transformBinaryOperation( + node, + accessExpression, + tstl.createParenthesizedExpression(right), + replacementOperator + ); const assignStatement = tstl.createAssignmentStatement(accessExpression, operatorExpression); return tstl.createDoStatement([objAndIndexDeclaration, assignStatement]); } else { // Simple statements // ${left} = ${left} ${replacementOperator} ${right} - const operatorExpression = tstl.createBinaryExpression(left, right, replacementOperator); + const operatorExpression = this.transformBinaryOperation(node, left, right, replacementOperator); return this.transformAssignment(lhs, operatorExpression); } } - public transformBitOperation(node: ts.BinaryExpression, lhs: tstl.Expression, rhs: tstl.Expression): ExpressionVisitResult { - throw TSTLErrors.UnsupportedForTarget("Bitwise operations", this.options.luaTarget, node); + public transformUnaryBitLibOperation( + node: ts.Node, + expression: tstl.Expression, + operator: tstl.UnaryBitwiseOperator, + lib: string + ): ExpressionVisitResult { + let bitFunction: string; + switch (operator) { + case tstl.SyntaxKind.BitwiseNotOperator: + bitFunction = "bnot"; + break; + default: + throw TSTLErrors.UnsupportedKind("unary bitwise operator", operator, node); + } + return tstl.createCallExpression( + tstl.createTableIndexExpression(tstl.createIdentifier(lib), tstl.createStringLiteral(bitFunction)), + [expression], + undefined, + node + ); + } + + public transformUnaryBitOperation( + node: ts.Node, + expression: tstl.Expression, + operator: tstl.UnaryBitwiseOperator + ): ExpressionVisitResult { + switch (this.options.luaTarget) { + case LuaTarget.Lua51: + throw TSTLErrors.UnsupportedForTarget("Bitwise operations", this.options.luaTarget, node); + + case LuaTarget.Lua52: + return this.transformUnaryBitLibOperation(node, expression, operator, "bit32"); + + case LuaTarget.LuaJIT: + return this.transformUnaryBitLibOperation(node, expression, operator, "bit"); + + default: + return tstl.createUnaryExpression(expression, operator, undefined, node); + } + } + + public transformBinaryBitLibOperation( + node: ts.Node, + left: tstl.Expression, + right: tstl.Expression, + operator: tstl.BinaryBitwiseOperator, + lib: string + ): ExpressionVisitResult { + let bitFunction: string; + switch (operator) { + case tstl.SyntaxKind.BitwiseAndOperator: + bitFunction = "band"; + break; + case tstl.SyntaxKind.BitwiseOrOperator: + bitFunction = "bor"; + break; + case tstl.SyntaxKind.BitwiseExclusiveOrOperator: + bitFunction = "bxor"; + break; + case tstl.SyntaxKind.BitwiseLeftShiftOperator: + bitFunction = "lshift"; + break; + case tstl.SyntaxKind.BitwiseRightShiftOperator: + bitFunction = "rshift"; + break; + case tstl.SyntaxKind.BitwiseArithmeticRightShift: + bitFunction = "arshift"; + break; + default: + throw TSTLErrors.UnsupportedKind("binary bitwise operator", operator, node); + } + return tstl.createCallExpression( + tstl.createTableIndexExpression(tstl.createIdentifier(lib), tstl.createStringLiteral(bitFunction)), + [left, right], + undefined, + node + ); + } + + public transformBinaryBitOperation( + node: ts.Node, + left: tstl.Expression, + right: tstl.Expression, + operator: tstl.BinaryBitwiseOperator + ): ExpressionVisitResult { + switch (this.options.luaTarget) { + case LuaTarget.Lua51: + throw TSTLErrors.UnsupportedForTarget("Bitwise operations", this.options.luaTarget, node); + + case LuaTarget.Lua52: + return this.transformBinaryBitLibOperation(node, left, right, operator, "bit32"); + + case LuaTarget.LuaJIT: + return this.transformBinaryBitLibOperation(node, left, right, operator, "bit"); + + default: + if (operator === tstl.SyntaxKind.BitwiseArithmeticRightShift) { + throw TSTLErrors.UnsupportedForTarget("Bitwise >>> operator", this.options.luaTarget, node); + } + return tstl.createBinaryExpression(left, right, operator, undefined, node); + } } public transformConditionalExpression(node: ts.ConditionalExpression, brackets?: boolean): tstl.CallExpression { @@ -1599,20 +1737,49 @@ export class LuaTransformer { } public transformPostfixUnaryExpression(expression: ts.PostfixUnaryExpression): tstl.Expression { - const replacementOperator = - expression.operator === ts.SyntaxKind.PlusPlusToken ? tstl.SyntaxKind.AdditionOperator : tstl.SyntaxKind.SubractionOperator; - return this.transformCompoundAssignmentExpression(expression.operand, ts.createLiteral(1), replacementOperator, true); + switch (expression.operator) { + case ts.SyntaxKind.PlusPlusToken: + return this.transformCompoundAssignmentExpression( + expression, + expression.operand, + ts.createLiteral(1), + tstl.SyntaxKind.AdditionOperator, + true + ); + + case ts.SyntaxKind.MinusMinusToken: + return this.transformCompoundAssignmentExpression( + expression, + expression.operand, + ts.createLiteral(1), + tstl.SyntaxKind.SubractionOperator, + true + ); + + default: + throw TSTLErrors.UnsupportedKind("unary postfix operator", expression.operator, expression); + } } public transformPrefixUnaryExpression(expression: ts.PrefixUnaryExpression): tstl.Expression { switch (expression.operator) { case ts.SyntaxKind.PlusPlusToken: return this.transformCompoundAssignmentExpression( - expression.operand, ts.createLiteral(1), tstl.SyntaxKind.AdditionOperator, false); + expression, + expression.operand, + ts.createLiteral(1), + tstl.SyntaxKind.AdditionOperator, + false + ); case ts.SyntaxKind.MinusMinusToken: return this.transformCompoundAssignmentExpression( - expression.operand, ts.createLiteral(1), tstl.SyntaxKind.SubractionOperator, false); + expression, + expression.operand, + ts.createLiteral(1), + tstl.SyntaxKind.SubractionOperator, + false + ); case ts.SyntaxKind.PlusToken: return this.transformExpression(expression.operand); @@ -1624,7 +1791,14 @@ export class LuaTransformer { return tstl.createUnaryExpression(this.transformExpression(expression.operand), tstl.SyntaxKind.NotOperator); case ts.SyntaxKind.TildeToken: - return tstl.createUnaryExpression(this.transformExpression(expression.operand), tstl.SyntaxKind.BitwiseNotOperator); + return this.transformUnaryBitOperation( + expression, + this.transformExpression(expression.operand), + tstl.SyntaxKind.BitwiseNotOperator + ); + + default: + throw TSTLErrors.UnsupportedKind("unary prefix operator", expression.operator, expression); } } @@ -2338,7 +2512,19 @@ export class LuaTransformer { } public createUnpackCall(expression: tstl.Expression): tstl.Expression { - return tstl.createCallExpression(tstl.createIdentifier("unpack"), [expression]); + switch (this.options.luaTarget) { + case LuaTarget.Lua51: + case LuaTarget.LuaJIT: + return tstl.createCallExpression(tstl.createIdentifier("unpack"), [expression]); + + case LuaTarget.Lua52: + case LuaTarget.Lua53: + default: + return tstl.createCallExpression( + tstl.createTableIndexExpression(tstl.createIdentifier("table"), tstl.createStringLiteral("unpack")), + [expression] + ); + } } private getAbsoluteImportPath(relativePath: string): string { diff --git a/src/TSHelper.ts b/src/TSHelper.ts index 6c1922f70..5c8b6fa3b 100644 --- a/src/TSHelper.ts +++ b/src/TSHelper.ts @@ -293,30 +293,12 @@ export class TSHelper { case ts.SyntaxKind.GreaterThanGreaterThanEqualsToken: return [true, tstl.SyntaxKind.BitwiseRightShiftOperator]; case ts.SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken: - // return [true, ts.SyntaxKind.GreaterThanGreaterThanGreaterThanToken]; - throw new Error("Not yet implemented in lua AST"); // TODO + return [true, tstl.SyntaxKind.BitwiseArithmeticRightShift]; } return [false, undefined]; } - public static isCompoundPrefixUnaryOperator(node: ts.PrefixUnaryExpression): boolean { - return node.operator !== ts.SyntaxKind.ExclamationToken && node.operator !== ts.SyntaxKind.MinusToken && - node.operator !== ts.SyntaxKind.PlusToken && node.operator !== ts.SyntaxKind.TildeToken; - } - - public static getUnaryCompoundAssignmentOperator(node: ts.PrefixUnaryExpression | ts.PostfixUnaryExpression): ts.BinaryOperator { - switch (node.operator) { - case ts.SyntaxKind.PlusPlusToken: - return ts.SyntaxKind.PlusToken; - case ts.SyntaxKind.MinusMinusToken: - return ts.SyntaxKind.MinusToken; - default: - throw TSTLErrors.UnsupportedKind( - `unary ${ts.isPrefixUnaryExpression(node) ? "prefix" : "postfix"} operator`, node.operator, node); - } - } - public static isExpressionStatement(node: ts.Expression): boolean { return node.parent === undefined || ts.isExpressionStatement(node.parent) || ts.isForStatement(node.parent); } diff --git a/src/targets/LuaTransformer.52.ts b/src/targets/LuaTransformer.52.ts index 8a6a1560d..5be693c57 100644 --- a/src/targets/LuaTransformer.52.ts +++ b/src/targets/LuaTransformer.52.ts @@ -1,7 +1,7 @@ import * as ts from "typescript"; import * as tstl from "../LuaAST"; -import {LuaTransformer, StatementVisitResult, ScopeType} from "../LuaTransformer"; +import {ExpressionVisitResult, StatementVisitResult, ScopeType} from "../LuaTransformer"; import {TSHelper as tsHelper} from "../TSHelper"; import { LuaTransformer51 } from "./LuaTransformer.51"; @@ -31,13 +31,6 @@ export class LuaTransformer52 extends LuaTransformer51 ); } - /** @override */ - public createUnpackCall(expression: tstl.Expression): tstl.Expression { - return tstl.createCallExpression( - tstl.createTableIndexExpression(tstl.createIdentifier("table"), tstl.createStringLiteral("unpack")), - [expression]); - } - // /** @override */ // public transpileBreak(node: ts.BreakStatement): string { // const topScope = this.peekSpecialScope(); @@ -49,36 +42,6 @@ export class LuaTransformer52 extends LuaTransformer51 // } // } - // /** @override */ - // public transpileUnaryBitOperation(node: ts.PrefixUnaryExpression, operand: string): string { - // switch (node.operator) { - // case ts.SyntaxKind.TildeToken: - // return `bit32.bnot(${operand})`; - // default: - // throw TSTLErrors.UnsupportedKind("bitwise operator", node.operator, node); - // } - // } - - // /** @override */ - // public transpileBitOperation(node: ts.BinaryExpression, lhs: string, rhs: string): string { - // switch (node.operatorToken.kind) { - // case ts.SyntaxKind.AmpersandToken: - // return `bit32.band(${lhs},${rhs})`; - // case ts.SyntaxKind.BarToken: - // return `bit32.bor(${lhs},${rhs})`; - // case ts.SyntaxKind.CaretToken: - // return `bit32.bxor(${lhs},${rhs})`; - // case ts.SyntaxKind.LessThanLessThanToken: - // return `bit32.lshift(${lhs},${rhs})`; - // case ts.SyntaxKind.GreaterThanGreaterThanToken: - // return `bit32.rshift(${lhs},${rhs})`; - // case ts.SyntaxKind.GreaterThanGreaterThanGreaterThanToken: - // return `bit32.arshift(${lhs},${rhs})`; - // default: - // throw TSTLErrors.UnsupportedKind("bitwise operator", node.operatorToken.kind, node); - // } - // } - // /** @override */ // public transpileSwitch(node: ts.SwitchStatement): string { // const expression = this.transpileExpression(node.expression, true); diff --git a/src/targets/LuaTransformer.53.ts b/src/targets/LuaTransformer.53.ts index 1d88c639e..0564a8692 100644 --- a/src/targets/LuaTransformer.53.ts +++ b/src/targets/LuaTransformer.53.ts @@ -1,40 +1,13 @@ import * as ts from "typescript"; -import {LuaTransformer} from "../LuaTransformer"; +import * as tstl from "../LuaAST"; +import {ExpressionVisitResult} from "../LuaTransformer"; import {TSHelper as tsHelper} from "../TSHelper"; import { LuaTransformer52 } from "./LuaTransformer.52"; +import {TSTLErrors} from "../TSTLErrors"; export class LuaTransformer53 extends LuaTransformer52 { // TODO - // /** @override */ - // public transpileUnaryBitOperation(node: ts.PrefixUnaryExpression, operand: string): string { - // switch (node.operator) { - // case ts.SyntaxKind.TildeToken: - // return `~${operand}`; - // default: - // throw TSTLErrors.UnsupportedKind("bitwise operator", node.operator, node); - // } - // } - - // /** @override */ - // public transpileBitOperation(node: ts.BinaryExpression, lhs: string, rhs: string): string { - // switch (node.operatorToken.kind) { - // case ts.SyntaxKind.AmpersandToken: - // return `${lhs} & ${rhs}`; - // case ts.SyntaxKind.BarToken: - // return `${lhs} | ${rhs}`; - // case ts.SyntaxKind.CaretToken: - // return `${lhs} ~ ${rhs}`; - // case ts.SyntaxKind.LessThanLessThanToken: - // return `${lhs} << ${rhs}`; - // case ts.SyntaxKind.GreaterThanGreaterThanToken: - // return `${lhs} >> ${rhs}`; - // case ts.SyntaxKind.GreaterThanGreaterThanGreaterThanToken: - // throw TSTLErrors.UnsupportedForTarget("Bitwise >>> operator", this.options.luaTarget, node); - // default: - // throw TSTLErrors.UnsupportedKind("bitwise operator", node.operatorToken.kind, node); - // } - // } // /** @override */ // public getValidStringProperties(): { [js: string]: string } { diff --git a/src/targets/LuaTransformer.JIT.ts b/src/targets/LuaTransformer.JIT.ts index 44f0fbc11..c0ec9191e 100644 --- a/src/targets/LuaTransformer.JIT.ts +++ b/src/targets/LuaTransformer.JIT.ts @@ -6,11 +6,6 @@ import {TSHelper as tsHelper} from "../TSHelper"; import { LuaTransformer52 } from "./LuaTransformer.52"; export class LuaTransformerJIT extends LuaTransformer52 { - /** @override */ - public createUnpackCall(expression: tstl.Expression): tstl.Expression { - return tstl.createCallExpression(tstl.createIdentifier("unpack"), [expression]); - } - // TODO // /** @override */ // public transpileUnaryBitOperation(node: ts.PrefixUnaryExpression, operand: string): string { @@ -22,26 +17,6 @@ export class LuaTransformerJIT extends LuaTransformer52 { // } // } - // /** @override */ - // public transpileBitOperation(node: ts.BinaryExpression, lhs: string, rhs: string): string { - // switch (node.operatorToken.kind) { - // case ts.SyntaxKind.AmpersandToken: - // return `bit.band(${lhs},${rhs})`; - // case ts.SyntaxKind.BarToken: - // return `bit.bor(${lhs},${rhs})`; - // case ts.SyntaxKind.CaretToken: - // return `bit.bxor(${lhs},${rhs})`; - // case ts.SyntaxKind.LessThanLessThanToken: - // return `bit.lshift(${lhs},${rhs})`; - // case ts.SyntaxKind.GreaterThanGreaterThanToken: - // return `bit.rshift(${lhs},${rhs})`; - // case ts.SyntaxKind.GreaterThanGreaterThanGreaterThanToken: - // return `bit.arshift(${lhs},${rhs})`; - // default: - // throw TSTLErrors.UnsupportedKind("bitwise operator", node.operatorToken.kind, node); - // } - // } - // /** @override */ // public transpileDestructingAssignmentValue(node: ts.Expression): string { // return `unpack(${this.transpileExpression(node)})`; diff --git a/test/unit/expressions.spec.ts b/test/unit/expressions.spec.ts index ac603bf67..045591858 100644 --- a/test/unit/expressions.spec.ts +++ b/test/unit/expressions.spec.ts @@ -113,36 +113,36 @@ export class ExpressionTests { } @TestCase("~a", "bit.bnot(a);") - @TestCase("a&b", "bit.band(a,b);") - @TestCase("a&=b", "a = (bit.band(a,b));") - @TestCase("a|b", "bit.bor(a,b);") - @TestCase("a|=b", "a = (bit.bor(a,b));") - @TestCase("a^b", "bit.bxor(a,b);") - @TestCase("a^=b", "a = (bit.bxor(a,b));") - @TestCase("a<>b", "bit.rshift(a,b);") - @TestCase("a>>=b", "a = (bit.rshift(a,b));") - @TestCase("a>>>b", "bit.arshift(a,b);") - @TestCase("a>>>=b", "a = (bit.arshift(a,b));") + @TestCase("a&b", "bit.band(a, b);") + @TestCase("a&=b", "a = bit.band(a, b);") + @TestCase("a|b", "bit.bor(a, b);") + @TestCase("a|=b", "a = bit.bor(a, b);") + @TestCase("a^b", "bit.bxor(a, b);") + @TestCase("a^=b", "a = bit.bxor(a, b);") + @TestCase("a<>b", "bit.rshift(a, b);") + @TestCase("a>>=b", "a = bit.rshift(a, b);") + @TestCase("a>>>b", "bit.arshift(a, b);") + @TestCase("a>>>=b", "a = bit.arshift(a, b);") @Test("Bitop [JIT]") public bitOperatorOverrideJIT(input: string, lua: string): void { Expect(util.transpileString(input, { luaTarget: LuaTarget.LuaJIT, luaLibImport: "none" })).toBe(lua); } @TestCase("~a", "bit32.bnot(a);") - @TestCase("a&b", "bit32.band(a,b);") - @TestCase("a&=b", "a = (bit32.band(a,b));") - @TestCase("a|b", "bit32.bor(a,b);") - @TestCase("a|=b", "a = (bit32.bor(a,b));") - @TestCase("a^b", "bit32.bxor(a,b);") - @TestCase("a^=b", "a = (bit32.bxor(a,b));") - @TestCase("a<>b", "bit32.rshift(a,b);") - @TestCase("a>>=b", "a = (bit32.rshift(a,b));") - @TestCase("a>>>b", "bit32.arshift(a,b);") - @TestCase("a>>>=b", "a = (bit32.arshift(a,b));") + @TestCase("a&b", "bit32.band(a, b);") + @TestCase("a&=b", "a = bit32.band(a, b);") + @TestCase("a|b", "bit32.bor(a, b);") + @TestCase("a|=b", "a = bit32.bor(a, b);") + @TestCase("a^b", "bit32.bxor(a, b);") + @TestCase("a^=b", "a = bit32.bxor(a, b);") + @TestCase("a<>b", "bit32.rshift(a, b);") + @TestCase("a>>=b", "a = bit32.rshift(a, b);") + @TestCase("a>>>b", "bit32.arshift(a, b);") + @TestCase("a>>>=b", "a = bit32.arshift(a, b);") @Test("Bitop [5.2]") public bitOperatorOverride52(input: string, lua: string): void { Expect(util.transpileString(input, { luaTarget: LuaTarget.Lua52, luaLibImport: "none" })).toBe(lua); @@ -150,15 +150,15 @@ export class ExpressionTests { @TestCase("~a", "~a;") @TestCase("a&b", "a & b;") - @TestCase("a&=b", "a = (a & b);") + @TestCase("a&=b", "a = a & b;") @TestCase("a|b", "a | b;") - @TestCase("a|=b", "a = (a | b);") + @TestCase("a|=b", "a = a | b;") @TestCase("a^b", "a ~ b;") - @TestCase("a^=b", "a = (a ~ b);") + @TestCase("a^=b", "a = a ~ b;") @TestCase("a<>b", "a >> b;") - @TestCase("a>>=b", "a = (a >> b);") + @TestCase("a>>=b", "a = a >> b;") @Test("Bitop [5.3]") public bitOperatorOverride53(input: string, lua: string): void { Expect(util.transpileString(input, { luaTarget: LuaTarget.Lua53, luaLibImport: "none" })).toBe(lua);