Skip to content

Commit 6b7451d

Browse files
authored
Merge branch 'master' into patch-branch
2 parents 45df3a5 + 114dfd8 commit 6b7451d

12 files changed

+128
-14
lines changed

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "typescript-to-lua",
33
"license": "MIT",
4-
"version": "0.0.15",
4+
"version": "0.0.16",
55
"repository": "https://github.com/Perryvw/TypescriptToLua",
66
"scripts": {
77
"build": "tsc -p tsconfig.json",

src/TSHelper.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,12 @@ export class TSHelper {
8383
&& this.hasCustomDecorator(type, checker, "!Phantom");
8484
}
8585

86+
static isTupleReturnFunction(type: ts.Type, checker: ts.TypeChecker): boolean {
87+
return type.symbol
88+
&& ((type.symbol.flags & ts.SymbolFlags.Function) != 0)
89+
&& this.hasCustomDecorator(type, checker, "!TupleReturn");
90+
}
91+
8692
static hasCustomDecorator(type: ts.Type, checker: ts.TypeChecker, decorator: string): boolean {
8793
if (type.symbol) {
8894
var comment = type.symbol.getDocumentationComment(checker);
@@ -106,4 +112,17 @@ export class TSHelper {
106112
}
107113
}
108114
}
115+
116+
// Search up until finding a node satisfying the callback
117+
static findFirstNodeAbove<T extends ts.Node>(node: ts.Node, callback: (n: ts.Node) => n is T): T {
118+
let current = node;
119+
while (current.parent) {
120+
if (callback(current.parent)) {
121+
return current.parent;
122+
} else {
123+
current = current.parent;
124+
}
125+
}
126+
return null;
127+
}
109128
}

