Skip to content

Commit dccea87

Browse files
authored
ensuring statement expressions are transpiled to valid lua (#506)
* ensuring statement expressions are transpiled to valid lua * test format fix * fixed test to actually run the lua
1 parent 422a36a commit dccea87

File tree

5 files changed

+74
-35
lines changed

5 files changed

+74
-35
lines changed

src/LuaTransformer.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1817,6 +1817,14 @@ export class LuaTransformer {
18171817
);
18181818
}
18191819

1820+
if (!ts.isCallLikeExpression(expression)) {
1821+
// Assign expression statements to dummy to make sure they're legal lua
1822+
return tstl.createVariableDeclarationStatement(
1823+
tstl.createAnnonymousIdentifier(),
1824+
this.transformExpression(expression)
1825+
);
1826+
}
1827+
18201828
return tstl.createExpressionStatement(this.transformExpression(expression));
18211829
}
18221830

@@ -3481,7 +3489,10 @@ export class LuaTransformer {
34813489
const logCall1 = tstl.createCallExpression(log1, params);
34823490
const e = tstl.createNumericLiteral(expressionName === "log10" ? Math.LN10 : Math.LN2);
34833491
const div = tstl.createBinaryExpression(logCall1, e, tstl.SyntaxKind.DivisionOperator);
3484-
return tstl.createParenthesizedExpression(div, node);
3492+
return ts.isExpressionStatement(node.parent)
3493+
// if used as a stand-alone statement, needs to be a call expression to be valid lua
3494+
? this.createImmediatelyInvokedFunctionExpression([], div, node)
3495+
: tstl.createParenthesizedExpression(div, node);
34853496
}
34863497

34873498
// math.log(1 + x)
@@ -4235,7 +4246,8 @@ export class LuaTransformer {
42354246
{
42364247
const body = statements ? statements.slice(0) : [];
42374248
body.push(tstl.createReturnStatement(Array.isArray(result) ? result : [result]));
4238-
const iife = tstl.createFunctionExpression(tstl.createBlock(body));
4249+
const flags = statements.length === 0 ? tstl.FunctionExpressionFlags.Inline : tstl.FunctionExpressionFlags.None;
4250+
const iife = tstl.createFunctionExpression(tstl.createBlock(body), undefined, undefined, undefined, flags);
42394251
return tstl.createCallExpression(tstl.createParenthesizedExpression(iife), [], tsOriginal);
42404252
}
42414253

test/unit/compiler/configuration/options.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ test.each([LuaTarget.LuaJIT, "jit", "JiT"])("Options luaTarget case-insensitive
55
const options = { luaTarget: target as LuaTarget };
66
const result = util.transpileString("~a", options);
77

8-
expect(result).toBe("bit.bnot(a);");
8+
expect(result).toBe("local ____ = bit.bnot(a);");
99
});
1010

