]> git.lizzy.rs Git - rust.git/blob - crates/parser/src/lexed_str.rs
Merge #11458
[rust.git] / crates / parser / src / lexed_str.rs
1 //! Lexing `&str` into a sequence of Rust tokens.
2 //!
3 //! Note that strictly speaking the parser in this crate is not required to work
4 //! on tokens which originated from text. Macros, eg, can synthesize tokens out
5 //! of thin air. So, ideally, lexer should be an orthogonal crate. It is however
6 //! convenient to include a text-based lexer here!
7 //!
8 //! Note that these tokens, unlike the tokens we feed into the parser, do
9 //! include info about comments and whitespace.
10
11 use std::ops;
12
13 use crate::{
14     SyntaxKind::{self, *},
15     T,
16 };
17
18 pub struct LexedStr<'a> {
19     text: &'a str,
20     kind: Vec<SyntaxKind>,
21     start: Vec<u32>,
22     error: Vec<LexError>,
23 }
24
25 struct LexError {
26     msg: String,
27     token: u32,
28 }
29
30 impl<'a> LexedStr<'a> {
31     pub fn new(text: &'a str) -> LexedStr<'a> {
32         let mut res = LexedStr { text, kind: Vec::new(), start: Vec::new(), error: Vec::new() };
33
34         let mut offset = 0;
35         if let Some(shebang_len) = rustc_lexer::strip_shebang(text) {
36             res.push(SHEBANG, offset);
37             offset = shebang_len
38         };
39         for token in rustc_lexer::tokenize(&text[offset..]) {
40             let token_text = &text[offset..][..token.len];
41
42             let (kind, err) = from_rustc(&token.kind, token_text);
43             res.push(kind, offset);
44             offset += token.len;
45
46             if let Some(err) = err {
47                 let token = res.len() as u32;
48                 let msg = err.to_string();
49                 res.error.push(LexError { msg, token });
50             }
51         }
52         res.push(EOF, offset);
53
54         res
55     }
56
57     pub fn single_token(text: &'a str) -> Option<(SyntaxKind, Option<String>)> {
58         if text.is_empty() {
59             return None;
60         }
61
62         let token = rustc_lexer::first_token(text);
63         if token.len != text.len() {
64             return None;
65         }
66
67         let (kind, err) = from_rustc(&token.kind, text);
68         Some((kind, err.map(|it| it.to_owned())))
69     }
70
71     pub fn as_str(&self) -> &str {
72         self.text
73     }
74
75     pub fn len(&self) -> usize {
76         self.kind.len() - 1
77     }
78
79     pub fn is_empty(&self) -> bool {
80         self.len() == 0
81     }
82
83     pub fn kind(&self, i: usize) -> SyntaxKind {
84         assert!(i < self.len());
85         self.kind[i]
86     }
87
88     pub fn text(&self, i: usize) -> &str {
89         self.range_text(i..i + 1)
90     }
91     pub fn range_text(&self, r: ops::Range<usize>) -> &str {
92         assert!(r.start < r.end && r.end <= self.len());
93         let lo = self.start[r.start] as usize;
94         let hi = self.start[r.end] as usize;
95         &self.text[lo..hi]
96     }
97
98     // Naming is hard.
99     pub fn text_range(&self, i: usize) -> ops::Range<usize> {
100         assert!(i < self.len());
101         let lo = self.start[i] as usize;
102         let hi = self.start[i + 1] as usize;
103         lo..hi
104     }
105     pub fn text_start(&self, i: usize) -> usize {
106         assert!(i <= self.len());
107         self.start[i] as usize
108     }
109     pub fn text_len(&self, i: usize) -> usize {
110         assert!(i < self.len());
111         let r = self.text_range(i);
112         r.end - r.start
113     }
114
115     pub fn error(&self, i: usize) -> Option<&str> {
116         assert!(i < self.len());
117         let err = self.error.binary_search_by_key(&(i as u32), |i| i.token).ok()?;
118         Some(self.error[err].msg.as_str())
119     }
120
121     pub fn errors(&self) -> impl Iterator<Item = (usize, &str)> + '_ {
122         self.error.iter().map(|it| (it.token as usize, it.msg.as_str()))
123     }
124
125     fn push(&mut self, kind: SyntaxKind, offset: usize) {
126         self.kind.push(kind);
127         self.start.push(offset as u32);
128     }
129 }
130
131 /// Returns `SyntaxKind` and an optional tokenize error message.
132 fn from_rustc(
133     kind: &rustc_lexer::TokenKind,
134     token_text: &str,
135 ) -> (SyntaxKind, Option<&'static str>) {
136     // A note on an intended tradeoff:
137     // We drop some useful information here (see patterns with double dots `..`)
138     // Storing that info in `SyntaxKind` is not possible due to its layout requirements of
139     // being `u16` that come from `rowan::SyntaxKind`.
140     let mut err = "";
141
142     let syntax_kind = {
143         match kind {
144             rustc_lexer::TokenKind::LineComment { doc_style: _ } => COMMENT,
145             rustc_lexer::TokenKind::BlockComment { doc_style: _, terminated } => {
146                 if !terminated {
147                     err = "Missing trailing `*/` symbols to terminate the block comment";
148                 }
149                 COMMENT
150             }
151
152             rustc_lexer::TokenKind::Whitespace => WHITESPACE,
153
154             rustc_lexer::TokenKind::Ident if token_text == "_" => UNDERSCORE,
155             rustc_lexer::TokenKind::Ident => SyntaxKind::from_keyword(token_text).unwrap_or(IDENT),
156
157             rustc_lexer::TokenKind::RawIdent => IDENT,
158             rustc_lexer::TokenKind::Literal { kind, .. } => return from_rustc_literal(kind),
159
160             rustc_lexer::TokenKind::Lifetime { starts_with_number } => {
161                 if *starts_with_number {
162                     err = "Lifetime name cannot start with a number";
163                 }
164                 LIFETIME_IDENT
165             }
166
167             rustc_lexer::TokenKind::Semi => T![;],
168             rustc_lexer::TokenKind::Comma => T![,],
169             rustc_lexer::TokenKind::Dot => T![.],
170             rustc_lexer::TokenKind::OpenParen => T!['('],
171             rustc_lexer::TokenKind::CloseParen => T![')'],
172             rustc_lexer::TokenKind::OpenBrace => T!['{'],
173             rustc_lexer::TokenKind::CloseBrace => T!['}'],
174             rustc_lexer::TokenKind::OpenBracket => T!['['],
175             rustc_lexer::TokenKind::CloseBracket => T![']'],
176             rustc_lexer::TokenKind::At => T![@],
177             rustc_lexer::TokenKind::Pound => T![#],
178             rustc_lexer::TokenKind::Tilde => T![~],
179             rustc_lexer::TokenKind::Question => T![?],
180             rustc_lexer::TokenKind::Colon => T![:],
181             rustc_lexer::TokenKind::Dollar => T![$],
182             rustc_lexer::TokenKind::Eq => T![=],
183             rustc_lexer::TokenKind::Bang => T![!],
184             rustc_lexer::TokenKind::Lt => T![<],
185             rustc_lexer::TokenKind::Gt => T![>],
186             rustc_lexer::TokenKind::Minus => T![-],
187             rustc_lexer::TokenKind::And => T![&],
188             rustc_lexer::TokenKind::Or => T![|],
189             rustc_lexer::TokenKind::Plus => T![+],
190             rustc_lexer::TokenKind::Star => T![*],
191             rustc_lexer::TokenKind::Slash => T![/],
192             rustc_lexer::TokenKind::Caret => T![^],
193             rustc_lexer::TokenKind::Percent => T![%],
194             rustc_lexer::TokenKind::Unknown => ERROR,
195         }
196     };
197
198     let err = if err.is_empty() { None } else { Some(err) };
199     (syntax_kind, err)
200 }
201
202 fn from_rustc_literal(kind: &rustc_lexer::LiteralKind) -> (SyntaxKind, Option<&'static str>) {
203     let mut err = "";
204
205     let syntax_kind = match *kind {
206         rustc_lexer::LiteralKind::Int { empty_int, base: _ } => {
207             if empty_int {
208                 err = "Missing digits after the integer base prefix";
209             }
210             INT_NUMBER
211         }
212         rustc_lexer::LiteralKind::Float { empty_exponent, base: _ } => {
213             if empty_exponent {
214                 err = "Missing digits after the exponent symbol";
215             }
216             FLOAT_NUMBER
217         }
218         rustc_lexer::LiteralKind::Char { terminated } => {
219             if !terminated {
220                 err = "Missing trailing `'` symbol to terminate the character literal";
221             }
222             CHAR
223         }
224         rustc_lexer::LiteralKind::Byte { terminated } => {
225             if !terminated {
226                 err = "Missing trailing `'` symbol to terminate the byte literal";
227             }
228             BYTE
229         }
230         rustc_lexer::LiteralKind::Str { terminated } => {
231             if !terminated {
232                 err = "Missing trailing `\"` symbol to terminate the string literal";
233             }
234             STRING
235         }
236         rustc_lexer::LiteralKind::ByteStr { terminated } => {
237             if !terminated {
238                 err = "Missing trailing `\"` symbol to terminate the byte string literal";
239             }
240             BYTE_STRING
241         }
242         rustc_lexer::LiteralKind::RawStr { err: raw_str_err, .. } => {
243             if let Some(raw_str_err) = raw_str_err {
244                 err = match raw_str_err {
245                     rustc_lexer::RawStrError::InvalidStarter { .. } => "Missing `\"` symbol after `#` symbols to begin the raw string literal",
246                     rustc_lexer::RawStrError::NoTerminator { expected, found, .. } => if expected == found {
247                         "Missing trailing `\"` to terminate the raw string literal"
248                     } else {
249                         "Missing trailing `\"` with `#` symbols to terminate the raw string literal"
250                     },
251                     rustc_lexer::RawStrError::TooManyDelimiters { .. } => "Too many `#` symbols: raw strings may be delimited by up to 65535 `#` symbols",
252                 };
253             };
254             STRING
255         }
256         rustc_lexer::LiteralKind::RawByteStr { err: raw_str_err, .. } => {
257             if let Some(raw_str_err) = raw_str_err {
258                 err = match raw_str_err {
259                     rustc_lexer::RawStrError::InvalidStarter { .. } => "Missing `\"` symbol after `#` symbols to begin the raw byte string literal",
260                     rustc_lexer::RawStrError::NoTerminator { expected, found, .. } => if expected == found {
261                         "Missing trailing `\"` to terminate the raw byte string literal"
262                     } else {
263                         "Missing trailing `\"` with `#` symbols to terminate the raw byte string literal"
264                     },
265                     rustc_lexer::RawStrError::TooManyDelimiters { .. } => "Too many `#` symbols: raw byte strings may be delimited by up to 65535 `#` symbols",
266                 };
267             };
268
269             BYTE_STRING
270         }
271     };
272
273     let err = if err.is_empty() { None } else { Some(err) };
274     (syntax_kind, err)
275 }