Skip to content

Commit e697c6e

Browse files
authored
Support loop continue for 5.0, 5.1, and universal targets (#1500)
* feat: support loop continue for 5.0, 5.1, and universal targets * test: test loops w/ continue against all lua versions
1 parent 4bb5178 commit e697c6e

File tree

5 files changed

+97
-218
lines changed

5 files changed

+97
-218
lines changed

src/transformation/utils/scope.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@ interface FunctionDefinitionInfo {
2222
definition?: lua.VariableDeclarationStatement | lua.AssignmentStatement;
2323
}
2424

25+
export enum LoopContinued {
26+
WithGoto,
27+
WithRepeatBreak,
28+
}
29+
2530
export interface Scope {
2631
type: ScopeType;
2732
id: number;
@@ -30,7 +35,7 @@ export interface Scope {
3035
variableDeclarations?: lua.VariableDeclarationStatement[];
3136
functionDefinitions?: Map<lua.SymbolId, FunctionDefinitionInfo>;
3237
importStatements?: lua.Statement[];
33-
loopContinued?: boolean;
38+
loopContinued?: LoopContinued;
3439
functionReturned?: boolean;
3540
}
3641

src/transformation/visitors/break-continue.ts

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,28 +2,36 @@ import * as ts from "typescript";
22
import { LuaTarget } from "../../CompilerOptions";
33
import * as lua from "../../LuaAST";
44
import { FunctionVisitor } from "../context";
5-
import { unsupportedForTarget } from "../utils/diagnostics";
6-
import { findScope, ScopeType } from "../utils/scope";
5+
import { findScope, LoopContinued, ScopeType } from "../utils/scope";
76

87
export const transformBreakStatement: FunctionVisitor<ts.BreakStatement> = (breakStatement, context) => {
98
void context;
109
return lua.createBreakStatement(breakStatement);
1110
};
1211

1312
export const transformContinueStatement: FunctionVisitor<ts.ContinueStatement> = (statement, context) => {
14-
if (
13+
const scope = findScope(context, ScopeType.Loop);
14+
const continuedWith =
1515
context.luaTarget === LuaTarget.Universal ||
1616
context.luaTarget === LuaTarget.Lua50 ||
1717
context.luaTarget === LuaTarget.Lua51
18-
) {
19-
context.diagnostics.push(unsupportedForTarget(statement, "Continue statement", context.luaTarget));
20-
}
21-
22-
const scope = findScope(context, ScopeType.Loop);
18+
? LoopContinued.WithRepeatBreak
19+
: LoopContinued.WithGoto;
2320

2421
if (scope) {
25-
scope.loopContinued = true;
22+
scope.loopContinued = continuedWith;
2623
}
2724

28-
return lua.createGotoStatement(`__continue${scope?.id ?? ""}`, statement);
25+
const label = `__continue${scope?.id ?? ""}`;
26+
27+
switch (continuedWith) {
28+
case LoopContinued.WithGoto:
29+
return lua.createGotoStatement(label, statement);
30+
31+
case LoopContinued.WithRepeatBreak:
32+
return [
33+
lua.createAssignmentStatement(lua.createIdentifier(label), lua.createBooleanLiteral(true), statement),
34+
lua.createBreakStatement(statement),
35+
];
36+
}
2937
};

src/transformation/visitors/loops/utils.ts

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import * as ts from "typescript";
22
import * as lua from "../../../LuaAST";
33
import { TransformationContext } from "../../context";
44
import { transformInPrecedingStatementScope } from "../../utils/preceding-statements";
5-
import { performHoisting, ScopeType } from "../../utils/scope";
5+
import { LoopContinued, performHoisting, ScopeType } from "../../utils/scope";
66
import { isAssignmentPattern } from "../../utils/typescript";
77
import { transformAssignment } from "../binary-expression/assignments";
88
import { transformAssignmentPattern } from "../binary-expression/destructuring-assignments";
@@ -19,15 +19,31 @@ export function transformLoopBody(
1919
const scope = context.popScope();
2020
const scopeId = scope.id;
2121

22-
if (!scope.loopContinued) {
23-
return body;
24-
}
22+
switch (scope.loopContinued) {
23+
case undefined:
24+
return body;
25+
26+
case LoopContinued.WithGoto:
27+
return [lua.createDoStatement(body), lua.createLabelStatement(`__continue${scopeId}`)];
2528

26-
const baseResult: lua.Statement[] = [lua.createDoStatement(body)];
27-
const continueLabel = lua.createLabelStatement(`__continue${scopeId}`);
28-
baseResult.push(continueLabel);
29+
case LoopContinued.WithRepeatBreak:
30+
const identifier = lua.createIdentifier(`__continue${scopeId}`);
31+
const literalTrue = lua.createBooleanLiteral(true);
2932

30-
return baseResult;
33+
return [
34+
lua.createDoStatement([
35+
lua.createVariableDeclarationStatement(identifier),
36+
lua.createRepeatStatement(
37+
lua.createBlock([...body, lua.createAssignmentStatement(identifier, literalTrue)]),
38+
literalTrue
39+
),
40+
lua.createIfStatement(
41+
lua.createUnaryExpression(identifier, lua.SyntaxKind.NotOperator),
42+
lua.createBlock([lua.createBreakStatement()])
43+
),
44+
]),
45+
];
46+
}
3147
}
3248

3349
export function getVariableDeclarationBinding(

test/unit/__snapshots__/loops.spec.ts.snap

Lines changed: 0 additions & 177 deletions
Original file line numberDiff line numberDiff line change
@@ -11,180 +11,3 @@ return ____exports"
1111
`;
1212

1313
exports[`forin[Array]: diagnostics 1`] = `"main.ts(3,9): error TSTL: Iterating over arrays with 'for ... in' is not allowed."`;
14-
15-
exports[`loop continue (do { continue; } while (false)) [5.0]: code 1`] = `
16-
"repeat
17-
do
18-
do
19-
goto __continue2
20-
end
21-
::__continue2::
22-
end
23-
until not false"
24-
`;
25-
26-
exports[`loop continue (do { continue; } while (false)) [5.0]: diagnostics 1`] = `"main.ts(1,6): error TSTL: Continue statement is/are not supported for target Lua 5.0."`;
27-
28-
exports[`loop continue (do { continue; } while (false)) [5.1]: code 1`] = `
29-
"repeat
30-
do
31-
do
32-
goto __continue2
33-
end
34-
::__continue2::
35-
end
36-
until not false"
37-
`;
38-
39-
exports[`loop continue (do { continue; } while (false)) [5.1]: diagnostics 1`] = `"main.ts(1,6): error TSTL: Continue statement is/are not supported for target Lua 5.1."`;
40-
41-
exports[`loop continue (do { continue; } while (false)) [universal]: code 1`] = `
42-
"repeat
43-
do
44-
do
45-
goto __continue2
46-
end
47-
::__continue2::
48-
end
49-
until not false"
50-
`;
51-
52-
exports[`loop continue (do { continue; } while (false)) [universal]: diagnostics 1`] = `"main.ts(1,6): error TSTL: Continue statement is/are not supported for target Lua universal."`;
53-
54-
exports[`loop continue (for (;;) { continue; }) [5.0]: code 1`] = `
55-
"do
56-
while true do
57-
do
58-
goto __continue2
59-
end
60-
::__continue2::
61-
end
62-
end"
63-
`;
64-
65-
exports[`loop continue (for (;;) { continue; }) [5.0]: diagnostics 1`] = `"main.ts(1,12): error TSTL: Continue statement is/are not supported for target Lua 5.0."`;
66-
67-
exports[`loop continue (for (;;) { continue; }) [5.1]: code 1`] = `
68-
"do
69-
while true do
70-
do
71-
goto __continue2
72-
end
73-
::__continue2::
74-
end
75-
end"
76-
`;
77-
78-
exports[`loop continue (for (;;) { continue; }) [5.1]: diagnostics 1`] = `"main.ts(1,12): error TSTL: Continue statement is/are not supported for target Lua 5.1."`;
79-
80-
exports[`loop continue (for (;;) { continue; }) [universal]: code 1`] = `
81-
"do
82-
while true do
83-
do
84-
goto __continue2
85-
end
86-
::__continue2::
87-
end
88-
end"
89-
`;
90-
91-
exports[`loop continue (for (;;) { continue; }) [universal]: diagnostics 1`] = `"main.ts(1,12): error TSTL: Continue statement is/are not supported for target Lua universal."`;
92-
93-
exports[`loop continue (for (const a in {}) { continue; }) [5.0]: code 1`] = `
94-
"for a in pairs({}) do
95-
do
96-
goto __continue2
97-
end
98-
::__continue2::
99-
end"
100-
`;
101-
102-
exports[`loop continue (for (const a in {}) { continue; }) [5.0]: diagnostics 1`] = `"main.ts(1,23): error TSTL: Continue statement is/are not supported for target Lua 5.0."`;
103-
104-
exports[`loop continue (for (const a in {}) { continue; }) [5.1]: code 1`] = `
105-
"for a in pairs({}) do
106-
do
107-
goto __continue2
108-
end
109-
::__continue2::
110-
end"
111-
`;
112-
113-
exports[`loop continue (for (const a in {}) { continue; }) [5.1]: diagnostics 1`] = `"main.ts(1,23): error TSTL: Continue statement is/are not supported for target Lua 5.1."`;
114-
115-
exports[`loop continue (for (const a in {}) { continue; }) [universal]: code 1`] = `
116-
"for a in pairs({}) do
117-
do
118-
goto __continue2
119-
end
120-
::__continue2::
121-
end"
122-
`;
123-
124-
exports[`loop continue (for (const a in {}) { continue; }) [universal]: diagnostics 1`] = `"main.ts(1,23): error TSTL: Continue statement is/are not supported for target Lua universal."`;
125-
126-
exports[`loop continue (for (const a of []) { continue; }) [5.0]: code 1`] = `
127-
"for ____, a in ipairs({}) do
128-
do
129-
goto __continue2
130-
end
131-
::__continue2::
132-
end"
133-
`;
134-
135-
exports[`loop continue (for (const a of []) { continue; }) [5.0]: diagnostics 1`] = `"main.ts(1,23): error TSTL: Continue statement is/are not supported for target Lua 5.0."`;
136-
137-
exports[`loop continue (for (const a of []) { continue; }) [5.1]: code 1`] = `
138-
"for ____, a in ipairs({}) do
139-
do
140-
goto __continue2
141-
end
142-
::__continue2::
143-
end"
144-
`;
145-
146-
exports[`loop continue (for (const a of []) { continue; }) [5.1]: diagnostics 1`] = `"main.ts(1,23): error TSTL: Continue statement is/are not supported for target Lua 5.1."`;
147-
148-
exports[`loop continue (for (const a of []) { continue; }) [universal]: code 1`] = `
149-
"for ____, a in ipairs({}) do
150-
do
151-
goto __continue2
152-
end
153-
::__continue2::
154-
end"
155-
`;
156-
157-
exports[`loop continue (for (const a of []) { continue; }) [universal]: diagnostics 1`] = `"main.ts(1,23): error TSTL: Continue statement is/are not supported for target Lua universal."`;
158-
159-
exports[`loop continue (while (false) { continue; }) [5.0]: code 1`] = `
160-
"while false do
161-
do
162-
goto __continue2
163-
end
164-
::__continue2::
165-
end"
166-
`;
167-
168-
exports[`loop continue (while (false) { continue; }) [5.0]: diagnostics 1`] = `"main.ts(1,17): error TSTL: Continue statement is/are not supported for target Lua 5.0."`;
169-
170-
exports[`loop continue (while (false) { continue; }) [5.1]: code 1`] = `
171-
"while false do
172-
do
173-
goto __continue2
174-
end
175-
::__continue2::
176-
end"
177-
`;
178-
179-
exports[`loop continue (while (false) { continue; }) [5.1]: diagnostics 1`] = `"main.ts(1,17): error TSTL: Continue statement is/are not supported for target Lua 5.1."`;
180-
181-
exports[`loop continue (while (false) { continue; }) [universal]: code 1`] = `
182-
"while false do
183-
do
184-
goto __continue2
185-
end
186-
::__continue2::
187-
end"
188-
`;
189-
190-
exports[`loop continue (while (false) { continue; }) [universal]: diagnostics 1`] = `"main.ts(1,17): error TSTL: Continue statement is/are not supported for target Lua universal."`;

0 commit comments

Comments
 (0)