Skip to content

Commit 596db3c

Browse files
Attach trailing trivia to tokens (luau-lang#102)
1 parent 8e1aabc commit 596db3c

File tree

2 files changed

+69
-3
lines changed

2 files changed

+69
-3
lines changed

luau/src/luau.cpp

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,8 @@ struct AstSerialize : public Luau::AstVisitor
126126

127127
// absolute index for the table where we're storing locals
128128
int localTableIndex;
129+
// reference to previously serialized token
130+
int lastTokenRef = LUA_NOREF;
129131

130132
AstSerialize(lua_State* L, std::string_view source, Luau::CstNodeMap cstNodeMap, std::vector<Luau::Comment> commentLocations)
131133
: L(L)
@@ -255,6 +257,26 @@ struct AstSerialize : public Luau::AstVisitor
255257
return result;
256258
}
257259

260+
// Splits a list of trivia into trailing trivia for the previos token, and leading trivia for the next token
261+
// The trailing trivia consists of all trivia up to and including the first '\n' character seen
262+
static std::pair<std::vector<Trivia>, std::vector<Trivia>> splitTrivia(std::vector<Trivia> trivia)
263+
{
264+
size_t i = 0;
265+
for (i = 0; i < trivia.size(); i++)
266+
{
267+
if (trivia[i].kind == Trivia::Whitespace && trivia[i].text.find('\n') != std::string::npos)
268+
break;
269+
}
270+
271+
if (i == trivia.size())
272+
return {trivia, {}};
273+
274+
auto middleIter(trivia.begin());
275+
std::advance(middleIter, i + 1);
276+
277+
return {std::vector<Trivia>(trivia.begin(), middleIter), std::vector<Trivia>(middleIter, trivia.end())};
278+
}
279+
258280
void serialize(Luau::Position position)
259281
{
260282
lua_rawcheckstack(L, 2);
@@ -409,9 +431,27 @@ struct AstSerialize : public Luau::AstVisitor
409431
lua_rawcheckstack(L, 2);
410432
lua_createtable(L, 0, nrec + 3);
411433

412-
// TODO: split up into leading / trailing trivia
413-
const auto leadingTrivia = extractTrivia(position);
414-
serializeTrivia(leadingTrivia);
434+
const auto trivia = extractTrivia(position);
435+
if (lastTokenRef != LUA_NOREF)
436+
{
437+
const auto [trailingTrivia, leadingTrivia] = splitTrivia(trivia);
438+
439+
lua_getref(L, lastTokenRef);
440+
LUAU_ASSERT(lua_istable(L, -1));
441+
442+
serializeTrivia(trailingTrivia);
443+
lua_setfield(L, -2, "trailingTrivia");
444+
lua_pop(L, 1);
445+
lua_unref(L, lastTokenRef);
446+
lastTokenRef = LUA_NOREF;
447+
448+
serializeTrivia(leadingTrivia);
449+
}
450+
else
451+
{
452+
serializeTrivia(trivia);
453+
}
454+
LUAU_ASSERT(lua_istable(L, -2));
415455
lua_setfield(L, -2, "leadingTrivia");
416456

417457
serialize(position);
@@ -424,6 +464,8 @@ struct AstSerialize : public Luau::AstVisitor
424464
lua_rawcheckstack(L, 2);
425465
lua_createtable(L, 0, 0);
426466
lua_setfield(L, -2, "trailingTrivia");
467+
468+
lastTokenRef = lua_ref(L, -1);
427469
}
428470

429471
void serializeLocals(Luau::AstArray<Luau::AstLocal*>& locals, size_t nrec = 0)

tests/testAstSerializer.spec.luau

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,29 @@ local function test_tokenizeWhitespace()
9494
assert(token.leadingTrivia[3].text == "\n")
9595
end
9696

97+
local function test_triviaSplitBetweenLeadingAndTrailing()
98+
local block = luau.parse("local x = 'test' -- comment\n" .. "-- comment 2\nlocal y = 'value'").root
99+
assert(#block.statements == 2)
100+
101+
local firstStmt = block.statements[1]
102+
assert(firstStmt.tag == "local")
103+
104+
local trailingToken = firstStmt.values[1].node
105+
assert(trailingToken.tag == "string")
106+
assert(#trailingToken.trailingTrivia == 3)
107+
assert(trailingToken.trailingTrivia[1].text == " ")
108+
assert(trailingToken.trailingTrivia[2].text == "-- comment")
109+
assert(trailingToken.trailingTrivia[3].text == "\n")
110+
111+
local secondStmt = block.statements[2]
112+
assert(secondStmt.tag == "local")
113+
114+
local leadingToken = secondStmt["local"]
115+
assert(#leadingToken.leadingTrivia == 2)
116+
assert(leadingToken.leadingTrivia[1].text == "-- comment 2")
117+
assert(leadingToken.leadingTrivia[2].text == "\n")
118+
end
119+
97120
local function test_roundtrippableAst()
98121
local files = {
99122
"examples/a.luau",
@@ -116,4 +139,5 @@ test_tokenContainsLeadingNewline()
116139
test_tokenContainsLeadingSingleLineComment()
117140
test_tokenContainsLeadingBlockComment()
118141
test_tokenizeWhitespace()
142+
test_triviaSplitBetweenLeadingAndTrailing()
119143
test_roundtrippableAst()

0 commit comments

Comments
 (0)