Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/LuaAST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -677,7 +677,7 @@ export function createTableFieldExpression(
tsOriginal?: ts.Node,
parent?: Node
): TableFieldExpression {
const expression = createNode(SyntaxKind.TableExpression, tsOriginal, parent) as TableFieldExpression;
const expression = createNode(SyntaxKind.TableFieldExpression, tsOriginal, parent) as TableFieldExpression;
setParent(value, expression);
expression.value = value;
setParent(key, expression);
Expand Down
63 changes: 38 additions & 25 deletions src/LuaPrinter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -571,17 +571,8 @@ export class LuaPrinter {

chunks.push("{");

if (expression.fields && expression.fields.length > 0) {
if (expression.fields.length === 1) {
// Inline tables with only one entry
chunks.push(this.printTableFieldExpression(expression.fields[0]));
} else {
chunks.push("\n");
this.pushIndent();
expression.fields.forEach(f => chunks.push(this.indent(), this.printTableFieldExpression(f), ",\n"));
this.popIndent();
chunks.push(this.indent());
}
if (expression.fields) {
chunks.push(...this.printExpressionList(expression.fields));
}

chunks.push("}");
Expand Down Expand Up @@ -632,30 +623,33 @@ export class LuaPrinter {
public printCallExpression(expression: tstl.CallExpression): SourceNode {
const chunks = [];

const parameterChunks =
expression.params !== undefined ? expression.params.map(e => this.printExpression(e)) : [];
chunks.push(this.printExpression(expression.expression), "(");

chunks.push(this.printExpression(expression.expression), "(", ...this.joinChunks(", ", parameterChunks), ")");
if (expression.params) {
chunks.push(...this.printExpressionList(expression.params));
}

chunks.push(")");

return this.createSourceNode(expression, chunks);
}

public printMethodCallExpression(expression: tstl.MethodCallExpression): SourceNode {
const prefix = this.printExpression(expression.prefixExpression);
const chunks = [];

const parameterChunks =
expression.params !== undefined ? expression.params.map(e => this.printExpression(e)) : [];
const prefix = this.printExpression(expression.prefixExpression);

const name = this.printIdentifier(expression.name);

return this.createSourceNode(expression, [
prefix,
":",
name,
"(",
...this.joinChunks(", ", parameterChunks),
")",
]);
chunks.push(prefix, ":", name, "(");

if (expression.params) {
chunks.push(...this.printExpressionList(expression.params));
}

chunks.push(")");

return this.createSourceNode(expression, chunks);
}

public printIdentifier(expression: tstl.Identifier): SourceNode {
Expand Down Expand Up @@ -715,6 +709,25 @@ export class LuaPrinter {
return result;
}

protected printExpressionList(expressions: tstl.Expression[]): SourceChunk[] {
const chunks: SourceChunk[] = [];

if (expressions.every(e => tsHelper.isSimpleExpression(e))) {
chunks.push(...this.joinChunks(", ", expressions.map(e => this.printExpression(e))));
} else {
chunks.push("\n");
this.pushIndent();
expressions.forEach((p, i) => {
const tail = i < expressions.length - 1 ? ",\n" : "\n";
chunks.push(this.indent(), this.printExpression(p), tail);
});
this.popIndent();
chunks.push(this.indent());
}

return chunks;
}

// The key difference between this and SourceNode.toStringWithSourceMap() is that SourceNodes with null line/column
// will not generate 'empty' mappings in the source map that point to nothing in the original TS.
private buildSourceMap(sourceFile: string, sourceRoot: string, rootSourceNode: SourceNode): SourceMapGenerator {
Expand Down
37 changes: 37 additions & 0 deletions src/TSHelper.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as ts from "typescript";
import { Decorator, DecoratorKind } from "./Decorator";
import * as tstl from "./LuaAST";

export enum ContextType {
None,
Expand Down Expand Up @@ -835,3 +836,39 @@ export function isArrayLengthAssignment(

return name === "length";
}

// Returns true if expression contains no function calls
export function isSimpleExpression(expression: tstl.Expression): boolean {
switch (expression.kind) {
case tstl.SyntaxKind.CallExpression:
case tstl.SyntaxKind.MethodCallExpression:
case tstl.SyntaxKind.FunctionExpression:
return false;

case tstl.SyntaxKind.TableExpression:
const tableExpression = expression as tstl.TableExpression;
return !tableExpression.fields || tableExpression.fields.every(e => isSimpleExpression(e));

case tstl.SyntaxKind.TableFieldExpression:
const fieldExpression = expression as tstl.TableFieldExpression;
return (
(!fieldExpression.key || isSimpleExpression(fieldExpression.key)) &&
isSimpleExpression(fieldExpression.value)
);

case tstl.SyntaxKind.TableIndexExpression:
const indexExpression = expression as tstl.TableIndexExpression;
return isSimpleExpression(indexExpression.table) && isSimpleExpression(indexExpression.index);

case tstl.SyntaxKind.UnaryExpression:
return isSimpleExpression((expression as tstl.UnaryExpression).operand);

case tstl.SyntaxKind.BinaryExpression:
const binaryExpression = expression as tstl.BinaryExpression;
return isSimpleExpression(binaryExpression.left) && isSimpleExpression(binaryExpression.right);

case tstl.SyntaxKind.ParenthesizedExpression:
return isSimpleExpression((expression as tstl.ParenthesizedExpression).innerExpression);
}
return true;
}
85 changes: 46 additions & 39 deletions test/translation/__snapshots__/transformation.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -228,28 +228,12 @@ end"
`;

exports[`Transformation (forIn) 1`] = `
"for i in pairs({
a = 1,
b = 2,
c = 3,
d = 4,
}) do
"for i in pairs({a = 1, b = 2, c = 3, d = 4}) do
end"
`;

exports[`Transformation (forOf) 1`] = `
"for ____, i in ipairs({
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
}) do
"for ____, i in ipairs({1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) do
end"
`;

Expand Down Expand Up @@ -577,9 +561,11 @@ f = function(____, x) return ({x = x}) end"

exports[`Transformation (tryCatch) 1`] = `
"do
local ____TS_try, er = pcall(function()
local a = 42
end)
local ____TS_try, er = pcall(
function()
local a = 42
end
)
if not ____TS_try then
local b = \\"fail\\"
end
Expand All @@ -588,9 +574,11 @@ end"

exports[`Transformation (tryCatchFinally) 1`] = `
"do
local ____TS_try, er = pcall(function()
local a = 42
end)
local ____TS_try, er = pcall(
function()
local a = 42
end
)
if not ____TS_try then
local b = \\"fail\\"
end
Expand All @@ -602,9 +590,11 @@ end"

exports[`Transformation (tryFinally) 1`] = `
"do
pcall(function()
local a = 42
end)
pcall(
function()
local a = 42
end
)
do
local b = \\"finally\\"
end
Expand All @@ -618,30 +608,47 @@ end
tupleReturn(_G)
noTupleReturn(_G)
local a, b = tupleReturn(_G)
local c, d = table.unpack(noTupleReturn(_G))
local c, d = table.unpack(
noTupleReturn(_G)
)
a, b = tupleReturn(_G)
c, d = table.unpack(noTupleReturn(_G))
local e = ({tupleReturn(_G)})
c, d = table.unpack(
noTupleReturn(_G)
)
local e = ({
tupleReturn(_G)
})
local f = noTupleReturn(_G)
e = ({tupleReturn(_G)})
e = ({
tupleReturn(_G)
})
f = noTupleReturn(_G)
foo(_G, ({tupleReturn(_G)}))
foo(_G, noTupleReturn(_G))
foo(
_G,
({
tupleReturn(_G)
})
)
foo(
_G,
noTupleReturn(_G)
)
function tupleReturnFromVar(self)
local r = {
1,
\\"baz\\",
}
local r = {1, \\"baz\\"}
return table.unpack(r)
end
function tupleReturnForward(self)
return tupleReturn(_G)
end
function tupleNoForward(self)
return ({tupleReturn(_G)})
return ({
tupleReturn(_G)
})
end
function tupleReturnUnpack(self)
return table.unpack(tupleNoForward(_G))
return table.unpack(
tupleNoForward(_G)
)
end"
`;

Expand Down
6 changes: 3 additions & 3 deletions test/unit/assignmentDestructuring.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,23 @@ test("Assignment destructuring [5.1]", () => {
luaTarget: tstl.LuaTarget.Lua51,
luaLibImport: tstl.LuaLibImportKind.None,
});
expect(lua).toBe(`local a, b = unpack(myFunc())`);
expect(lua).toBe(`local a, b = unpack(\n myFunc()\n)`);
});

test("Assignment destructuring [5.2]", () => {
const lua = util.transpileString(assignmentDestruturingTs, {
luaTarget: tstl.LuaTarget.Lua52,
luaLibImport: tstl.LuaLibImportKind.None,
});
expect(lua).toBe(`local a, b = table.unpack(myFunc())`);
expect(lua).toBe(`local a, b = table.unpack(\n myFunc()\n)`);
});

test("Assignment destructuring [JIT]", () => {
const lua = util.transpileString(assignmentDestruturingTs, {
luaTarget: tstl.LuaTarget.LuaJIT,
luaLibImport: tstl.LuaLibImportKind.None,
});
expect(lua).toBe(`local a, b = unpack(myFunc())`);
expect(lua).toBe(`local a, b = unpack(\n myFunc()\n)`);
});

test.each([
Expand Down
14 changes: 7 additions & 7 deletions test/unit/assignments/assignments.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import * as util from "../../util";
test.each([
{ inp: `"abc"`, out: `"abc"` },
{ inp: "3", out: "3" },
{ inp: "[1,2,3]", out: "{\n 1,\n 2,\n 3,\n}" },
{ inp: "[1,2,3]", out: "{1, 2, 3}" },
{ inp: "true", out: "true" },
{ inp: "false", out: "false" },
{ inp: `{a:3,b:"4"}`, out: `{\n a = 3,\n b = "4",\n}` },
{ inp: `{a:3,b:"4"}`, out: `{a = 3, b = "4"}` },
])("Const assignment (%p)", ({ inp, out }) => {
const lua = util.transpileString(`const myvar = ${inp}`);
expect(lua).toBe(`local myvar = ${out}`);
Expand All @@ -16,10 +16,10 @@ test.each([
test.each([
{ inp: `"abc"`, out: `"abc"` },
{ inp: "3", out: "3" },
{ inp: "[1,2,3]", out: "{\n 1,\n 2,\n 3,\n}" },
{ inp: "[1,2,3]", out: "{1, 2, 3}" },
{ inp: "true", out: "true" },
{ inp: "false", out: "false" },
{ inp: `{a:3,b:"4"}`, out: `{\n a = 3,\n b = "4",\n}` },
{ inp: `{a:3,b:"4"}`, out: `{a = 3, b = "4"}` },
])("Let assignment (%p)", ({ inp, out }) => {
const lua = util.transpileString(`let myvar = ${inp}`);
expect(lua).toBe(`local myvar = ${out}`);
Expand All @@ -28,10 +28,10 @@ test.each([
test.each([
{ inp: `"abc"`, out: `"abc"` },
{ inp: "3", out: "3" },
{ inp: "[1,2,3]", out: "{\n 1,\n 2,\n 3,\n}" },
{ inp: "[1,2,3]", out: "{1, 2, 3}" },
{ inp: "true", out: "true" },
{ inp: "false", out: "false" },
{ inp: `{a:3,b:"4"}`, out: `{\n a = 3,\n b = "4",\n}` },
{ inp: `{a:3,b:"4"}`, out: `{a = 3, b = "4"}` },
])("Var assignment (%p)", ({ inp, out }) => {
const lua = util.transpileString(`var myvar = ${inp}`);
expect(lua).toBe(`myvar = ${out}`);
Expand Down Expand Up @@ -95,7 +95,7 @@ test("TupleReturn Single assignment", () => {
`;

const lua = util.transpileString(code);
expect(lua).toBe("local a = ({abc()})\na = ({abc()})");
expect(lua).toBe("local a = ({\n abc()\n})\na = ({\n abc()\n})");
});

test("TupleReturn interface assignment", () => {
Expand Down
Loading