Skip to main content

syn/
lit.rs

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 = token.to_string();
441        if let Some((digits, suffix)) = 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 = token.to_string();
513        if let Some((digits, suffix)) = 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 = if self.value { "true" } else { "false" };
552        Ident::new(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}