Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Roblox
GitHub Repository: Roblox/luau
Path: blob/master/tests/Lexer.test.cpp
2723 views
1
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
2
#include "Luau/Lexer.h"
3
4
#include "Fixture.h"
5
#include "ScopedFlags.h"
6
7
#include "doctest.h"
8
9
using namespace Luau;
10
11
TEST_SUITE_BEGIN("LexerTests");
12
13
TEST_CASE("broken_string_works")
14
{
15
const std::string testInput = "[[";
16
Luau::Allocator alloc;
17
AstNameTable table(alloc);
18
Lexer lexer(testInput.c_str(), testInput.size(), table);
19
Lexeme lexeme = lexer.next();
20
CHECK_EQ(lexeme.type, Lexeme::Type::BrokenString);
21
CHECK_EQ(lexeme.location, Luau::Location(Luau::Position(0, 0), Luau::Position(0, 2)));
22
}
23
24
TEST_CASE("broken_comment")
25
{
26
const std::string testInput = "--[[ ";
27
Luau::Allocator alloc;
28
AstNameTable table(alloc);
29
Lexer lexer(testInput.c_str(), testInput.size(), table);
30
Lexeme lexeme = lexer.next();
31
CHECK_EQ(lexeme.type, Lexeme::Type::BrokenComment);
32
CHECK_EQ(lexeme.location, Luau::Location(Luau::Position(0, 0), Luau::Position(0, 6)));
33
}
34
35
TEST_CASE("broken_comment_kept")
36
{
37
const std::string testInput = "--[[ ";
38
Luau::Allocator alloc;
39
AstNameTable table(alloc);
40
Lexer lexer(testInput.c_str(), testInput.size(), table);
41
lexer.setSkipComments(true);
42
CHECK_EQ(lexer.next().type, Lexeme::Type::BrokenComment);
43
}
44
45
TEST_CASE("comment_skipped")
46
{
47
const std::string testInput = "-- ";
48
Luau::Allocator alloc;
49
AstNameTable table(alloc);
50
Lexer lexer(testInput.c_str(), testInput.size(), table);
51
lexer.setSkipComments(true);
52
CHECK_EQ(lexer.next().type, Lexeme::Type::Eof);
53
}
54
55
TEST_CASE("multilineCommentWithLexemeInAndAfter")
56
{
57
const std::string testInput = "--[[ function \n"
58
"]] end";
59
Luau::Allocator alloc;
60
AstNameTable table(alloc);
61
Lexer lexer(testInput.c_str(), testInput.size(), table);
62
Lexeme comment = lexer.next();
63
Lexeme end = lexer.next();
64
65
CHECK_EQ(comment.type, Lexeme::Type::BlockComment);
66
CHECK_EQ(comment.location, Luau::Location(Luau::Position(0, 0), Luau::Position(1, 2)));
67
CHECK_EQ(end.type, Lexeme::Type::ReservedEnd);
68
CHECK_EQ(end.location, Luau::Location(Luau::Position(1, 3), Luau::Position(1, 6)));
69
}
70
71
TEST_CASE("testBrokenEscapeTolerant")
72
{
73
const std::string testInput = "'\\3729472897292378'";
74
Luau::Allocator alloc;
75
AstNameTable table(alloc);
76
Lexer lexer(testInput.c_str(), testInput.size(), table);
77
Lexeme item = lexer.next();
78
79
CHECK_EQ(item.type, Lexeme::QuotedString);
80
CHECK_EQ(item.location, Luau::Location(Luau::Position(0, 0), Luau::Position(0, int(testInput.size()))));
81
}
82
83
TEST_CASE("testBigDelimiters")
84
{
85
const std::string testInput = "--[===[\n"
86
"\n"
87
"\n"
88
"\n"
89
"]===]";
90
Luau::Allocator alloc;
91
AstNameTable table(alloc);
92
Lexer lexer(testInput.c_str(), testInput.size(), table);
93
Lexeme item = lexer.next();
94
95
CHECK_EQ(item.type, Lexeme::Type::BlockComment);
96
CHECK_EQ(item.location, Luau::Location(Luau::Position(0, 0), Luau::Position(4, 5)));
97
}
98
99
TEST_CASE("lookahead")
100
{
101
const std::string testInput = "foo --[[ comment ]] bar : nil end";
102
103
Luau::Allocator alloc;
104
AstNameTable table(alloc);
105
Lexer lexer(testInput.c_str(), testInput.size(), table);
106
lexer.setSkipComments(true);
107
lexer.next(); // must call next() before reading data from lexer at least once
108
109
CHECK_EQ(lexer.current().type, Lexeme::Name);
110
CHECK_EQ(lexer.current().name, std::string("foo"));
111
CHECK_EQ(lexer.lookahead().type, Lexeme::Name);
112
CHECK_EQ(lexer.lookahead().name, std::string("bar"));
113
114
lexer.next();
115
116
CHECK_EQ(lexer.current().type, Lexeme::Name);
117
CHECK_EQ(lexer.current().name, std::string("bar"));
118
CHECK_EQ(lexer.lookahead().type, ':');
119
120
lexer.next();
121
122
CHECK_EQ(lexer.current().type, ':');
123
CHECK_EQ(lexer.lookahead().type, Lexeme::ReservedNil);
124
125
lexer.next();
126
127
CHECK_EQ(lexer.current().type, Lexeme::ReservedNil);
128
CHECK_EQ(lexer.lookahead().type, Lexeme::ReservedEnd);
129
130
lexer.next();
131
132
CHECK_EQ(lexer.current().type, Lexeme::ReservedEnd);
133
CHECK_EQ(lexer.lookahead().type, Lexeme::Eof);
134
135
lexer.next();
136
137
CHECK_EQ(lexer.current().type, Lexeme::Eof);
138
CHECK_EQ(lexer.lookahead().type, Lexeme::Eof);
139
}
140
141
TEST_CASE("string_interpolation_basic")
142
{
143
const std::string testInput = R"(`foo {"bar"}`)";
144
Luau::Allocator alloc;
145
AstNameTable table(alloc);
146
Lexer lexer(testInput.c_str(), testInput.size(), table);
147
148
Lexeme interpBegin = lexer.next();
149
CHECK_EQ(interpBegin.type, Lexeme::InterpStringBegin);
150
151
Lexeme quote = lexer.next();
152
CHECK_EQ(quote.type, Lexeme::QuotedString);
153
154
Lexeme interpEnd = lexer.next();
155
CHECK_EQ(interpEnd.type, Lexeme::InterpStringEnd);
156
// The InterpStringEnd should start with }, not `.
157
CHECK_EQ(interpEnd.location.begin.column, 11);
158
}
159
160
TEST_CASE("string_interpolation_full")
161
{
162
const std::string testInput = R"(`foo {"bar"} {"baz"} end`)";
163
Luau::Allocator alloc;
164
AstNameTable table(alloc);
165
Lexer lexer(testInput.c_str(), testInput.size(), table);
166
167
Lexeme interpBegin = lexer.next();
168
CHECK_EQ(interpBegin.type, Lexeme::InterpStringBegin);
169
CHECK_EQ(interpBegin.toString(), "`foo {");
170
171
Lexeme quote1 = lexer.next();
172
CHECK_EQ(quote1.type, Lexeme::QuotedString);
173
CHECK_EQ(quote1.toString(), "\"bar\"");
174
175
Lexeme interpMid = lexer.next();
176
CHECK_EQ(interpMid.type, Lexeme::InterpStringMid);
177
CHECK_EQ(interpMid.toString(), "} {");
178
CHECK_EQ(interpMid.location.begin.column, 11);
179
180
Lexeme quote2 = lexer.next();
181
CHECK_EQ(quote2.type, Lexeme::QuotedString);
182
CHECK_EQ(quote2.toString(), "\"baz\"");
183
184
Lexeme interpEnd = lexer.next();
185
CHECK_EQ(interpEnd.type, Lexeme::InterpStringEnd);
186
CHECK_EQ(interpEnd.toString(), "} end`");
187
CHECK_EQ(interpEnd.location.begin.column, 19);
188
}
189
190
TEST_CASE("string_interpolation_double_brace")
191
{
192
const std::string testInput = R"(`foo{{bad}}bar`)";
193
Luau::Allocator alloc;
194
AstNameTable table(alloc);
195
Lexer lexer(testInput.c_str(), testInput.size(), table);
196
197
auto brokenInterpBegin = lexer.next();
198
CHECK_EQ(brokenInterpBegin.type, Lexeme::BrokenInterpDoubleBrace);
199
CHECK_EQ(std::string(brokenInterpBegin.data, brokenInterpBegin.getLength()), std::string("foo"));
200
201
CHECK_EQ(lexer.next().type, Lexeme::Name);
202
203
auto interpEnd = lexer.next();
204
CHECK_EQ(interpEnd.type, Lexeme::InterpStringEnd);
205
CHECK_EQ(std::string(interpEnd.data, interpEnd.getLength()), std::string("}bar"));
206
}
207
208
TEST_CASE("string_interpolation_double_but_unmatched_brace")
209
{
210
const std::string testInput = R"(`{{oops}`, 1)";
211
Luau::Allocator alloc;
212
AstNameTable table(alloc);
213
Lexer lexer(testInput.c_str(), testInput.size(), table);
214
215
CHECK_EQ(lexer.next().type, Lexeme::BrokenInterpDoubleBrace);
216
CHECK_EQ(lexer.next().type, Lexeme::Name);
217
CHECK_EQ(lexer.next().type, Lexeme::InterpStringEnd);
218
CHECK_EQ(lexer.next().type, ',');
219
CHECK_EQ(lexer.next().type, Lexeme::Number);
220
}
221
222
TEST_CASE("string_interpolation_unmatched_brace")
223
{
224
const std::string testInput = R"({
225
`hello {"world"}
226
} -- this might be incorrectly parsed as a string)";
227
Luau::Allocator alloc;
228
AstNameTable table(alloc);
229
Lexer lexer(testInput.c_str(), testInput.size(), table);
230
231
CHECK_EQ(lexer.next().type, '{');
232
CHECK_EQ(lexer.next().type, Lexeme::InterpStringBegin);
233
CHECK_EQ(lexer.next().type, Lexeme::QuotedString);
234
CHECK_EQ(lexer.next().type, Lexeme::BrokenString);
235
CHECK_EQ(lexer.next().type, '}');
236
}
237
238
TEST_CASE("string_interpolation_with_unicode_escape")
239
{
240
const std::string testInput = R"(`\u{1F41B}`)";
241
Luau::Allocator alloc;
242
AstNameTable table(alloc);
243
Lexer lexer(testInput.c_str(), testInput.size(), table);
244
245
CHECK_EQ(lexer.next().type, Lexeme::InterpStringSimple);
246
CHECK_EQ(lexer.next().type, Lexeme::Eof);
247
}
248
249
TEST_CASE("single_quoted_string")
250
{
251
const std::string testInput = "'test'";
252
Luau::Allocator alloc;
253
AstNameTable table(alloc);
254
Lexer lexer(testInput.c_str(), testInput.size(), table);
255
256
Lexeme lexeme = lexer.next();
257
CHECK_EQ(lexeme.type, Lexeme::QuotedString);
258
CHECK_EQ(lexeme.getQuoteStyle(), Lexeme::QuoteStyle::Single);
259
}
260
261
TEST_CASE("double_quoted_string")
262
{
263
const std::string testInput = R"("test")";
264
Luau::Allocator alloc;
265
AstNameTable table(alloc);
266
Lexer lexer(testInput.c_str(), testInput.size(), table);
267
268
Lexeme lexeme = lexer.next();
269
CHECK_EQ(lexeme.type, Lexeme::QuotedString);
270
CHECK_EQ(lexeme.getQuoteStyle(), Lexeme::QuoteStyle::Double);
271
}
272
273
TEST_CASE("lexer_determines_string_block_depth_0")
274
{
275
const std::string testInput = "[[ test ]]";
276
Luau::Allocator alloc;
277
AstNameTable table(alloc);
278
Lexer lexer(testInput.c_str(), testInput.size(), table);
279
280
Lexeme lexeme = lexer.next();
281
REQUIRE_EQ(lexeme.type, Lexeme::RawString);
282
CHECK_EQ(lexeme.getBlockDepth(), 0);
283
}
284
285
TEST_CASE("lexer_determines_string_block_depth_0_multiline_1")
286
{
287
const std::string testInput = R"([[ test
288
]])";
289
290
Luau::Allocator alloc;
291
AstNameTable table(alloc);
292
Lexer lexer(testInput.c_str(), testInput.size(), table);
293
294
Lexeme lexeme = lexer.next();
295
REQUIRE_EQ(lexeme.type, Lexeme::RawString);
296
CHECK_EQ(lexeme.getBlockDepth(), 0);
297
}
298
299
TEST_CASE("lexer_determines_string_block_depth_0_multiline_2")
300
{
301
const std::string testInput = R"([[
302
test
303
]])";
304
305
Luau::Allocator alloc;
306
AstNameTable table(alloc);
307
Lexer lexer(testInput.c_str(), testInput.size(), table);
308
309
Lexeme lexeme = lexer.next();
310
REQUIRE_EQ(lexeme.type, Lexeme::RawString);
311
CHECK_EQ(lexeme.getBlockDepth(), 0);
312
}
313
314
TEST_CASE("lexer_determines_string_block_depth_0_multiline_3")
315
{
316
const std::string testInput = R"([[
317
test ]])";
318
319
Luau::Allocator alloc;
320
AstNameTable table(alloc);
321
Lexer lexer(testInput.c_str(), testInput.size(), table);
322
323
Lexeme lexeme = lexer.next();
324
REQUIRE_EQ(lexeme.type, Lexeme::RawString);
325
CHECK_EQ(lexeme.getBlockDepth(), 0);
326
}
327
328
TEST_CASE("lexer_determines_string_block_depth_1")
329
{
330
const std::string testInput = "[=[[%s]]=]";
331
Luau::Allocator alloc;
332
AstNameTable table(alloc);
333
Lexer lexer(testInput.c_str(), testInput.size(), table);
334
335
Lexeme lexeme = lexer.next();
336
REQUIRE_EQ(lexeme.type, Lexeme::RawString);
337
CHECK_EQ(lexeme.getBlockDepth(), 1);
338
}
339
340
TEST_CASE("lexer_determines_string_block_depth_2")
341
{
342
const std::string testInput = "[==[ test ]==]";
343
Luau::Allocator alloc;
344
AstNameTable table(alloc);
345
Lexer lexer(testInput.c_str(), testInput.size(), table);
346
347
Lexeme lexeme = lexer.next();
348
REQUIRE_EQ(lexeme.type, Lexeme::RawString);
349
CHECK_EQ(lexeme.getBlockDepth(), 2);
350
}
351
352
TEST_CASE("lexer_determines_string_block_depth_2_multiline_1")
353
{
354
const std::string testInput = R"([==[ test
355
]==])";
356
Luau::Allocator alloc;
357
AstNameTable table(alloc);
358
Lexer lexer(testInput.c_str(), testInput.size(), table);
359
360
Lexeme lexeme = lexer.next();
361
REQUIRE_EQ(lexeme.type, Lexeme::RawString);
362
CHECK_EQ(lexeme.getBlockDepth(), 2);
363
}
364
365
TEST_CASE("lexer_determines_string_block_depth_2_multiline_2")
366
{
367
const std::string testInput = R"([==[
368
test
369
]==])";
370
Luau::Allocator alloc;
371
AstNameTable table(alloc);
372
Lexer lexer(testInput.c_str(), testInput.size(), table);
373
374
Lexeme lexeme = lexer.next();
375
REQUIRE_EQ(lexeme.type, Lexeme::RawString);
376
CHECK_EQ(lexeme.getBlockDepth(), 2);
377
}
378
379
TEST_CASE("lexer_determines_string_block_depth_2_multiline_3")
380
{
381
const std::string testInput = R"([==[
382
383
test ]==])";
384
Luau::Allocator alloc;
385
AstNameTable table(alloc);
386
Lexer lexer(testInput.c_str(), testInput.size(), table);
387
388
Lexeme lexeme = lexer.next();
389
REQUIRE_EQ(lexeme.type, Lexeme::RawString);
390
CHECK_EQ(lexeme.getBlockDepth(), 2);
391
}
392
393
394
TEST_CASE("lexer_determines_comment_block_depth_0")
395
{
396
const std::string testInput = "--[[ test ]]";
397
Luau::Allocator alloc;
398
AstNameTable table(alloc);
399
Lexer lexer(testInput.c_str(), testInput.size(), table);
400
401
Lexeme lexeme = lexer.next();
402
REQUIRE_EQ(lexeme.type, Lexeme::BlockComment);
403
CHECK_EQ(lexeme.getBlockDepth(), 0);
404
}
405
406
TEST_CASE("lexer_determines_string_block_depth_1")
407
{
408
const std::string testInput = "--[=[ μέλλον ]=]";
409
Luau::Allocator alloc;
410
AstNameTable table(alloc);
411
Lexer lexer(testInput.c_str(), testInput.size(), table);
412
413
Lexeme lexeme = lexer.next();
414
REQUIRE_EQ(lexeme.type, Lexeme::BlockComment);
415
CHECK_EQ(lexeme.getBlockDepth(), 1);
416
}
417
418
TEST_CASE("lexer_determines_string_block_depth_2")
419
{
420
const std::string testInput = "--[==[ test ]==]";
421
Luau::Allocator alloc;
422
AstNameTable table(alloc);
423
Lexer lexer(testInput.c_str(), testInput.size(), table);
424
425
Lexeme lexeme = lexer.next();
426
REQUIRE_EQ(lexeme.type, Lexeme::BlockComment);
427
CHECK_EQ(lexeme.getBlockDepth(), 2);
428
}
429
430
TEST_SUITE_END();
431
432