Branch data Line data Source code
1 : : // Copyright (C) 2020-2025 Free Software Foundation, Inc.
2 : :
3 : : // This file is part of GCC.
4 : :
5 : : // GCC is free software; you can redistribute it and/or modify it under
6 : : // the terms of the GNU General Public License as published by the Free
7 : : // Software Foundation; either version 3, or (at your option) any later
8 : : // version.
9 : :
10 : : // GCC is distributed in the hope that it will be useful, but WITHOUT ANY
11 : : // WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 : : // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 : : // for more details.
14 : :
15 : : // You should have received a copy of the GNU General Public License
16 : : // along with GCC; see the file COPYING3. If not see
17 : : // <http://www.gnu.org/licenses/>.
18 : :
19 : : #include "rust-codepoint.h"
20 : : #include "rust-system.h"
21 : : #include "rust-lex.h"
22 : : #include "rust-diagnostics.h"
23 : : #include "rust-linemap.h"
24 : : #include "rust-edition.h"
25 : : #include "safe-ctype.h"
26 : : #include "cpplib.h"
27 : : #include "rust-keyword-values.h"
28 : :
29 : : namespace Rust {
30 : : // TODO: move to separate compilation unit?
31 : : // overload += for uint32_t to allow 32-bit encoded utf-8 to be added
32 : : std::string &
33 : 2747805 : operator+= (std::string &str, Codepoint char32)
34 : : {
35 : 2747805 : if (char32.value < 0x80)
36 : : {
37 : 2746806 : str += static_cast<char> (char32.value);
38 : : }
39 : 999 : else if (char32.value < (0x1F + 1) << (1 * 6))
40 : : {
41 : 674 : str += static_cast<char> (0xC0 | ((char32.value >> 6) & 0x1F));
42 : 674 : str += static_cast<char> (0x80 | ((char32.value >> 0) & 0x3F));
43 : : }
44 : 325 : else if (char32.value < (0x0F + 1) << (2 * 6))
45 : : {
46 : 315 : str += static_cast<char> (0xE0 | ((char32.value >> 12) & 0x0F));
47 : 315 : str += static_cast<char> (0x80 | ((char32.value >> 6) & 0x3F));
48 : 315 : str += static_cast<char> (0x80 | ((char32.value >> 0) & 0x3F));
49 : : }
50 : 10 : else if (char32.value < (0x07 + 1) << (3 * 6))
51 : : {
52 : 6 : str += static_cast<char> (0xF0 | ((char32.value >> 18) & 0x07));
53 : 6 : str += static_cast<char> (0x80 | ((char32.value >> 12) & 0x3F));
54 : 6 : str += static_cast<char> (0x80 | ((char32.value >> 6) & 0x3F));
55 : 6 : str += static_cast<char> (0x80 | ((char32.value >> 0) & 0x3F));
56 : : }
57 : : else
58 : : {
59 : 4 : rust_debug ("Invalid unicode codepoint found: '%u' ", char32.value);
60 : : }
61 : 2747805 : return str;
62 : : }
63 : :
64 : : std::string
65 : 2471551 : Codepoint::as_string ()
66 : : {
67 : 2471551 : std::string str;
68 : :
69 : : // str += Codepoint (value);
70 : 2471551 : str += *this;
71 : :
72 : 2471551 : return str;
73 : : }
74 : :
75 : : /* Includes all allowable float digits EXCEPT _ and . as that needs lookahead
76 : : * for handling. */
77 : : bool
78 : 0 : is_float_digit (uint32_t number)
79 : : {
80 : 0 : return ISDIGIT (number) || number == 'E' || number == 'e';
81 : : }
82 : :
83 : : /* Basically ISXDIGIT from safe-ctype but may change if Rust's encoding or
84 : : * whatever is different */
85 : : bool
86 : 1864 : is_x_digit (uint32_t number)
87 : : {
88 : 1864 : return ISXDIGIT (number);
89 : : }
90 : :
91 : : bool
92 : 53 : is_octal_digit (uint32_t number)
93 : : {
94 : 53 : return number >= '0' && number <= '7';
95 : : }
96 : :
97 : : bool
98 : 193 : is_bin_digit (uint32_t number)
99 : : {
100 : 193 : return number == '0' || number == '1';
101 : : }
102 : :
103 : : bool
104 : 124 : check_valid_float_dot_end (uint32_t character)
105 : : {
106 : 124 : return character != '.' && character != '_' && !ISALPHA (character);
107 : : }
108 : :
109 : : bool
110 : 843 : is_whitespace (uint32_t character)
111 : : {
112 : : // https://doc.rust-lang.org/reference/whitespace.html
113 : 843 : return character == '\t' || character == '\n' || character == '\v'
114 : 843 : || character == '\f' || character == '\r' || character == ' '
115 : 416 : || character == 0x0085 // next line
116 : 416 : || character == 0x200e // left-to-right mark
117 : 416 : || character == 0x200f // right-to-left mark
118 : 416 : || character == 0x2028 // line separator
119 : 1259 : || character == 0x2029; // pragraph separator
120 : : }
121 : :
122 : : bool
123 : 3309 : is_non_decimal_int_literal_separator (uint32_t character)
124 : : {
125 : 3309 : return character == 'x' || character == 'o' || character == 'b';
126 : : }
127 : :
128 : : bool
129 : 287180 : is_identifier_start (uint32_t codepoint)
130 : : {
131 : 287180 : return (cpp_check_xid_property (codepoint) & CPP_XID_START)
132 : 287180 : || codepoint == '_';
133 : : }
134 : :
135 : : bool
136 : 997042 : is_identifier_continue (uint32_t codepoint)
137 : : {
138 : 997042 : return cpp_check_xid_property (codepoint) & CPP_XID_CONTINUE;
139 : : }
140 : :
141 : 103 : Lexer::Lexer (const std::string &input, Linemap *linemap)
142 : 103 : : input (RAIIFile::create_error ()), current_line (1), current_column (1),
143 : 103 : line_map (linemap), dump_lex_out ({}),
144 : 103 : raw_input_source (new BufferInputSource (input, 0)),
145 : 103 : input_queue{*raw_input_source}, token_queue (TokenSource (this))
146 : 103 : {}
147 : :
148 : 4457 : Lexer::Lexer (const char *filename, RAIIFile file_input, Linemap *linemap,
149 : 4457 : tl::optional<std::ofstream &> dump_lex_opt)
150 : 4457 : : input (std::move (file_input)), current_line (1), current_column (1),
151 : 4457 : line_map (linemap), dump_lex_out (dump_lex_opt),
152 : 8914 : raw_input_source (new FileInputSource (input.get_raw ())),
153 : 8914 : input_queue{*raw_input_source}, token_queue (TokenSource (this))
154 : : {
155 : : // inform line_table that file is being entered and is in line 1
156 : 4457 : if (linemap)
157 : 4457 : line_map->start_file (filename, current_line);
158 : 4457 : }
159 : :
160 : 4558 : Lexer::~Lexer ()
161 : : {
162 : : /* ok apparently stop (which is equivalent of original code in destructor) is
163 : : * meant to be called after all files have finished parsing, for cleanup. On
164 : : * the other hand, actual code that it calls to leave a certain line map is
165 : : * mentioned in GCC docs as being useful for "just leaving an included header"
166 : : * and stuff like that, so this line mapping functionality may need fixing.
167 : : * FIXME: find out whether this occurs. */
168 : :
169 : : // line_map->stop();
170 : 4558 : }
171 : :
172 : : bool
173 : 4403 : Lexer::input_source_is_valid_utf8 ()
174 : : {
175 : 4403 : return raw_input_source->is_valid ();
176 : : }
177 : :
178 : : location_t
179 : 1708763 : Lexer::get_current_location ()
180 : : {
181 : 1708763 : if (line_map)
182 : 1708594 : return linemap_position_for_column (line_table, current_column);
183 : : else
184 : : // If we have no linemap, we're lexing something without proper locations
185 : : return UNDEF_LOCATION;
186 : : }
187 : :
188 : : Codepoint
189 : 3812370 : Lexer::peek_input (int n)
190 : : {
191 : 3812370 : return input_queue.peek (n);
192 : : }
193 : :
194 : : Codepoint
195 : 3715945 : Lexer::peek_input ()
196 : : {
197 : 3715945 : return peek_input (0);
198 : : }
199 : :
200 : : void
201 : 3197800 : Lexer::skip_input (int n)
202 : : {
203 : 3197800 : input_queue.skip (n);
204 : 3197800 : }
205 : :
206 : : void
207 : 3187615 : Lexer::skip_input ()
208 : : {
209 : 3187615 : skip_input (0);
210 : 3187615 : }
211 : :
212 : : void
213 : 635287 : Lexer::skip_token (int n)
214 : : {
215 : : // dump tokens if dump-lex option is enabled
216 : 635287 : if (dump_lex_out.has_value ())
217 : 0 : dump_and_skip (n);
218 : : else
219 : 635287 : token_queue.skip (n);
220 : 635287 : }
221 : :
222 : : void
223 : 0 : Lexer::dump_and_skip (int n)
224 : : {
225 : 0 : std::ofstream &out = dump_lex_out.value ();
226 : 0 : bool found_eof = false;
227 : 0 : const_TokenPtr tok;
228 : 0 : for (int i = 0; i < n + 1; i++)
229 : : {
230 : 0 : if (!found_eof)
231 : : {
232 : 0 : tok = peek_token ();
233 : 0 : found_eof |= tok->get_id () == Rust::END_OF_FILE;
234 : :
235 : 0 : location_t loc = tok->get_locus ();
236 : :
237 : 0 : out << "<id=";
238 : 0 : out << tok->token_id_to_str ();
239 : 0 : out << (tok->has_str () ? (std::string (", text=") + tok->get_str ()
240 : 0 : + std::string (", typehint=")
241 : 0 : + std::string (tok->get_type_hint_str ()))
242 : 0 : : "")
243 : 0 : << " ";
244 : 0 : out << Linemap::location_to_string (loc) << '\n';
245 : : }
246 : :
247 : 0 : token_queue.skip (0);
248 : : }
249 : 0 : }
250 : :
251 : : void
252 : 0 : Lexer::replace_current_token (TokenPtr replacement)
253 : : {
254 : 0 : token_queue.replace_current_value (replacement);
255 : :
256 : 0 : rust_debug ("called 'replace_current_token' - this is deprecated");
257 : 0 : }
258 : :
259 : : /* Determines whether the string passed in is a keyword or not. If it is, it
260 : : * returns the keyword name. */
261 : : TokenId
262 : 257705 : Lexer::classify_keyword (const std::string &str)
263 : : {
264 : 257705 : auto &keywords = Rust::Values::Keywords::keywords_tokens;
265 : 257705 : auto keyword = keywords.find (str);
266 : :
267 : 257705 : if (keyword == keywords.end ())
268 : : return IDENTIFIER;
269 : :
270 : 91692 : auto id = keyword->second;
271 : :
272 : : // We now have the expected token ID of the reserved keyword. However, some
273 : : // keywords are reserved starting in certain editions. For example, `try` is
274 : : // only a reserved keyword in editions >=2018. The language might gain new
275 : : // reserved keywords in the future.
276 : : //
277 : : // https://doc.rust-lang.org/reference/keywords.html#reserved-keywords
278 : :
279 : : // `try` is not a reserved keyword before 2018
280 : 91692 : if (get_rust_edition () == Edition::E2015 && id == TRY)
281 : : return IDENTIFIER;
282 : :
283 : : return id;
284 : : }
285 : :
286 : : TokenPtr
287 : 643030 : Lexer::build_token ()
288 : : {
289 : : // loop to go through multiple characters to build a single token
290 : 1696470 : while (true)
291 : : {
292 : 1696470 : location_t loc = get_current_location ();
293 : :
294 : 1696470 : current_char = peek_input ();
295 : 1696470 : skip_input ();
296 : :
297 : : // detect shebang
298 : : // Must be the first thing on the first line, starting with #!
299 : : // But since an attribute can also start with an #! we don't count it as a
300 : : // shebang line when after any whitespace or comments there is a [. If it
301 : : // is a shebang line we simple drop the line. Otherwise we don't consume
302 : : // any characters and fall through to the real tokenizer.
303 : 30648 : if (current_line == 1 && current_column == 1 && current_char == '#'
304 : 1727118 : && peek_input () == '!')
305 : : {
306 : : int n = 1;
307 : 471 : while (true)
308 : : {
309 : 471 : Codepoint next_char = peek_input (n);
310 : 471 : if (is_whitespace (next_char.value))
311 : 91 : n++;
312 : 380 : else if ((next_char == '/' && peek_input (n + 1) == '/'
313 : 21 : && peek_input (n + 2) != '!'
314 : 21 : && peek_input (n + 2) != '/')
315 : 415 : || (next_char == '/' && peek_input (n + 1) == '/'
316 : 0 : && peek_input (n + 2) == '/'
317 : 0 : && peek_input (n + 3) == '/'))
318 : : {
319 : : // two // or four ////
320 : : // A single line comment
321 : : // (but not an inner or outer doc comment)
322 : 21 : n += 2;
323 : 21 : next_char = peek_input (n);
324 : 371 : while (next_char != '\n' && !next_char.is_eof ())
325 : : {
326 : 350 : n++;
327 : 350 : next_char = peek_input (n);
328 : : }
329 : 21 : if (next_char == '\n')
330 : 21 : n++;
331 : : }
332 : 359 : else if (next_char == '/' && peek_input (n + 1) == '*'
333 : 14 : && peek_input (n + 2) == '*'
334 : 359 : && peek_input (n + 3) == '/')
335 : : {
336 : : /**/
337 : 0 : n += 4;
338 : : }
339 : 359 : else if (next_char == '/' && peek_input (n + 1) == '*'
340 : 14 : && peek_input (n + 2) == '*' && peek_input (n + 3) == '*'
341 : 359 : && peek_input (n + 4) == '/')
342 : : {
343 : : /***/
344 : 0 : n += 5;
345 : : }
346 : 359 : else if ((next_char == '/' && peek_input (n + 1) == '*'
347 : 14 : && peek_input (n + 2) != '*'
348 : 14 : && peek_input (n + 2) != '!')
349 : 380 : || (next_char == '/' && peek_input (n + 1) == '*'
350 : 0 : && peek_input (n + 2) == '*'
351 : 0 : && peek_input (n + 3) == '*'))
352 : : {
353 : : // one /* or three /***
354 : : // Start of a block comment
355 : : // (but not an inner or outer doc comment)
356 : 14 : n += 2;
357 : 14 : int level = 1;
358 : 1029 : while (level > 0)
359 : : {
360 : 1015 : if (peek_input (n).is_eof ())
361 : : break;
362 : 1015 : else if (peek_input (n) == '/'
363 : 1015 : && peek_input (n + 1) == '*')
364 : : {
365 : 7 : n += 2;
366 : 7 : level += 1;
367 : : }
368 : 1008 : else if (peek_input (n) == '*'
369 : 1008 : && peek_input (n + 1) == '/')
370 : : {
371 : 21 : n += 2;
372 : 21 : level -= 1;
373 : : }
374 : : else
375 : 987 : n++;
376 : : }
377 : : }
378 : 345 : else if (next_char != '[')
379 : : {
380 : : // definitely shebang, ignore the first line
381 : 532 : while (current_char != '\n' && !current_char.is_eof ())
382 : : {
383 : 504 : current_char = peek_input ();
384 : 504 : skip_input ();
385 : : }
386 : :
387 : : // newline
388 : 28 : current_line++;
389 : 28 : current_column = 1;
390 : : // tell line_table that new line starts
391 : 28 : start_line (current_line, max_column_hint);
392 : 28 : break;
393 : : }
394 : : else
395 : : break; /* Definitely not a shebang line. */
396 : : }
397 : : }
398 : :
399 : : // return end of file token if end of file
400 : 1696470 : if (current_char.is_eof ())
401 : 4818 : return Token::make (END_OF_FILE, loc);
402 : :
403 : : // if not end of file, start tokenising
404 : 1691652 : switch (current_char.value)
405 : : {
406 : : /* ignore whitespace characters for tokens but continue updating
407 : : * location */
408 : 140849 : case '\n': // newline
409 : 140849 : case 0x0085: // next line
410 : 140849 : case 0x2028: // line separator
411 : 140849 : case 0x2029: // paragraph separator
412 : 140849 : current_line++;
413 : 140849 : current_column = 1;
414 : : // tell line_table that new line starts
415 : 140849 : start_line (current_line, max_column_hint);
416 : 140849 : continue;
417 : 252 : case '\r': // cr
418 : : // Ignore, we expect a newline (lf) soon.
419 : 252 : continue;
420 : 902935 : case ' ': // space
421 : 902935 : current_column++;
422 : 902935 : continue;
423 : 113 : case '\t': // horizontal tab
424 : : // width of a tab is not well-defined, assume 8 spaces
425 : 113 : current_column += 8;
426 : 113 : continue;
427 : 28 : case '\v': // vertical tab
428 : 28 : case 0x000c: // form feed
429 : 28 : case 0x200e: // left-to-right mark
430 : 28 : case 0x200f: // right-to-left mark
431 : : // Ignored.
432 : 28 : continue;
433 : :
434 : : // punctuation - actual tokens
435 : 27123 : case '=':
436 : 27123 : if (peek_input () == '>')
437 : : {
438 : : // match arm arrow
439 : 3014 : skip_input ();
440 : 3014 : current_column += 2;
441 : 3014 : loc += 1;
442 : :
443 : 3014 : return Token::make (MATCH_ARROW, loc);
444 : : }
445 : 24109 : else if (peek_input () == '=')
446 : : {
447 : : // equality operator
448 : 661 : skip_input ();
449 : 661 : current_column += 2;
450 : 661 : loc += 1;
451 : :
452 : 661 : return Token::make (EQUAL_EQUAL, loc);
453 : : }
454 : : else
455 : : {
456 : : // assignment operator
457 : 23448 : current_column++;
458 : 23448 : return Token::make (EQUAL, loc);
459 : : }
460 : 38022 : case '(':
461 : 38022 : current_column++;
462 : 38022 : return Token::make (LEFT_PAREN, loc);
463 : 11218 : case '-':
464 : 11218 : if (peek_input () == '>')
465 : : {
466 : : // return type specifier
467 : 10012 : skip_input ();
468 : 10012 : current_column += 2;
469 : 10012 : loc += 1;
470 : :
471 : 10012 : return Token::make (RETURN_TYPE, loc);
472 : : }
473 : 1206 : else if (peek_input () == '=')
474 : : {
475 : : // minus-assign
476 : 70 : skip_input ();
477 : 70 : current_column += 2;
478 : 70 : loc += 1;
479 : :
480 : 70 : return Token::make (MINUS_EQ, loc);
481 : : }
482 : : else
483 : : {
484 : : // minus
485 : 1136 : current_column++;
486 : 1136 : return Token::make (MINUS, loc);
487 : : }
488 : 1725 : case '+':
489 : 1725 : if (peek_input () == '=')
490 : : {
491 : : // add-assign
492 : 149 : skip_input ();
493 : 149 : current_column += 2;
494 : 149 : loc += 1;
495 : :
496 : 149 : return Token::make (PLUS_EQ, loc);
497 : : }
498 : : else
499 : : {
500 : : // add
501 : 1576 : current_column++;
502 : 1576 : return Token::make (PLUS, loc);
503 : : }
504 : 38003 : case ')':
505 : 38003 : current_column++;
506 : 38003 : return Token::make (RIGHT_PAREN, loc);
507 : 28890 : case ';':
508 : 28890 : current_column++;
509 : 28890 : return Token::make (SEMICOLON, loc);
510 : 10584 : case '*':
511 : 10584 : if (peek_input () == '=')
512 : : {
513 : : // multiplication-assign
514 : 7 : skip_input ();
515 : 7 : current_column += 2;
516 : 7 : loc += 1;
517 : :
518 : 7 : return Token::make (ASTERISK_EQ, loc);
519 : : }
520 : : else
521 : : {
522 : : // multiplication
523 : 10577 : current_column++;
524 : 10577 : return Token::make (ASTERISK, loc);
525 : : }
526 : 21738 : case ',':
527 : 21738 : current_column++;
528 : 21738 : return Token::make (COMMA, loc);
529 : 17365 : case '/':
530 : 17365 : if (peek_input () == '=')
531 : : {
532 : : // division-assign
533 : 7 : skip_input ();
534 : 7 : current_column += 2;
535 : 7 : loc += 1;
536 : :
537 : 7 : return Token::make (DIV_EQ, loc);
538 : : }
539 : 17358 : else if ((peek_input () == '/' && peek_input (1) != '!'
540 : 16331 : && peek_input (1) != '/')
541 : 25318 : || (peek_input () == '/' && peek_input (1) == '/'
542 : 7898 : && peek_input (2) == '/'))
543 : : {
544 : : // two // or four ////
545 : : // single line comment
546 : : // (but not an inner or outer doc comment)
547 : 8448 : skip_input ();
548 : 8448 : current_column += 2;
549 : 8448 : current_char = peek_input ();
550 : :
551 : : // basically ignore until line finishes
552 : 412873 : while (current_char != '\n' && !current_char.is_eof ())
553 : : {
554 : 395977 : skip_input ();
555 : 395977 : current_column++; // not used
556 : 395977 : current_char = peek_input ();
557 : : }
558 : 8448 : continue;
559 : : }
560 : 8910 : else if (peek_input () == '/'
561 : 8910 : && (peek_input (1) == '!' || peek_input (1) == '/'))
562 : : {
563 : : /* single line doc comment, inner or outer. */
564 : 7945 : bool is_inner = peek_input (1) == '!';
565 : 7945 : skip_input (1);
566 : 7945 : current_column += 3;
567 : :
568 : 7945 : std::string str;
569 : 7945 : str.reserve (32);
570 : 7945 : current_char = peek_input ();
571 : 190486 : while (current_char != '\n')
572 : : {
573 : 174645 : skip_input ();
574 : 174645 : if (current_char == '\r')
575 : : {
576 : 51 : Codepoint next_char = peek_input ();
577 : 51 : if (next_char == '\n')
578 : : {
579 : 49 : current_char = '\n';
580 : 49 : break;
581 : : }
582 : 2 : rust_error_at (
583 : : loc, "Isolated CR %<\\r%> not allowed in doc comment");
584 : 2 : current_char = next_char;
585 : 2 : continue;
586 : 2 : }
587 : 174594 : if (current_char.is_eof ())
588 : : {
589 : 0 : rust_error_at (
590 : : loc, ErrorCode::E0758,
591 : : "unexpected EOF while looking for end of comment");
592 : 0 : break;
593 : : }
594 : 174594 : str += current_char;
595 : 174594 : current_char = peek_input ();
596 : : }
597 : 7945 : skip_input ();
598 : 7945 : current_line++;
599 : 7945 : current_column = 1;
600 : : // tell line_table that new line starts
601 : 7945 : start_line (current_line, max_column_hint);
602 : :
603 : 7945 : str.shrink_to_fit ();
604 : :
605 : 7945 : loc += str.size () - 1;
606 : 7945 : if (is_inner)
607 : 62 : return Token::make_inner_doc_comment (loc, std::move (str));
608 : : else
609 : 7883 : return Token::make_outer_doc_comment (loc, std::move (str));
610 : 7945 : }
611 : 965 : else if (peek_input () == '*' && peek_input (1) == '*'
612 : 1050 : && peek_input (2) == '/')
613 : : {
614 : : /**/
615 : 14 : skip_input (2);
616 : 14 : current_column += 4;
617 : 14 : continue;
618 : : }
619 : 951 : else if (peek_input () == '*' && peek_input (1) == '*'
620 : 1022 : && peek_input (2) == '*' && peek_input (3) == '/')
621 : : {
622 : : /***/
623 : 14 : skip_input (3);
624 : 14 : current_column += 5;
625 : 14 : continue;
626 : : }
627 : 937 : else if ((peek_input () == '*' && peek_input (1) != '!'
628 : 829 : && peek_input (1) != '*')
629 : 1067 : || (peek_input () == '*' && peek_input (1) == '*'
630 : 57 : && peek_input (2) == '*'))
631 : : {
632 : : // one /* or three /***
633 : : // block comment
634 : : // (but not an inner or outer doc comment)
635 : 786 : skip_input ();
636 : 786 : current_column += 2;
637 : :
638 : 786 : int level = 1;
639 : 35394 : while (level > 0)
640 : : {
641 : 34609 : current_char = peek_input ();
642 : :
643 : 34609 : if (current_char.is_eof ())
644 : : {
645 : 1 : rust_error_at (
646 : : loc, ErrorCode::E0758,
647 : : "unexpected EOF while looking for end of comment");
648 : 1 : break;
649 : : }
650 : :
651 : : // if /* found
652 : 34608 : if (current_char == '/' && peek_input (1) == '*')
653 : : {
654 : : // skip /* characters
655 : 49 : skip_input (1);
656 : :
657 : 49 : current_column += 2;
658 : :
659 : 49 : level += 1;
660 : 49 : continue;
661 : : }
662 : :
663 : : // ignore until */ is found
664 : 34559 : if (current_char == '*' && peek_input (1) == '/')
665 : : {
666 : : // skip */ characters
667 : 834 : skip_input (1);
668 : :
669 : 834 : current_column += 2;
670 : :
671 : 834 : level -= 1;
672 : 834 : continue;
673 : : }
674 : :
675 : 33725 : if (current_char == '\n')
676 : : {
677 : 398 : skip_input ();
678 : 398 : current_line++;
679 : 398 : current_column = 1;
680 : : // tell line_table that new line starts
681 : 398 : start_line (current_line, max_column_hint);
682 : 398 : continue;
683 : : }
684 : :
685 : 33327 : skip_input ();
686 : 33327 : current_column++;
687 : : }
688 : :
689 : : // refresh new token
690 : 786 : continue;
691 : 786 : }
692 : 151 : else if (peek_input () == '*'
693 : 151 : && (peek_input (1) == '!' || peek_input (1) == '*'))
694 : : {
695 : : // block doc comment, inner /*! or outer /**
696 : 116 : bool is_inner = peek_input (1) == '!';
697 : 116 : skip_input (1);
698 : 116 : current_column += 3;
699 : :
700 : 116 : std::string str;
701 : 116 : str.reserve (96);
702 : :
703 : 116 : int level = 1;
704 : 116 : while (level > 0)
705 : : {
706 : 2685 : current_char = peek_input ();
707 : :
708 : 2685 : if (current_char.is_eof ())
709 : : {
710 : 0 : rust_error_at (
711 : : loc, ErrorCode::E0758,
712 : : "unexpected EOF while looking for end of comment");
713 : 0 : break;
714 : : }
715 : :
716 : : // if /* found
717 : 2685 : if (current_char == '/' && peek_input (1) == '*')
718 : : {
719 : : // skip /* characters
720 : 84 : skip_input (1);
721 : 84 : current_column += 2;
722 : :
723 : 84 : level += 1;
724 : 84 : str += "/*";
725 : 84 : continue;
726 : : }
727 : :
728 : : // ignore until */ is found
729 : 2601 : if (current_char == '*' && peek_input (1) == '/')
730 : : {
731 : : // skip */ characters
732 : 200 : skip_input (1);
733 : 200 : current_column += 2;
734 : :
735 : 200 : level -= 1;
736 : 200 : if (level > 0)
737 : 84 : str += "*/";
738 : 200 : continue;
739 : : }
740 : :
741 : 2401 : if (current_char == '\r' && peek_input (1) != '\n')
742 : 2 : rust_error_at (
743 : : loc, "Isolated CR %<\\r%> not allowed in doc comment");
744 : :
745 : 2401 : if (current_char == '\n')
746 : : {
747 : 0 : skip_input ();
748 : 0 : current_line++;
749 : 0 : current_column = 1;
750 : : // tell line_table that new line starts
751 : 0 : start_line (current_line, max_column_hint);
752 : 0 : str += '\n';
753 : 0 : continue;
754 : : }
755 : :
756 : 2401 : str += current_char;
757 : 2401 : skip_input ();
758 : 2401 : current_column++;
759 : : }
760 : :
761 : 116 : str.shrink_to_fit ();
762 : :
763 : 116 : loc += str.size () - 1;
764 : 116 : if (is_inner)
765 : 73 : return Token::make_inner_doc_comment (loc, std::move (str));
766 : : else
767 : 43 : return Token::make_outer_doc_comment (loc, std::move (str));
768 : 116 : }
769 : : else
770 : : {
771 : : // division
772 : 35 : current_column++;
773 : 35 : return Token::make (DIV, loc);
774 : : }
775 : 43 : case '%':
776 : 43 : if (peek_input () == '=')
777 : : {
778 : : // modulo-assign
779 : 7 : skip_input ();
780 : 7 : current_column += 2;
781 : 7 : loc += 1;
782 : :
783 : 7 : return Token::make (PERCENT_EQ, loc);
784 : : }
785 : : else
786 : : {
787 : : // modulo
788 : 36 : current_column++;
789 : 36 : return Token::make (PERCENT, loc);
790 : : }
791 : 147 : case '^':
792 : 147 : if (peek_input () == '=')
793 : : {
794 : : // xor-assign?
795 : 84 : skip_input ();
796 : 84 : current_column += 2;
797 : 84 : loc += 1;
798 : :
799 : 84 : return Token::make (CARET_EQ, loc);
800 : : }
801 : : else
802 : : {
803 : : // xor?
804 : 63 : current_column++;
805 : 63 : return Token::make (CARET, loc);
806 : : }
807 : 8352 : case '<':
808 : 8352 : if (peek_input () == '<')
809 : : {
810 : 65 : if (peek_input (1) == '=')
811 : : {
812 : : // left-shift assign
813 : 7 : skip_input (1);
814 : 7 : current_column += 3;
815 : 7 : loc += 2;
816 : :
817 : 7 : return Token::make (LEFT_SHIFT_EQ, loc);
818 : : }
819 : : else
820 : : {
821 : : // left-shift
822 : 58 : skip_input ();
823 : 58 : current_column += 2;
824 : 58 : loc += 1;
825 : :
826 : 58 : return Token::make (LEFT_SHIFT, loc);
827 : : }
828 : : }
829 : 8287 : else if (peek_input () == '=')
830 : : {
831 : : // smaller than or equal to
832 : 224 : skip_input ();
833 : 224 : current_column += 2;
834 : 224 : loc += 1;
835 : :
836 : 224 : return Token::make (LESS_OR_EQUAL, loc);
837 : : }
838 : : else
839 : : {
840 : : // smaller than
841 : 8063 : current_column++;
842 : 8063 : return Token::make (LEFT_ANGLE, loc);
843 : : }
844 : 8200 : break;
845 : 8200 : case '>':
846 : 8200 : if (peek_input () == '>')
847 : : {
848 : 125 : if (peek_input (1) == '=')
849 : : {
850 : : // right-shift-assign
851 : 7 : skip_input (1);
852 : 7 : current_column += 3;
853 : 7 : loc += 2;
854 : :
855 : 7 : return Token::make (RIGHT_SHIFT_EQ, loc);
856 : : }
857 : : else
858 : : {
859 : : // right-shift
860 : 118 : skip_input ();
861 : 118 : current_column += 2;
862 : 118 : loc += 1;
863 : :
864 : 118 : return Token::make (RIGHT_SHIFT, loc);
865 : : }
866 : : }
867 : 8075 : else if (peek_input () == '=')
868 : : {
869 : : // larger than or equal to
870 : 203 : skip_input ();
871 : 203 : current_column += 2;
872 : 203 : loc += 1;
873 : :
874 : 203 : return Token::make (GREATER_OR_EQUAL, loc);
875 : : }
876 : : else
877 : : {
878 : : // larger than
879 : 7872 : current_column++;
880 : 7872 : return Token::make (RIGHT_ANGLE, loc);
881 : : }
882 : 28262 : case ':':
883 : 28262 : if (peek_input () == ':')
884 : : {
885 : : // scope resolution ::
886 : 9678 : skip_input ();
887 : 9678 : current_column += 2;
888 : 9678 : loc += 1;
889 : :
890 : 9678 : return Token::make (SCOPE_RESOLUTION, loc);
891 : : }
892 : : else
893 : : {
894 : : // single colon :
895 : 18584 : current_column++;
896 : 18584 : return Token::make (COLON, loc);
897 : : }
898 : 3570 : case '!':
899 : : // no special handling for macros in lexer?
900 : 3570 : if (peek_input () == '=')
901 : : {
902 : : // not equal boolean operator
903 : 154 : skip_input ();
904 : 154 : current_column += 2;
905 : 154 : loc += 1;
906 : :
907 : 154 : return Token::make (NOT_EQUAL, loc);
908 : : }
909 : : else
910 : : {
911 : : // not equal unary operator
912 : 3416 : current_column++;
913 : :
914 : 3416 : return Token::make (EXCLAM, loc);
915 : : }
916 : 356 : case '?':
917 : 356 : current_column++;
918 : 356 : return Token::make (QUESTION_MARK, loc);
919 : 8746 : case '#':
920 : 8746 : current_column++;
921 : 8746 : return Token::make (HASH, loc);
922 : 10556 : case '[':
923 : 10556 : current_column++;
924 : 10556 : return Token::make (LEFT_SQUARE, loc);
925 : 10549 : case ']':
926 : 10549 : current_column++;
927 : 10549 : return Token::make (RIGHT_SQUARE, loc);
928 : 33464 : case '{':
929 : 33464 : current_column++;
930 : 33464 : return Token::make (LEFT_CURLY, loc);
931 : 33414 : case '}':
932 : 33414 : current_column++;
933 : 33414 : return Token::make (RIGHT_CURLY, loc);
934 : 9 : case '@':
935 : 9 : current_column++;
936 : 9 : return Token::make (PATTERN_BIND, loc);
937 : 3500 : case '$':
938 : 3500 : current_column++;
939 : 3500 : return Token::make (DOLLAR_SIGN, loc);
940 : 0 : case '~':
941 : 0 : current_column++;
942 : 0 : return Token::make (TILDE, loc);
943 : 0 : case '\\':
944 : 0 : current_column++;
945 : 0 : return Token::make (BACKSLASH, loc);
946 : 0 : case '`':
947 : 0 : current_column++;
948 : 0 : return Token::make (BACKTICK, loc);
949 : 466 : case '|':
950 : 466 : if (peek_input () == '=')
951 : : {
952 : : // bitwise or-assign?
953 : 28 : skip_input ();
954 : 28 : current_column += 2;
955 : 28 : loc += 1;
956 : :
957 : 28 : return Token::make (PIPE_EQ, loc);
958 : : }
959 : 438 : else if (peek_input () == '|')
960 : : {
961 : : // logical or
962 : 68 : skip_input ();
963 : 68 : current_column += 2;
964 : 68 : loc += 1;
965 : :
966 : 68 : return Token::make (OR, loc);
967 : : }
968 : : else
969 : : {
970 : : // bitwise or
971 : 370 : current_column++;
972 : :
973 : 370 : return Token::make (PIPE, loc);
974 : : }
975 : 9874 : case '&':
976 : 9874 : if (peek_input () == '=')
977 : : {
978 : : // bitwise and-assign?
979 : 21 : skip_input ();
980 : 21 : current_column += 2;
981 : 21 : loc += 1;
982 : :
983 : 21 : return Token::make (AMP_EQ, loc);
984 : : }
985 : 9853 : else if (peek_input () == '&')
986 : : {
987 : : // logical and
988 : 305 : skip_input ();
989 : 305 : current_column += 2;
990 : 305 : loc += 1;
991 : :
992 : 305 : return Token::make (LOGICAL_AND, loc);
993 : : }
994 : : else
995 : : {
996 : : // bitwise and/reference
997 : 9548 : current_column++;
998 : :
999 : 9548 : return Token::make (AMP, loc);
1000 : : }
1001 : 6510 : case '.':
1002 : 6510 : if (peek_input () == '.')
1003 : : {
1004 : 1039 : if (peek_input (1) == '.')
1005 : : {
1006 : : // ellipsis
1007 : 830 : skip_input (1);
1008 : 830 : current_column += 3;
1009 : 830 : loc += 2;
1010 : :
1011 : 830 : return Token::make (ELLIPSIS, loc);
1012 : : }
1013 : 209 : else if (peek_input (1) == '=')
1014 : : {
1015 : : // ..=
1016 : 28 : skip_input (1);
1017 : 28 : current_column += 3;
1018 : 28 : loc += 2;
1019 : :
1020 : 28 : return Token::make (DOT_DOT_EQ, loc);
1021 : : }
1022 : : else
1023 : : {
1024 : : // ..
1025 : 181 : skip_input ();
1026 : 181 : current_column += 2;
1027 : 181 : loc += 1;
1028 : :
1029 : 181 : return Token::make (DOT_DOT, loc);
1030 : : }
1031 : : }
1032 : : else /*if (!ISDIGIT (peek_input ()))*/
1033 : : {
1034 : : // single dot .
1035 : : // Only if followed by a non-number - otherwise is float
1036 : : // nope, float cannot start with '.'.
1037 : 5471 : current_column++;
1038 : 5471 : return Token::make (DOT, loc);
1039 : : }
1040 : 1044177 : }
1041 : : // TODO: special handling of _ in the lexer? instead of being identifier
1042 : :
1043 : : // byte character, byte string and raw byte string literals
1044 : 286789 : if (current_char == 'b')
1045 : : {
1046 : 10447 : if (peek_input () == '\'')
1047 : 78 : return parse_byte_char (loc);
1048 : 10369 : else if (peek_input () == '"')
1049 : 63 : return parse_byte_string (loc);
1050 : 10306 : else if (peek_input () == 'r'
1051 : 10306 : && (peek_input (1) == '#' || peek_input (1) == '"'))
1052 : 32 : return parse_raw_byte_string (loc);
1053 : : }
1054 : :
1055 : : // raw identifiers and raw strings
1056 : 286616 : if (current_char == 'r')
1057 : : {
1058 : 3785 : Codepoint peek = peek_input ();
1059 : 3785 : Codepoint peek1 = peek_input (1);
1060 : :
1061 : : // TODO (tamaron) parse Unicode ident
1062 : 3785 : if (peek == '#' && is_identifier_start (peek1.value))
1063 : : {
1064 : 81 : TokenPtr raw_ident_ptr = parse_raw_identifier (loc);
1065 : 81 : if (raw_ident_ptr != nullptr)
1066 : 80 : return raw_ident_ptr;
1067 : : else
1068 : 1 : continue; /* input got parsed, it just wasn't valid. An error
1069 : : was produced. */
1070 : 81 : }
1071 : : else
1072 : : {
1073 : 3704 : TokenPtr maybe_raw_string_ptr = maybe_parse_raw_string (loc);
1074 : 3704 : if (maybe_raw_string_ptr != nullptr)
1075 : 25 : return maybe_raw_string_ptr;
1076 : 3704 : }
1077 : : }
1078 : :
1079 : : // find identifiers and keywords.
1080 : 286510 : if (is_identifier_start (current_char.value))
1081 : 258891 : return parse_identifier_or_keyword (loc);
1082 : :
1083 : : // int and float literals
1084 : 27619 : if (ISDIGIT (current_char.value))
1085 : : { // _ not allowed as first char
1086 : 14703 : if (current_char == '0'
1087 : 14703 : && is_non_decimal_int_literal_separator (peek_input ().value))
1088 : : {
1089 : : // handle binary, octal, hex literals
1090 : 191 : TokenPtr non_dec_int_lit_ptr
1091 : 191 : = parse_non_decimal_int_literals (loc);
1092 : 191 : if (non_dec_int_lit_ptr != nullptr)
1093 : 191 : return non_dec_int_lit_ptr;
1094 : 191 : }
1095 : : else
1096 : : {
1097 : : // handle decimals (integer or float)
1098 : 14512 : TokenPtr decimal_or_float_ptr = parse_decimal_int_or_float (loc);
1099 : 14512 : if (decimal_or_float_ptr != nullptr)
1100 : 14512 : return decimal_or_float_ptr;
1101 : 14512 : }
1102 : : }
1103 : :
1104 : : // string literals
1105 : 12916 : if (current_char == '"')
1106 : 12108 : return parse_string (loc);
1107 : :
1108 : : // char literals and lifetime names
1109 : 808 : if (current_char == '\'')
1110 : : {
1111 : 808 : TokenPtr char_or_lifetime_ptr = parse_char_or_lifetime (loc);
1112 : 808 : if (char_or_lifetime_ptr != nullptr)
1113 : 808 : return char_or_lifetime_ptr;
1114 : 808 : }
1115 : :
1116 : : // DEBUG: check for specific character problems:
1117 : 0 : if (current_char == '0')
1118 : 0 : rust_debug ("'0' uncaught before unexpected character");
1119 : 0 : else if (current_char == ']')
1120 : 0 : rust_debug ("']' uncaught before unexpected character");
1121 : : else if (current_char == 0x5d)
1122 : : rust_debug ("whatever 0x5d is (not '0' or ']') uncaught before "
1123 : : "unexpected character");
1124 : :
1125 : : // didn't match anything so error
1126 : 0 : rust_error_at (loc, "unexpected character %<%x%>", current_char.value);
1127 : 0 : current_column++;
1128 : : }
1129 : : }
1130 : :
1131 : : // Parses in a type suffix.
1132 : : std::pair<PrimitiveCoreType, int>
1133 : 14695 : Lexer::parse_in_type_suffix ()
1134 : : {
1135 : 14695 : std::string suffix;
1136 : 14695 : suffix.reserve (5);
1137 : :
1138 : 14695 : int additional_length_offset = 0;
1139 : :
1140 : : // get suffix
1141 : 31631 : while (ISALPHA (current_char.value) || ISDIGIT (current_char.value)
1142 : 32963 : || current_char == '_')
1143 : : {
1144 : 3573 : if (current_char == '_')
1145 : : {
1146 : : // don't add _ to suffix
1147 : 0 : skip_input ();
1148 : 0 : current_char = peek_input ();
1149 : :
1150 : 0 : additional_length_offset++;
1151 : :
1152 : 0 : continue;
1153 : : }
1154 : :
1155 : 3573 : additional_length_offset++;
1156 : :
1157 : 3573 : suffix += current_char;
1158 : 3573 : skip_input ();
1159 : 3573 : current_char = peek_input ();
1160 : : }
1161 : :
1162 : 14695 : if (suffix.empty ())
1163 : : {
1164 : : // no type suffix: do nothing but also no error
1165 : 13523 : return std::make_pair (CORETYPE_UNKNOWN, additional_length_offset);
1166 : : }
1167 : 1172 : else if (suffix == "f32")
1168 : : {
1169 : 496 : return std::make_pair (CORETYPE_F32, additional_length_offset);
1170 : : }
1171 : 676 : else if (suffix == "f64")
1172 : : {
1173 : 221 : return std::make_pair (CORETYPE_F64, additional_length_offset);
1174 : : }
1175 : 455 : else if (suffix == "i8")
1176 : : {
1177 : 23 : return std::make_pair (CORETYPE_I8, additional_length_offset);
1178 : : }
1179 : 432 : else if (suffix == "i16")
1180 : : {
1181 : 15 : return std::make_pair (CORETYPE_I16, additional_length_offset);
1182 : : }
1183 : 417 : else if (suffix == "i32")
1184 : : {
1185 : 176 : return std::make_pair (CORETYPE_I32, additional_length_offset);
1186 : : }
1187 : 241 : else if (suffix == "i64")
1188 : : {
1189 : 15 : return std::make_pair (CORETYPE_I64, additional_length_offset);
1190 : : }
1191 : 226 : else if (suffix == "i128")
1192 : : {
1193 : 15 : return std::make_pair (CORETYPE_I128, additional_length_offset);
1194 : : }
1195 : 211 : else if (suffix == "isize")
1196 : : {
1197 : 3 : return std::make_pair (CORETYPE_ISIZE, additional_length_offset);
1198 : : }
1199 : 208 : else if (suffix == "u8")
1200 : : {
1201 : 30 : return std::make_pair (CORETYPE_U8, additional_length_offset);
1202 : : }
1203 : 178 : else if (suffix == "u16")
1204 : : {
1205 : 25 : return std::make_pair (CORETYPE_U16, additional_length_offset);
1206 : : }
1207 : 153 : else if (suffix == "u32")
1208 : : {
1209 : 76 : return std::make_pair (CORETYPE_U32, additional_length_offset);
1210 : : }
1211 : 77 : else if (suffix == "u64")
1212 : : {
1213 : 25 : return std::make_pair (CORETYPE_U64, additional_length_offset);
1214 : : }
1215 : 52 : else if (suffix == "u128")
1216 : : {
1217 : 15 : return std::make_pair (CORETYPE_U128, additional_length_offset);
1218 : : }
1219 : 37 : else if (suffix == "usize")
1220 : : {
1221 : 37 : return std::make_pair (CORETYPE_USIZE, additional_length_offset);
1222 : : }
1223 : : else
1224 : : {
1225 : 0 : rust_error_at (get_current_location (), "unknown number suffix %qs",
1226 : : suffix.c_str ());
1227 : :
1228 : 0 : return std::make_pair (CORETYPE_UNKNOWN, additional_length_offset);
1229 : : }
1230 : 14695 : }
1231 : :
1232 : : // Parses in the exponent part (if any) of a float literal.
1233 : : std::pair<std::string, int>
1234 : 337 : Lexer::parse_in_exponent_part ()
1235 : : {
1236 : 337 : int additional_length_offset = 0;
1237 : 337 : std::string str;
1238 : 337 : if (current_char == 'E' || current_char == 'e')
1239 : : {
1240 : : // add exponent to string as strtod works with it
1241 : 7 : str += current_char;
1242 : 7 : skip_input ();
1243 : 7 : current_char = peek_input ();
1244 : :
1245 : 7 : additional_length_offset++;
1246 : :
1247 : : // special - and + handling
1248 : 7 : if (current_char == '-')
1249 : : {
1250 : 0 : str += '-';
1251 : :
1252 : 0 : skip_input ();
1253 : 0 : current_char = peek_input ();
1254 : :
1255 : 0 : additional_length_offset++;
1256 : : }
1257 : 7 : else if (current_char == '+')
1258 : : {
1259 : : // don't add + but still skip input
1260 : 7 : skip_input ();
1261 : 7 : current_char = peek_input ();
1262 : :
1263 : 7 : additional_length_offset++;
1264 : : }
1265 : :
1266 : : // parse another decimal number for exponent
1267 : 7 : auto str_length = parse_in_decimal ();
1268 : 7 : str += std::get<0> (str_length);
1269 : 7 : additional_length_offset += std::get<1> (str_length);
1270 : 7 : }
1271 : 674 : return std::make_pair (str, additional_length_offset);
1272 : 337 : }
1273 : :
1274 : : // Parses a decimal integer.
1275 : : std::tuple<std::string, int, bool>
1276 : 14856 : Lexer::parse_in_decimal ()
1277 : : {
1278 : : /* A pure decimal contains only digits. */
1279 : 14856 : bool pure_decimal = true;
1280 : 14856 : int additional_length_offset = 0;
1281 : 14856 : std::string str;
1282 : 21843 : while (ISDIGIT (current_char.value) || current_char.value == '_')
1283 : : {
1284 : 6987 : if (current_char == '_')
1285 : : {
1286 : 9 : pure_decimal = false;
1287 : : // don't add _ to number
1288 : 9 : skip_input ();
1289 : 9 : current_char = peek_input ();
1290 : :
1291 : 9 : additional_length_offset++;
1292 : :
1293 : 9 : continue;
1294 : : }
1295 : :
1296 : 6978 : additional_length_offset++;
1297 : :
1298 : 6978 : str += current_char;
1299 : 6978 : skip_input ();
1300 : 6978 : current_char = peek_input ();
1301 : : }
1302 : 29712 : return std::make_tuple (str, additional_length_offset, pure_decimal);
1303 : 14856 : }
1304 : :
1305 : : /* Parses escapes (and string continues) in "byte" strings and characters. Does
1306 : : * not support unicode. */
1307 : : std::tuple<char, int, bool>
1308 : 60 : Lexer::parse_escape (char opening_char)
1309 : : {
1310 : 60 : int additional_length_offset = 0;
1311 : 60 : char output_char = 0;
1312 : :
1313 : : // skip to actual letter
1314 : 60 : skip_input ();
1315 : 60 : current_char = peek_input ();
1316 : 60 : additional_length_offset++;
1317 : :
1318 : 60 : switch (current_char.value)
1319 : : {
1320 : 17 : case 'x':
1321 : 17 : {
1322 : 17 : auto hex_escape_pair = parse_partial_hex_escape ();
1323 : 17 : long hexLong = hex_escape_pair.first;
1324 : 17 : additional_length_offset += hex_escape_pair.second;
1325 : :
1326 : 17 : if (hexLong > 255 || hexLong < 0)
1327 : 0 : rust_error_at (
1328 : : get_current_location (),
1329 : : "byte \\x escape %<\\x%x%> out of range - allows up to %<\\xFF%>",
1330 : : static_cast<unsigned int> (hexLong));
1331 : : /* TODO: restore capital for escape output - gcc pretty-printer doesn't
1332 : : * support %X directly */
1333 : 17 : char hexChar = static_cast<char> (hexLong);
1334 : :
1335 : 17 : output_char = hexChar;
1336 : : }
1337 : 17 : break;
1338 : : case 'n':
1339 : : output_char = '\n';
1340 : : break;
1341 : 0 : case 'r':
1342 : 0 : output_char = '\r';
1343 : 0 : break;
1344 : 1 : case 't':
1345 : 1 : output_char = '\t';
1346 : 1 : break;
1347 : 8 : case '\\':
1348 : 8 : output_char = '\\';
1349 : 8 : break;
1350 : 8 : case '0':
1351 : 8 : output_char = '\0';
1352 : 8 : break;
1353 : 15 : case '\'':
1354 : 15 : output_char = '\'';
1355 : 15 : break;
1356 : 1 : case '"':
1357 : 1 : output_char = '"';
1358 : 1 : break;
1359 : 2 : case 'u':
1360 : 3 : rust_error_at (get_current_location (),
1361 : : "cannot have a unicode escape \\u in a byte %s",
1362 : : opening_char == '\'' ? "character" : "string");
1363 : : // Try to parse it anyway, just to skip it
1364 : 2 : parse_partial_unicode_escape ();
1365 : 2 : return std::make_tuple (output_char, additional_length_offset, false);
1366 : 0 : case '\r':
1367 : 0 : case '\n':
1368 : : // string continue
1369 : 0 : return std::make_tuple (0, parse_partial_string_continue (), true);
1370 : 1 : default:
1371 : 1 : rust_error_at (get_current_location (),
1372 : : "unknown escape sequence %<\\%s%>",
1373 : 1 : current_char.as_string ().c_str ());
1374 : : // returns false if no parsing could be done
1375 : : // return false;
1376 : 1 : return std::make_tuple (output_char, additional_length_offset, false);
1377 : 57 : break;
1378 : : }
1379 : : // all non-special cases (string continue) should skip their used char
1380 : 57 : skip_input ();
1381 : 57 : current_char = peek_input ();
1382 : 57 : additional_length_offset++;
1383 : :
1384 : : // returns true if parsing was successful
1385 : : // return true;
1386 : 57 : return std::make_tuple (output_char, additional_length_offset, false);
1387 : : }
1388 : :
1389 : : /* Parses an escape (or string continue) in a string or character. Supports
1390 : : * unicode escapes. */
1391 : : std::tuple<Codepoint, int, bool>
1392 : 2697 : Lexer::parse_utf8_escape ()
1393 : : {
1394 : 2697 : Codepoint output_char;
1395 : 2697 : int additional_length_offset = 0;
1396 : :
1397 : : // skip to actual letter
1398 : 2697 : skip_input ();
1399 : 2697 : current_char = peek_input ();
1400 : 2697 : additional_length_offset++;
1401 : :
1402 : 2697 : switch (current_char.value)
1403 : : {
1404 : 17 : case 'x':
1405 : 17 : {
1406 : 17 : auto hex_escape_pair = parse_partial_hex_escape ();
1407 : 17 : long hexLong = hex_escape_pair.first;
1408 : 17 : additional_length_offset += hex_escape_pair.second;
1409 : :
1410 : 17 : if (hexLong > 127 || hexLong < 0)
1411 : 4 : rust_error_at (
1412 : : get_current_location (),
1413 : : "ascii \\x escape %<\\x%x%> out of range - allows up to %<\\x7F%>",
1414 : : static_cast<unsigned int> (hexLong));
1415 : : /* TODO: restore capital for escape output - gcc pretty-printer doesn't
1416 : : * support %X directly */
1417 : 17 : char hexChar = static_cast<char> (hexLong);
1418 : :
1419 : 17 : output_char = hexChar;
1420 : : }
1421 : 17 : break;
1422 : : case 'n':
1423 : : output_char = '\n';
1424 : : break;
1425 : 0 : case 'r':
1426 : 0 : output_char = '\r';
1427 : 0 : break;
1428 : 1 : case 't':
1429 : 1 : output_char = '\t';
1430 : 1 : break;
1431 : 1 : case '\\':
1432 : 1 : output_char = '\\';
1433 : 1 : break;
1434 : 1309 : case '0':
1435 : 1309 : output_char = '\0';
1436 : 1309 : break;
1437 : 1 : case '\'':
1438 : 1 : output_char = '\'';
1439 : 1 : break;
1440 : 1 : case '"':
1441 : 1 : output_char = '"';
1442 : 1 : break;
1443 : 46 : case 'u':
1444 : 46 : {
1445 : 46 : auto unicode_escape_pair = parse_partial_unicode_escape ();
1446 : 46 : output_char = unicode_escape_pair.first;
1447 : 46 : additional_length_offset += unicode_escape_pair.second;
1448 : :
1449 : 46 : return std::make_tuple (output_char, additional_length_offset, false);
1450 : : }
1451 : 28 : break;
1452 : 28 : case '\r':
1453 : 28 : case '\n':
1454 : : // string continue
1455 : 28 : return std::make_tuple (0, parse_partial_string_continue (), true);
1456 : 1 : default:
1457 : 1 : rust_error_at (get_current_location (),
1458 : : "unknown escape sequence %<\\%s%>",
1459 : 1 : current_char.as_string ().c_str ());
1460 : : // returns false if no parsing could be done
1461 : : // return false;
1462 : 1 : return std::make_tuple (output_char, additional_length_offset, false);
1463 : 2622 : break;
1464 : : }
1465 : : /* all non-special cases (unicode, string continue) should skip their used
1466 : : * char */
1467 : 2622 : skip_input ();
1468 : 2622 : current_char = peek_input ();
1469 : 2622 : additional_length_offset++;
1470 : :
1471 : : // returns true if parsing was successful
1472 : : // return true;
1473 : 2622 : return std::make_tuple (output_char, additional_length_offset, false);
1474 : : }
1475 : :
1476 : : // Parses the body of a string continue that has been found in an escape.
1477 : : int
1478 : 28 : Lexer::parse_partial_string_continue ()
1479 : : {
1480 : 28 : int additional_length_offset = 1;
1481 : :
1482 : : // string continue
1483 : : // TODO use utf-8 codepoint to skip whitespaces
1484 : 364 : while (is_whitespace (current_char.value))
1485 : : {
1486 : 336 : if (current_char == '\n')
1487 : : {
1488 : 28 : current_line++;
1489 : 28 : current_column = 1;
1490 : : // tell line_table that new line starts
1491 : 28 : start_line (current_line, max_column_hint);
1492 : :
1493 : : // reset "length"
1494 : 28 : additional_length_offset = 1;
1495 : :
1496 : : // get next char
1497 : 28 : skip_input ();
1498 : 28 : current_char = peek_input ();
1499 : :
1500 : 28 : continue;
1501 : : }
1502 : :
1503 : 308 : skip_input ();
1504 : 308 : current_char = peek_input ();
1505 : 308 : additional_length_offset++;
1506 : : }
1507 : :
1508 : 28 : return additional_length_offset;
1509 : : }
1510 : :
1511 : : /* Parses the body of a '\x' escape. Note that it does not check that the number
1512 : : * is valid and smaller than 255. */
1513 : : std::pair<long, int>
1514 : 34 : Lexer::parse_partial_hex_escape ()
1515 : : {
1516 : : // hex char string (null-terminated)
1517 : 34 : char hexNum[3] = {0, 0, 0};
1518 : :
1519 : : // first hex char
1520 : 34 : current_char = peek_input (1);
1521 : 34 : int additional_length_offset = 1;
1522 : :
1523 : 34 : if (!is_x_digit (current_char.value))
1524 : : {
1525 : 4 : rust_error_at (get_current_location (),
1526 : : "invalid character %<\\x%s%> in \\x sequence",
1527 : 4 : current_char.as_string ().c_str ());
1528 : 4 : return std::make_pair (0, 0);
1529 : : }
1530 : 30 : hexNum[0] = current_char.value;
1531 : :
1532 : : // second hex char
1533 : 30 : skip_input ();
1534 : 30 : current_char = peek_input (1);
1535 : 30 : additional_length_offset++;
1536 : :
1537 : 30 : if (!is_x_digit (current_char.value))
1538 : : {
1539 : 2 : rust_error_at (get_current_location (),
1540 : 2 : "invalid character %<\\x%c%s%> in \\x sequence", hexNum[0],
1541 : 2 : current_char.as_string ().c_str ());
1542 : 2 : return std::make_pair (0, 1);
1543 : : }
1544 : 28 : skip_input ();
1545 : 28 : hexNum[1] = current_char.value;
1546 : :
1547 : 28 : long hexLong = std::strtol (hexNum, nullptr, 16);
1548 : :
1549 : 28 : return std::make_pair (hexLong, additional_length_offset);
1550 : : }
1551 : :
1552 : : // Parses the body of a unicode escape.
1553 : : std::pair<Codepoint, int>
1554 : 48 : Lexer::parse_partial_unicode_escape ()
1555 : : {
1556 : 48 : skip_input ();
1557 : 48 : current_char = peek_input ();
1558 : 48 : int additional_length_offset = 0;
1559 : :
1560 : 48 : if (current_char != '{')
1561 : : {
1562 : 2 : rust_error_at (get_current_location (),
1563 : : "unicode escape should start with %<{%>");
1564 : : /* Skip what should probaby have been between brackets. */
1565 : 10 : while (is_x_digit (current_char.value) || current_char == '_')
1566 : : {
1567 : 6 : skip_input ();
1568 : 6 : current_char = peek_input ();
1569 : 6 : additional_length_offset++;
1570 : : }
1571 : 2 : return std::make_pair (Codepoint (0), additional_length_offset);
1572 : : }
1573 : :
1574 : 46 : skip_input ();
1575 : 46 : current_char = peek_input ();
1576 : 46 : additional_length_offset++;
1577 : :
1578 : 46 : if (current_char == '_')
1579 : : {
1580 : 2 : rust_error_at (get_current_location (),
1581 : : "unicode escape cannot start with %<_%>");
1582 : 2 : skip_input ();
1583 : 2 : current_char = peek_input ();
1584 : 2 : additional_length_offset++;
1585 : : // fallthrough and try to parse the rest anyway
1586 : : }
1587 : :
1588 : : // parse unicode escape - 1-6 hex digits
1589 : 46 : std::string num_str;
1590 : 46 : num_str.reserve (6);
1591 : :
1592 : : // loop through to add entire hex number to string
1593 : 304 : while (is_x_digit (current_char.value) || current_char.value == '_')
1594 : : {
1595 : 212 : if (current_char == '_')
1596 : : {
1597 : : // don't add _ to number
1598 : 24 : skip_input ();
1599 : 24 : current_char = peek_input ();
1600 : :
1601 : 24 : additional_length_offset++;
1602 : :
1603 : 24 : continue;
1604 : : }
1605 : :
1606 : 188 : additional_length_offset++;
1607 : :
1608 : : // add raw hex numbers
1609 : 188 : num_str += current_char;
1610 : :
1611 : 188 : skip_input ();
1612 : 188 : current_char = peek_input ();
1613 : : }
1614 : :
1615 : 46 : if (current_char == '}')
1616 : : {
1617 : 44 : skip_input ();
1618 : 44 : current_char = peek_input ();
1619 : 44 : additional_length_offset++;
1620 : : }
1621 : : else
1622 : : {
1623 : : // actually an error, but allow propagation anyway Assume that
1624 : : // wrong bracketm whitespace or single/double quotes are wrong
1625 : : // termination, otherwise it is a wrong character, then skip to the actual
1626 : : // terminator.
1627 : : // TODO use utf-8 codepoint to skip whitespaces
1628 : 2 : if (current_char == '{' || is_whitespace (current_char.value)
1629 : 4 : || current_char == '\'' || current_char == '"')
1630 : : {
1631 : 0 : rust_error_at (get_current_location (),
1632 : : "expected terminating %<}%> in unicode escape");
1633 : 0 : return std::make_pair (Codepoint (0), additional_length_offset);
1634 : : }
1635 : : else
1636 : : {
1637 : 2 : rust_error_at (get_current_location (),
1638 : : "invalid character %qs in unicode escape",
1639 : 2 : current_char.as_string ().c_str ());
1640 : : // TODO use utf-8 codepoint to skip whitespaces
1641 : 8 : while (current_char != '}' && current_char != '{'
1642 : 6 : && !is_whitespace (current_char.value) && current_char != '\''
1643 : 14 : && current_char != '"')
1644 : : {
1645 : 6 : skip_input ();
1646 : 6 : current_char = peek_input ();
1647 : 6 : additional_length_offset++;
1648 : : }
1649 : : // Consume the actual closing bracket if found
1650 : 2 : if (current_char == '}')
1651 : : {
1652 : 2 : skip_input ();
1653 : 2 : current_char = peek_input ();
1654 : 2 : additional_length_offset++;
1655 : : }
1656 : 2 : return std::make_pair (Codepoint (0), additional_length_offset);
1657 : : }
1658 : : }
1659 : :
1660 : : // ensure 1-6 hex characters
1661 : 44 : if (num_str.length () > 6 || num_str.length () < 1)
1662 : : {
1663 : 4 : rust_error_at (get_current_location (),
1664 : : "unicode escape should be between 1 and 6 hex "
1665 : : "characters; it is %lu",
1666 : 4 : (unsigned long) num_str.length ());
1667 : : // return false;
1668 : 4 : return std::make_pair (Codepoint (0), additional_length_offset);
1669 : : }
1670 : :
1671 : 40 : unsigned long hex_num = std::strtoul (num_str.c_str (), nullptr, 16);
1672 : :
1673 : 40 : if (hex_num > 0xd7ff && hex_num < 0xe000)
1674 : : {
1675 : 4 : rust_error_at (
1676 : : get_current_location (),
1677 : : "unicode escape cannot be a surrogate value (D800 to DFFF)");
1678 : 4 : return std::make_pair (Codepoint (0), additional_length_offset);
1679 : : }
1680 : :
1681 : 36 : if (hex_num > 0x10ffff)
1682 : : {
1683 : 4 : rust_error_at (get_current_location (),
1684 : : "unicode escape cannot be larger than 10FFFF");
1685 : 4 : return std::make_pair (Codepoint (0), additional_length_offset);
1686 : : }
1687 : :
1688 : : // return true;
1689 : 32 : return std::make_pair (Codepoint (static_cast<uint32_t> (hex_num)),
1690 : : additional_length_offset);
1691 : 46 : }
1692 : :
1693 : : // Parses a byte character.
1694 : : TokenPtr
1695 : 78 : Lexer::parse_byte_char (location_t loc)
1696 : : {
1697 : 78 : skip_input ();
1698 : 78 : current_column++;
1699 : : // make current char the next character
1700 : 78 : current_char = peek_input ();
1701 : :
1702 : 78 : int length = 1;
1703 : :
1704 : : // char to save
1705 : 78 : Codepoint byte_char = 0;
1706 : :
1707 : : // detect escapes
1708 : 78 : if (current_char == '\\')
1709 : : {
1710 : 30 : auto escape_length_pair = parse_escape ('\'');
1711 : 30 : byte_char = std::get<0> (escape_length_pair);
1712 : 30 : length += std::get<1> (escape_length_pair);
1713 : :
1714 : 30 : current_char = peek_input ();
1715 : :
1716 : 30 : if (current_char != '\'')
1717 : : {
1718 : 0 : rust_error_at (get_current_location (), "unclosed %<byte char%>");
1719 : : }
1720 : :
1721 : 30 : skip_input ();
1722 : 30 : current_char = peek_input ();
1723 : 30 : length++; // go to next char
1724 : : }
1725 : 48 : else if (current_char != '\'')
1726 : : {
1727 : : // otherwise, get character from direct input character
1728 : 48 : byte_char = current_char;
1729 : :
1730 : 48 : if (!byte_char.is_ascii ())
1731 : : {
1732 : 2 : rust_error_at (get_current_location (),
1733 : : "non-ASCII character in %<byte char%>");
1734 : : }
1735 : :
1736 : 48 : skip_input ();
1737 : 48 : current_char = peek_input ();
1738 : 48 : length++;
1739 : :
1740 : 48 : if (current_char != '\'')
1741 : : {
1742 : 0 : rust_error_at (get_current_location (), "unclosed %<byte char%>");
1743 : : }
1744 : :
1745 : 48 : skip_input ();
1746 : 48 : current_char = peek_input ();
1747 : 48 : length++; // go to next char
1748 : : }
1749 : : else
1750 : : {
1751 : 0 : rust_error_at (get_current_location (),
1752 : : "no character inside %<%> for %<byte char%>");
1753 : : }
1754 : :
1755 : 78 : current_column += length;
1756 : :
1757 : 78 : loc += length - 1;
1758 : 78 : return Token::make_byte_char (loc, byte_char.value);
1759 : : }
1760 : :
1761 : : // Parses a byte string.
1762 : : TokenPtr
1763 : 63 : Lexer::parse_byte_string (location_t loc)
1764 : : {
1765 : : // byte string
1766 : :
1767 : : // skip quote character
1768 : 63 : skip_input ();
1769 : 63 : current_column++;
1770 : :
1771 : 63 : std::string str;
1772 : 63 : str.reserve (16); // some sensible default
1773 : :
1774 : 63 : current_char = peek_input ();
1775 : :
1776 : 63 : const location_t string_begin_locus = get_current_location ();
1777 : :
1778 : 435 : while (current_char != '"' && !current_char.is_eof ())
1779 : : {
1780 : 309 : if (current_char == '\\')
1781 : : {
1782 : 30 : int length = 1;
1783 : 30 : auto escape_length_pair = parse_escape ('"');
1784 : 30 : char output_char = std::get<0> (escape_length_pair);
1785 : :
1786 : 30 : if (output_char == 0 && std::get<2> (escape_length_pair))
1787 : 0 : length = std::get<1> (escape_length_pair) - 1;
1788 : : else
1789 : 30 : length += std::get<1> (escape_length_pair);
1790 : :
1791 : 30 : if (output_char != 0 || !std::get<2> (escape_length_pair))
1792 : 30 : str += output_char;
1793 : :
1794 : 30 : current_column += length;
1795 : :
1796 : 30 : continue;
1797 : 30 : }
1798 : :
1799 : 279 : current_column++;
1800 : 279 : if (current_char.value == '\n')
1801 : : {
1802 : 23 : current_line++;
1803 : 23 : current_column = 1;
1804 : : // tell line_table that new line starts
1805 : 23 : start_line (current_line, max_column_hint);
1806 : : }
1807 : :
1808 : 279 : str += current_char;
1809 : 279 : skip_input ();
1810 : 279 : current_char = peek_input ();
1811 : : }
1812 : :
1813 : 63 : if (current_char == '"')
1814 : : {
1815 : 56 : current_column++;
1816 : :
1817 : 56 : skip_input ();
1818 : 56 : current_char = peek_input ();
1819 : : }
1820 : 7 : else if (current_char.is_eof ())
1821 : : {
1822 : 7 : rust_error_at (string_begin_locus, "unended byte string literal");
1823 : 7 : return Token::make (END_OF_FILE, get_current_location ());
1824 : : }
1825 : : else
1826 : : {
1827 : : rust_unreachable ();
1828 : : }
1829 : :
1830 : 56 : str.shrink_to_fit ();
1831 : 56 : loc += str.size () - 1;
1832 : :
1833 : 56 : return Token::make_byte_string (loc, std::move (str));
1834 : 63 : }
1835 : :
1836 : : // Parses a raw byte string.
1837 : : TokenPtr
1838 : 32 : Lexer::parse_raw_byte_string (location_t loc)
1839 : : {
1840 : : // raw byte string literals
1841 : 32 : std::string str;
1842 : 32 : str.reserve (16); // some sensible default
1843 : :
1844 : 32 : int length = 1;
1845 : 32 : int hash_count = 0;
1846 : :
1847 : 32 : const location_t string_begin_locus = get_current_location ();
1848 : :
1849 : : // get hash count at beginnning
1850 : 32 : skip_input ();
1851 : 32 : current_char = peek_input ();
1852 : 32 : length++;
1853 : 32 : current_column++;
1854 : 54 : while (current_char == '#')
1855 : : {
1856 : 22 : hash_count++;
1857 : 22 : length++;
1858 : 22 : current_column++;
1859 : :
1860 : 22 : skip_input ();
1861 : 22 : current_char = peek_input ();
1862 : : }
1863 : :
1864 : 32 : if (current_char != '"')
1865 : : {
1866 : 0 : rust_error_at (get_current_location (),
1867 : : "raw byte string has no opening %<\"%>");
1868 : : }
1869 : :
1870 : 32 : skip_input ();
1871 : 32 : current_char = peek_input ();
1872 : 32 : length++;
1873 : 32 : current_column++;
1874 : :
1875 : 330 : while (true)
1876 : : {
1877 : 181 : if (current_char == '"')
1878 : : {
1879 : 51 : bool enough_hashes = true;
1880 : :
1881 : 51 : for (int i = 0; i < hash_count; i++)
1882 : : {
1883 : 26 : if (peek_input (i + 1) != '#')
1884 : : {
1885 : : enough_hashes = false;
1886 : : break;
1887 : : }
1888 : : }
1889 : :
1890 : 35 : if (enough_hashes)
1891 : : {
1892 : : // skip enough input and peek enough input
1893 : 25 : skip_input (hash_count);
1894 : 25 : current_char = peek_input ();
1895 : 25 : length += hash_count + 1;
1896 : 25 : current_column += hash_count + 1;
1897 : 25 : break;
1898 : : }
1899 : : }
1900 : 146 : else if (current_char.is_eof ())
1901 : : {
1902 : 7 : rust_error_at (string_begin_locus, "unended raw byte string literal");
1903 : 7 : return Token::make (END_OF_FILE, get_current_location ());
1904 : : }
1905 : 139 : else if (current_char.value > 127)
1906 : : {
1907 : 1 : rust_error_at (get_current_location (),
1908 : : "character %qs in raw byte string out of range",
1909 : 1 : current_char.as_string ().c_str ());
1910 : 1 : current_char = 0;
1911 : : }
1912 : :
1913 : 149 : length++;
1914 : 149 : current_column++;
1915 : 149 : if (current_char == '\n')
1916 : : {
1917 : 22 : current_line++;
1918 : 22 : current_column = 1;
1919 : 22 : start_line (current_line, max_column_hint);
1920 : : }
1921 : :
1922 : 149 : str += current_char;
1923 : 149 : skip_input ();
1924 : 149 : current_char = peek_input ();
1925 : 149 : }
1926 : :
1927 : 25 : loc += length - 1;
1928 : :
1929 : 25 : str.shrink_to_fit ();
1930 : :
1931 : 25 : return Token::make_byte_string (loc, std::move (str));
1932 : 32 : }
1933 : :
1934 : : // Parses a raw identifier.
1935 : : TokenPtr
1936 : 81 : Lexer::parse_raw_identifier (location_t loc)
1937 : : {
1938 : : // raw identifier
1939 : 81 : std::string str;
1940 : 81 : str.reserve (16); // default
1941 : :
1942 : 81 : skip_input ();
1943 : 81 : current_char = peek_input ();
1944 : :
1945 : 81 : current_column += 2;
1946 : :
1947 : 81 : bool first_is_underscore = current_char == '_';
1948 : :
1949 : 81 : int length = 0;
1950 : 81 : current_char = peek_input ();
1951 : : // loop through entire name
1952 : 475 : while (is_identifier_continue (current_char.value))
1953 : : {
1954 : 313 : length++;
1955 : :
1956 : 313 : str += current_char;
1957 : 313 : skip_input ();
1958 : 313 : current_char = peek_input ();
1959 : : }
1960 : :
1961 : 81 : current_column += length;
1962 : :
1963 : 81 : rust_debug ("raw ident: %s", str.c_str ());
1964 : :
1965 : : // if just a single underscore, not an identifier
1966 : 81 : if (first_is_underscore && length == 1)
1967 : 1 : rust_error_at (get_current_location (),
1968 : : "%<_%> is not a valid raw identifier");
1969 : :
1970 : 81 : using namespace Rust::Values;
1971 : 81 : std::set<std::string> invalid{
1972 : 81 : Keywords::CRATE, Keywords::EXTERN_KW, Keywords::SELF,
1973 : 81 : Keywords::SUPER, Keywords::SELF_ALIAS,
1974 : 486 : };
1975 : :
1976 : 81 : if (invalid.find (str) != invalid.end ())
1977 : : {
1978 : 1 : rust_error_at (get_current_location (),
1979 : : "%qs is a forbidden raw identifier", str.c_str ());
1980 : :
1981 : 1 : return nullptr;
1982 : : }
1983 : : else
1984 : : {
1985 : 80 : str.shrink_to_fit ();
1986 : 80 : loc += length - 1;
1987 : :
1988 : 80 : return Token::make_identifier (loc, std::move (str));
1989 : : }
1990 : 81 : }
1991 : :
1992 : : // skip broken string input (unterminated strings)
1993 : : void
1994 : 0 : Lexer::skip_broken_string_input (Codepoint current_char)
1995 : : {
1996 : 0 : while (current_char != '"' && !current_char.is_eof ())
1997 : : {
1998 : 0 : if (current_char == '\n')
1999 : : {
2000 : 0 : current_line++;
2001 : 0 : current_column = 1;
2002 : : }
2003 : : else
2004 : : {
2005 : 0 : current_column++;
2006 : : }
2007 : 0 : skip_input ();
2008 : 0 : current_char = peek_input ();
2009 : : }
2010 : 0 : if (current_char == '"')
2011 : : {
2012 : 0 : current_column++;
2013 : :
2014 : 0 : skip_input ();
2015 : 0 : current_char = peek_input ();
2016 : : }
2017 : 0 : rust_debug ("skipped to %d:%d due to bad quotes", current_line,
2018 : : current_column);
2019 : 0 : }
2020 : :
2021 : : // Parses a string.
2022 : : TokenPtr
2023 : 12108 : Lexer::parse_string (location_t loc)
2024 : : {
2025 : 12108 : std::string str;
2026 : 12108 : str.reserve (16); // some sensible default
2027 : :
2028 : 12108 : current_char = peek_input ();
2029 : :
2030 : 12108 : const location_t string_begin_locus = get_current_location ();
2031 : :
2032 : : // FIXME: This fails if the input ends. How do we check for EOF?
2033 : 98046 : while (current_char.value != '"' && !current_char.is_eof ())
2034 : : {
2035 : 73830 : if (current_char.value == '\\')
2036 : : {
2037 : 2674 : int length = 1;
2038 : :
2039 : : // parse escape
2040 : 2674 : auto utf8_escape_pair = parse_utf8_escape ();
2041 : 2674 : current_char = std::get<0> (utf8_escape_pair);
2042 : :
2043 : 2674 : if (current_char == Codepoint (0) && std::get<2> (utf8_escape_pair))
2044 : 28 : length = std::get<1> (utf8_escape_pair) - 1;
2045 : : else
2046 : 2646 : length += std::get<1> (utf8_escape_pair);
2047 : :
2048 : 2674 : if (current_char != Codepoint (0) || !std::get<2> (utf8_escape_pair))
2049 : 5292 : str += current_char.as_string ();
2050 : :
2051 : 2674 : current_column += length;
2052 : :
2053 : : // FIXME: should remove this but can't.
2054 : : // `parse_utf8_escape` does not update `current_char` correctly.
2055 : 2674 : current_char = peek_input ();
2056 : 2674 : continue;
2057 : 2674 : }
2058 : :
2059 : 71156 : current_column++;
2060 : 71156 : if (current_char.value == '\n')
2061 : : {
2062 : 67 : current_line++;
2063 : 67 : current_column = 1;
2064 : : // tell line_table that new line starts
2065 : 67 : start_line (current_line, max_column_hint);
2066 : : }
2067 : :
2068 : 71156 : str += current_char;
2069 : 71156 : skip_input ();
2070 : 71156 : current_char = peek_input ();
2071 : : }
2072 : :
2073 : 12108 : if (current_char.value == '"')
2074 : : {
2075 : 12094 : current_column++;
2076 : :
2077 : 12094 : skip_input ();
2078 : 12094 : current_char = peek_input ();
2079 : : }
2080 : 14 : else if (current_char.is_eof ())
2081 : : {
2082 : 14 : rust_error_at (string_begin_locus, "unended string literal");
2083 : 14 : return Token::make (END_OF_FILE, get_current_location ());
2084 : : }
2085 : : else
2086 : : {
2087 : : rust_unreachable ();
2088 : : }
2089 : :
2090 : 12094 : str.shrink_to_fit ();
2091 : :
2092 : 12094 : return Token::make_string (loc, std::move (str));
2093 : 12108 : }
2094 : :
2095 : : // Parses an identifier or keyword.
2096 : : TokenPtr
2097 : 258891 : Lexer::parse_identifier_or_keyword (location_t loc)
2098 : : {
2099 : 258891 : std::string str;
2100 : 258891 : str.reserve (16); // default
2101 : 517782 : str += current_char.as_string ();
2102 : :
2103 : 258891 : bool first_is_underscore = current_char == '_';
2104 : :
2105 : 258891 : int length = 1;
2106 : 258891 : current_char = peek_input ();
2107 : :
2108 : : // loop through entire name
2109 : 1254302 : while (is_identifier_continue (current_char.value))
2110 : : {
2111 : 736520 : auto s = current_char.as_string ();
2112 : 736520 : length++;
2113 : :
2114 : 1473040 : str += current_char.as_string ();
2115 : 736520 : skip_input ();
2116 : 736520 : current_char = peek_input ();
2117 : 736520 : }
2118 : :
2119 : 258891 : current_column += length;
2120 : :
2121 : : // if just a single underscore, not an identifier
2122 : 258891 : if (first_is_underscore && length == 1)
2123 : 1186 : return Token::make (UNDERSCORE, loc);
2124 : :
2125 : 257705 : str.shrink_to_fit ();
2126 : :
2127 : 257705 : loc += length - 1;
2128 : :
2129 : 257705 : TokenId keyword = classify_keyword (str);
2130 : 257705 : if (keyword == IDENTIFIER)
2131 : 166014 : return Token::make_identifier (loc, std::move (str));
2132 : : else
2133 : 91691 : return Token::make (keyword, loc);
2134 : 258891 : }
2135 : :
2136 : : // Possibly returns a raw string token if it exists - otherwise returns null.
2137 : : TokenPtr
2138 : 3704 : Lexer::maybe_parse_raw_string (location_t loc)
2139 : : {
2140 : 3704 : int peek_index = 0;
2141 : 3713 : while (peek_input (peek_index) == '#')
2142 : 9 : peek_index++;
2143 : :
2144 : 3704 : if (peek_input (peek_index) == '"')
2145 : 25 : return parse_raw_string (loc, peek_index);
2146 : : else
2147 : 3679 : return nullptr;
2148 : : }
2149 : :
2150 : : // Returns a raw string token.
2151 : : TokenPtr
2152 : 25 : Lexer::parse_raw_string (location_t loc, int initial_hash_count)
2153 : : {
2154 : : // raw string literals
2155 : 25 : std::string str;
2156 : 25 : str.reserve (16); // some sensible default
2157 : :
2158 : 25 : int length = 1 + initial_hash_count;
2159 : 25 : current_column += length;
2160 : :
2161 : 25 : const location_t string_begin_locus = get_current_location ();
2162 : :
2163 : 25 : if (initial_hash_count > 0)
2164 : 7 : skip_input (initial_hash_count - 1);
2165 : :
2166 : 25 : current_char = peek_input ();
2167 : :
2168 : 25 : if (current_char != '"')
2169 : 0 : rust_error_at (get_current_location (), "raw string has no opening %<\"%>");
2170 : :
2171 : 25 : length++;
2172 : 25 : current_column++;
2173 : 25 : skip_input ();
2174 : 25 : current_char = peek_input ();
2175 : :
2176 : 181 : while (true)
2177 : : {
2178 : 103 : if (current_char.value == '"')
2179 : : {
2180 : 38 : bool enough_hashes = true;
2181 : :
2182 : 38 : for (int i = 0; i < initial_hash_count; i++)
2183 : : {
2184 : 13 : if (peek_input (i + 1) != '#')
2185 : : {
2186 : : enough_hashes = false;
2187 : : break;
2188 : : }
2189 : : }
2190 : :
2191 : 28 : if (enough_hashes)
2192 : : {
2193 : : // skip enough input and peek enough input
2194 : 25 : skip_input (initial_hash_count);
2195 : 25 : current_char = peek_input ();
2196 : 25 : length += initial_hash_count + 1;
2197 : 25 : current_column += initial_hash_count + 1;
2198 : 25 : break;
2199 : : }
2200 : : }
2201 : 75 : else if (current_char.is_eof ())
2202 : : {
2203 : 0 : rust_error_at (string_begin_locus, "unended raw string literal");
2204 : 0 : return Token::make (END_OF_FILE, get_current_location ());
2205 : : }
2206 : :
2207 : 78 : length++;
2208 : 78 : current_column++;
2209 : 78 : if (current_char == '\n')
2210 : : {
2211 : 1 : current_line++;
2212 : 1 : current_column = 1;
2213 : 1 : start_line (current_line, max_column_hint);
2214 : : }
2215 : :
2216 : 156 : str += current_char.as_string ();
2217 : 78 : skip_input ();
2218 : 78 : current_char = peek_input ();
2219 : 78 : }
2220 : :
2221 : 25 : loc += length - 1;
2222 : :
2223 : 25 : str.shrink_to_fit ();
2224 : :
2225 : 25 : return Token::make_raw_string (loc, std::move (str));
2226 : 25 : }
2227 : :
2228 : : template <typename IsDigitFunc>
2229 : : TokenPtr
2230 : 191 : Lexer::parse_non_decimal_int_literal (location_t loc, IsDigitFunc is_digit_func,
2231 : : std::string existent_str, int base)
2232 : : {
2233 : 191 : int length = 1;
2234 : :
2235 : 191 : skip_input ();
2236 : 191 : current_char = peek_input ();
2237 : :
2238 : 191 : length++;
2239 : :
2240 : : // loop through to add entire number to string
2241 : 1780 : while (is_digit_func (current_char.value) || current_char == '_')
2242 : : {
2243 : 1589 : if (current_char == '_')
2244 : : {
2245 : : // don't add _ to number
2246 : 21 : skip_input ();
2247 : 21 : current_char = peek_input ();
2248 : :
2249 : 21 : length++;
2250 : :
2251 : 21 : continue;
2252 : : }
2253 : :
2254 : 1568 : length++;
2255 : :
2256 : : // add raw numbers
2257 : 1568 : existent_str += current_char;
2258 : 1568 : skip_input ();
2259 : 1568 : current_char = peek_input ();
2260 : : }
2261 : :
2262 : : // convert value to decimal representation
2263 : 191 : long dec_num = std::strtol (existent_str.c_str (), nullptr, base);
2264 : :
2265 : 191 : existent_str = std::to_string (dec_num);
2266 : :
2267 : : // parse in type suffix if it exists
2268 : 191 : auto type_suffix_pair = parse_in_type_suffix ();
2269 : 191 : PrimitiveCoreType type_hint = type_suffix_pair.first;
2270 : 191 : length += type_suffix_pair.second;
2271 : :
2272 : 191 : current_column += length;
2273 : :
2274 : 191 : if (type_hint == CORETYPE_F32 || type_hint == CORETYPE_F64)
2275 : : {
2276 : 0 : rust_error_at (get_current_location (),
2277 : : "invalid type suffix %qs for integer (%s) literal",
2278 : : get_type_hint_string (type_hint),
2279 : : base == 16
2280 : : ? "hex"
2281 : : : (base == 8 ? "octal"
2282 : : : (base == 2 ? "binary"
2283 : : : "<insert unknown base>")));
2284 : 0 : return nullptr;
2285 : : }
2286 : :
2287 : 191 : loc += length - 1;
2288 : :
2289 : 191 : return Token::make_int (loc, std::move (existent_str), type_hint);
2290 : : }
2291 : :
2292 : : // Parses a hex, binary or octal int literal.
2293 : : TokenPtr
2294 : 191 : Lexer::parse_non_decimal_int_literals (location_t loc)
2295 : : {
2296 : 191 : std::string str;
2297 : 191 : str.reserve (16); // some sensible default
2298 : 191 : str += current_char;
2299 : :
2300 : 191 : current_char = peek_input ();
2301 : :
2302 : 191 : if (current_char == 'x')
2303 : : {
2304 : : // hex (integer only)
2305 : 159 : return parse_non_decimal_int_literal (loc, is_x_digit, str + "x", 16);
2306 : : }
2307 : 32 : else if (current_char == 'o')
2308 : : {
2309 : : // octal (integer only)
2310 : 32 : return parse_non_decimal_int_literal (loc, is_octal_digit,
2311 : 16 : std::move (str), 8);
2312 : : }
2313 : 16 : else if (current_char == 'b')
2314 : : {
2315 : : // binary (integer only)
2316 : 32 : return parse_non_decimal_int_literal (loc, is_bin_digit, std::move (str),
2317 : 16 : 2);
2318 : : }
2319 : : else
2320 : : {
2321 : 0 : return nullptr;
2322 : : }
2323 : 191 : }
2324 : :
2325 : : // Parses a decimal-based int literal or float literal.
2326 : : TokenPtr
2327 : 14512 : Lexer::parse_decimal_int_or_float (location_t loc)
2328 : : {
2329 : 14512 : std::string str;
2330 : 14512 : str.reserve (16); // some sensible default
2331 : 14512 : str += current_char;
2332 : :
2333 : 14512 : int length = 1;
2334 : 14512 : bool first_zero = current_char == '0';
2335 : :
2336 : 14512 : current_char = peek_input ();
2337 : :
2338 : : // parse initial decimal integer (or first integer part of float) literal
2339 : 14512 : auto initial_decimal = parse_in_decimal ();
2340 : 14512 : str += std::get<0> (initial_decimal);
2341 : 14512 : length += std::get<1> (initial_decimal);
2342 : :
2343 : : // detect float literal
2344 : : //
2345 : : // Note:
2346 : : //
2347 : : // We should not use is_float_digit () for this verification but instead
2348 : : // directly ISDIGIT because rust does not support non digit values right after
2349 : : // a dot.
2350 : : // The following value is not legal in rust:
2351 : : // let a = 3.e1;
2352 : : // A `0` should be put between the dot and the exponent to be valid
2353 : : // (eg. 3.0e1).
2354 : 14512 : if (current_char == '.' && ISDIGIT (peek_input (1).value))
2355 : : {
2356 : : // float with a '.', parse another decimal into it
2357 : :
2358 : : // add . to str
2359 : 337 : str += current_char;
2360 : 337 : skip_input ();
2361 : 337 : current_char = peek_input ();
2362 : 337 : length++;
2363 : :
2364 : : // parse another decimal number for float
2365 : 337 : auto second_decimal = parse_in_decimal ();
2366 : 337 : str += std::get<0> (second_decimal);
2367 : 337 : length += std::get<1> (second_decimal);
2368 : :
2369 : : // parse in exponent part if it exists
2370 : 337 : auto exponent_pair = parse_in_exponent_part ();
2371 : 337 : str += exponent_pair.first;
2372 : 337 : length += exponent_pair.second;
2373 : :
2374 : : // parse in type suffix if it exists
2375 : 337 : auto type_suffix_pair = parse_in_type_suffix ();
2376 : 337 : PrimitiveCoreType type_hint = type_suffix_pair.first;
2377 : 337 : length += type_suffix_pair.second;
2378 : :
2379 : 337 : if (type_hint != CORETYPE_F32 && type_hint != CORETYPE_F64
2380 : 337 : && type_hint != CORETYPE_UNKNOWN)
2381 : : {
2382 : 0 : rust_error_at (get_current_location (),
2383 : : "invalid type suffix %qs for floating-point literal",
2384 : : get_type_hint_string (type_hint));
2385 : : // ignore invalid type suffix as everything else seems fine
2386 : 0 : type_hint = CORETYPE_UNKNOWN;
2387 : : }
2388 : :
2389 : 337 : current_column += length;
2390 : :
2391 : 337 : loc += length - 1;
2392 : :
2393 : 337 : str.shrink_to_fit ();
2394 : 337 : return Token::make_float (loc, std::move (str), type_hint);
2395 : 337 : }
2396 : 14175 : else if (current_char == '.'
2397 : 14175 : && check_valid_float_dot_end (peek_input (1).value))
2398 : : {
2399 : : // float that is just an integer with a terminating '.' character
2400 : :
2401 : : // add . to str
2402 : 8 : str += current_char;
2403 : 8 : skip_input ();
2404 : 8 : current_char = peek_input ();
2405 : 8 : length++;
2406 : :
2407 : : // type hint not allowed
2408 : :
2409 : 8 : current_column += length;
2410 : :
2411 : 8 : loc += length - 1;
2412 : :
2413 : 8 : str.shrink_to_fit ();
2414 : 8 : return Token::make_float (loc, std::move (str), CORETYPE_UNKNOWN);
2415 : : }
2416 : 14167 : else if (current_char == 'E' || current_char == 'e')
2417 : : {
2418 : : // exponent float with no '.' character
2419 : :
2420 : : // parse exponent part
2421 : 0 : auto exponent_pair = parse_in_exponent_part ();
2422 : 0 : str += exponent_pair.first;
2423 : 0 : length += exponent_pair.second;
2424 : :
2425 : : // parse in type suffix if it exists
2426 : 0 : auto type_suffix_pair = parse_in_type_suffix ();
2427 : 0 : PrimitiveCoreType type_hint = type_suffix_pair.first;
2428 : 0 : length += type_suffix_pair.second;
2429 : :
2430 : 0 : if (type_hint != CORETYPE_F32 && type_hint != CORETYPE_F64
2431 : 0 : && type_hint != CORETYPE_UNKNOWN)
2432 : : {
2433 : 0 : rust_error_at (get_current_location (),
2434 : : "invalid type suffix %qs for floating-point literal",
2435 : : get_type_hint_string (type_hint));
2436 : : // ignore invalid type suffix as everything else seems fine
2437 : 0 : type_hint = CORETYPE_UNKNOWN;
2438 : : }
2439 : :
2440 : 0 : current_column += length;
2441 : :
2442 : 0 : loc += length - 1;
2443 : :
2444 : 0 : str.shrink_to_fit ();
2445 : 0 : return Token::make_float (loc, std::move (str), type_hint);
2446 : 0 : }
2447 : : else
2448 : : {
2449 : : // is an integer
2450 : :
2451 : : // parse in type suffix if it exists
2452 : 14167 : auto type_suffix_pair = parse_in_type_suffix ();
2453 : 14167 : PrimitiveCoreType type_hint = type_suffix_pair.first;
2454 : : /* A "real" pure decimal doesn't have a suffix and no zero prefix. */
2455 : 14167 : if (type_hint == CORETYPE_UNKNOWN)
2456 : : {
2457 : 13059 : bool pure_decimal = std::get<2> (initial_decimal);
2458 : 16039 : if (pure_decimal && (!first_zero || str.size () == 1))
2459 : : type_hint = CORETYPE_PURE_DECIMAL;
2460 : : }
2461 : 14167 : length += type_suffix_pair.second;
2462 : :
2463 : 14167 : current_column += length;
2464 : :
2465 : 14167 : loc += length - 1;
2466 : :
2467 : 14167 : str.shrink_to_fit ();
2468 : 14167 : return Token::make_int (loc, std::move (str), type_hint);
2469 : : }
2470 : 14512 : }
2471 : :
2472 : : TokenPtr
2473 : 808 : Lexer::parse_char_or_lifetime (location_t loc)
2474 : : {
2475 : 808 : int length = 1;
2476 : :
2477 : 808 : current_char = peek_input ();
2478 : 808 : if (current_char.is_eof ())
2479 : 0 : return nullptr;
2480 : :
2481 : : // parse escaped char literal
2482 : 808 : if (current_char.value == '\\')
2483 : : {
2484 : : // parse escape
2485 : 23 : auto utf8_escape_pair = parse_utf8_escape ();
2486 : 23 : Codepoint escaped_char = std::get<0> (utf8_escape_pair);
2487 : 23 : length += std::get<1> (utf8_escape_pair);
2488 : :
2489 : 23 : if (peek_input ().value != '\'')
2490 : : {
2491 : 0 : rust_error_at (get_current_location (), "unended character literal");
2492 : : }
2493 : : else
2494 : : {
2495 : 23 : skip_input ();
2496 : 23 : current_char = peek_input ();
2497 : 23 : length++;
2498 : : }
2499 : :
2500 : 23 : current_column += length;
2501 : :
2502 : 23 : loc += length - 1;
2503 : :
2504 : 23 : return Token::make_char (loc, escaped_char);
2505 : : }
2506 : : else
2507 : : {
2508 : 785 : skip_input ();
2509 : :
2510 : 785 : if (peek_input ().value == '\'')
2511 : : {
2512 : : // parse non-escaped char literal
2513 : 203 : Codepoint non_escaped_char = current_char;
2514 : :
2515 : : // skip the ' character
2516 : 203 : skip_input ();
2517 : 203 : current_char = peek_input ();
2518 : :
2519 : : // TODO fix due to different widths of utf-8 chars?
2520 : 203 : current_column += 3;
2521 : :
2522 : 203 : loc += 2;
2523 : :
2524 : 203 : return Token::make_char (loc, non_escaped_char);
2525 : : }
2526 : 582 : else if (is_identifier_start (current_char.value))
2527 : : {
2528 : : // parse lifetime name
2529 : 582 : std::string str;
2530 : 1164 : str += current_char.as_string ();
2531 : 582 : length++;
2532 : :
2533 : 582 : current_char = peek_input ();
2534 : 1819 : while (is_identifier_continue (current_char.value))
2535 : : {
2536 : 1310 : str += current_char.as_string ();
2537 : 655 : skip_input ();
2538 : 655 : current_char = peek_input ();
2539 : 655 : length++;
2540 : : }
2541 : :
2542 : 582 : current_column += length;
2543 : :
2544 : 582 : loc += length - 1;
2545 : :
2546 : : // TODO some keywords cannot be used for a lifetime label #2306
2547 : : // https://doc.rust-lang.org/reference/tokens.html
2548 : :
2549 : 582 : str.shrink_to_fit ();
2550 : 582 : return Token::make_lifetime (loc, std::move (str));
2551 : 582 : }
2552 : : else
2553 : : {
2554 : 0 : rust_error_at (
2555 : : get_current_location (),
2556 : : "expected %' after character constant in character literal");
2557 : 0 : return nullptr;
2558 : : }
2559 : : }
2560 : : }
2561 : :
2562 : : void
2563 : 99 : Lexer::split_current_token (TokenId new_left, TokenId new_right)
2564 : : {
2565 : : /* TODO: assert that this TokenId is a "simple token" like punctuation and not
2566 : : * like "IDENTIFIER"? */
2567 : 99 : location_t current_loc = peek_token ()->get_locus ();
2568 : 99 : TokenPtr new_left_tok = Token::make (new_left, current_loc);
2569 : 99 : TokenPtr new_right_tok = Token::make (new_right, current_loc + 1);
2570 : :
2571 : 99 : token_queue.replace_current_value (std::move (new_left_tok));
2572 : 99 : token_queue.insert (1, std::move (new_right_tok));
2573 : 99 : }
2574 : :
2575 : : void
2576 : 2 : Lexer::split_current_token (std::vector<TokenPtr> new_tokens)
2577 : : {
2578 : 2 : rust_assert (new_tokens.size () > 0);
2579 : 4 : token_queue.replace_current_value (new_tokens[0]);
2580 : :
2581 : 5 : for (size_t i = 1; i < new_tokens.size (); i++)
2582 : : {
2583 : 6 : token_queue.insert (i, new_tokens[i]);
2584 : : }
2585 : 2 : }
2586 : :
2587 : : void
2588 : 149361 : Lexer::start_line (int current_line, int current_column)
2589 : : {
2590 : 149361 : if (line_map)
2591 : 149361 : linemap_line_start (line_table, current_line, current_column);
2592 : 149361 : }
2593 : :
2594 : : } // namespace Rust
2595 : :
2596 : : #if CHECKING_P
2597 : :
2598 : : namespace selftest {
2599 : :
2600 : : // Checks if `src` has the same contents as the given characters
2601 : : static void
2602 : 6 : assert_source_content (Rust::InputSource &src,
2603 : : const std::vector<uint32_t> &expected)
2604 : : {
2605 : 6 : Rust::Codepoint src_char = src.next ();
2606 : 41 : for (auto expected_char : expected)
2607 : : {
2608 : : // Make sure that `src` is not shorter than `expected`
2609 : 35 : ASSERT_FALSE (src_char.is_eof ());
2610 : : // Checks skipped character is expeceted one.
2611 : 35 : ASSERT_EQ (src_char.value, expected_char);
2612 : 35 : src_char = src.next ();
2613 : : }
2614 : : // Checks if `src` and `chars` has the same length.
2615 : 6 : ASSERT_TRUE (src_char.is_eof ());
2616 : 6 : }
2617 : :
2618 : : static void
2619 : 4 : test_buffer_input_source (std::string str,
2620 : : const std::vector<uint32_t> &expected)
2621 : : {
2622 : 4 : Rust::BufferInputSource source (str, 0);
2623 : 4 : assert_source_content (source, expected);
2624 : 4 : }
2625 : :
2626 : : static void
2627 : 2 : test_file_input_source (std::string str, const std::vector<uint32_t> &expected)
2628 : : {
2629 : 2 : FILE *tmpf = tmpfile ();
2630 : : // Moves to the first character
2631 : 2 : fputs (str.c_str (), tmpf);
2632 : 2 : std::rewind (tmpf);
2633 : 2 : Rust::FileInputSource source (tmpf);
2634 : 2 : assert_source_content (source, expected);
2635 : 2 : }
2636 : :
2637 : : void
2638 : 1 : rust_input_source_test ()
2639 : : {
2640 : : // ASCII
2641 : 1 : std::string src = u8"_abcde\tXYZ\v\f";
2642 : 1 : std::vector<uint32_t> expected
2643 : 1 : = {'_', 'a', 'b', 'c', 'd', 'e', '\t', 'X', 'Y', 'Z', '\v', '\f'};
2644 : 2 : test_buffer_input_source (src, expected);
2645 : :
2646 : : // BOM
2647 : 1 : src = u8"\xef\xbb\xbfOK";
2648 : 1 : expected = {'O', 'K'};
2649 : 2 : test_buffer_input_source (src, expected);
2650 : :
2651 : : // Russian
2652 : 1 : src = u8"приве́т";
2653 : 1 : expected = {L'п',
2654 : : L'р',
2655 : : L'и',
2656 : : L'в',
2657 : : 0x0435 /* CYRILLIC SMALL LETTER IE е */,
2658 : : 0x301 /* COMBINING ACUTE ACCENT ́ */,
2659 : 1 : L'т'};
2660 : 2 : test_buffer_input_source (src, expected);
2661 : :
2662 : 1 : src = u8"❤️🦀";
2663 : 1 : expected = {0x2764 /* HEAVY BLACK HEART */,
2664 : 1 : 0xfe0f /* VARIATION SELECTOR-16 */, L'🦀'};
2665 : 2 : test_buffer_input_source (src, expected);
2666 : :
2667 : 1 : src = u8"こんにちは";
2668 : 1 : expected = {L'こ', L'ん', L'に', L'ち', L'は'};
2669 : 2 : test_file_input_source (src, expected);
2670 : :
2671 : 1 : src = u8"👮♂👩⚕";
2672 : 1 : expected
2673 : : = {0x1f46e /* POLICE OFFICER */, 0x200d /* ZERO WIDTH JOINER */,
2674 : : 0x2642 /* MALE SIGN */, 0x1f469 /* WOMAN */,
2675 : 1 : 0x200d /* ZERO WIDTH JOINER */, 0x2695 /* STAFF OF AESCULAPIUS */};
2676 : 2 : test_file_input_source (src, expected);
2677 : 1 : }
2678 : :
2679 : : } // namespace selftest
2680 : :
2681 : : #endif // CHECKING_P
|