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