1111
test.each([LuaLibImportKind.None, "none", "NoNe"])(

test/unit/expressions.spec.ts

Lines changed: 51 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ test.each([
88
{ input: "++i", lua: "i = i + 1;" },
99
{ input: "i--", lua: "i = i - 1;" },
1010
{ input: "--i", lua: "i = i - 1;" },
11-
{ input: "!a", lua: "not a;" },
12-
{ input: "-a", lua: "-a;" },
13-
{ input: "+a", lua: "a;" },
11+
{ input: "!a", lua: "local ____ = not a;" },
12+
{ input: "-a", lua: "local ____ = -a;" },
13+
{ input: "+a", lua: "local ____ = a;" },
1414
{
1515
input: "let a = delete tbl['test']",
1616
lua: "local a = (function()\n tbl.test = nil;\n return true;\nend)();",
@@ -106,54 +106,54 @@ test.each([
106106
});
107107

108108
test.each([
109-
{ input: "~a", lua: "bit.bnot(a);" },
110-
{ input: "a&b", lua: "bit.band(a, b);" },
109+
{ input: "~a", lua: "local ____ = bit.bnot(a);" },
110+
{ input: "a&b", lua: "local ____ = bit.band(a, b);" },
111111
{ input: "a&=b", lua: "a = bit.band(a, b);" },
112-
{ input: "a|b", lua: "bit.bor(a, b);" },
112+
{ input: "a|b", lua: "local ____ = bit.bor(a, b);" },
113113
{ input: "a|=b", lua: "a = bit.bor(a, b);" },
114-
{ input: "a^b", lua: "bit.bxor(a, b);" },
114+
{ input: "a^b", lua: "local ____ = bit.bxor(a, b);" },
115115
{ input: "a^=b", lua: "a = bit.bxor(a, b);" },
116-
{ input: "a<<b", lua: "bit.lshift(a, b);" },
116+
{ input: "a<<b", lua: "local ____ = bit.lshift(a, b);" },
117117
{ input: "a<<=b", lua: "a = bit.lshift(a, b);" },
118-
{ input: "a>>b", lua: "bit.arshift(a, b);" },
118+
{ input: "a>>b", lua: "local ____ = bit.arshift(a, b);" },
119119
{ input: "a>>=b", lua: "a = bit.arshift(a, b);" },
120-
{ input: "a>>>b", lua: "bit.rshift(a, b);" },
120+
{ input: "a>>>b", lua: "local ____ = bit.rshift(a, b);" },
121121
{ input: "a>>>=b", lua: "a = bit.rshift(a, b);" },
122122
])("Bitop [JIT] (%p)", ({ input, lua }) => {
123123
const options = { luaTarget: LuaTarget.LuaJIT, luaLibImport: LuaLibImportKind.None };
124124
expect(util.transpileString(input, options)).toBe(lua);
125125
});
126126

127127
test.each([
128-
{ input: "~a", lua: "bit32.bnot(a);" },
129-
{ input: "a&b", lua: "bit32.band(a, b);" },
128+
{ input: "~a", lua: "local ____ = bit32.bnot(a);" },
129+
{ input: "a&b", lua: "local ____ = bit32.band(a, b);" },
130130
{ input: "a&=b", lua: "a = bit32.band(a, b);" },
131-
{ input: "a|b", lua: "bit32.bor(a, b);" },
131+
{ input: "a|b", lua: "local ____ = bit32.bor(a, b);" },
132132
{ input: "a|=b", lua: "a = bit32.bor(a, b);" },
133-
{ input: "a^b", lua: "bit32.bxor(a, b);" },
133+
{ input: "a^b", lua: "local ____ = bit32.bxor(a, b);" },
134134
{ input: "a^=b", lua: "a = bit32.bxor(a, b);" },
135-
{ input: "a<<b", lua: "bit32.lshift(a, b);" },
135+
{ input: "a<<b", lua: "local ____ = bit32.lshift(a, b);" },
136136
{ input: "a<<=b", lua: "a = bit32.lshift(a, b);" },
137-
{ input: "a>>b", lua: "bit32.arshift(a, b);" },
137+
{ input: "a>>b", lua: "local ____ = bit32.arshift(a, b);" },
138138
{ input: "a>>=b", lua: "a = bit32.arshift(a, b);" },
139-
{ input: "a>>>b", lua: "bit32.rshift(a, b);" },
139+
{ input: "a>>>b", lua: "local ____ = bit32.rshift(a, b);" },
140140
{ input: "a>>>=b", lua: "a = bit32.rshift(a, b);" },
141141
])("Bitop [5.2] (%p)", ({ input, lua }) => {
142142
const options = { luaTarget: LuaTarget.Lua52, luaLibImport: LuaLibImportKind.None };
143143
expect(util.transpileString(input, options)).toBe(lua);
144144
});
145145

146146
test.each([
147-
{ input: "~a", lua: "~a;" },
148-
{ input: "a&b", lua: "a & b;" },
147+
{ input: "~a", lua: "local ____ = ~a;" },
148+
{ input: "a&b", lua: "local ____ = a & b;" },
149149
{ input: "a&=b", lua: "a = a & b;" },
150-
{ input: "a|b", lua: "a | b;" },
150+
{ input: "a|b", lua: "local ____ = a | b;" },
151151
{ input: "a|=b", lua: "a = a | b;" },
152-
{ input: "a^b", lua: "a ~ b;" },
152+
{ input: "a^b", lua: "local ____ = a ~ b;" },
153153
{ input: "a^=b", lua: "a = a ~ b;" },
154-
{ input: "a<<b", lua: "a << b;" },
154+
{ input: "a<<b", lua: "local ____ = a << b;" },
155155
{ input: "a<<=b", lua: "a = a << b;" },
156-
{ input: "a>>>b", lua: "a >> b;" },
156+
{ input: "a>>>b", lua: "local ____ = a >> b;" },
157157
{ input: "a>>>=b", lua: "a = a >> b;" },
158158
])("Bitop [5.3] (%p)", ({ input, lua }) => {
159159
const options = { luaTarget: LuaTarget.Lua53, luaLibImport: LuaLibImportKind.None };
@@ -183,7 +183,7 @@ test.each([
183183
{ input: "1*(3+4*2)", lua: "1 * (3 + 4 * 2);" },
184184
{ input: "10-(4+5)", lua: "10 - (4 + 5);" },
185185
])("Binary expressions ordering parentheses (%p)", ({ input, lua }) => {
186-
expect(util.transpileString(input)).toBe(lua);
186+
expect(util.transpileString(input)).toBe("local ____ = " + lua);
187187
});
188188

189189
test.each([
@@ -208,11 +208,11 @@ test("Binary Comma Statement in For Loop", () => {
208208
});
209209

210210
test("Null Expression", () => {
211-
expect(util.transpileString("null")).toBe("nil;");
211+
expect(util.transpileString("null")).toBe("local ____ = nil;");
212212
});
213213

214214
test("Undefined Expression", () => {
215-
expect(util.transpileString("undefined")).toBe("nil;");
215+
expect(util.transpileString("undefined")).toBe("local ____ = nil;");
216216
});
217217

218218
test.each([
@@ -553,3 +553,28 @@ test("Unsupported object literal element error", () => {
553553
),
554554
);
555555
});
556+
557+
test.each([
558+
'"foobar"',
559+
"17",
560+
"true",
561+
"{}",
562+
"[]",
563+
"[].length",
564+
"foo() + foo()",
565+
"!foo()",
566+
"foo()",
567+
"typeof foo",
568+
'"bar" in bar',
569+
"foo as Function",
570+
"Math.log2(2)",
571+
"Math.log10(2)",
572+
])("Expression statements (%p)", input => {
573+
const code = `
574+
function foo() { return 17; }
575+
const bar = {};
576+
${input};
577+
return 1;
578+
`;
579+
expect(util.transpileAndExecute(code)).toBe(1);
580+
});

test/unit/math.spec.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@ test.each([
55
{ inp: "Math.sin()", expected: "math.sin();" },
66
{ inp: "Math.min()", expected: "math.min();" },
77
{ inp: "Math.atan2(2, 3)", expected: "math.atan(2 / 3);" },
8-
{ inp: "Math.log2(3)", expected: `(math.log(3) / ${Math.LN2});` },
9-
{ inp: "Math.log10(3)", expected: `(math.log(3) / ${Math.LN10});` },
8+
{ inp: "Math.log2(3)", expected: `(function() return math.log(3) / ${Math.LN2}; end)();` },
9+
{ inp: "Math.log10(3)", expected: `(function() return math.log(3) / ${Math.LN10}; end)();` },
10+
{ inp: "const x = Math.log2(3)", expected: `local x = (math.log(3) / ${Math.LN2});` },
11+
{ inp: "const x = Math.log10(3)", expected: `local x = (math.log(3) / ${Math.LN10});` },
1012
{ inp: "Math.log1p(3)", expected: "math.log(1 + 3);" },
1113
{ inp: "Math.round(3.3)", expected: "math.floor(3.3 + 0.5);" },
12-
{ inp: "Math.PI", expected: "math.pi;" },
14+
{ inp: "Math.PI", expected: "local ____ = math.pi;" },
1315
])("Math (%p)", ({ inp, expected }) => {
1416
const lua = util.transpileString(inp);
1517

test/unit/spreadElement.spec.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,17 @@ test("Spread Element Lua 5.1", () => {
2121
test("Spread Element Lua 5.2", () => {
2222
const options = { luaTarget: LuaTarget.Lua52, luaLibImport: LuaLibImportKind.None };
2323
const lua = util.transpileString(`[...[0, 1, 2]]`, options);
24-
expect(lua).toBe("{table.unpack({\n 0,\n 1,\n 2,\n})};");
24+
expect(lua).toBe("local ____ = {table.unpack({\n 0,\n 1,\n 2,\n})};");
2525
});
2626

2727
test("Spread Element Lua 5.3", () => {
2828
const options = { luaTarget: LuaTarget.Lua53, luaLibImport: LuaLibImportKind.None };
2929
const lua = util.transpileString(`[...[0, 1, 2]]`, options);
30-
expect(lua).toBe("{table.unpack({\n 0,\n 1,\n 2,\n})};");
30+
expect(lua).toBe("local ____ = {table.unpack({\n 0,\n 1,\n 2,\n})};");
3131
});
3232

3333
test("Spread Element Lua JIT", () => {
3434
const options = { luaTarget: "JiT" as LuaTarget, luaLibImport: LuaLibImportKind.None };
3535
const lua = util.transpileString(`[...[0, 1, 2]]`, options);
36-
expect(lua).toBe("{unpack({\n 0,\n 1,\n 2,\n})};");
36+
expect(lua).toBe("local ____ = {unpack({\n 0,\n 1,\n 2,\n})};");
3737
});

0 commit comments

Comments
 (0)