X Tutup
Skip to content

Commit a6e1dcc

Browse files
committed
Switch statements using gotos
Added test for jump local scope Added test for returns inside switches
1 parent ac5cfd1 commit a6e1dcc

File tree

2 files changed

+104
-46
lines changed

2 files changed

+104
-46
lines changed

src/Transpiler.ts

Lines changed: 45 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -585,70 +585,69 @@ export abstract class LuaTranspiler {
585585

586586
let result = this.indent + "-------Switch statement start-------\n";
587587

588-
const jumpTableName = "____switch" + this.genVarCounter;
588+
const switchVarName = "____switch" + this.genVarCounter;
589589
this.genVarCounter++;
590590

591-
result += this.indent + `local ${jumpTableName} = {}\n`;
591+
result += this.indent + `local ${switchVarName} = ${expression}\n`;
592+
593+
let hasDefaultClause = false;
592594

593595
// If statement to go to right entry label
594596
clauses.forEach((clause, index) => {
595597
if (ts.isCaseClause(clause)) {
596-
result += this.indent + `-- case:\n`;
597598
result += this.indent +
598-
`${jumpTableName}[${this.transpileExpression(clause.expression, true)}] = function()\n`;
599-
}
600-
if (ts.isDefaultClause(clause)) {
601-
result += this.indent + `-- default:\n`;
602-
result += this.indent + `${jumpTableName}["____default${this.genVarCounter}"] = function()\n`;
599+
`if ${this.transpileExpression(clause.expression, true)} == ${switchVarName} then\n`;
600+
601+
this.pushIndent();
602+
result += this.indent + `goto ${switchVarName}_case_${index}\n`;
603+
this.popIndent();
604+
605+
result += this.indent + "end\n";
606+
} else if (ts.isDefaultClause(clause)) {
607+
hasDefaultClause = true;
603608
}
604-
this.pushIndent();
609+
});
605610

611+
result += "\n";
612+
613+
// If no case condition is matched jump to end or default immediately
614+
if (hasDefaultClause) {
615+
result += this.indent + `goto ${switchVarName}_default\n`;
616+
} else {
617+
result += this.indent + `goto ${switchVarName}_end\n`;
618+
}
619+
620+
result += "\n";
621+
622+
const transpileClauseBody = (clause: ts.CaseOrDefaultClause) => {
606623
this.transpilingSwitch++;
624+
result += this.indent + "do\n";
625+
this.pushIndent();
607626
result += this.transpileBlock(ts.createBlock(clause.statements));
627+
this.popIndent();
628+
result += this.indent + "end\n";
608629
this.transpilingSwitch--;
630+
};
609631

610-
let i = index + 1;
611-
if (i < clauses.length
612-
&& !tsHelper.containsStatement(clause.statements, ts.SyntaxKind.BreakStatement)
613-
&& !tsHelper.containsStatement(clause.statements, ts.SyntaxKind.ReturnStatement)
614-
) {
615-
let nextClause = clauses[i];
616-
while (i < clauses.length
617-
&& ts.isCaseClause(nextClause)
618-
&& nextClause.statements.length === 0
619-
) {
620-
i++;
621-
nextClause = clauses[i];
622-
}
632+
clauses.forEach((clause, index) => {
633+
if (ts.isCaseClause(clause)) {
634+
result += this.indent + `::${switchVarName}_case_${index}::\n`;
623635

624-
if (i !== index && nextClause) {
625-
if (ts.isCaseClause(nextClause)) {
626-
const nextValue = this.transpileExpression(nextClause.expression, true);
627-
result += this.indent + `return ${jumpTableName}[${nextValue}]()\n`;
628-
} else {
629-
result += this.indent + `${jumpTableName}["____default${this.genVarCounter}"]()\n`;
630-
}
636+
transpileClauseBody(clause);
637+
638+
if (tsHelper.containsStatement(clause.statements, ts.SyntaxKind.BreakStatement)) {
639+
result += this.indent + `goto ${switchVarName}_end\n`;
631640
}
632-
} else {
633-
result += this.indent + `-- break;\n`;
641+
} else if (ts.isDefaultClause(clause)) {
642+
result += this.indent + `::${switchVarName}_default::\n`;
643+
644+
transpileClauseBody(clause);
634645
}
646+
});
635647

636-
this.popIndent();
648+
result += this.indent + `::${switchVarName}_end::\n`;
649+
result += this.indent + "-------Switch statement end-------\n";
637650

638-
result += this.indent + `end\n`;
639-
});
640-
result += this.indent + `if ${jumpTableName}[${expression}] then\n`
641-
+ this.indent + ` local ${jumpTableName}Return = ${jumpTableName}[${expression}]()\n`
642-
+ this.indent + ` if ${jumpTableName}Return ~= nil then return ${jumpTableName}Return end\n`;
643-
result += this.indent + `elseif ${jumpTableName}["____default${this.genVarCounter}"] then\n`
644-
+ this.indent + ` local ${jumpTableName}Return = ${jumpTableName}["____default${this.genVarCounter}"]()\n`
645-
+ this.indent + ` if ${jumpTableName}Return ~= nil then return ${jumpTableName}Return end\n`
646-
+ this.indent + `end\n`;
647-
result += this.indent +
648-
"--------Switch statement end--------\n";
649-
650-
// Increment counter for next switch statement
651-
this.genVarCounter += clauses.length;
652651
return result;
653652
}
654653

test/unit/conditionals.spec.ts

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,65 @@ export class LuaConditionalsTests {
252252
Expect(result).toBe(expected);
253253
}
254254

255+
@Test("switchLocalScope")
256+
@TestCase(0, 0)
257+
@TestCase(1, 2)
258+
@TestCase(2, 2)
259+
public switchLocalScope(inp: number, expected: number): void {
260+
// Transpile
261+
const lua = util.transpileString(
262+
`let result = -1;
263+
264+
switch (${inp}) {
265+
case 0:
266+
let x = 0;
267+
result = 0;
268+
break;
269+
case 1:
270+
let x = 1;
271+
result = x;
272+
case 2:
273+
let x = 2;
274+
result = x;
275+
break;
276+
}
277+
return result;`
278+
);
279+
280+
// Execute
281+
const result = util.executeLua(lua);
282+
283+
// Assert
284+
Expect(result).toBe(expected);
285+
}
286+
287+
@Test("switchReturn")
288+
@TestCase(0, 0)
289+
@TestCase(1, 1)
290+
@TestCase(2, 2)
291+
public switchReturn(inp: number, expected: number): void {
292+
// Transpile
293+
const lua = util.transpileString(
294+
`switch (${inp}) {
295+
case 0:
296+
return 0;
297+
break;
298+
case 1:
299+
return 1;
300+
case 2:
301+
return 2;
302+
break;
303+
}
304+
return result;`
305+
);
306+
307+
// Execute
308+
const result = util.executeLua(lua);
309+
310+
// Assert
311+
Expect(result).toBe(expected);
312+
}
313+
255314
@Test("If dead code after return")
256315
public ifDeadCodeAfterReturn(): void {
257316
const result = util.transpileAndExecute(

0 commit comments

Comments
 (0)
X Tutup