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
33 changes: 23 additions & 10 deletions src/Transpiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -558,28 +558,41 @@ export abstract class LuaTranspiler {
const variable = (node.initializer as ts.VariableDeclarationList).declarations[0];

// Transpile expression
const expression = this.transpileExpression(node.expression);
const iterable = this.transpileExpression(node.expression);

// Use ipairs for array types, pairs otherwise
const isArray = tsHelper.isArrayType(this.checker.getTypeAtLocation(node.expression), this.checker);
const pairs = isArray ? "ipairs" : "pairs";

// Make header
let result = "";
if (ts.isIdentifier(variable.name)) {
result = this.indent + `for _, ${this.transpileIdentifier(variable.name)} in ${pairs}(${expression}) do\n`;
} else if (ts.isArrayBindingPattern(variable.name)) {
const valueVar = "__forOfValue" + this.genVarCounter;
result = this.indent + `for _, ${valueVar} in ${pairs}(${expression}) do\n`;
const declaration = ts.createVariableDeclaration(variable.name, undefined, ts.createIdentifier(valueVar));
result += this.indent + this.transpileVariableDeclaration(declaration);

if (!isArray && ts.isIdentifier(variable.name)) {
result = this.indent + `for _, ${this.transpileIdentifier(variable.name)} in pairs(${iterable}) do\n`;
} else {
let itemVariable: ts.Identifier;
if (isArray) {
// Cache the expression result
result += this.indent + `local __loopVariable${this.genVarCounter} = ${iterable};\n`;
result += this.indent + `for i${this.genVarCounter}=1, #__loopVariable${this.genVarCounter} do\n`;
itemVariable = ts.createIdentifier(`__loopVariable${this.genVarCounter}[i${this.genVarCounter}]`);
} else {
const variableName = `__forOfValue${this.genVarCounter}`;
itemVariable = ts.createIdentifier(variableName);
result += this.indent + `for _, ${variableName} in pairs(${iterable}) do\n`;
}

const declaration = ts.createVariableDeclaration(variable.name, undefined, itemVariable);
this.pushIndent();
result += this.indent + this.transpileVariableDeclaration(declaration) + ";\n";
this.popIndent();
}

// For body
this.pushIndent();
result += this.transpileLoopBody(node);
this.popIndent();

this.genVarCounter++;

return result + this.indent + "end\n";
}

Expand Down
4 changes: 3 additions & 1 deletion test/translation/lua/forOf.lua
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
for _, i in ipairs({1,2,3,4,5,6,7,8,9,10}) do
local __loopVariable0 = {1,2,3,4,5,6,7,8,9,10};
for i0=1, #__loopVariable0 do
Copy link
Copy Markdown
Member

@lolleko lolleko Oct 20, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just realized that using # operator is unsafe. Not sure if this applies only to this part of the transpiler or to other parts aswell.

If you do

local x = {1,2}
x[5] = 12
print(#x)

The result is undefined because the array has gaps, i got 2 as result when running in https://www.lua.org/cgi-bin/demo

var arr = [1, 2];
arr[5] = 12;
console.log(arr.length);

However returns 6 as "expected".

local i = __loopVariable0[i0];
do
end
::__continue0::
Expand Down
20 changes: 20 additions & 0 deletions test/unit/loops.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,26 @@ export class LuaLoopTests {
Expect(result).toBe(JSON.stringify(expected));
}

@TestCase([[1, 2], [2, 3], [3, 4]], [3, 5, 7])
@Test("forof destructing")
public forofDestructing(inp: number[][], expected: any): void {
// Transpile
const lua = util.transpileString(
`let objTest = ${JSON.stringify(inp)};
let arrResultTest = [];
for (let [a,b] of objTest) {
arrResultTest.push(a + b)
}
return JSONStringify(arrResultTest);`
);

// Execute
const result = util.executeLua(lua);

// Assert
Expect(result).toBe(JSON.stringify(expected));
}

@TestCase([0, 1, 2, 3, 4], [0, 0, 2, 0, 4])
@Test("forof with continue")
public forofWithContinue(inp: number[], expected: number[]): void {
Expand Down