Skip to content

Commit 942a04a

Browse files
CST: Handle for loops (#223)
This PR extends the CST support to include all the necessary tokens in numeric (`for i = 1, 10, 2 do`) and for-in (`for ... in ... do`) loops. As mentioned previously, a future change will standardise the AST property names to switch away from using keywords
1 parent 54fa59f commit 942a04a

File tree

6 files changed

+143
-19
lines changed

6 files changed

+143
-19
lines changed

batteries/syntax/ast_types.luau

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,31 @@ export type AstStatLocal = {
226226
values: Punctuated<AstExpr>,
227227
}
228228

229+
export type AstStatFor = {
230+
tag: "for",
231+
["for"]: Token<"for">,
232+
variable: Token<string>,
233+
equals: Token<"=">,
234+
from: AstExpr,
235+
toComma: Token<",">,
236+
to: AstExpr,
237+
stepComma: Token<",">?,
238+
step: AstExpr?,
239+
["do"]: Token<"do">,
240+
body: AstStatBlock,
241+
["end"]: Token<"end">,
242+
}
243+
244+
export type AstStatForIn = {
245+
tag: "forin",
246+
["for"]: Token<"for">,
247+
variables: Punctuated<Token<string>>,
248+
["in"]: Token<"in">,
249+
values: Punctuated<Token<string>>,
250+
["do"]: Token<"do">,
251+
body: AstStatBlock,
252+
["end"]: Token<"end">,
253+
}
229254
export type AstStat =
230255
| AstStatBlock
231256
| AstStatIf
@@ -236,5 +261,7 @@ export type AstStat =
236261
| AstStatReturn
237262
| AstStatExpr
238263
| AstStatLocal
264+
| AstStatFor
265+
| AstStatForIn
239266

240267
return {}

batteries/syntax/visitor.luau

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ type Visitor = {
99
visitRepeat: (T.AstStatRepeat) -> boolean,
1010
visitReturn: (T.AstStatReturn) -> boolean,
1111
visitLocalDeclaration: (T.AstStatLocal) -> boolean,
12+
visitFor: (T.AstStatFor) -> boolean,
13+
visitForIn: (T.AstStatForIn) -> boolean,
1214

1315
visitLocalReference: (T.AstExprLocal) -> boolean,
1416
visitGlobal: (T.AstExprGlobal) -> boolean,
@@ -42,6 +44,8 @@ local defaultVisitor: Visitor = {
4244
visitRepeat = alwaysVisit :: any,
4345
visitReturn = alwaysVisit :: any,
4446
visitLocalDeclaration = alwaysVisit :: any,
47+
visitFor = alwaysVisit :: any,
48+
visitForIn = alwaysVisit :: any,
4549

4650
visitLocalReference = alwaysVisit :: any,
4751
visitGlobal = alwaysVisit :: any,
@@ -149,6 +153,38 @@ local function visitLocalStatement(node: T.AstStatLocal, visitor: Visitor)
149153
end
150154
end
151155

156+
local function visitFor(node: T.AstStatFor, visitor: Visitor)
157+
if visitor.visitFor(node) then
158+
visitToken(node["for"], visitor)
159+
visitLocal(node.variable, visitor)
160+
visitToken(node.equals, visitor)
161+
visitExpression(node.from, visitor)
162+
visitToken(node.toComma, visitor)
163+
visitExpression(node.to, visitor)
164+
if node.stepComma then
165+
visitToken(node.stepComma, visitor)
166+
end
167+
if node.step then
168+
visitExpression(node.step, visitor)
169+
end
170+
visitToken(node["do"], visitor)
171+
visitBlock(node.body, visitor)
172+
visitToken(node["end"], visitor)
173+
end
174+
end
175+
176+
local function visitForIn(node: T.AstStatForIn, visitor: Visitor)
177+
if visitor.visitForIn(node) then
178+
visitToken(node["for"], visitor)
179+
visitPunctuated(node.variables, visitor, visitLocal)
180+
visitToken(node["in"], visitor)
181+
visitPunctuated(node.values, visitor, visitExpression)
182+
visitToken(node["do"], visitor)
183+
visitBlock(node.body, visitor)
184+
visitToken(node["end"], visitor)
185+
end
186+
end
187+
152188
local function visitString(node: T.AstExprConstantString, visitor: Visitor)
153189
if visitor.visitString(node) then
154190
visitor.visitToken(node)
@@ -348,6 +384,10 @@ function visitStatement(statement: T.AstStat, visitor: Visitor)
348384
visitToken(statement, visitor)
349385
elseif statement.tag == "repeat" then
350386
visitRepeat(statement, visitor)
387+
elseif statement.tag == "for" then
388+
visitFor(statement, visitor)
389+
elseif statement.tag == "forin" then
390+
visitForIn(statement, visitor)
351391
else
352392
exhaustiveMatch(statement.tag)
353393
end

luau/src/luau.cpp

Lines changed: 56 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1146,59 +1146,96 @@ struct AstSerialize : public Luau::AstVisitor
11461146
void serializeStat(Luau::AstStatFor* node)
11471147
{
11481148
lua_rawcheckstack(L, 2);
1149-
lua_createtable(L, 0, preambleSize + 6);
1149+
lua_createtable(L, 0, preambleSize + 11);
1150+
1151+
const auto cstNode = lookupCstNode<Luau::CstStatFor>(node);
11501152

11511153
serializeNodePreamble(node, "for");
11521154

1155+
serializeToken(node->location.begin, "for");
1156+
lua_setfield(L, -2, "for");
1157+
11531158
serialize(node->var);
11541159
lua_setfield(L, -2, "variable");
11551160

1161+
if (cstNode)
1162+
{
1163+
serializeToken(cstNode->equalsPosition, "=");
1164+
lua_setfield(L, -2, "equals");
1165+
}
1166+
11561167
node->from->visit(this);
11571168
lua_setfield(L, -2, "from");
11581169

1170+
if (cstNode)
1171+
{
1172+
serializeToken(cstNode->endCommaPosition, ",");
1173+
lua_setfield(L, -2, "toComma");
1174+
}
1175+
11591176
node->to->visit(this);
11601177
lua_setfield(L, -2, "to");
11611178

1162-
node->step->visit(this);
1163-
lua_setfield(L, -2, "step");
1179+
if (cstNode && cstNode->stepCommaPosition)
1180+
{
1181+
serializeToken(*cstNode->stepCommaPosition, ",");
1182+
lua_setfield(L, -2, "stepComma");
1183+
}
11641184

1165-
node->body->visit(this);
1166-
lua_setfield(L, -2, "body");
1185+
if (node->step)
1186+
node->step->visit(this);
1187+
else
1188+
lua_pushnil(L);
1189+
lua_setfield(L, -2, "step");
11671190

11681191
if (node->hasDo)
1169-
serialize(node->doLocation);
1192+
serializeToken(node->doLocation.begin, "do");
11701193
else
11711194
lua_pushnil(L);
1172-
lua_setfield(L, -2, "doLocation");
1195+
lua_setfield(L, -2, "do");
1196+
1197+
node->body->visit(this);
1198+
lua_setfield(L, -2, "body");
1199+
1200+
serializeToken(node->body->location.end, "end");
1201+
lua_setfield(L, -2, "end");
11731202
}
11741203

11751204
void serializeStat(Luau::AstStatForIn* node)
11761205
{
11771206
lua_rawcheckstack(L, 2);
1178-
lua_createtable(L, 0, preambleSize + 5);
1207+
lua_createtable(L, 0, preambleSize + 7);
11791208

1180-
serializeNodePreamble(node, "forin");
1209+
const auto cstNode = lookupCstNode<Luau::CstStatForIn>(node);
11811210

1182-
serializeLocals(node->vars);
1183-
lua_setfield(L, -2, "variables");
1211+
serializeNodePreamble(node, "forin");
11841212

1185-
serializeExprs(node->values);
1186-
lua_setfield(L, -2, "values");
1213+
serializeToken(node->location.begin, "for");
1214+
lua_setfield(L, -2, "for");
11871215

1188-
node->body->visit(this);
1189-
lua_setfield(L, -2, "body");
1216+
serializePunctuated(node->vars, cstNode ? cstNode->varsCommaPositions : Luau::AstArray<Luau::Position>{}, ",");
1217+
lua_setfield(L, -2, "variables");
11901218

11911219
if (node->hasIn)
1192-
serialize(node->inLocation);
1220+
serializeToken(node->inLocation.begin, "in");
11931221
else
11941222
lua_pushnil(L);
1195-
lua_setfield(L, -2, "inLocation");
1223+
lua_setfield(L, -2, "in");
1224+
1225+
serializePunctuated(node->values, cstNode ? cstNode->valuesCommaPositions : Luau::AstArray<Luau::Position>{}, ",");
1226+
lua_setfield(L, -2, "values");
11961227

11971228
if (node->hasDo)
1198-
serialize(node->doLocation);
1229+
serializeToken(node->doLocation.begin, "do");
11991230
else
12001231
lua_pushnil(L);
1201-
lua_setfield(L, -2, "doLocation");
1232+
lua_setfield(L, -2, "do");
1233+
1234+
node->body->visit(this);
1235+
lua_setfield(L, -2, "body");
1236+
1237+
serializeToken(node->body->location.end, "end");
1238+
lua_setfield(L, -2, "end");
12021239
}
12031240

12041241
void serializeStat(Luau::AstStatAssign* node)
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
for index, value in pairs(list) do
2+
call(index, value)
3+
end
4+
5+
for index, value in next, list do
6+
call(index, value)
7+
end
8+
9+
for index, value in list do
10+
call(index, value)
11+
end
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
for index = 1, 10 do
2+
call(index)
3+
end
4+
for _ = start, final do
5+
end
6+
for _ = 1, 10, 2 do
7+
end

tests/testAstSerializer.spec.luau

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,8 @@ local function test_roundtrippableAst()
130130
"examples/time_example.luau",
131131
"examples/writeFile.luau",
132132
"tests/astSerializerTests/break-continue-1.luau",
133+
"tests/astSerializerTests/generic-for-loop-1.luau",
134+
"tests/astSerializerTests/numeric-for-loop-1.luau",
133135
"tests/astSerializerTests/while-1.luau",
134136
"tests/astSerializerTests/repeat-until-1.luau",
135137
}

0 commit comments

Comments
 (0)