LLVM OpenMP
TestOMPTraitLexer.cpp
Go to the documentation of this file.
1//===- TestOMPTraitLexer.cpp - Tests for OMP Trait Lexer -----------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "kmp_traits.h"
10#include "gtest/gtest.h"
11
12#include <cstring>
13#include <string>
14
15using namespace lexer;
16
17namespace {
18
19//===----------------------------------------------------------------------===//
20// Helpers
21//===----------------------------------------------------------------------===//
22
23// Compare a token's text against a C string.
24static bool text_is(const token &tok, const char *expected) {
25 size_t len = strlen(expected);
26 if (tok.text.length() != len)
27 return false;
28 if (len == 0)
29 return true;
30 return memcmp(tok.text.begin(), expected, len) == 0;
31}
32
33// Lex the whole spec into a vector of tokens (excluding the trailing END).
34static kmp_vector<token> tokenize(const char *spec) {
35 kmp_lexer lex{kmp_str_ref(spec)};
36 kmp_vector<token> tokens;
37 for (token tok = lex.next(); tok.kind != token_kind::END; tok = lex.next())
38 tokens.push_back(tok);
39 return tokens;
40}
41
42// Assert a token has the expected kind and text.
43static void expect_token(const token &tok, token_kind kind, const char *text) {
44 EXPECT_EQ(tok.kind, kind);
45 EXPECT_TRUE(text_is(tok, text))
46 << "expected token text \"" << text << "\" but got \""
47 << std::string(tok.text.begin(), tok.text.length()) << "\"";
48}
49
50//===----------------------------------------------------------------------===//
51// Single Tokens
52//===----------------------------------------------------------------------===//
53
54TEST(LexerTest, EmptyInput) {
55 kmp_lexer lex{kmp_str_ref("")};
56 EXPECT_EQ(lex.next().kind, token_kind::END);
57}
58
59TEST(LexerTest, WhitespaceOnly) {
60 kmp_lexer lex{kmp_str_ref(" \t ")};
61 EXPECT_EQ(lex.next().kind, token_kind::END);
62}
63
64TEST(LexerTest, EndIsSticky) {
65 // Once the input is exhausted, next() keeps returning END.
66 kmp_lexer lex{kmp_str_ref("*")};
67 EXPECT_EQ(lex.next().kind, token_kind::STAR);
68 EXPECT_EQ(lex.next().kind, token_kind::END);
69 EXPECT_EQ(lex.next().kind, token_kind::END);
70}
71
72TEST(LexerTest, Punctuation) {
73 kmp_vector<token> tokens = tokenize(",*!()[]:");
74
75 ASSERT_EQ(tokens.size(), 8u);
76 expect_token(tokens[0], token_kind::COMMA, ",");
77 expect_token(tokens[1], token_kind::STAR, "*");
78 expect_token(tokens[2], token_kind::NOT, "!");
79 expect_token(tokens[3], token_kind::L_PAREN, "(");
80 expect_token(tokens[4], token_kind::R_PAREN, ")");
81 expect_token(tokens[5], token_kind::L_BRACKET, "[");
82 expect_token(tokens[6], token_kind::R_BRACKET, "]");
83 expect_token(tokens[7], token_kind::COLON, ":");
84}
85
86TEST(LexerTest, DoubleColon) {
87 // "::" is lexed as two COLON tokens.
88 kmp_vector<token> tokens = tokenize("::");
89
90 ASSERT_EQ(tokens.size(), 2u);
91 expect_token(tokens[0], token_kind::COLON, ":");
92 expect_token(tokens[1], token_kind::COLON, ":");
93}
94
95TEST(LexerTest, AndOperator) {
96 kmp_vector<token> tokens = tokenize("&&");
97
98 ASSERT_EQ(tokens.size(), 1u);
99 expect_token(tokens[0], token_kind::AND, "&&");
100}
101
102TEST(LexerTest, OrOperator) {
103 kmp_vector<token> tokens = tokenize("||");
104
105 ASSERT_EQ(tokens.size(), 1u);
106 expect_token(tokens[0], token_kind::OR, "||");
107}
108
109//===----------------------------------------------------------------------===//
110// Numbers
111//
112// The lexer does not have a dedicated number token: a run of digits is just a
113// WORD. Recognizing a device number is the parser's job (see consume_clause).
114//===----------------------------------------------------------------------===//
115
116TEST(LexerTest, SingleDigitNumber) {
117 kmp_vector<token> tokens = tokenize("0");
118
119 ASSERT_EQ(tokens.size(), 1u);
120 expect_token(tokens[0], token_kind::WORD, "0");
121}
122
123TEST(LexerTest, MultiDigitNumber) {
124 kmp_vector<token> tokens = tokenize("12345");
125
126 ASSERT_EQ(tokens.size(), 1u);
127 expect_token(tokens[0], token_kind::WORD, "12345");
128}
129
130TEST(LexerTest, NumberList) {
131 kmp_vector<token> tokens = tokenize("1,2,3");
132
133 ASSERT_EQ(tokens.size(), 5u);
134 expect_token(tokens[0], token_kind::WORD, "1");
135 expect_token(tokens[1], token_kind::COMMA, ",");
136 expect_token(tokens[2], token_kind::WORD, "2");
137 expect_token(tokens[3], token_kind::COMMA, ",");
138 expect_token(tokens[4], token_kind::WORD, "3");
139}
140
141//===----------------------------------------------------------------------===//
142// Words
143//===----------------------------------------------------------------------===//
144
145TEST(LexerTest, SimpleWord) {
146 kmp_vector<token> tokens = tokenize("uid");
147
148 ASSERT_EQ(tokens.size(), 1u);
149 expect_token(tokens[0], token_kind::WORD, "uid");
150}
151
152TEST(LexerTest, WordWithDash) {
153 // A uid_value may contain '-'; it stays part of the word.
154 kmp_vector<token> tokens = tokenize("device-0");
155
156 ASSERT_EQ(tokens.size(), 1u);
157 expect_token(tokens[0], token_kind::WORD, "device-0");
158}
159
160TEST(LexerTest, WordWithUnderscore) {
161 kmp_vector<token> tokens = tokenize("my_device_123");
162
163 ASSERT_EQ(tokens.size(), 1u);
164 expect_token(tokens[0], token_kind::WORD, "my_device_123");
165}
166
167TEST(LexerTest, LeadingMinusIsWord) {
168 // The grammar allows a leading "-" for device numbers, but since '-' is also
169 // a uid_value symbol the lexer keeps the contiguous run together as a single
170 // WORD. Distinguishing a negative device number is left to the parser.
171 kmp_vector<token> tokens = tokenize("-5");
172
173 ASSERT_EQ(tokens.size(), 1u);
174 expect_token(tokens[0], token_kind::WORD, "-5");
175}
176
177TEST(LexerTest, WhitespaceBreaksWords) {
178 // A uid_value may not contain whitespace, so spaces split the run.
179 kmp_vector<token> tokens = tokenize("device - 0");
180
181 ASSERT_EQ(tokens.size(), 3u);
182 expect_token(tokens[0], token_kind::WORD, "device");
183 expect_token(tokens[1], token_kind::WORD, "-");
184 expect_token(tokens[2], token_kind::WORD, "0");
185}
186
187//===----------------------------------------------------------------------===//
188// Full trait expressions
189//===----------------------------------------------------------------------===//
190
191TEST(LexerTest, UidTrait) {
192 kmp_vector<token> tokens = tokenize("uid(device-0)");
193
194 ASSERT_EQ(tokens.size(), 4u);
195 expect_token(tokens[0], token_kind::WORD, "uid");
196 expect_token(tokens[1], token_kind::L_PAREN, "(");
197 expect_token(tokens[2], token_kind::WORD, "device-0");
198 expect_token(tokens[3], token_kind::R_PAREN, ")");
199}
200
201TEST(LexerTest, NegatedUidTrait) {
202 kmp_vector<token> tokens = tokenize("!uid(a)");
203
204 ASSERT_EQ(tokens.size(), 5u);
205 expect_token(tokens[0], token_kind::NOT, "!");
206 expect_token(tokens[1], token_kind::WORD, "uid");
207 expect_token(tokens[2], token_kind::L_PAREN, "(");
208 expect_token(tokens[3], token_kind::WORD, "a");
209 expect_token(tokens[4], token_kind::R_PAREN, ")");
210}
211
212TEST(LexerTest, AndGroup) {
213 kmp_vector<token> tokens = tokenize("(uid(a) && uid(b))");
214
215 ASSERT_EQ(tokens.size(), 11u);
216 expect_token(tokens[0], token_kind::L_PAREN, "(");
217 expect_token(tokens[1], token_kind::WORD, "uid");
218 expect_token(tokens[2], token_kind::L_PAREN, "(");
219 expect_token(tokens[3], token_kind::WORD, "a");
220 expect_token(tokens[4], token_kind::R_PAREN, ")");
221 expect_token(tokens[5], token_kind::AND, "&&");
222 expect_token(tokens[6], token_kind::WORD, "uid");
223 expect_token(tokens[7], token_kind::L_PAREN, "(");
224 expect_token(tokens[8], token_kind::WORD, "b");
225 expect_token(tokens[9], token_kind::R_PAREN, ")");
226 expect_token(tokens[10], token_kind::R_PAREN, ")");
227}
228
229TEST(LexerTest, OrGroup) {
230 kmp_vector<token> tokens = tokenize("uid(a)||uid(b)");
231
232 ASSERT_EQ(tokens.size(), 9u);
233 expect_token(tokens[0], token_kind::WORD, "uid");
234 expect_token(tokens[1], token_kind::L_PAREN, "(");
235 expect_token(tokens[2], token_kind::WORD, "a");
236 expect_token(tokens[3], token_kind::R_PAREN, ")");
237 expect_token(tokens[4], token_kind::OR, "||");
238 expect_token(tokens[5], token_kind::WORD, "uid");
239 expect_token(tokens[6], token_kind::L_PAREN, "(");
240 expect_token(tokens[7], token_kind::WORD, "b");
241 expect_token(tokens[8], token_kind::R_PAREN, ")");
242}
243
244TEST(LexerTest, WildcardWithLiterals) {
245 kmp_vector<token> tokens = tokenize("1, *, 3");
246
247 ASSERT_EQ(tokens.size(), 5u);
248 expect_token(tokens[0], token_kind::WORD, "1");
249 expect_token(tokens[1], token_kind::COMMA, ",");
250 expect_token(tokens[2], token_kind::STAR, "*");
251 expect_token(tokens[3], token_kind::COMMA, ",");
252 expect_token(tokens[4], token_kind::WORD, "3");
253}
254
255TEST(LexerTest, IndexExpression) {
256 kmp_vector<token> tokens = tokenize("[1:2:3]");
257
258 ASSERT_EQ(tokens.size(), 7u);
259 expect_token(tokens[0], token_kind::L_BRACKET, "[");
260 expect_token(tokens[1], token_kind::WORD, "1");
261 expect_token(tokens[2], token_kind::COLON, ":");
262 expect_token(tokens[3], token_kind::WORD, "2");
263 expect_token(tokens[4], token_kind::COLON, ":");
264 expect_token(tokens[5], token_kind::WORD, "3");
265 expect_token(tokens[6], token_kind::R_BRACKET, "]");
266}
267
268//===----------------------------------------------------------------------===//
269// Whitespace handling
270//===----------------------------------------------------------------------===//
271
272TEST(LexerTest, LeadingAndTrailingWhitespace) {
273 kmp_vector<token> tokens = tokenize(" uid ( a ) ");
274
275 ASSERT_EQ(tokens.size(), 4u);
276 expect_token(tokens[0], token_kind::WORD, "uid");
277 expect_token(tokens[1], token_kind::L_PAREN, "(");
278 expect_token(tokens[2], token_kind::WORD, "a");
279 expect_token(tokens[3], token_kind::R_PAREN, ")");
280}
281
282TEST(LexerTest, WhitespaceAroundOperators) {
283 kmp_vector<token> tokens = tokenize("uid(a) && uid(b)");
284
285 ASSERT_EQ(tokens.size(), 9u);
286 expect_token(tokens[4], token_kind::AND, "&&");
287}
288
289//===----------------------------------------------------------------------===//
290// Unknown / error characters
291//===----------------------------------------------------------------------===//
292
293TEST(LexerTest, UnknownCharacter) {
294 kmp_vector<token> tokens = tokenize("@");
295
296 ASSERT_EQ(tokens.size(), 1u);
297 expect_token(tokens[0], token_kind::UNKNOWN, "@");
298}
299
300TEST(LexerTest, LoneAmpersandIsUnknown) {
301 kmp_vector<token> tokens = tokenize("&");
302
303 ASSERT_EQ(tokens.size(), 1u);
304 expect_token(tokens[0], token_kind::UNKNOWN, "&");
305}
306
307TEST(LexerTest, LonePipeIsUnknown) {
308 kmp_vector<token> tokens = tokenize("|");
309
310 ASSERT_EQ(tokens.size(), 1u);
311 expect_token(tokens[0], token_kind::UNKNOWN, "|");
312}
313
314TEST(LexerTest, TripleAmpersand) {
315 // "&&&" is "&&" followed by a lone (unknown) '&'.
316 kmp_vector<token> tokens = tokenize("&&&");
317
318 ASSERT_EQ(tokens.size(), 2u);
319 expect_token(tokens[0], token_kind::AND, "&&");
320 expect_token(tokens[1], token_kind::UNKNOWN, "&");
321}
322
323//===----------------------------------------------------------------------===//
324// peek()
325//===----------------------------------------------------------------------===//
326
327TEST(LexerTest, PeekDoesNotAdvance) {
328 kmp_lexer lex{kmp_str_ref("uid(a)")};
329
330 // Peek repeatedly returns the same token without consuming it.
331 EXPECT_EQ(lex.peek().kind, token_kind::WORD);
332 EXPECT_TRUE(text_is(lex.peek(), "uid"));
333 EXPECT_EQ(lex.peek().kind, token_kind::WORD);
334
335 // next() returns the peeked token, then advances.
336 token tok = lex.next();
337 expect_token(tok, token_kind::WORD, "uid");
338 EXPECT_EQ(lex.peek().kind, token_kind::L_PAREN);
339 expect_token(lex.next(), token_kind::L_PAREN, "(");
340}
341
342TEST(LexerTest, PeekAtEnd) {
343 kmp_lexer lex{kmp_str_ref("")};
344
345 EXPECT_EQ(lex.peek().kind, token_kind::END);
346 EXPECT_EQ(lex.next().kind, token_kind::END);
347 EXPECT_EQ(lex.peek().kind, token_kind::END);
348}
349
350} // namespace
#define TEST(test_suite_name, test_name)
kmp_str_ref is a non-owning string class (similar to llvm::StringRef).
Definition kmp_adt.h:34
size_t length() const
Get the length of the string.
Definition kmp_adt.h:122
const char * begin() const
Iterator support (raw pointers work as iterators for contiguous storage)
Definition kmp_adt.h:141
kmp_vector is a vector class for managing small vectors.
Definition kmp_adt.h:148
size_t size() const
Definition kmp_adt.h:318
void push_back(const T &value)
Add a new element to the end of the vector.
Definition kmp_adt.h:302
kmp_str_ref text
Definition kmp_traits.h:43
token_kind kind
Definition kmp_traits.h:42