]>
Commit | Line | Data |
---|---|---|
e74abb32 XL |
1 | use proc_macro2::{Literal, Span}; |
2 | use std::fmt::{self, Display}; | |
3 | use std::str::{self, FromStr}; | |
4 | ||
5 | #[cfg(feature = "printing")] | |
6 | use proc_macro2::Ident; | |
7 | ||
8 | #[cfg(feature = "parsing")] | |
9 | use proc_macro2::TokenStream; | |
10 | ||
11 | use proc_macro2::TokenTree; | |
12 | ||
13 | #[cfg(feature = "extra-traits")] | |
14 | use std::hash::{Hash, Hasher}; | |
15 | ||
16 | #[cfg(feature = "parsing")] | |
17 | use crate::lookahead; | |
18 | #[cfg(feature = "parsing")] | |
19 | use crate::parse::{Parse, Parser}; | |
20 | use crate::{Error, Result}; | |
21 | ||
22 | ast_enum_of_structs! { | |
23 | /// A Rust literal such as a string or integer or boolean. | |
24 | /// | |
e74abb32 XL |
25 | /// # Syntax tree enum |
26 | /// | |
27 | /// This type is a [syntax tree enum]. | |
28 | /// | |
29 | /// [syntax tree enum]: enum.Expr.html#syntax-tree-enums | |
30 | // | |
31 | // TODO: change syntax-tree-enum link to an intra rustdoc link, currently | |
32 | // blocked on https://github.com/rust-lang/rust/issues/62833 | |
1b1a35ee | 33 | pub enum Lit { |
e74abb32 XL |
34 | /// A UTF-8 string literal: `"foo"`. |
35 | Str(LitStr), | |
36 | ||
37 | /// A byte string literal: `b"foo"`. | |
38 | ByteStr(LitByteStr), | |
39 | ||
40 | /// A byte literal: `b'f'`. | |
41 | Byte(LitByte), | |
42 | ||
43 | /// A character literal: `'a'`. | |
44 | Char(LitChar), | |
45 | ||
46 | /// An integer literal: `1` or `1u16`. | |
47 | Int(LitInt), | |
48 | ||
49 | /// A floating point literal: `1f64` or `1.0e10f64`. | |
50 | /// | |
51 | /// Must be finite. May not be infinte or NaN. | |
52 | Float(LitFloat), | |
53 | ||
54 | /// A boolean literal: `true` or `false`. | |
55 | Bool(LitBool), | |
56 | ||
57 | /// A raw token literal not interpreted by Syn. | |
58 | Verbatim(Literal), | |
59 | } | |
60 | } | |
61 | ||
62 | ast_struct! { | |
63 | /// A UTF-8 string literal: `"foo"`. | |
1b1a35ee | 64 | pub struct LitStr { |
f035d41b | 65 | repr: Box<LitRepr>, |
e74abb32 XL |
66 | } |
67 | } | |
68 | ||
e74abb32 XL |
69 | ast_struct! { |
70 | /// A byte string literal: `b"foo"`. | |
1b1a35ee | 71 | pub struct LitByteStr { |
f035d41b | 72 | repr: Box<LitRepr>, |
e74abb32 XL |
73 | } |
74 | } | |
75 | ||
76 | ast_struct! { | |
77 | /// A byte literal: `b'f'`. | |
1b1a35ee | 78 | pub struct LitByte { |
f035d41b | 79 | repr: Box<LitRepr>, |
e74abb32 XL |
80 | } |
81 | } | |
82 | ||
83 | ast_struct! { | |
84 | /// A character literal: `'a'`. | |
1b1a35ee | 85 | pub struct LitChar { |
f035d41b | 86 | repr: Box<LitRepr>, |
e74abb32 XL |
87 | } |
88 | } | |
89 | ||
f035d41b XL |
90 | struct LitRepr { |
91 | token: Literal, | |
92 | suffix: Box<str>, | |
93 | } | |
94 | ||
e74abb32 XL |
95 | ast_struct! { |
96 | /// An integer literal: `1` or `1u16`. | |
1b1a35ee | 97 | pub struct LitInt { |
e74abb32 XL |
98 | repr: Box<LitIntRepr>, |
99 | } | |
100 | } | |
101 | ||
e74abb32 XL |
102 | struct LitIntRepr { |
103 | token: Literal, | |
104 | digits: Box<str>, | |
105 | suffix: Box<str>, | |
106 | } | |
107 | ||
108 | ast_struct! { | |
109 | /// A floating point literal: `1f64` or `1.0e10f64`. | |
110 | /// | |
111 | /// Must be finite. May not be infinte or NaN. | |
1b1a35ee | 112 | pub struct LitFloat { |
e74abb32 XL |
113 | repr: Box<LitFloatRepr>, |
114 | } | |
115 | } | |
116 | ||
e74abb32 XL |
117 | struct LitFloatRepr { |
118 | token: Literal, | |
119 | digits: Box<str>, | |
120 | suffix: Box<str>, | |
121 | } | |
122 | ||
123 | ast_struct! { | |
124 | /// A boolean literal: `true` or `false`. | |
1b1a35ee | 125 | pub struct LitBool { |
e74abb32 XL |
126 | pub value: bool, |
127 | pub span: Span, | |
128 | } | |
129 | } | |
130 | ||
e74abb32 XL |
131 | impl LitStr { |
132 | pub fn new(value: &str, span: Span) -> Self { | |
f035d41b XL |
133 | let mut token = Literal::string(value); |
134 | token.set_span(span); | |
e74abb32 | 135 | LitStr { |
f035d41b XL |
136 | repr: Box::new(LitRepr { |
137 | token, | |
e74abb32 XL |
138 | suffix: Box::<str>::default(), |
139 | }), | |
140 | } | |
141 | } | |
142 | ||
143 | pub fn value(&self) -> String { | |
f035d41b XL |
144 | let repr = self.repr.token.to_string(); |
145 | let (value, _suffix) = value::parse_lit_str(&repr); | |
e74abb32 XL |
146 | String::from(value) |
147 | } | |
148 | ||
149 | /// Parse a syntax tree node from the content of this string literal. | |
150 | /// | |
151 | /// All spans in the syntax tree will point to the span of this `LitStr`. | |
152 | /// | |
153 | /// # Example | |
154 | /// | |
155 | /// ``` | |
156 | /// use proc_macro2::Span; | |
157 | /// use syn::{Attribute, Error, Ident, Lit, Meta, MetaNameValue, Path, Result}; | |
158 | /// | |
159 | /// // Parses the path from an attribute that looks like: | |
160 | /// // | |
161 | /// // #[path = "a::b::c"] | |
162 | /// // | |
163 | /// // or returns `None` if the input is some other attribute. | |
164 | /// fn get_path(attr: &Attribute) -> Result<Option<Path>> { | |
165 | /// if !attr.path.is_ident("path") { | |
166 | /// return Ok(None); | |
167 | /// } | |
168 | /// | |
169 | /// match attr.parse_meta()? { | |
170 | /// Meta::NameValue(MetaNameValue { lit: Lit::Str(lit_str), .. }) => { | |
171 | /// lit_str.parse().map(Some) | |
172 | /// } | |
173 | /// _ => { | |
174 | /// let message = "expected #[path = \"...\"]"; | |
175 | /// Err(Error::new_spanned(attr, message)) | |
176 | /// } | |
177 | /// } | |
178 | /// } | |
179 | /// ``` | |
180 | #[cfg(feature = "parsing")] | |
181 | pub fn parse<T: Parse>(&self) -> Result<T> { | |
182 | self.parse_with(T::parse) | |
183 | } | |
184 | ||
185 | /// Invoke parser on the content of this string literal. | |
186 | /// | |
187 | /// All spans in the syntax tree will point to the span of this `LitStr`. | |
188 | /// | |
189 | /// # Example | |
190 | /// | |
191 | /// ``` | |
192 | /// # use proc_macro2::Span; | |
193 | /// # use syn::{LitStr, Result}; | |
194 | /// # | |
195 | /// # fn main() -> Result<()> { | |
196 | /// # let lit_str = LitStr::new("a::b::c", Span::call_site()); | |
197 | /// # | |
198 | /// # const IGNORE: &str = stringify! { | |
199 | /// let lit_str: LitStr = /* ... */; | |
200 | /// # }; | |
201 | /// | |
202 | /// // Parse a string literal like "a::b::c" into a Path, not allowing | |
203 | /// // generic arguments on any of the path segments. | |
204 | /// let basic_path = lit_str.parse_with(syn::Path::parse_mod_style)?; | |
205 | /// # | |
206 | /// # Ok(()) | |
207 | /// # } | |
208 | /// ``` | |
209 | #[cfg(feature = "parsing")] | |
210 | pub fn parse_with<F: Parser>(&self, parser: F) -> Result<F::Output> { | |
211 | use proc_macro2::Group; | |
212 | ||
213 | // Token stream with every span replaced by the given one. | |
214 | fn respan_token_stream(stream: TokenStream, span: Span) -> TokenStream { | |
215 | stream | |
216 | .into_iter() | |
217 | .map(|token| respan_token_tree(token, span)) | |
218 | .collect() | |
219 | } | |
220 | ||
221 | // Token tree with every span replaced by the given one. | |
222 | fn respan_token_tree(mut token: TokenTree, span: Span) -> TokenTree { | |
223 | match &mut token { | |
224 | TokenTree::Group(g) => { | |
60c5eb7d | 225 | let stream = respan_token_stream(g.stream(), span); |
e74abb32 XL |
226 | *g = Group::new(g.delimiter(), stream); |
227 | g.set_span(span); | |
228 | } | |
229 | other => other.set_span(span), | |
230 | } | |
231 | token | |
232 | } | |
233 | ||
234 | // Parse string literal into a token stream with every span equal to the | |
235 | // original literal's span. | |
236 | let mut tokens = crate::parse_str(&self.value())?; | |
237 | tokens = respan_token_stream(tokens, self.span()); | |
238 | ||
239 | parser.parse2(tokens) | |
240 | } | |
241 | ||
242 | pub fn span(&self) -> Span { | |
243 | self.repr.token.span() | |
244 | } | |
245 | ||
246 | pub fn set_span(&mut self, span: Span) { | |
247 | self.repr.token.set_span(span) | |
248 | } | |
249 | ||
250 | pub fn suffix(&self) -> &str { | |
251 | &self.repr.suffix | |
252 | } | |
253 | } | |
254 | ||
255 | impl LitByteStr { | |
256 | pub fn new(value: &[u8], span: Span) -> Self { | |
257 | let mut token = Literal::byte_string(value); | |
258 | token.set_span(span); | |
f035d41b XL |
259 | LitByteStr { |
260 | repr: Box::new(LitRepr { | |
261 | token, | |
262 | suffix: Box::<str>::default(), | |
263 | }), | |
264 | } | |
e74abb32 XL |
265 | } |
266 | ||
267 | pub fn value(&self) -> Vec<u8> { | |
f035d41b XL |
268 | let repr = self.repr.token.to_string(); |
269 | let (value, _suffix) = value::parse_lit_byte_str(&repr); | |
270 | value | |
e74abb32 XL |
271 | } |
272 | ||
273 | pub fn span(&self) -> Span { | |
f035d41b | 274 | self.repr.token.span() |
e74abb32 XL |
275 | } |
276 | ||
277 | pub fn set_span(&mut self, span: Span) { | |
f035d41b XL |
278 | self.repr.token.set_span(span) |
279 | } | |
280 | ||
281 | pub fn suffix(&self) -> &str { | |
282 | &self.repr.suffix | |
e74abb32 XL |
283 | } |
284 | } | |
285 | ||
286 | impl LitByte { | |
287 | pub fn new(value: u8, span: Span) -> Self { | |
288 | let mut token = Literal::u8_suffixed(value); | |
289 | token.set_span(span); | |
f035d41b XL |
290 | LitByte { |
291 | repr: Box::new(LitRepr { | |
292 | token, | |
293 | suffix: Box::<str>::default(), | |
294 | }), | |
295 | } | |
e74abb32 XL |
296 | } |
297 | ||
298 | pub fn value(&self) -> u8 { | |
f035d41b XL |
299 | let repr = self.repr.token.to_string(); |
300 | let (value, _suffix) = value::parse_lit_byte(&repr); | |
301 | value | |
e74abb32 XL |
302 | } |
303 | ||
304 | pub fn span(&self) -> Span { | |
f035d41b | 305 | self.repr.token.span() |
e74abb32 XL |
306 | } |
307 | ||
308 | pub fn set_span(&mut self, span: Span) { | |
f035d41b XL |
309 | self.repr.token.set_span(span) |
310 | } | |
311 | ||
312 | pub fn suffix(&self) -> &str { | |
313 | &self.repr.suffix | |
e74abb32 XL |
314 | } |
315 | } | |
316 | ||
317 | impl LitChar { | |
318 | pub fn new(value: char, span: Span) -> Self { | |
319 | let mut token = Literal::character(value); | |
320 | token.set_span(span); | |
f035d41b XL |
321 | LitChar { |
322 | repr: Box::new(LitRepr { | |
323 | token, | |
324 | suffix: Box::<str>::default(), | |
325 | }), | |
326 | } | |
e74abb32 XL |
327 | } |
328 | ||
329 | pub fn value(&self) -> char { | |
f035d41b XL |
330 | let repr = self.repr.token.to_string(); |
331 | let (value, _suffix) = value::parse_lit_char(&repr); | |
332 | value | |
e74abb32 XL |
333 | } |
334 | ||
335 | pub fn span(&self) -> Span { | |
f035d41b | 336 | self.repr.token.span() |
e74abb32 XL |
337 | } |
338 | ||
339 | pub fn set_span(&mut self, span: Span) { | |
f035d41b XL |
340 | self.repr.token.set_span(span) |
341 | } | |
342 | ||
343 | pub fn suffix(&self) -> &str { | |
344 | &self.repr.suffix | |
e74abb32 XL |
345 | } |
346 | } | |
347 | ||
348 | impl LitInt { | |
349 | pub fn new(repr: &str, span: Span) -> Self { | |
f035d41b XL |
350 | let (digits, suffix) = match value::parse_lit_int(repr) { |
351 | Some(parse) => parse, | |
352 | None => panic!("Not an integer literal: `{}`", repr), | |
353 | }; | |
354 | ||
355 | let mut token = match value::to_literal(repr, &digits, &suffix) { | |
356 | Some(token) => token, | |
357 | None => panic!("Unsupported integer literal: `{}`", repr), | |
358 | }; | |
359 | ||
360 | token.set_span(span); | |
361 | LitInt { | |
362 | repr: Box::new(LitIntRepr { | |
363 | token, | |
364 | digits, | |
365 | suffix, | |
366 | }), | |
e74abb32 XL |
367 | } |
368 | } | |
369 | ||
370 | pub fn base10_digits(&self) -> &str { | |
371 | &self.repr.digits | |
372 | } | |
373 | ||
374 | /// Parses the literal into a selected number type. | |
375 | /// | |
376 | /// This is equivalent to `lit.base10_digits().parse()` except that the | |
377 | /// resulting errors will be correctly spanned to point to the literal token | |
378 | /// in the macro input. | |
379 | /// | |
380 | /// ``` | |
381 | /// use syn::LitInt; | |
382 | /// use syn::parse::{Parse, ParseStream, Result}; | |
383 | /// | |
384 | /// struct Port { | |
385 | /// value: u16, | |
386 | /// } | |
387 | /// | |
388 | /// impl Parse for Port { | |
389 | /// fn parse(input: ParseStream) -> Result<Self> { | |
390 | /// let lit: LitInt = input.parse()?; | |
391 | /// let value = lit.base10_parse::<u16>()?; | |
392 | /// Ok(Port { value }) | |
393 | /// } | |
394 | /// } | |
395 | /// ``` | |
396 | pub fn base10_parse<N>(&self) -> Result<N> | |
397 | where | |
398 | N: FromStr, | |
399 | N::Err: Display, | |
400 | { | |
401 | self.base10_digits() | |
402 | .parse() | |
403 | .map_err(|err| Error::new(self.span(), err)) | |
404 | } | |
405 | ||
406 | pub fn suffix(&self) -> &str { | |
407 | &self.repr.suffix | |
408 | } | |
409 | ||
410 | pub fn span(&self) -> Span { | |
411 | self.repr.token.span() | |
412 | } | |
413 | ||
414 | pub fn set_span(&mut self, span: Span) { | |
415 | self.repr.token.set_span(span) | |
416 | } | |
417 | } | |
418 | ||
419 | impl From<Literal> for LitInt { | |
420 | fn from(token: Literal) -> Self { | |
421 | let repr = token.to_string(); | |
422 | if let Some((digits, suffix)) = value::parse_lit_int(&repr) { | |
423 | LitInt { | |
424 | repr: Box::new(LitIntRepr { | |
425 | token, | |
426 | digits, | |
427 | suffix, | |
428 | }), | |
429 | } | |
430 | } else { | |
431 | panic!("Not an integer literal: `{}`", repr); | |
432 | } | |
433 | } | |
434 | } | |
435 | ||
436 | impl Display for LitInt { | |
437 | fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { | |
438 | self.repr.token.fmt(formatter) | |
439 | } | |
440 | } | |
441 | ||
442 | impl LitFloat { | |
443 | pub fn new(repr: &str, span: Span) -> Self { | |
f035d41b XL |
444 | let (digits, suffix) = match value::parse_lit_float(repr) { |
445 | Some(parse) => parse, | |
446 | None => panic!("Not a float literal: `{}`", repr), | |
447 | }; | |
448 | ||
449 | let mut token = match value::to_literal(repr, &digits, &suffix) { | |
450 | Some(token) => token, | |
451 | None => panic!("Unsupported float literal: `{}`", repr), | |
452 | }; | |
453 | ||
454 | token.set_span(span); | |
455 | LitFloat { | |
456 | repr: Box::new(LitFloatRepr { | |
457 | token, | |
458 | digits, | |
459 | suffix, | |
460 | }), | |
e74abb32 XL |
461 | } |
462 | } | |
463 | ||
464 | pub fn base10_digits(&self) -> &str { | |
465 | &self.repr.digits | |
466 | } | |
467 | ||
468 | pub fn base10_parse<N>(&self) -> Result<N> | |
469 | where | |
470 | N: FromStr, | |
471 | N::Err: Display, | |
472 | { | |
473 | self.base10_digits() | |
474 | .parse() | |
475 | .map_err(|err| Error::new(self.span(), err)) | |
476 | } | |
477 | ||
478 | pub fn suffix(&self) -> &str { | |
479 | &self.repr.suffix | |
480 | } | |
481 | ||
482 | pub fn span(&self) -> Span { | |
483 | self.repr.token.span() | |
484 | } | |
485 | ||
486 | pub fn set_span(&mut self, span: Span) { | |
487 | self.repr.token.set_span(span) | |
488 | } | |
489 | } | |
490 | ||
491 | impl From<Literal> for LitFloat { | |
492 | fn from(token: Literal) -> Self { | |
493 | let repr = token.to_string(); | |
494 | if let Some((digits, suffix)) = value::parse_lit_float(&repr) { | |
495 | LitFloat { | |
496 | repr: Box::new(LitFloatRepr { | |
497 | token, | |
498 | digits, | |
499 | suffix, | |
500 | }), | |
501 | } | |
502 | } else { | |
503 | panic!("Not a float literal: `{}`", repr); | |
504 | } | |
505 | } | |
506 | } | |
507 | ||
508 | impl Display for LitFloat { | |
509 | fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { | |
510 | self.repr.token.fmt(formatter) | |
511 | } | |
512 | } | |
513 | ||
514 | #[cfg(feature = "extra-traits")] | |
515 | mod debug_impls { | |
516 | use super::*; | |
517 | use std::fmt::{self, Debug}; | |
518 | ||
519 | impl Debug for LitStr { | |
520 | fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { | |
521 | formatter | |
522 | .debug_struct("LitStr") | |
523 | .field("token", &format_args!("{}", self.repr.token)) | |
524 | .finish() | |
525 | } | |
526 | } | |
527 | ||
528 | impl Debug for LitByteStr { | |
529 | fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { | |
530 | formatter | |
531 | .debug_struct("LitByteStr") | |
f035d41b | 532 | .field("token", &format_args!("{}", self.repr.token)) |
e74abb32 XL |
533 | .finish() |
534 | } | |
535 | } | |
536 | ||
537 | impl Debug for LitByte { | |
538 | fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { | |
539 | formatter | |
540 | .debug_struct("LitByte") | |
f035d41b | 541 | .field("token", &format_args!("{}", self.repr.token)) |
e74abb32 XL |
542 | .finish() |
543 | } | |
544 | } | |
545 | ||
546 | impl Debug for LitChar { | |
547 | fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { | |
548 | formatter | |
549 | .debug_struct("LitChar") | |
f035d41b | 550 | .field("token", &format_args!("{}", self.repr.token)) |
e74abb32 XL |
551 | .finish() |
552 | } | |
553 | } | |
554 | ||
555 | impl Debug for LitInt { | |
556 | fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { | |
557 | formatter | |
558 | .debug_struct("LitInt") | |
559 | .field("token", &format_args!("{}", self.repr.token)) | |
560 | .finish() | |
561 | } | |
562 | } | |
563 | ||
564 | impl Debug for LitFloat { | |
565 | fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { | |
566 | formatter | |
567 | .debug_struct("LitFloat") | |
568 | .field("token", &format_args!("{}", self.repr.token)) | |
569 | .finish() | |
570 | } | |
571 | } | |
572 | ||
573 | impl Debug for LitBool { | |
574 | fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { | |
575 | formatter | |
576 | .debug_struct("LitBool") | |
577 | .field("value", &self.value) | |
578 | .finish() | |
579 | } | |
580 | } | |
581 | } | |
582 | ||
1b1a35ee XL |
583 | #[cfg(feature = "clone-impls")] |
584 | impl Clone for LitRepr { | |
585 | fn clone(&self) -> Self { | |
586 | LitRepr { | |
587 | token: self.token.clone(), | |
588 | suffix: self.suffix.clone(), | |
589 | } | |
590 | } | |
591 | } | |
592 | ||
593 | #[cfg(feature = "clone-impls")] | |
594 | impl Clone for LitIntRepr { | |
595 | fn clone(&self) -> Self { | |
596 | LitIntRepr { | |
597 | token: self.token.clone(), | |
598 | digits: self.digits.clone(), | |
599 | suffix: self.suffix.clone(), | |
600 | } | |
601 | } | |
602 | } | |
603 | ||
604 | #[cfg(feature = "clone-impls")] | |
605 | impl Clone for LitFloatRepr { | |
606 | fn clone(&self) -> Self { | |
607 | LitFloatRepr { | |
608 | token: self.token.clone(), | |
609 | digits: self.digits.clone(), | |
610 | suffix: self.suffix.clone(), | |
611 | } | |
612 | } | |
613 | } | |
614 | ||
e74abb32 | 615 | macro_rules! lit_extra_traits { |
1b1a35ee XL |
616 | ($ty:ident) => { |
617 | #[cfg(feature = "clone-impls")] | |
618 | impl Clone for $ty { | |
619 | fn clone(&self) -> Self { | |
620 | $ty { | |
621 | repr: self.repr.clone(), | |
622 | } | |
623 | } | |
624 | } | |
e74abb32 XL |
625 | |
626 | #[cfg(feature = "extra-traits")] | |
627 | impl PartialEq for $ty { | |
628 | fn eq(&self, other: &Self) -> bool { | |
1b1a35ee | 629 | self.repr.token.to_string() == other.repr.token.to_string() |
e74abb32 XL |
630 | } |
631 | } | |
632 | ||
633 | #[cfg(feature = "extra-traits")] | |
634 | impl Hash for $ty { | |
635 | fn hash<H>(&self, state: &mut H) | |
636 | where | |
637 | H: Hasher, | |
638 | { | |
1b1a35ee | 639 | self.repr.token.to_string().hash(state); |
e74abb32 XL |
640 | } |
641 | } | |
642 | ||
643 | #[cfg(feature = "parsing")] | |
644 | #[doc(hidden)] | |
645 | #[allow(non_snake_case)] | |
646 | pub fn $ty(marker: lookahead::TokenMarker) -> $ty { | |
647 | match marker {} | |
648 | } | |
649 | }; | |
650 | } | |
651 | ||
1b1a35ee XL |
652 | lit_extra_traits!(LitStr); |
653 | lit_extra_traits!(LitByteStr); | |
654 | lit_extra_traits!(LitByte); | |
655 | lit_extra_traits!(LitChar); | |
656 | lit_extra_traits!(LitInt); | |
657 | lit_extra_traits!(LitFloat); | |
658 | ||
659 | #[cfg(feature = "parsing")] | |
660 | #[doc(hidden)] | |
661 | #[allow(non_snake_case)] | |
662 | pub fn LitBool(marker: lookahead::TokenMarker) -> LitBool { | |
663 | match marker {} | |
664 | } | |
e74abb32 XL |
665 | |
666 | ast_enum! { | |
667 | /// The style of a string literal, either plain quoted or a raw string like | |
668 | /// `r##"data"##`. | |
e74abb32 XL |
669 | pub enum StrStyle #no_visit { |
670 | /// An ordinary string like `"data"`. | |
671 | Cooked, | |
672 | /// A raw string like `r##"data"##`. | |
673 | /// | |
674 | /// The unsigned integer is the number of `#` symbols used. | |
675 | Raw(usize), | |
676 | } | |
677 | } | |
678 | ||
679 | #[cfg(feature = "parsing")] | |
680 | #[doc(hidden)] | |
681 | #[allow(non_snake_case)] | |
682 | pub fn Lit(marker: lookahead::TokenMarker) -> Lit { | |
683 | match marker {} | |
684 | } | |
685 | ||
686 | #[cfg(feature = "parsing")] | |
687 | pub mod parsing { | |
688 | use super::*; | |
f035d41b | 689 | use crate::buffer::Cursor; |
e74abb32 | 690 | use crate::parse::{Parse, ParseStream, Result}; |
f035d41b | 691 | use proc_macro2::Punct; |
e74abb32 XL |
692 | |
693 | impl Parse for Lit { | |
694 | fn parse(input: ParseStream) -> Result<Self> { | |
695 | input.step(|cursor| { | |
696 | if let Some((lit, rest)) = cursor.literal() { | |
697 | return Ok((Lit::new(lit), rest)); | |
698 | } | |
f035d41b XL |
699 | |
700 | if let Some((ident, rest)) = cursor.ident() { | |
701 | let value = ident == "true"; | |
702 | if value || ident == "false" { | |
703 | let lit_bool = LitBool { | |
704 | value, | |
705 | span: ident.span(), | |
706 | }; | |
707 | return Ok((Lit::Bool(lit_bool), rest)); | |
708 | } | |
709 | } | |
710 | ||
711 | if let Some((punct, rest)) = cursor.punct() { | |
712 | if punct.as_char() == '-' { | |
713 | if let Some((lit, rest)) = parse_negative_lit(punct, rest) { | |
714 | return Ok((lit, rest)); | |
715 | } | |
716 | } | |
e74abb32 | 717 | } |
f035d41b | 718 | |
e74abb32 XL |
719 | Err(cursor.error("expected literal")) |
720 | }) | |
721 | } | |
722 | } | |
723 | ||
f035d41b XL |
724 | fn parse_negative_lit(neg: Punct, cursor: Cursor) -> Option<(Lit, Cursor)> { |
725 | let (lit, rest) = cursor.literal()?; | |
726 | ||
727 | let mut span = neg.span(); | |
728 | span = span.join(lit.span()).unwrap_or(span); | |
729 | ||
730 | let mut repr = lit.to_string(); | |
731 | repr.insert(0, '-'); | |
732 | ||
733 | if !(repr.ends_with("f32") || repr.ends_with("f64")) { | |
734 | if let Some((digits, suffix)) = value::parse_lit_int(&repr) { | |
735 | if let Some(mut token) = value::to_literal(&repr, &digits, &suffix) { | |
736 | token.set_span(span); | |
737 | return Some(( | |
738 | Lit::Int(LitInt { | |
739 | repr: Box::new(LitIntRepr { | |
740 | token, | |
741 | digits, | |
742 | suffix, | |
743 | }), | |
744 | }), | |
745 | rest, | |
746 | )); | |
747 | } | |
748 | } | |
749 | } | |
750 | ||
751 | let (digits, suffix) = value::parse_lit_float(&repr)?; | |
752 | let mut token = value::to_literal(&repr, &digits, &suffix)?; | |
753 | token.set_span(span); | |
754 | Some(( | |
755 | Lit::Float(LitFloat { | |
756 | repr: Box::new(LitFloatRepr { | |
757 | token, | |
758 | digits, | |
759 | suffix, | |
760 | }), | |
761 | }), | |
762 | rest, | |
763 | )) | |
764 | } | |
765 | ||
e74abb32 XL |
766 | impl Parse for LitStr { |
767 | fn parse(input: ParseStream) -> Result<Self> { | |
768 | let head = input.fork(); | |
769 | match input.parse()? { | |
770 | Lit::Str(lit) => Ok(lit), | |
771 | _ => Err(head.error("expected string literal")), | |
772 | } | |
773 | } | |
774 | } | |
775 | ||
776 | impl Parse for LitByteStr { | |
777 | fn parse(input: ParseStream) -> Result<Self> { | |
778 | let head = input.fork(); | |
779 | match input.parse()? { | |
780 | Lit::ByteStr(lit) => Ok(lit), | |
781 | _ => Err(head.error("expected byte string literal")), | |
782 | } | |
783 | } | |
784 | } | |
785 | ||
786 | impl Parse for LitByte { | |
787 | fn parse(input: ParseStream) -> Result<Self> { | |
788 | let head = input.fork(); | |
789 | match input.parse()? { | |
790 | Lit::Byte(lit) => Ok(lit), | |
791 | _ => Err(head.error("expected byte literal")), | |
792 | } | |
793 | } | |
794 | } | |
795 | ||
796 | impl Parse for LitChar { | |
797 | fn parse(input: ParseStream) -> Result<Self> { | |
798 | let head = input.fork(); | |
799 | match input.parse()? { | |
800 | Lit::Char(lit) => Ok(lit), | |
801 | _ => Err(head.error("expected character literal")), | |
802 | } | |
803 | } | |
804 | } | |
805 | ||
806 | impl Parse for LitInt { | |
807 | fn parse(input: ParseStream) -> Result<Self> { | |
808 | let head = input.fork(); | |
809 | match input.parse()? { | |
810 | Lit::Int(lit) => Ok(lit), | |
811 | _ => Err(head.error("expected integer literal")), | |
812 | } | |
813 | } | |
814 | } | |
815 | ||
816 | impl Parse for LitFloat { | |
817 | fn parse(input: ParseStream) -> Result<Self> { | |
818 | let head = input.fork(); | |
819 | match input.parse()? { | |
820 | Lit::Float(lit) => Ok(lit), | |
821 | _ => Err(head.error("expected floating point literal")), | |
822 | } | |
823 | } | |
824 | } | |
825 | ||
826 | impl Parse for LitBool { | |
827 | fn parse(input: ParseStream) -> Result<Self> { | |
828 | let head = input.fork(); | |
829 | match input.parse()? { | |
830 | Lit::Bool(lit) => Ok(lit), | |
831 | _ => Err(head.error("expected boolean literal")), | |
832 | } | |
833 | } | |
834 | } | |
835 | } | |
836 | ||
837 | #[cfg(feature = "printing")] | |
838 | mod printing { | |
839 | use super::*; | |
840 | use proc_macro2::TokenStream; | |
841 | use quote::{ToTokens, TokenStreamExt}; | |
842 | ||
843 | impl ToTokens for LitStr { | |
844 | fn to_tokens(&self, tokens: &mut TokenStream) { | |
845 | self.repr.token.to_tokens(tokens); | |
846 | } | |
847 | } | |
848 | ||
849 | impl ToTokens for LitByteStr { | |
850 | fn to_tokens(&self, tokens: &mut TokenStream) { | |
f035d41b | 851 | self.repr.token.to_tokens(tokens); |
e74abb32 XL |
852 | } |
853 | } | |
854 | ||
855 | impl ToTokens for LitByte { | |
856 | fn to_tokens(&self, tokens: &mut TokenStream) { | |
f035d41b | 857 | self.repr.token.to_tokens(tokens); |
e74abb32 XL |
858 | } |
859 | } | |
860 | ||
861 | impl ToTokens for LitChar { | |
862 | fn to_tokens(&self, tokens: &mut TokenStream) { | |
f035d41b | 863 | self.repr.token.to_tokens(tokens); |
e74abb32 XL |
864 | } |
865 | } | |
866 | ||
867 | impl ToTokens for LitInt { | |
868 | fn to_tokens(&self, tokens: &mut TokenStream) { | |
869 | self.repr.token.to_tokens(tokens); | |
870 | } | |
871 | } | |
872 | ||
873 | impl ToTokens for LitFloat { | |
874 | fn to_tokens(&self, tokens: &mut TokenStream) { | |
875 | self.repr.token.to_tokens(tokens); | |
876 | } | |
877 | } | |
878 | ||
879 | impl ToTokens for LitBool { | |
880 | fn to_tokens(&self, tokens: &mut TokenStream) { | |
881 | let s = if self.value { "true" } else { "false" }; | |
882 | tokens.append(Ident::new(s, self.span)); | |
883 | } | |
884 | } | |
885 | } | |
886 | ||
887 | mod value { | |
888 | use super::*; | |
889 | use crate::bigint::BigInt; | |
890 | use proc_macro2::TokenStream; | |
891 | use std::char; | |
892 | use std::ops::{Index, RangeFrom}; | |
893 | ||
894 | impl Lit { | |
895 | /// Interpret a Syn literal from a proc-macro2 literal. | |
896 | pub fn new(token: Literal) -> Self { | |
897 | let repr = token.to_string(); | |
898 | ||
899 | match byte(&repr, 0) { | |
900 | b'"' | b'r' => { | |
901 | let (_, suffix) = parse_lit_str(&repr); | |
902 | return Lit::Str(LitStr { | |
f035d41b | 903 | repr: Box::new(LitRepr { token, suffix }), |
e74abb32 XL |
904 | }); |
905 | } | |
906 | b'b' => match byte(&repr, 1) { | |
907 | b'"' | b'r' => { | |
f035d41b XL |
908 | let (_, suffix) = parse_lit_byte_str(&repr); |
909 | return Lit::ByteStr(LitByteStr { | |
910 | repr: Box::new(LitRepr { token, suffix }), | |
911 | }); | |
e74abb32 XL |
912 | } |
913 | b'\'' => { | |
f035d41b XL |
914 | let (_, suffix) = parse_lit_byte(&repr); |
915 | return Lit::Byte(LitByte { | |
916 | repr: Box::new(LitRepr { token, suffix }), | |
917 | }); | |
e74abb32 XL |
918 | } |
919 | _ => {} | |
920 | }, | |
921 | b'\'' => { | |
f035d41b XL |
922 | let (_, suffix) = parse_lit_char(&repr); |
923 | return Lit::Char(LitChar { | |
924 | repr: Box::new(LitRepr { token, suffix }), | |
925 | }); | |
e74abb32 XL |
926 | } |
927 | b'0'..=b'9' | b'-' => { | |
928 | if !(repr.ends_with("f32") || repr.ends_with("f64")) { | |
929 | if let Some((digits, suffix)) = parse_lit_int(&repr) { | |
930 | return Lit::Int(LitInt { | |
931 | repr: Box::new(LitIntRepr { | |
932 | token, | |
933 | digits, | |
934 | suffix, | |
935 | }), | |
936 | }); | |
937 | } | |
938 | } | |
939 | if let Some((digits, suffix)) = parse_lit_float(&repr) { | |
940 | return Lit::Float(LitFloat { | |
941 | repr: Box::new(LitFloatRepr { | |
942 | token, | |
943 | digits, | |
944 | suffix, | |
945 | }), | |
946 | }); | |
947 | } | |
948 | } | |
949 | b't' | b'f' => { | |
950 | if repr == "true" || repr == "false" { | |
951 | return Lit::Bool(LitBool { | |
952 | value: repr == "true", | |
953 | span: token.span(), | |
954 | }); | |
955 | } | |
956 | } | |
957 | _ => {} | |
958 | } | |
959 | ||
960 | panic!("Unrecognized literal: `{}`", repr); | |
961 | } | |
f035d41b XL |
962 | |
963 | pub fn suffix(&self) -> &str { | |
964 | match self { | |
965 | Lit::Str(lit) => lit.suffix(), | |
966 | Lit::ByteStr(lit) => lit.suffix(), | |
967 | Lit::Byte(lit) => lit.suffix(), | |
968 | Lit::Char(lit) => lit.suffix(), | |
969 | Lit::Int(lit) => lit.suffix(), | |
970 | Lit::Float(lit) => lit.suffix(), | |
971 | Lit::Bool(_) | Lit::Verbatim(_) => "", | |
972 | } | |
973 | } | |
3dfed10e XL |
974 | |
975 | pub fn span(&self) -> Span { | |
976 | match self { | |
977 | Lit::Str(lit) => lit.span(), | |
978 | Lit::ByteStr(lit) => lit.span(), | |
979 | Lit::Byte(lit) => lit.span(), | |
980 | Lit::Char(lit) => lit.span(), | |
981 | Lit::Int(lit) => lit.span(), | |
982 | Lit::Float(lit) => lit.span(), | |
983 | Lit::Bool(lit) => lit.span, | |
984 | Lit::Verbatim(lit) => lit.span(), | |
985 | } | |
986 | } | |
987 | ||
988 | pub fn set_span(&mut self, span: Span) { | |
989 | match self { | |
990 | Lit::Str(lit) => lit.set_span(span), | |
991 | Lit::ByteStr(lit) => lit.set_span(span), | |
992 | Lit::Byte(lit) => lit.set_span(span), | |
993 | Lit::Char(lit) => lit.set_span(span), | |
994 | Lit::Int(lit) => lit.set_span(span), | |
995 | Lit::Float(lit) => lit.set_span(span), | |
996 | Lit::Bool(lit) => lit.span = span, | |
997 | Lit::Verbatim(lit) => lit.set_span(span), | |
998 | } | |
999 | } | |
e74abb32 XL |
1000 | } |
1001 | ||
1002 | /// Get the byte at offset idx, or a default of `b'\0'` if we're looking | |
1003 | /// past the end of the input buffer. | |
1004 | pub fn byte<S: AsRef<[u8]> + ?Sized>(s: &S, idx: usize) -> u8 { | |
1005 | let s = s.as_ref(); | |
1006 | if idx < s.len() { | |
1007 | s[idx] | |
1008 | } else { | |
1009 | 0 | |
1010 | } | |
1011 | } | |
1012 | ||
1013 | fn next_chr(s: &str) -> char { | |
1014 | s.chars().next().unwrap_or('\0') | |
1015 | } | |
1016 | ||
1017 | // Returns (content, suffix). | |
1018 | pub fn parse_lit_str(s: &str) -> (Box<str>, Box<str>) { | |
1019 | match byte(s, 0) { | |
1020 | b'"' => parse_lit_str_cooked(s), | |
1021 | b'r' => parse_lit_str_raw(s), | |
1022 | _ => unreachable!(), | |
1023 | } | |
1024 | } | |
1025 | ||
1026 | // Clippy false positive | |
1027 | // https://github.com/rust-lang-nursery/rust-clippy/issues/2329 | |
1028 | #[allow(clippy::needless_continue)] | |
1029 | fn parse_lit_str_cooked(mut s: &str) -> (Box<str>, Box<str>) { | |
1030 | assert_eq!(byte(s, 0), b'"'); | |
1031 | s = &s[1..]; | |
1032 | ||
1033 | let mut content = String::new(); | |
1034 | 'outer: loop { | |
1035 | let ch = match byte(s, 0) { | |
1036 | b'"' => break, | |
1037 | b'\\' => { | |
1038 | let b = byte(s, 1); | |
1039 | s = &s[2..]; | |
1040 | match b { | |
1041 | b'x' => { | |
1042 | let (byte, rest) = backslash_x(s); | |
1043 | s = rest; | |
1044 | assert!(byte <= 0x80, "Invalid \\x byte in string literal"); | |
1045 | char::from_u32(u32::from(byte)).unwrap() | |
1046 | } | |
1047 | b'u' => { | |
1048 | let (chr, rest) = backslash_u(s); | |
1049 | s = rest; | |
1050 | chr | |
1051 | } | |
1052 | b'n' => '\n', | |
1053 | b'r' => '\r', | |
1054 | b't' => '\t', | |
1055 | b'\\' => '\\', | |
1056 | b'0' => '\0', | |
1057 | b'\'' => '\'', | |
1058 | b'"' => '"', | |
1059 | b'\r' | b'\n' => loop { | |
1060 | let ch = next_chr(s); | |
1061 | if ch.is_whitespace() { | |
1062 | s = &s[ch.len_utf8()..]; | |
1063 | } else { | |
1064 | continue 'outer; | |
1065 | } | |
1066 | }, | |
1067 | b => panic!("unexpected byte {:?} after \\ character in byte literal", b), | |
1068 | } | |
1069 | } | |
1070 | b'\r' => { | |
1071 | assert_eq!(byte(s, 1), b'\n', "Bare CR not allowed in string"); | |
1072 | s = &s[2..]; | |
1073 | '\n' | |
1074 | } | |
1075 | _ => { | |
1076 | let ch = next_chr(s); | |
1077 | s = &s[ch.len_utf8()..]; | |
1078 | ch | |
1079 | } | |
1080 | }; | |
1081 | content.push(ch); | |
1082 | } | |
1083 | ||
1084 | assert!(s.starts_with('"')); | |
1085 | let content = content.into_boxed_str(); | |
1086 | let suffix = s[1..].to_owned().into_boxed_str(); | |
1087 | (content, suffix) | |
1088 | } | |
1089 | ||
1090 | fn parse_lit_str_raw(mut s: &str) -> (Box<str>, Box<str>) { | |
1091 | assert_eq!(byte(s, 0), b'r'); | |
1092 | s = &s[1..]; | |
1093 | ||
1094 | let mut pounds = 0; | |
1095 | while byte(s, pounds) == b'#' { | |
1096 | pounds += 1; | |
1097 | } | |
1098 | assert_eq!(byte(s, pounds), b'"'); | |
f035d41b XL |
1099 | let close = s.rfind('"').unwrap(); |
1100 | for end in s[close + 1..close + 1 + pounds].bytes() { | |
e74abb32 XL |
1101 | assert_eq!(end, b'#'); |
1102 | } | |
1103 | ||
f035d41b XL |
1104 | let content = s[pounds + 1..close].to_owned().into_boxed_str(); |
1105 | let suffix = s[close + 1 + pounds..].to_owned().into_boxed_str(); | |
e74abb32 XL |
1106 | (content, suffix) |
1107 | } | |
1108 | ||
f035d41b XL |
1109 | // Returns (content, suffix). |
1110 | pub fn parse_lit_byte_str(s: &str) -> (Vec<u8>, Box<str>) { | |
e74abb32 XL |
1111 | assert_eq!(byte(s, 0), b'b'); |
1112 | match byte(s, 1) { | |
1113 | b'"' => parse_lit_byte_str_cooked(s), | |
1114 | b'r' => parse_lit_byte_str_raw(s), | |
1115 | _ => unreachable!(), | |
1116 | } | |
1117 | } | |
1118 | ||
1119 | // Clippy false positive | |
1120 | // https://github.com/rust-lang-nursery/rust-clippy/issues/2329 | |
1121 | #[allow(clippy::needless_continue)] | |
f035d41b | 1122 | fn parse_lit_byte_str_cooked(mut s: &str) -> (Vec<u8>, Box<str>) { |
e74abb32 XL |
1123 | assert_eq!(byte(s, 0), b'b'); |
1124 | assert_eq!(byte(s, 1), b'"'); | |
1125 | s = &s[2..]; | |
1126 | ||
1127 | // We're going to want to have slices which don't respect codepoint boundaries. | |
f035d41b | 1128 | let mut v = s.as_bytes(); |
e74abb32 XL |
1129 | |
1130 | let mut out = Vec::new(); | |
1131 | 'outer: loop { | |
f035d41b | 1132 | let byte = match byte(v, 0) { |
e74abb32 XL |
1133 | b'"' => break, |
1134 | b'\\' => { | |
f035d41b XL |
1135 | let b = byte(v, 1); |
1136 | v = &v[2..]; | |
e74abb32 XL |
1137 | match b { |
1138 | b'x' => { | |
f035d41b XL |
1139 | let (b, rest) = backslash_x(v); |
1140 | v = rest; | |
e74abb32 XL |
1141 | b |
1142 | } | |
1143 | b'n' => b'\n', | |
1144 | b'r' => b'\r', | |
1145 | b't' => b'\t', | |
1146 | b'\\' => b'\\', | |
1147 | b'0' => b'\0', | |
1148 | b'\'' => b'\'', | |
1149 | b'"' => b'"', | |
1150 | b'\r' | b'\n' => loop { | |
f035d41b | 1151 | let byte = byte(v, 0); |
e74abb32 XL |
1152 | let ch = char::from_u32(u32::from(byte)).unwrap(); |
1153 | if ch.is_whitespace() { | |
f035d41b | 1154 | v = &v[1..]; |
e74abb32 XL |
1155 | } else { |
1156 | continue 'outer; | |
1157 | } | |
1158 | }, | |
1159 | b => panic!("unexpected byte {:?} after \\ character in byte literal", b), | |
1160 | } | |
1161 | } | |
1162 | b'\r' => { | |
f035d41b XL |
1163 | assert_eq!(byte(v, 1), b'\n', "Bare CR not allowed in string"); |
1164 | v = &v[2..]; | |
e74abb32 XL |
1165 | b'\n' |
1166 | } | |
1167 | b => { | |
f035d41b | 1168 | v = &v[1..]; |
e74abb32 XL |
1169 | b |
1170 | } | |
1171 | }; | |
1172 | out.push(byte); | |
1173 | } | |
1174 | ||
f035d41b XL |
1175 | assert_eq!(byte(v, 0), b'"'); |
1176 | let suffix = s[s.len() - v.len() + 1..].to_owned().into_boxed_str(); | |
1177 | (out, suffix) | |
e74abb32 XL |
1178 | } |
1179 | ||
f035d41b | 1180 | fn parse_lit_byte_str_raw(s: &str) -> (Vec<u8>, Box<str>) { |
e74abb32 | 1181 | assert_eq!(byte(s, 0), b'b'); |
f035d41b XL |
1182 | let (value, suffix) = parse_lit_str_raw(&s[1..]); |
1183 | (String::from(value).into_bytes(), suffix) | |
e74abb32 XL |
1184 | } |
1185 | ||
f035d41b XL |
1186 | // Returns (value, suffix). |
1187 | pub fn parse_lit_byte(s: &str) -> (u8, Box<str>) { | |
e74abb32 XL |
1188 | assert_eq!(byte(s, 0), b'b'); |
1189 | assert_eq!(byte(s, 1), b'\''); | |
1190 | ||
1191 | // We're going to want to have slices which don't respect codepoint boundaries. | |
f035d41b | 1192 | let mut v = s[2..].as_bytes(); |
e74abb32 | 1193 | |
f035d41b | 1194 | let b = match byte(v, 0) { |
e74abb32 | 1195 | b'\\' => { |
f035d41b XL |
1196 | let b = byte(v, 1); |
1197 | v = &v[2..]; | |
e74abb32 XL |
1198 | match b { |
1199 | b'x' => { | |
f035d41b XL |
1200 | let (b, rest) = backslash_x(v); |
1201 | v = rest; | |
e74abb32 XL |
1202 | b |
1203 | } | |
1204 | b'n' => b'\n', | |
1205 | b'r' => b'\r', | |
1206 | b't' => b'\t', | |
1207 | b'\\' => b'\\', | |
1208 | b'0' => b'\0', | |
1209 | b'\'' => b'\'', | |
1210 | b'"' => b'"', | |
1211 | b => panic!("unexpected byte {:?} after \\ character in byte literal", b), | |
1212 | } | |
1213 | } | |
1214 | b => { | |
f035d41b | 1215 | v = &v[1..]; |
e74abb32 XL |
1216 | b |
1217 | } | |
1218 | }; | |
1219 | ||
f035d41b XL |
1220 | assert_eq!(byte(v, 0), b'\''); |
1221 | let suffix = s[s.len() - v.len() + 1..].to_owned().into_boxed_str(); | |
1222 | (b, suffix) | |
e74abb32 XL |
1223 | } |
1224 | ||
f035d41b XL |
1225 | // Returns (value, suffix). |
1226 | pub fn parse_lit_char(mut s: &str) -> (char, Box<str>) { | |
e74abb32 XL |
1227 | assert_eq!(byte(s, 0), b'\''); |
1228 | s = &s[1..]; | |
1229 | ||
1230 | let ch = match byte(s, 0) { | |
1231 | b'\\' => { | |
1232 | let b = byte(s, 1); | |
1233 | s = &s[2..]; | |
1234 | match b { | |
1235 | b'x' => { | |
1236 | let (byte, rest) = backslash_x(s); | |
1237 | s = rest; | |
1238 | assert!(byte <= 0x80, "Invalid \\x byte in string literal"); | |
1239 | char::from_u32(u32::from(byte)).unwrap() | |
1240 | } | |
1241 | b'u' => { | |
1242 | let (chr, rest) = backslash_u(s); | |
1243 | s = rest; | |
1244 | chr | |
1245 | } | |
1246 | b'n' => '\n', | |
1247 | b'r' => '\r', | |
1248 | b't' => '\t', | |
1249 | b'\\' => '\\', | |
1250 | b'0' => '\0', | |
1251 | b'\'' => '\'', | |
1252 | b'"' => '"', | |
1253 | b => panic!("unexpected byte {:?} after \\ character in byte literal", b), | |
1254 | } | |
1255 | } | |
1256 | _ => { | |
1257 | let ch = next_chr(s); | |
1258 | s = &s[ch.len_utf8()..]; | |
1259 | ch | |
1260 | } | |
1261 | }; | |
f035d41b XL |
1262 | assert_eq!(byte(s, 0), b'\''); |
1263 | let suffix = s[1..].to_owned().into_boxed_str(); | |
1264 | (ch, suffix) | |
e74abb32 XL |
1265 | } |
1266 | ||
1267 | fn backslash_x<S>(s: &S) -> (u8, &S) | |
1268 | where | |
1269 | S: Index<RangeFrom<usize>, Output = S> + AsRef<[u8]> + ?Sized, | |
1270 | { | |
1271 | let mut ch = 0; | |
1272 | let b0 = byte(s, 0); | |
1273 | let b1 = byte(s, 1); | |
1274 | ch += 0x10 | |
1275 | * match b0 { | |
1276 | b'0'..=b'9' => b0 - b'0', | |
1277 | b'a'..=b'f' => 10 + (b0 - b'a'), | |
1278 | b'A'..=b'F' => 10 + (b0 - b'A'), | |
1279 | _ => panic!("unexpected non-hex character after \\x"), | |
1280 | }; | |
1281 | ch += match b1 { | |
1282 | b'0'..=b'9' => b1 - b'0', | |
1283 | b'a'..=b'f' => 10 + (b1 - b'a'), | |
1284 | b'A'..=b'F' => 10 + (b1 - b'A'), | |
1285 | _ => panic!("unexpected non-hex character after \\x"), | |
1286 | }; | |
1287 | (ch, &s[2..]) | |
1288 | } | |
1289 | ||
1290 | fn backslash_u(mut s: &str) -> (char, &str) { | |
1291 | if byte(s, 0) != b'{' { | |
1292 | panic!("expected {{ after \\u"); | |
1293 | } | |
1294 | s = &s[1..]; | |
1295 | ||
1296 | let mut ch = 0; | |
1297 | for _ in 0..6 { | |
1298 | let b = byte(s, 0); | |
1299 | match b { | |
1300 | b'0'..=b'9' => { | |
1301 | ch *= 0x10; | |
1302 | ch += u32::from(b - b'0'); | |
1303 | s = &s[1..]; | |
1304 | } | |
1305 | b'a'..=b'f' => { | |
1306 | ch *= 0x10; | |
1307 | ch += u32::from(10 + b - b'a'); | |
1308 | s = &s[1..]; | |
1309 | } | |
1310 | b'A'..=b'F' => { | |
1311 | ch *= 0x10; | |
1312 | ch += u32::from(10 + b - b'A'); | |
1313 | s = &s[1..]; | |
1314 | } | |
1315 | b'}' => break, | |
1316 | _ => panic!("unexpected non-hex character after \\u"), | |
1317 | } | |
1318 | } | |
1319 | assert!(byte(s, 0) == b'}'); | |
1320 | s = &s[1..]; | |
1321 | ||
1322 | if let Some(ch) = char::from_u32(ch) { | |
1323 | (ch, s) | |
1324 | } else { | |
1325 | panic!("character code {:x} is not a valid unicode character", ch); | |
1326 | } | |
1327 | } | |
1328 | ||
1329 | // Returns base 10 digits and suffix. | |
1330 | pub fn parse_lit_int(mut s: &str) -> Option<(Box<str>, Box<str>)> { | |
1331 | let negative = byte(s, 0) == b'-'; | |
1332 | if negative { | |
1333 | s = &s[1..]; | |
1334 | } | |
1335 | ||
1336 | let base = match (byte(s, 0), byte(s, 1)) { | |
1337 | (b'0', b'x') => { | |
1338 | s = &s[2..]; | |
1339 | 16 | |
1340 | } | |
1341 | (b'0', b'o') => { | |
1342 | s = &s[2..]; | |
1343 | 8 | |
1344 | } | |
1345 | (b'0', b'b') => { | |
1346 | s = &s[2..]; | |
1347 | 2 | |
1348 | } | |
1349 | (b'0'..=b'9', _) => 10, | |
1350 | _ => return None, | |
1351 | }; | |
1352 | ||
1353 | let mut value = BigInt::new(); | |
1354 | loop { | |
1355 | let b = byte(s, 0); | |
1356 | let digit = match b { | |
1357 | b'0'..=b'9' => b - b'0', | |
1358 | b'a'..=b'f' if base > 10 => b - b'a' + 10, | |
1359 | b'A'..=b'F' if base > 10 => b - b'A' + 10, | |
1360 | b'_' => { | |
1361 | s = &s[1..]; | |
1362 | continue; | |
1363 | } | |
1364 | // NOTE: Looking at a floating point literal, we don't want to | |
1365 | // consider these integers. | |
1366 | b'.' if base == 10 => return None, | |
1367 | b'e' | b'E' if base == 10 => return None, | |
1368 | _ => break, | |
1369 | }; | |
1370 | ||
1371 | if digit >= base { | |
1372 | return None; | |
1373 | } | |
1374 | ||
1375 | value *= base; | |
1376 | value += digit; | |
1377 | s = &s[1..]; | |
1378 | } | |
1379 | ||
1380 | let suffix = s; | |
1381 | if suffix.is_empty() || crate::ident::xid_ok(&suffix) { | |
1382 | let mut repr = value.to_string(); | |
1383 | if negative { | |
1384 | repr.insert(0, '-'); | |
1385 | } | |
1386 | Some((repr.into_boxed_str(), suffix.to_owned().into_boxed_str())) | |
1387 | } else { | |
1388 | None | |
1389 | } | |
1390 | } | |
1391 | ||
1392 | // Returns base 10 digits and suffix. | |
1393 | pub fn parse_lit_float(input: &str) -> Option<(Box<str>, Box<str>)> { | |
1394 | // Rust's floating point literals are very similar to the ones parsed by | |
1395 | // the standard library, except that rust's literals can contain | |
1396 | // ignorable underscores. Let's remove those underscores. | |
1397 | ||
1398 | let mut bytes = input.to_owned().into_bytes(); | |
1399 | ||
1400 | let start = (*bytes.get(0)? == b'-') as usize; | |
1401 | match bytes.get(start)? { | |
1402 | b'0'..=b'9' => {} | |
1403 | _ => return None, | |
1404 | } | |
1405 | ||
1406 | let mut read = start; | |
1407 | let mut write = start; | |
1408 | let mut has_dot = false; | |
1409 | let mut has_e = false; | |
1410 | let mut has_sign = false; | |
1411 | let mut has_exponent = false; | |
1412 | while read < bytes.len() { | |
1413 | match bytes[read] { | |
1414 | b'_' => { | |
1415 | // Don't increase write | |
1416 | read += 1; | |
1417 | continue; | |
1418 | } | |
1419 | b'0'..=b'9' => { | |
1420 | if has_e { | |
1421 | has_exponent = true; | |
1422 | } | |
1423 | bytes[write] = bytes[read]; | |
1424 | } | |
1425 | b'.' => { | |
1426 | if has_e || has_dot { | |
1427 | return None; | |
1428 | } | |
1429 | has_dot = true; | |
1430 | bytes[write] = b'.'; | |
1431 | } | |
1432 | b'e' | b'E' => { | |
1433 | if has_e { | |
1b1a35ee XL |
1434 | if has_exponent { |
1435 | break; | |
1436 | } else { | |
1437 | return None; | |
1438 | } | |
e74abb32 XL |
1439 | } |
1440 | has_e = true; | |
1441 | bytes[write] = b'e'; | |
1442 | } | |
1443 | b'-' | b'+' => { | |
1444 | if has_sign || has_exponent || !has_e { | |
1445 | return None; | |
1446 | } | |
1447 | has_sign = true; | |
1448 | if bytes[read] == b'-' { | |
1449 | bytes[write] = bytes[read]; | |
1450 | } else { | |
1451 | // Omit '+' | |
1452 | read += 1; | |
1453 | continue; | |
1454 | } | |
1455 | } | |
1456 | _ => break, | |
1457 | } | |
1458 | read += 1; | |
1459 | write += 1; | |
1460 | } | |
1461 | ||
1462 | if has_e && !has_exponent { | |
1463 | return None; | |
1464 | } | |
1465 | ||
1466 | let mut digits = String::from_utf8(bytes).unwrap(); | |
1467 | let suffix = digits.split_off(read); | |
1468 | digits.truncate(write); | |
1469 | if suffix.is_empty() || crate::ident::xid_ok(&suffix) { | |
1470 | Some((digits.into_boxed_str(), suffix.into_boxed_str())) | |
1471 | } else { | |
1472 | None | |
1473 | } | |
1474 | } | |
1475 | ||
f035d41b XL |
1476 | pub fn to_literal(repr: &str, digits: &str, suffix: &str) -> Option<Literal> { |
1477 | if repr.starts_with('-') { | |
1478 | if suffix == "f64" { | |
1479 | digits.parse().ok().map(Literal::f64_suffixed) | |
1480 | } else if suffix == "f32" { | |
1481 | digits.parse().ok().map(Literal::f32_suffixed) | |
1482 | } else if suffix == "i64" { | |
1483 | digits.parse().ok().map(Literal::i64_suffixed) | |
1484 | } else if suffix == "i32" { | |
1485 | digits.parse().ok().map(Literal::i32_suffixed) | |
1486 | } else if suffix == "i16" { | |
1487 | digits.parse().ok().map(Literal::i16_suffixed) | |
1488 | } else if suffix == "i8" { | |
1489 | digits.parse().ok().map(Literal::i8_suffixed) | |
1490 | } else if !suffix.is_empty() { | |
1491 | None | |
1492 | } else if digits.contains('.') { | |
1493 | digits.parse().ok().map(Literal::f64_unsuffixed) | |
1494 | } else { | |
1495 | digits.parse().ok().map(Literal::i64_unsuffixed) | |
1496 | } | |
1497 | } else { | |
1498 | let stream = repr.parse::<TokenStream>().unwrap(); | |
1499 | match stream.into_iter().next().unwrap() { | |
1500 | TokenTree::Literal(l) => Some(l), | |
1501 | _ => unreachable!(), | |
1502 | } | |
e74abb32 XL |
1503 | } |
1504 | } | |
1505 | } |