src/Transpiler.ts

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,14 @@ export class LuaTranspiler {
474474

475475
transpileReturn(node: ts.ReturnStatement): string {
476476
if (node.expression) {
477+
// If parent function is a TupleReturn function and return expression is an array literal, leave out brackets.
478+
var declaration = tsEx.findFirstNodeAbove(node, ts.isFunctionDeclaration);
479+
if (declaration && tsEx.isTupleReturnFunction(this.checker.getTypeAtLocation(declaration), this.checker)
480+
&& ts.isArrayLiteralExpression(node.expression)) {
481+
return "return " + node.expression.elements.map(elem => this.transpileExpression(elem)).join(",");
482+
}
483+
484+
// Otherwise just do a normal return
477485
return "return " + this.transpileExpression(node.expression);
478486
} else {
479487
return "return"
@@ -1057,17 +1065,21 @@ export class LuaTranspiler {
10571065
} else if (ts.isArrayBindingPattern(node.name)) {
10581066
// Destructuring type
10591067
const value = this.transpileExpression(node.initializer);
1060-
let parentName = `__destr${this.genVarCounter}`;
1061-
this.genVarCounter++;
1062-
let result = `local ${parentName} = ${value}\n`;
1063-
node.name.elements.forEach((elem: ts.BindingElement, index: number) => {
1064-
if (!elem.dotDotDotToken) {
1065-
result += this.indent + `local ${(<ts.Identifier>elem.name).escapedText} = ${parentName}[${index + 1}]\n`;
1066-
} else {
1067-
result += this.indent + `local ${(<ts.Identifier>elem.name).escapedText} = TS_slice(${parentName}, ${index})\n`;
1068-
}
1069-
});
1070-
return result;
1068+
1069+
// Disallow ellipsis destruction
1070+
if (node.name.elements.some(elem => !ts.isBindingElement(elem) || elem.dotDotDotToken !== undefined)) {
1071+
throw new TranspileError(`Ellipsis destruction is not allowed.`, node);
1072+
}
1073+
1074+
const vars = node.name.elements.map(element => (<ts.Identifier>(<ts.BindingElement>element).name).escapedText).join(",");
1075+
1076+
// Don't unpack TupleReturn decorated functions
1077+
if (ts.isCallExpression(node.initializer)
1078+
&& tsEx.isTupleReturnFunction(this.checker.getTypeAtLocation(node.initializer.expression), this.checker)) {
1079+
return `local ${vars}=${value}`;
1080+
} else {
1081+
return `local ${vars}=unpack(${value})`;
1082+
}
10711083
} else {
10721084
throw new TranspileError("Unsupported variable declaration type " + tsEx.enumName(node.name.kind, ts.SyntaxKind), node);
10731085
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { Expect, Test, TestCase } from "alsatian";
2+
3+
import * as util from "../src/util";
4+
const fs = require("fs");
5+
6+
export class AssignmentTests {
7+
8+
// Expect the passed lua string to be equal to the file's contents.
9+
private ExpectEqualToFile(lua: string, path: string) {
10+
const expected = fs.readFileSync(path).toString();
11+
Expect(lua).toBe(expected.trim().split("\r\n").join("\n"));
12+
}
13+
14+
@TestCase(`"abc"`, `"abc"`)
15+
@TestCase("3", "3")
16+
@TestCase("[1,2,3]", "{1,2,3}")
17+
@TestCase("true", "true")
18+
@TestCase("false", "false")
19+
@TestCase(`{a:3,b:"4"}`, `{a=3,b="4"}`)
20+
@Test("Const assignment")
21+
public constAssignment(inp: string, out: string) {
22+
var lua = util.transpileString(`const myvar = ${inp};`)
23+
Expect(lua).toBe(`local myvar = ${out}`);
24+
}
25+
26+
@TestCase(`"abc"`, `"abc"`)
27+
@TestCase("3", "3")
28+
@TestCase("[1,2,3]", "{1,2,3}")
29+
@TestCase("true", "true")
30+
@TestCase("false", "false")
31+
@TestCase(`{a:3,b:"4"}`, `{a=3,b="4"}`)
32+
@Test("Const assignment")
33+
public letAssignment(inp: string, out: string) {
34+
var lua = util.transpileString(`let myvar = ${inp};`)
35+
Expect(lua).toBe(`local myvar = ${out}`);
36+
}
37+
38+
@TestCase(`"abc"`, `"abc"`)
39+
@TestCase("3", "3")
40+
@TestCase("[1,2,3]", "{1,2,3}")
41+
@TestCase("true", "true")
42+
@TestCase("false", "false")
43+
@TestCase(`{a:3,b:"4"}`, `{a=3,b="4"}`)
44+
@Test("Const assignment")
45+
public varAssignment(inp: string, out: string) {
46+
var lua = util.transpileString(`var myvar = ${inp};`)
47+
Expect(lua).toBe(`local myvar = ${out}`);
48+
}
49+
50+
@Test("Destructing assignment")
51+
public destructingAssignment() {
52+
const lua = util.transpileFile("test/integration/testfiles/assignmentDestructing.ts");
53+
this.ExpectEqualToFile(lua, "test/integration/testfiles/assignmentDestructing.lua");
54+
}
55+
}

test/integration/decorators.spec.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Expect, Test, TestCase } from "alsatian";
1+
import { Expect, Test, TestCase, IgnoreTest } from "alsatian";
22

33
import * as util from "../src/util";
44
const fs = require("fs");
@@ -46,4 +46,16 @@ export class DecoratorTests {
4646
const lua = util.transpileFile("test/integration/testfiles/namespacePhantom.ts");
4747
this.ExpectEqualToFile(lua, "test/integration/testfiles/namespacePhantom.lua");
4848
}
49+
50+
@Test("TupleReturnDefinition")
51+
public tupleReturnDefinition() {
52+
const lua = util.transpileFile("test/integration/testfiles/tupleReturnDefinition.ts");
53+
this.ExpectEqualToFile(lua, "test/integration/testfiles/tupleReturnDefinition.lua");
54+
}
55+
56+
@Test("TupleReturnAssignment")
57+
public tupleReturnAssignment() {
58+
const lua = util.transpileFile("test/integration/testfiles/tupleReturnAssignment.ts");
59+
this.ExpectEqualToFile(lua, "test/integration/testfiles/tupleReturnAssignment.lua");
60+
}
4961
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
local a,b=unpack(myFunc())
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
declare function myFunc(): [number, string];
2+
3+
let [a, b] = myFunc();
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
local a,b=myFunc()
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
/** !TupleReturn */
2+
declare function myFunc(): [number, string];
3+
4+
let [a, b] = myFunc();

0 commit comments

Comments
 (0)