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