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