Skip to content

Commit 3a92068

Browse files
committed
Minor refactor of transpileCallExpression and fixed issue with tupleReturn in expressions
1 parent 1e51d6f commit 3a92068

File tree

8 files changed

+124
-62
lines changed

8 files changed

+124
-62
lines changed

src/TSHelper.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ export class TSHelper {
3939
return false;
4040
}
4141

42+
public static isInDestructingAssignment(node: ts.Node) {
43+
return node.parent && ts.isVariableDeclaration(node.parent) && ts.isArrayBindingPattern(node.parent.name);
44+
}
45+
4246
public static isStringType(type: ts.Type): boolean {
4347
return (type.flags & ts.TypeFlags.String) !== 0
4448
|| (type.flags & ts.TypeFlags.StringLike) !== 0

src/Transpiler.ts

Lines changed: 60 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -735,19 +735,11 @@ export abstract class LuaTranspiler {
735735
result = `${lhs}<=${rhs}`;
736736
break;
737737
case ts.SyntaxKind.EqualsToken:
738-
let assignmentValue = rhs;
739-
// If the right-hand side of the equation is a tuple return method
740-
// and the left-hand side is not a destructing statement, wrap the
741-
// rhs in { }.
742-
if (ts.isIdentifier(node.left) && tsHelper.isTupleReturnCall(node.right, this.checker)) {
743-
assignmentValue = `{ ${rhs} }`;
744-
}
745-
746738
if (tsHelper.hasSetAccessor(node.left, this.checker)) {
747-
return this.transpileSetAccessor(node.left as ts.PropertyAccessExpression, assignmentValue);
739+
return this.transpileSetAccessor(node.left as ts.PropertyAccessExpression, rhs);
748740
}
749741

750-
result = `${lhs}=${assignmentValue}`;
742+
result = `${lhs}=${rhs}`;
751743
break;
752744
case ts.SyntaxKind.EqualsEqualsToken:
753745
case ts.SyntaxKind.EqualsEqualsEqualsToken:
@@ -844,46 +836,13 @@ export abstract class LuaTranspiler {
844836
// Check for calls on primitives to override
845837
let params;
846838
let callPath;
847-
if (ts.isPropertyAccessExpression(node.expression)) {
848-
// If the function being called is of type owner.func, get the type of owner
849-
const ownerType = this.checker.getTypeAtLocation(node.expression.expression);
850-
851-
if (ownerType.symbol && ownerType.symbol.escapedName === "Math") {
852-
params = this.transpileArguments(node.arguments);
853-
return this.transpileMathExpression(node.expression.name) + `(${params})`;
854-
}
855839

856-
if (this.transpileExpression((node.expression as ts.PropertyAccessExpression).expression) === "String") {
857-
params = this.transpileArguments(node.arguments);
858-
return this.transpileStringExpression(node.expression.name) + `(${params})`;
859-
}
860-
861-
switch (ownerType.flags) {
862-
case ts.TypeFlags.String:
863-
case ts.TypeFlags.StringLiteral:
864-
return this.transpileStringCallExpression(node);
865-
866-
}
867-
if (tsHelper.isArrayType(ownerType, this.checker)) {
868-
return this.transpileArrayCallExpression(node);
869-
}
840+
const isTupleReturn = tsHelper.isTupleReturnCall(node, this.checker);
841+
const isInDestructingAssignment = tsHelper.isInDestructingAssignment(node);
870842

871-
// Get the type of the function
872-
const functionType = this.checker.getTypeAtLocation(node.expression);
873-
// Don't replace . with : for namespaces
874-
if ((ownerType.symbol && (ownerType.symbol.flags & ts.SymbolFlags.Namespace))
875-
// If function is defined as property with lambda type use . instead of :
876-
|| (functionType.symbol && (functionType.symbol.flags & ts.SymbolFlags.TypeLiteral))) {
877-
callPath = this.transpileExpression(node.expression);
878-
params = this.transpileArguments(node.arguments);
879-
return `${callPath}(${params})`;
880-
} else {
881-
// Replace last . with : here
882-
callPath =
883-
`${this.transpileExpression(node.expression.expression)}:${node.expression.name.escapedText}`;
884-
params = this.transpileArguments(node.arguments);
885-
return `${callPath}(${params})`;
886-
}
843+
if (ts.isPropertyAccessExpression(node.expression)) {
844+
const result = this.transpilePropertyCall(node);
845+
return isTupleReturn && !isInDestructingAssignment ? `({ ${result} })` : result;
887846
}
888847

889848
// Handle super calls properly
@@ -896,7 +855,58 @@ export abstract class LuaTranspiler {
896855

897856
callPath = this.transpileExpression(node.expression);
898857
params = this.transpileArguments(node.arguments);
899-
return `${callPath}(${params})`;
858+
return isTupleReturn && !isInDestructingAssignment ? `({ ${callPath}(${params}) })` : `${callPath}(${params})`;
859+
}
860+
861+
public transpilePropertyCall(node: ts.CallExpression) {
862+
let params;
863+
let callPath;
864+
865+
// Check if call is actually on a property access expression
866+
if (!ts.isPropertyAccessExpression(node.expression)) {
867+
throw new TranspileError("Tried to transpile a non-property call as property call.", node);
868+
}
869+
870+
// If the function being called is of type owner.func, get the type of owner
871+
const ownerType = this.checker.getTypeAtLocation(node.expression.expression);
872+
873+
if (ownerType.symbol && ownerType.symbol.escapedName === "Math") {
874+
params = this.transpileArguments(node.arguments);
875+
return this.transpileMathExpression(node.expression.name) + `(${params})`;
876+
}
877+
878+
if (this.transpileExpression((node.expression as ts.PropertyAccessExpression).expression) === "String") {
879+
params = this.transpileArguments(node.arguments);
880+
return this.transpileStringExpression(node.expression.name) + `(${params})`;
881+
}
882+
883+
switch (ownerType.flags) {
884+
case ts.TypeFlags.String:
885+
case ts.TypeFlags.StringLiteral:
886+
return this.transpileStringCallExpression(node);
887+
888+
}
889+
890+
if (tsHelper.isArrayType(ownerType, this.checker)) {
891+
return this.transpileArrayCallExpression(node);
892+
}
893+
894+
// Get the type of the function
895+
const functionType = this.checker.getTypeAtLocation(node.expression);
896+
// Don't replace . with : for namespaces
897+
if ((ownerType.symbol && (ownerType.symbol.flags & ts.SymbolFlags.Namespace))
898+
// If function is defined as property with lambda type use . instead of :
899+
|| (functionType.symbol && (functionType.symbol.flags & ts.SymbolFlags.TypeLiteral))) {
900+
callPath = this.transpileExpression(node.expression);
901+
params = this.transpileArguments(node.arguments);
902+
return `${callPath}(${params})`;
903+
} else {
904+
// Replace last . with : here
905+
callPath =
906+
`${this.transpileExpression(node.expression.expression)}:${node.expression.name.escapedText}`;
907+
params = this.transpileArguments(node.arguments);
908+
return `${callPath}(${params})`;
909+
}
900910
}
901911

902912
public transpileStringCallExpression(node: ts.CallExpression): string {
@@ -1129,15 +1139,7 @@ export abstract class LuaTranspiler {
11291139
const identifier = node.name;
11301140
if (node.initializer) {
11311141
const value = this.transpileExpression(node.initializer);
1132-
1133-
// If the right-hand side of the equation is a tuple return method
1134-
// and the left-hand side is not a destructing statement, wrap the
1135-
// rhs in { }.
1136-
if (tsHelper.isTupleReturnCall(node.initializer, this.checker)) {
1137-
return `local ${identifier.escapedText} = { ${value} }\n`;
1138-
} else {
1139-
return `local ${identifier.escapedText} = ${value}\n`;
1140-
}
1142+
return `local ${identifier.escapedText} = ${value}\n`;
11411143
} else {
11421144
return `local ${identifier.escapedText} = nil\n`;
11431145
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
local a = ({ myFunc() })[0+1]
Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
11
/** !TupleReturn */
22
declare function myFunc(): [number, string];
3-
4-
let [a, b] = myFunc();
3+
let [a, b] = myFunc();
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
/** !TupleReturn */
22
function myFunc(): [number, string] {
33
return [3, "4"];
4-
}
4+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
/** !TupleReturn */
2+
declare function myFunc(): [number, string];
3+
let a = myFunc()[0];

test/unit/assignments.spec.ts

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ export class AssignmentTests {
9292
+ `a = abc();`;
9393

9494
const lua = util.transpileString(code);
95-
Expect(lua).toBe("local a = { abc() }\n\na={ abc() }");
95+
Expect(lua).toBe("local a = ({ abc() })\n\na=({ abc() })");
9696
}
9797

9898
@Test("TupleReturn interface assignment")
@@ -130,4 +130,45 @@ export class AssignmentTests {
130130
const lua = util.transpileString(code);
131131
Expect(lua).toBe("local jkl = def.new(true)\n\nlocal a,b=jkl:abc()");
132132
}
133+
134+
@Test("TupleReturn functional")
135+
public tupleReturnFunctional() {
136+
const code = `/** !TupleReturn */
137+
function abc(): [number, string] { return [3, "a"]; }
138+
const [a, b] = abc();
139+
return b + a;`;
140+
141+
const lua = util.transpileString(code);
142+
143+
const result = util.executeLua(lua);
144+
145+
Expect(result).toBe("a3");
146+
}
147+
148+
@Test("TupleReturn single")
149+
public tupleReturnSingle() {
150+
const code = `/** !TupleReturn */
151+
function abc(): [number, string] { return [3, "a"]; }
152+
const res = abc();
153+
return res.length`;
154+
155+
const lua = util.transpileString(code);
156+
157+
const result = util.executeLua(lua);
158+
159+
Expect(result).toBe(2);
160+
}
161+
162+
@Test("TupleReturn in expression")
163+
public tupleReturnInExpression() {
164+
const code = `/** !TupleReturn */
165+
function abc(): [number, string] { return [3, "a"]; }
166+
return abc()[1] + abc()[0];`;
167+
168+
const lua = util.transpileString(code);
169+
170+
const result = util.executeLua(lua);
171+
172+
Expect(result).toBe("a3");
173+
}
133174
}

test/unit/expressions.spec.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -500,4 +500,16 @@ export class ExpressionTests {
500500
Expect(() => transpiler.transpileObjectLiteral(mockObject as ts.ObjectLiteralExpression))
501501
.toThrowError(Error, "Encountered unsupported object literal element: FalseKeyword.");
502502
}
503+
504+
@Test("Invalid property access call transpilation")
505+
public invalidPropertyCall() {
506+
const transpiler = util.makeTestTranspiler();
507+
508+
const mockObject: any = {
509+
expression: ts.createLiteral("abc"),
510+
};
511+
512+
Expect(() => transpiler.transpilePropertyCall(mockObject as ts.CallExpression))
513+
.toThrowError(Error, "Tried to transpile a non-property call as property call.");
514+
}
503515
}

0 commit comments

Comments
 (0)