]> git.lizzy.rs Git - rust.git/blob - src/librustc_lexer/src/lib.rs
normalize use of backticks for compiler messages in remaining modules
[rust.git] / src / librustc_lexer / src / lib.rs
1 // We want to be able to build this crate with a stable compiler, so feature
2 // flags should optional.
3 #![cfg_attr(not(feature = "unicode-xid"), feature(rustc_private))]
4 #![cfg_attr(not(feature = "unicode-xid"), feature(unicode_internals))]
5
6 mod cursor;
7
8 use crate::cursor::{Cursor, EOF_CHAR};
9
10 pub struct Token {
11     pub kind: TokenKind,
12     pub len: usize,
13 }
14
15 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
16 pub enum TokenKind {
17     LineComment,
18     BlockComment { terminated: bool },
19     Whitespace,
20     Ident,
21     RawIdent,
22     Literal { kind: LiteralKind, suffix_start: usize },
23     Lifetime { starts_with_number: bool },
24     Semi,
25     Comma,
26     DotDotDot,
27     DotDotEq,
28     DotDot,
29     Dot,
30     OpenParen,
31     CloseParen,
32     OpenBrace,
33     CloseBrace,
34     OpenBracket,
35     CloseBracket,
36     At,
37     Pound,
38     Tilde,
39     Question,
40     ColonColon,
41     Colon,
42     Dollar,
43     EqEq,
44     Eq,
45     FatArrow,
46     Ne,
47     Not,
48     Le,
49     LArrow,
50     Lt,
51     ShlEq,
52     Shl,
53     Ge,
54     Gt,
55     ShrEq,
56     Shr,
57     RArrow,
58     Minus,
59     MinusEq,
60     And,
61     AndAnd,
62     AndEq,
63     Or,
64     OrOr,
65     OrEq,
66     PlusEq,
67     Plus,
68     StarEq,
69     Star,
70     SlashEq,
71     Slash,
72     CaretEq,
73     Caret,
74     PercentEq,
75     Percent,
76     Unknown,
77 }
78 use self::TokenKind::*;
79
80 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
81 pub enum LiteralKind {
82     Int { base: Base, empty_int: bool },
83     Float { base: Base, empty_exponent: bool },
84     Char { terminated: bool },
85     Byte { terminated: bool },
86     Str { terminated: bool },
87     ByteStr { terminated: bool },
88     RawStr { n_hashes: usize, started: bool, terminated: bool },
89     RawByteStr { n_hashes: usize, started: bool, terminated: bool },
90 }
91 use self::LiteralKind::*;
92
93 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
94 pub enum Base {
95     Binary,
96     Octal,
97     Hexadecimal,
98     Decimal,
99 }
100
101 impl Token {
102     fn new(kind: TokenKind, len: usize) -> Token {
103         Token { kind, len }
104     }
105 }
106
107 pub fn strip_shebang(input: &str) -> Option<usize> {
108     debug_assert!(!input.is_empty());
109     if !input.starts_with("#!") || input.starts_with("#![") {
110         return None;
111     }
112     Some(input.find('\n').unwrap_or(input.len()))
113 }
114
115 pub fn first_token(input: &str) -> Token {
116     debug_assert!(!input.is_empty());
117     Cursor::new(input).advance_token()
118 }
119
120 pub fn tokenize(mut input: &str) -> impl Iterator<Item = Token> + '_ {
121     std::iter::from_fn(move || {
122         if input.is_empty() {
123             return None;
124         }
125         let token = first_token(input);
126         input = &input[token.len..];
127         Some(token)
128     })
129 }
130
131 impl Cursor<'_> {
132     fn advance_token(&mut self) -> Token {
133         let first_char = self.bump().unwrap();
134         let token_kind = match first_char {
135             '/' => match self.nth_char(0) {
136                 '/' => self.line_comment(),
137                 '*' => self.block_comment(),
138                 _ => {
139                     if self.eat_assign() {
140                         SlashEq
141                     } else {
142                         Slash
143                     }
144                 }
145             },
146             c if character_properties::is_whitespace(c) => self.whitespace(),
147             'r' => match (self.nth_char(0), self.nth_char(1)) {
148                 ('#', c1) if character_properties::is_id_start(c1) => self.raw_ident(),
149                 ('#', _) | ('"', _) => {
150                     let (n_hashes, started, terminated) = self.raw_double_quoted_string();
151                     let suffix_start = self.len_consumed();
152                     if terminated {
153                         self.eat_literal_suffix();
154                     }
155                     let kind = RawStr { n_hashes, started, terminated };
156                     Literal { kind, suffix_start }
157                 }
158                 _ => self.ident(),
159             },
160             'b' => match (self.nth_char(0), self.nth_char(1)) {
161                 ('\'', _) => {
162                     self.bump();
163                     let terminated = self.single_quoted_string();
164                     let suffix_start = self.len_consumed();
165                     if terminated {
166                         self.eat_literal_suffix();
167                     }
168                     let kind = Byte { terminated };
169                     Literal { kind, suffix_start }
170                 }
171                 ('"', _) => {
172                     self.bump();
173                     let terminated = self.double_quoted_string();
174                     let suffix_start = self.len_consumed();
175                     if terminated {
176                         self.eat_literal_suffix();
177                     }
178                     let kind = ByteStr { terminated };
179                     Literal { kind, suffix_start }
180                 }
181                 ('r', '"') | ('r', '#') => {
182                     self.bump();
183                     let (n_hashes, started, terminated) = self.raw_double_quoted_string();
184                     let suffix_start = self.len_consumed();
185                     if terminated {
186                         self.eat_literal_suffix();
187                     }
188                     let kind = RawByteStr { n_hashes, started, terminated };
189                     Literal { kind, suffix_start }
190                 }
191                 _ => self.ident(),
192             },
193             c if character_properties::is_id_start(c) => self.ident(),
194             c @ '0'..='9' => {
195                 let literal_kind = self.number(c);
196                 let suffix_start = self.len_consumed();
197                 self.eat_literal_suffix();
198                 TokenKind::Literal { kind: literal_kind, suffix_start }
199             }
200             ';' => Semi,
201             ',' => Comma,
202             '.' => {
203                 if self.nth_char(0) == '.' {
204                     self.bump();
205                     if self.nth_char(0) == '.' {
206                         self.bump();
207                         DotDotDot
208                     } else if self.nth_char(0) == '=' {
209                         self.bump();
210                         DotDotEq
211                     } else {
212                         DotDot
213                     }
214                 } else {
215                     Dot
216                 }
217             }
218             '(' => OpenParen,
219             ')' => CloseParen,
220             '{' => OpenBrace,
221             '}' => CloseBrace,
222             '[' => OpenBracket,
223             ']' => CloseBracket,
224             '@' => At,
225             '#' => Pound,
226             '~' => Tilde,
227             '?' => Question,
228             ':' => {
229                 if self.nth_char(0) == ':' {
230                     self.bump();
231                     ColonColon
232                 } else {
233                     Colon
234                 }
235             }
236             '$' => Dollar,
237             '=' => {
238                 if self.nth_char(0) == '=' {
239                     self.bump();
240                     EqEq
241                 } else if self.nth_char(0) == '>' {
242                     self.bump();
243                     FatArrow
244                 } else {
245                     Eq
246                 }
247             }
248             '!' => {
249                 if self.nth_char(0) == '=' {
250                     self.bump();
251                     Ne
252                 } else {
253                     Not
254                 }
255             }
256             '<' => match self.nth_char(0) {
257                 '=' => {
258                     self.bump();
259                     Le
260                 }
261                 '<' => {
262                     self.bump();
263                     if self.eat_assign() { ShlEq } else { Shl }
264                 }
265                 '-' => {
266                     self.bump();
267                     LArrow
268                 }
269                 _ => Lt,
270             },
271             '>' => match self.nth_char(0) {
272                 '=' => {
273                     self.bump();
274                     Ge
275                 }
276                 '>' => {
277                     self.bump();
278                     if self.eat_assign() { ShrEq } else { Shr }
279                 }
280                 _ => Gt,
281             },
282             '-' => {
283                 if self.nth_char(0) == '>' {
284                     self.bump();
285                     RArrow
286                 } else {
287                     if self.eat_assign() { MinusEq } else { Minus }
288                 }
289             }
290             '&' => {
291                 if self.nth_char(0) == '&' {
292                     self.bump();
293                     AndAnd
294                 } else {
295                     if self.eat_assign() { AndEq } else { And }
296                 }
297             }
298             '|' => {
299                 if self.nth_char(0) == '|' {
300                     self.bump();
301                     OrOr
302                 } else {
303                     if self.eat_assign() { OrEq } else { Or }
304                 }
305             }
306             '+' => {
307                 if self.eat_assign() {
308                     PlusEq
309                 } else {
310                     Plus
311                 }
312             }
313             '*' => {
314                 if self.eat_assign() {
315                     StarEq
316                 } else {
317                     Star
318                 }
319             }
320             '^' => {
321                 if self.eat_assign() {
322                     CaretEq
323                 } else {
324                     Caret
325                 }
326             }
327             '%' => {
328                 if self.eat_assign() {
329                     PercentEq
330                 } else {
331                     Percent
332                 }
333             }
334             '\'' => self.lifetime_or_char(),
335             '"' => {
336                 let terminated = self.double_quoted_string();
337                 let suffix_start = self.len_consumed();
338                 if terminated {
339                     self.eat_literal_suffix();
340                 }
341                 let kind = Str { terminated };
342                 Literal { kind, suffix_start }
343             }
344             _ => Unknown,
345         };
346         Token::new(token_kind, self.len_consumed())
347     }
348
349     fn line_comment(&mut self) -> TokenKind {
350         debug_assert!(self.prev() == '/' && self.nth_char(0) == '/');
351         self.bump();
352         loop {
353             match self.nth_char(0) {
354                 '\n' => break,
355                 '\r' if self.nth_char(1) == '\n' => break,
356                 EOF_CHAR if self.is_eof() => break,
357                 _ => {
358                     self.bump();
359                 }
360             }
361         }
362         LineComment
363     }
364
365     fn block_comment(&mut self) -> TokenKind {
366         debug_assert!(self.prev() == '/' && self.nth_char(0) == '*');
367         self.bump();
368         let mut depth = 1usize;
369         while let Some(c) = self.bump() {
370             match c {
371                 '/' if self.nth_char(0) == '*' => {
372                     self.bump();
373                     depth += 1;
374                 }
375                 '*' if self.nth_char(0) == '/' => {
376                     self.bump();
377                     depth -= 1;
378                     if depth == 0 {
379                         break;
380                     }
381                 }
382                 _ => (),
383             }
384         }
385
386         BlockComment { terminated: depth == 0 }
387     }
388
389     fn whitespace(&mut self) -> TokenKind {
390         debug_assert!(character_properties::is_whitespace(self.prev()));
391         while character_properties::is_whitespace(self.nth_char(0)) {
392             self.bump();
393         }
394         Whitespace
395     }
396
397     fn raw_ident(&mut self) -> TokenKind {
398         debug_assert!(
399             self.prev() == 'r'
400                 && self.nth_char(0) == '#'
401                 && character_properties::is_id_start(self.nth_char(1))
402         );
403         self.bump();
404         self.bump();
405         while character_properties::is_id_continue(self.nth_char(0)) {
406             self.bump();
407         }
408         RawIdent
409     }
410
411     fn ident(&mut self) -> TokenKind {
412         debug_assert!(character_properties::is_id_start(self.prev()));
413         while character_properties::is_id_continue(self.nth_char(0)) {
414             self.bump();
415         }
416         Ident
417     }
418
419     fn number(&mut self, first_digit: char) -> LiteralKind {
420         debug_assert!('0' <= self.prev() && self.prev() <= '9');
421         let mut base = Base::Decimal;
422         if first_digit == '0' {
423             let has_digits = match self.nth_char(0) {
424                 'b' => {
425                     base = Base::Binary;
426                     self.bump();
427                     self.eat_decimal_digits()
428                 }
429                 'o' => {
430                     base = Base::Octal;
431                     self.bump();
432                     self.eat_decimal_digits()
433                 }
434                 'x' => {
435                     base = Base::Hexadecimal;
436                     self.bump();
437                     self.eat_hexadecimal_digits()
438                 }
439                 '0'..='9' | '_' | '.' | 'e' | 'E' => {
440                     self.eat_decimal_digits();
441                     true
442                 }
443                 // just a 0
444                 _ => return Int { base, empty_int: false },
445             };
446             if !has_digits {
447                 return Int { base, empty_int: true };
448             }
449         } else {
450             self.eat_decimal_digits();
451         };
452
453         match self.nth_char(0) {
454             // Don't be greedy if this is actually an
455             // integer literal followed by field/method access or a range pattern
456             // (`0..2` and `12.foo()`)
457             '.' if self.nth_char(1) != '.'
458                 && !character_properties::is_id_start(self.nth_char(1)) =>
459             {
460                 // might have stuff after the ., and if it does, it needs to start
461                 // with a number
462                 self.bump();
463                 let mut empty_exponent = false;
464                 if self.nth_char(0).is_digit(10) {
465                     self.eat_decimal_digits();
466                     match self.nth_char(0) {
467                         'e' | 'E' => {
468                             self.bump();
469                             empty_exponent = self.float_exponent().is_err()
470                         }
471                         _ => (),
472                     }
473                 }
474                 Float { base, empty_exponent }
475             }
476             'e' | 'E' => {
477                 self.bump();
478                 let empty_exponent = self.float_exponent().is_err();
479                 Float { base, empty_exponent }
480             }
481             _ => Int { base, empty_int: false },
482         }
483     }
484
485     fn lifetime_or_char(&mut self) -> TokenKind {
486         debug_assert!(self.prev() == '\'');
487         let mut starts_with_number = false;
488         if (character_properties::is_id_start(self.nth_char(0))
489             || self.nth_char(0).is_digit(10) && {
490                 starts_with_number = true;
491                 true
492             })
493             && self.nth_char(1) != '\''
494         {
495             self.bump();
496             while character_properties::is_id_continue(self.nth_char(0)) {
497                 self.bump();
498             }
499
500             return if self.nth_char(0) == '\'' {
501                 self.bump();
502                 let kind = Char { terminated: true };
503                 Literal { kind, suffix_start: self.len_consumed() }
504             } else {
505                 Lifetime { starts_with_number }
506             };
507         }
508         let terminated = self.single_quoted_string();
509         let suffix_start = self.len_consumed();
510         if terminated {
511             self.eat_literal_suffix();
512         }
513         let kind = Char { terminated };
514         return Literal { kind, suffix_start };
515     }
516
517     fn single_quoted_string(&mut self) -> bool {
518         debug_assert!(self.prev() == '\'');
519         // parse `'''` as a single char literal
520         if self.nth_char(0) == '\'' && self.nth_char(1) == '\'' {
521             self.bump();
522         }
523         let mut first = true;
524         loop {
525             match self.nth_char(0) {
526                 '/' if !first => break,
527                 '\n' if self.nth_char(1) != '\'' => break,
528                 '\r' if self.nth_char(1) == '\n' => break,
529                 EOF_CHAR if self.is_eof() => break,
530                 '\'' => {
531                     self.bump();
532                     return true;
533                 }
534                 '\\' => {
535                     self.bump();
536                     self.bump();
537                 }
538                 _ => {
539                     self.bump();
540                 }
541             }
542             first = false;
543         }
544         false
545     }
546
547     fn double_quoted_string(&mut self) -> bool {
548         debug_assert!(self.prev() == '"');
549         loop {
550             match self.nth_char(0) {
551                 '"' => {
552                     self.bump();
553                     return true;
554                 }
555                 EOF_CHAR if self.is_eof() => return false,
556                 '\\' if self.nth_char(1) == '\\' || self.nth_char(1) == '"' => {
557                     self.bump();
558                 }
559                 _ => (),
560             }
561             self.bump();
562         }
563     }
564
565     fn raw_double_quoted_string(&mut self) -> (usize, bool, bool) {
566         debug_assert!(self.prev() == 'r');
567         let n_hashes = {
568             let mut acc: usize = 0;
569             loop {
570                 match self.bump() {
571                     Some('#') => acc += 1,
572                     Some('"') => break acc,
573                     None | Some(_) => return (acc, false, false),
574                 }
575             }
576         };
577
578         loop {
579             match self.bump() {
580                 Some('"') => {
581                     let mut acc = n_hashes;
582                     while self.nth_char(0) == '#' && acc > 0 {
583                         self.bump();
584                         acc -= 1;
585                     }
586                     if acc == 0 {
587                         return (n_hashes, true, true);
588                     }
589                 }
590                 Some(_) => (),
591                 None => return (n_hashes, true, false),
592             }
593         }
594     }
595
596     fn eat_decimal_digits(&mut self) -> bool {
597         let mut has_digits = false;
598         loop {
599             match self.nth_char(0) {
600                 '_' => {
601                     self.bump();
602                 }
603                 '0'..='9' => {
604                     has_digits = true;
605                     self.bump();
606                 }
607                 _ => break,
608             }
609         }
610         has_digits
611     }
612
613     fn eat_hexadecimal_digits(&mut self) -> bool {
614         let mut has_digits = false;
615         loop {
616             match self.nth_char(0) {
617                 '_' => {
618                     self.bump();
619                 }
620                 '0'..='9' | 'a'..='f' | 'A'..='F' => {
621                     has_digits = true;
622                     self.bump();
623                 }
624                 _ => break,
625             }
626         }
627         has_digits
628     }
629
630     fn float_exponent(&mut self) -> Result<(), ()> {
631         debug_assert!(self.prev() == 'e' || self.prev() == 'E');
632         if self.nth_char(0) == '-' || self.nth_char(0) == '+' {
633             self.bump();
634         }
635         if self.eat_decimal_digits() { Ok(()) } else { Err(()) }
636     }
637
638     fn eat_literal_suffix(&mut self) {
639         if !character_properties::is_id_start(self.nth_char(0)) {
640             return;
641         }
642         self.bump();
643
644         while character_properties::is_id_continue(self.nth_char(0)) {
645             self.bump();
646         }
647     }
648
649     fn eat_assign(&mut self) -> bool {
650         if self.nth_char(0) == '=' {
651             self.bump();
652             true
653         } else {
654             false
655         }
656     }
657 }
658
659 pub mod character_properties {
660     // this is Pattern_White_Space
661     #[cfg(feature = "unicode-xid")]
662     pub fn is_whitespace(c: char) -> bool {
663         match c {
664             '\u{0009}' | '\u{000A}' | '\u{000B}' | '\u{000C}' | '\u{000D}' | '\u{0020}'
665             | '\u{0085}' | '\u{200E}' | '\u{200F}' | '\u{2028}' | '\u{2029}' => true,
666             _ => false,
667         }
668     }
669
670     #[cfg(not(feature = "unicode-xid"))]
671     pub fn is_whitespace(c: char) -> bool {
672         core::unicode::property::Pattern_White_Space(c)
673     }
674
675     // this is XID_Start OR '_' (which formally is not a XID_Start)
676     #[cfg(feature = "unicode-xid")]
677     pub fn is_id_start(c: char) -> bool {
678         ('a' <= c && c <= 'z')
679             || ('A' <= c && c <= 'Z')
680             || c == '_'
681             || (c > '\x7f' && unicode_xid::UnicodeXID::is_xid_start(c))
682     }
683
684     #[cfg(not(feature = "unicode-xid"))]
685     pub fn is_id_start(c: char) -> bool {
686         ('a' <= c && c <= 'z')
687             || ('A' <= c && c <= 'Z')
688             || c == '_'
689             || (c > '\x7f' && c.is_xid_start())
690     }
691
692     // this is XID_Continue
693     #[cfg(feature = "unicode-xid")]
694     pub fn is_id_continue(c: char) -> bool {
695         ('a' <= c && c <= 'z')
696             || ('A' <= c && c <= 'Z')
697             || ('0' <= c && c <= '9')
698             || c == '_'
699             || (c > '\x7f' && unicode_xid::UnicodeXID::is_xid_continue(c))
700     }
701
702     #[cfg(not(feature = "unicode-xid"))]
703     pub fn is_id_continue(c: char) -> bool {
704         ('a' <= c && c <= 'z')
705             || ('A' <= c && c <= 'Z')
706             || ('0' <= c && c <= '9')
707             || c == '_'
708             || (c > '\x7f' && c.is_xid_continue())
709     }
710 }