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
787ast_enum! {
788 /// The style of a string literal, either plain quoted or a raw string like
789 /// `r##"data"##`.
790 pub enum StrStyle #no_visit {
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
800#[cfg(feature = "parsing")]
801pub_if_not_doc! {
802 #[doc(hidden)]
803 #[allow(non_snake_case)]
804 pub fn Lit(marker: lookahead::TokenMarker) -> Lit {
805 match marker {}
806 }
807}
808
809#[cfg(feature = "parsing")]
810pub(crate) mod parsing {
811 use super::*;
812 use crate::buffer::Cursor;
813 use crate::parse::{Parse, ParseStream, Result};
814 use proc_macro2::Punct;
815
816 #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
817 impl Parse for Lit {
818 fn parse(input: ParseStream) -> Result<Self> {
819 input.step(|cursor| {
820 if let Some((lit, rest)) = cursor.literal() {
821 return Ok((Lit::new(lit), rest));
822 }
823
824 if let Some((ident, rest)) = cursor.ident() {
825 let value = ident == "true";
826 if value || ident == "false" {
827 let lit_bool = LitBool {
828 value,
829 span: ident.span(),
830 };
831 return Ok((Lit::Bool(lit_bool), rest));
832 }
833 }
834
835 if let Some((punct, rest)) = cursor.punct() {
836 if punct.as_char() == '-' {
837 if let Some((lit, rest)) = parse_negative_lit(punct, rest) {
838 return Ok((lit, rest));
839 }
840 }
841 }
842
843 Err(cursor.error("expected literal"))
844 })
845 }
846 }
847
848 fn parse_negative_lit(neg: Punct, cursor: Cursor) -> Option<(Lit, Cursor)> {
849 let (lit, rest) = cursor.literal()?;
850
851 let mut span = neg.span();
852 span = span.join(lit.span()).unwrap_or(span);
853
854 let mut repr = lit.to_string();
855 repr.insert(0, '-');
856
857 if let Some((digits, suffix)) = value::parse_lit_int(&repr) {
858 let mut token: Literal = repr.parse().unwrap();
859 token.set_span(span);
860 return Some((
861 Lit::Int(LitInt {
862 repr: Box::new(LitIntRepr {
863 token,
864 digits,
865 suffix,
866 }),
867 }),
868 rest,
869 ));
870 }
871
872 let (digits, suffix) = value::parse_lit_float(&repr)?;
873 let mut token: Literal = repr.parse().unwrap();
874 token.set_span(span);
875 Some((
876 Lit::Float(LitFloat {
877 repr: Box::new(LitFloatRepr {
878 token,
879 digits,
880 suffix,
881 }),
882 }),
883 rest,
884 ))
885 }
886
887 #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
888 impl Parse for LitStr {
889 fn parse(input: ParseStream) -> Result<Self> {
890 let head = input.fork();
891 match input.parse() {
892 Ok(Lit::Str(lit)) => Ok(lit),
893 _ => Err(head.error("expected string literal")),
894 }
895 }
896 }
897
898 #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
899 impl Parse for LitByteStr {
900 fn parse(input: ParseStream) -> Result<Self> {
901 let head = input.fork();
902 match input.parse() {
903 Ok(Lit::ByteStr(lit)) => Ok(lit),
904 _ => Err(head.error("expected byte string literal")),
905 }
906 }
907 }
908
909 #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
910 impl Parse for LitByte {
911 fn parse(input: ParseStream) -> Result<Self> {
912 let head = input.fork();
913 match input.parse() {
914 Ok(Lit::Byte(lit)) => Ok(lit),
915 _ => Err(head.error("expected byte literal")),
916 }
917 }
918 }
919
920 #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
921 impl Parse for LitChar {
922 fn parse(input: ParseStream) -> Result<Self> {
923 let head = input.fork();
924 match input.parse() {
925 Ok(Lit::Char(lit)) => Ok(lit),
926 _ => Err(head.error("expected character literal")),
927 }
928 }
929 }
930
931 #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
932 impl Parse for LitInt {
933 fn parse(input: ParseStream) -> Result<Self> {
934 let head = input.fork();
935 match input.parse() {
936 Ok(Lit::Int(lit)) => Ok(lit),
937 _ => Err(head.error("expected integer literal")),
938 }
939 }
940 }
941
942 #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
943 impl Parse for LitFloat {
944 fn parse(input: ParseStream) -> Result<Self> {
945 let head = input.fork();
946 match input.parse() {
947 Ok(Lit::Float(lit)) => Ok(lit),
948 _ => Err(head.error("expected floating point literal")),
949 }
950 }
951 }
952
953 #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
954 impl Parse for LitBool {
955 fn parse(input: ParseStream) -> Result<Self> {
956 let head = input.fork();
957 match input.parse() {
958 Ok(Lit::Bool(lit)) => Ok(lit),
959 _ => Err(head.error("expected boolean literal")),
960 }
961 }
962 }
963}
964
965#[cfg(feature = "printing")]
966mod printing {
967 use super::*;
968 use proc_macro2::TokenStream;
969 use quote::{ToTokens, TokenStreamExt};
970
971 #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))]
972 impl ToTokens for LitStr {
973 fn to_tokens(&self, tokens: &mut TokenStream) {
974 self.repr.token.to_tokens(tokens);
975 }
976 }
977
978 #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))]
979 impl ToTokens for LitByteStr {
980 fn to_tokens(&self, tokens: &mut TokenStream) {
981 self.repr.token.to_tokens(tokens);
982 }
983 }
984
985 #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))]
986 impl ToTokens for LitByte {
987 fn to_tokens(&self, tokens: &mut TokenStream) {
988 self.repr.token.to_tokens(tokens);
989 }
990 }
991
992 #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))]
993 impl ToTokens for LitChar {
994 fn to_tokens(&self, tokens: &mut TokenStream) {
995 self.repr.token.to_tokens(tokens);
996 }
997 }
998
999 #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))]
1000 impl ToTokens for LitInt {
1001 fn to_tokens(&self, tokens: &mut TokenStream) {
1002 self.repr.token.to_tokens(tokens);
1003 }
1004 }
1005
1006 #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))]
1007 impl ToTokens for LitFloat {
1008 fn to_tokens(&self, tokens: &mut TokenStream) {
1009 self.repr.token.to_tokens(tokens);
1010 }
1011 }
1012
1013 #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))]
1014 impl ToTokens for LitBool {
1015 fn to_tokens(&self, tokens: &mut TokenStream) {
1016 tokens.append(self.token());
1017 }
1018 }
1019}
1020
1021mod value {
1022 use super::*;
1023 use crate::bigint::BigInt;
1024 use std::char;
1025 use std::ops::{Index, RangeFrom};
1026
1027 impl Lit {
1028 /// Interpret a Syn literal from a proc-macro2 literal.
1029 pub fn new(token: Literal) -> Self {
1030 let repr = token.to_string();
1031
1032 match byte(&repr, 0) {
1033 // "...", r"...", r#"..."#
1034 b'"' | b'r' => {
1035 let (_, suffix) = parse_lit_str(&repr);
1036 return Lit::Str(LitStr {
1037 repr: Box::new(LitRepr { token, suffix }),
1038 });
1039 }
1040 b'b' => match byte(&repr, 1) {
1041 // b"...", br"...", br#"...#"
1042 b'"' | b'r' => {
1043 let (_, suffix) = parse_lit_byte_str(&repr);
1044 return Lit::ByteStr(LitByteStr {
1045 repr: Box::new(LitRepr { token, suffix }),
1046 });
1047 }
1048 // b'...'
1049 b'\'' => {
1050 let (_, suffix) = parse_lit_byte(&repr);
1051 return Lit::Byte(LitByte {
1052 repr: Box::new(LitRepr { token, suffix }),
1053 });
1054 }
1055 _ => {}
1056 },
1057 // '...'
1058 b'\'' => {
1059 let (_, suffix) = parse_lit_char(&repr);
1060 return Lit::Char(LitChar {
1061 repr: Box::new(LitRepr { token, suffix }),
1062 });
1063 }
1064 b'0'..=b'9' | b'-' => {
1065 // 0, 123, 0xFF, 0o77, 0b11
1066 if let Some((digits, suffix)) = parse_lit_int(&repr) {
1067 return Lit::Int(LitInt {
1068 repr: Box::new(LitIntRepr {
1069 token,
1070 digits,
1071 suffix,
1072 }),
1073 });
1074 }
1075 // 1.0, 1e-1, 1e+1
1076 if let Some((digits, suffix)) = parse_lit_float(&repr) {
1077 return Lit::Float(LitFloat {
1078 repr: Box::new(LitFloatRepr {
1079 token,
1080 digits,
1081 suffix,
1082 }),
1083 });
1084 }
1085 }
1086 // true, false
1087 b't' | b'f' => {
1088 if repr == "true" || repr == "false" {
1089 return Lit::Bool(LitBool {
1090 value: repr == "true",
1091 span: token.span(),
1092 });
1093 }
1094 }
1095 // c"...", cr"...", cr#"..."#
1096 // TODO: add a Lit::CStr variant?
1097 b'c' => return Lit::Verbatim(token),
1098 b'(' if repr == "(/*ERROR*/)" => return Lit::Verbatim(token),
1099 _ => {}
1100 }
1101
1102 panic!("Unrecognized literal: `{}`", repr);
1103 }
1104
1105 pub fn suffix(&self) -> &str {
1106 match self {
1107 Lit::Str(lit) => lit.suffix(),
1108 Lit::ByteStr(lit) => lit.suffix(),
1109 Lit::Byte(lit) => lit.suffix(),
1110 Lit::Char(lit) => lit.suffix(),
1111 Lit::Int(lit) => lit.suffix(),
1112 Lit::Float(lit) => lit.suffix(),
1113 Lit::Bool(_) | Lit::Verbatim(_) => "",
1114 }
1115 }
1116
1117 pub fn span(&self) -> Span {
1118 match self {
1119 Lit::Str(lit) => lit.span(),
1120 Lit::ByteStr(lit) => lit.span(),
1121 Lit::Byte(lit) => lit.span(),
1122 Lit::Char(lit) => lit.span(),
1123 Lit::Int(lit) => lit.span(),
1124 Lit::Float(lit) => lit.span(),
1125 Lit::Bool(lit) => lit.span,
1126 Lit::Verbatim(lit) => lit.span(),
1127 }
1128 }
1129
1130 pub fn set_span(&mut self, span: Span) {
1131 match self {
1132 Lit::Str(lit) => lit.set_span(span),
1133 Lit::ByteStr(lit) => lit.set_span(span),
1134 Lit::Byte(lit) => lit.set_span(span),
1135 Lit::Char(lit) => lit.set_span(span),
1136 Lit::Int(lit) => lit.set_span(span),
1137 Lit::Float(lit) => lit.set_span(span),
1138 Lit::Bool(lit) => lit.span = span,
1139 Lit::Verbatim(lit) => lit.set_span(span),
1140 }
1141 }
1142 }
1143
1144 /// Get the byte at offset idx, or a default of `b'\0'` if we're looking
1145 /// past the end of the input buffer.
1146 pub(crate) fn byte<S: AsRef<[u8]> + ?Sized>(s: &S, idx: usize) -> u8 {
1147 let s = s.as_ref();
1148 if idx < s.len() {
1149 s[idx]
1150 } else {
1151 0
1152 }
1153 }
1154
1155 fn next_chr(s: &str) -> char {
1156 s.chars().next().unwrap_or('\0')
1157 }
1158
1159 // Returns (content, suffix).
1160 pub(crate) fn parse_lit_str(s: &str) -> (Box<str>, Box<str>) {
1161 match byte(s, 0) {
1162 b'"' => parse_lit_str_cooked(s),
1163 b'r' => parse_lit_str_raw(s),
1164 _ => unreachable!(),
1165 }
1166 }
1167
1168 // Clippy false positive
1169 // https://github.com/rust-lang-nursery/rust-clippy/issues/2329
1170 #[allow(clippy::needless_continue)]
1171 fn parse_lit_str_cooked(mut s: &str) -> (Box<str>, Box<str>) {
1172 assert_eq!(byte(s, 0), b'"');
1173 s = &s[1..];
1174
1175 let mut content = String::new();
1176 'outer: loop {
1177 let ch = match byte(s, 0) {
1178 b'"' => break,
1179 b'\\' => {
1180 let b = byte(s, 1);
1181 s = &s[2..];
1182 match b {
1183 b'x' => {
1184 let (byte, rest) = backslash_x(s);
1185 s = rest;
1186 assert!(byte <= 0x7F, "Invalid \\x byte in string literal");
1187 char::from_u32(u32::from(byte)).unwrap()
1188 }
1189 b'u' => {
1190 let (chr, rest) = backslash_u(s);
1191 s = rest;
1192 chr
1193 }
1194 b'n' => '\n',
1195 b'r' => '\r',
1196 b't' => '\t',
1197 b'\\' => '\\',
1198 b'0' => '\0',
1199 b'\'' => '\'',
1200 b'"' => '"',
1201 b'\r' | b'\n' => loop {
1202 let b = byte(s, 0);
1203 match b {
1204 b' ' | b'\t' | b'\n' | b'\r' => s = &s[1..],
1205 _ => continue 'outer,
1206 }
1207 },
1208 b => panic!("unexpected byte {:?} after \\ character in byte literal", b),
1209 }
1210 }
1211 b'\r' => {
1212 assert_eq!(byte(s, 1), b'\n', "Bare CR not allowed in string");
1213 s = &s[2..];
1214 '\n'
1215 }
1216 _ => {
1217 let ch = next_chr(s);
1218 s = &s[ch.len_utf8()..];
1219 ch
1220 }
1221 };
1222 content.push(ch);
1223 }
1224
1225 assert!(s.starts_with('"'));
1226 let content = content.into_boxed_str();
1227 let suffix = s[1..].to_owned().into_boxed_str();
1228 (content, suffix)
1229 }
1230
1231 fn parse_lit_str_raw(mut s: &str) -> (Box<str>, Box<str>) {
1232 assert_eq!(byte(s, 0), b'r');
1233 s = &s[1..];
1234
1235 let mut pounds = 0;
1236 while byte(s, pounds) == b'#' {
1237 pounds += 1;
1238 }
1239 assert_eq!(byte(s, pounds), b'"');
1240 let close = s.rfind('"').unwrap();
1241 for end in s[close + 1..close + 1 + pounds].bytes() {
1242 assert_eq!(end, b'#');
1243 }
1244
1245 let content = s[pounds + 1..close].to_owned().into_boxed_str();
1246 let suffix = s[close + 1 + pounds..].to_owned().into_boxed_str();
1247 (content, suffix)
1248 }
1249
1250 // Returns (content, suffix).
1251 pub(crate) fn parse_lit_byte_str(s: &str) -> (Vec<u8>, Box<str>) {
1252 assert_eq!(byte(s, 0), b'b');
1253 match byte(s, 1) {
1254 b'"' => parse_lit_byte_str_cooked(s),
1255 b'r' => parse_lit_byte_str_raw(s),
1256 _ => unreachable!(),
1257 }
1258 }
1259
1260 // Clippy false positive
1261 // https://github.com/rust-lang-nursery/rust-clippy/issues/2329
1262 #[allow(clippy::needless_continue)]
1263 fn parse_lit_byte_str_cooked(mut s: &str) -> (Vec<u8>, Box<str>) {
1264 assert_eq!(byte(s, 0), b'b');
1265 assert_eq!(byte(s, 1), b'"');
1266 s = &s[2..];
1267
1268 // We're going to want to have slices which don't respect codepoint boundaries.
1269 let mut v = s.as_bytes();
1270
1271 let mut out = Vec::new();
1272 'outer: loop {
1273 let byte = match byte(v, 0) {
1274 b'"' => break,
1275 b'\\' => {
1276 let b = byte(v, 1);
1277 v = &v[2..];
1278 match b {
1279 b'x' => {
1280 let (b, rest) = backslash_x(v);
1281 v = rest;
1282 b
1283 }
1284 b'n' => b'\n',
1285 b'r' => b'\r',
1286 b't' => b'\t',
1287 b'\\' => b'\\',
1288 b'0' => b'\0',
1289 b'\'' => b'\'',
1290 b'"' => b'"',
1291 b'\r' | b'\n' => loop {
1292 let byte = byte(v, 0);
1293 if matches!(byte, b' ' | b'\t' | b'\n' | b'\r') {
1294 v = &v[1..];
1295 } else {
1296 continue 'outer;
1297 }
1298 },
1299 b => panic!("unexpected byte {:?} after \\ character in byte literal", b),
1300 }
1301 }
1302 b'\r' => {
1303 assert_eq!(byte(v, 1), b'\n', "Bare CR not allowed in string");
1304 v = &v[2..];
1305 b'\n'
1306 }
1307 b => {
1308 v = &v[1..];
1309 b
1310 }
1311 };
1312 out.push(byte);
1313 }
1314
1315 assert_eq!(byte(v, 0), b'"');
1316 let suffix = s[s.len() - v.len() + 1..].to_owned().into_boxed_str();
1317 (out, suffix)
1318 }
1319
1320 fn parse_lit_byte_str_raw(s: &str) -> (Vec<u8>, Box<str>) {
1321 assert_eq!(byte(s, 0), b'b');
1322 let (value, suffix) = parse_lit_str_raw(&s[1..]);
1323 (String::from(value).into_bytes(), suffix)
1324 }
1325
1326 // Returns (value, suffix).
1327 pub(crate) fn parse_lit_byte(s: &str) -> (u8, Box<str>) {
1328 assert_eq!(byte(s, 0), b'b');
1329 assert_eq!(byte(s, 1), b'\'');
1330
1331 // We're going to want to have slices which don't respect codepoint boundaries.
1332 let mut v = s[2..].as_bytes();
1333
1334 let b = match byte(v, 0) {
1335 b'\\' => {
1336 let b = byte(v, 1);
1337 v = &v[2..];
1338 match b {
1339 b'x' => {
1340 let (b, rest) = backslash_x(v);
1341 v = rest;
1342 b
1343 }
1344 b'n' => b'\n',
1345 b'r' => b'\r',
1346 b't' => b'\t',
1347 b'\\' => b'\\',
1348 b'0' => b'\0',
1349 b'\'' => b'\'',
1350 b'"' => b'"',
1351 b => panic!("unexpected byte {:?} after \\ character in byte literal", b),
1352 }
1353 }
1354 b => {
1355 v = &v[1..];
1356 b
1357 }
1358 };
1359
1360 assert_eq!(byte(v, 0), b'\'');
1361 let suffix = s[s.len() - v.len() + 1..].to_owned().into_boxed_str();
1362 (b, suffix)
1363 }
1364
1365 // Returns (value, suffix).
1366 pub(crate) fn parse_lit_char(mut s: &str) -> (char, Box<str>) {
1367 assert_eq!(byte(s, 0), b'\'');
1368 s = &s[1..];
1369
1370 let ch = match byte(s, 0) {
1371 b'\\' => {
1372 let b = byte(s, 1);
1373 s = &s[2..];
1374 match b {
1375 b'x' => {
1376 let (byte, rest) = backslash_x(s);
1377 s = rest;
1378 assert!(byte <= 0x80, "Invalid \\x byte in string literal");
1379 char::from_u32(u32::from(byte)).unwrap()
1380 }
1381 b'u' => {
1382 let (chr, rest) = backslash_u(s);
1383 s = rest;
1384 chr
1385 }
1386 b'n' => '\n',
1387 b'r' => '\r',
1388 b't' => '\t',
1389 b'\\' => '\\',
1390 b'0' => '\0',
1391 b'\'' => '\'',
1392 b'"' => '"',
1393 b => panic!("unexpected byte {:?} after \\ character in byte literal", b),
1394 }
1395 }
1396 _ => {
1397 let ch = next_chr(s);
1398 s = &s[ch.len_utf8()..];
1399 ch
1400 }
1401 };
1402 assert_eq!(byte(s, 0), b'\'');
1403 let suffix = s[1..].to_owned().into_boxed_str();
1404 (ch, suffix)
1405 }
1406
1407 fn backslash_x<S>(s: &S) -> (u8, &S)
1408 where
1409 S: Index<RangeFrom<usize>, Output = S> + AsRef<[u8]> + ?Sized,
1410 {
1411 let mut ch = 0;
1412 let b0 = byte(s, 0);
1413 let b1 = byte(s, 1);
1414 ch += 0x10
1415 * match b0 {
1416 b'0'..=b'9' => b0 - b'0',
1417 b'a'..=b'f' => 10 + (b0 - b'a'),
1418 b'A'..=b'F' => 10 + (b0 - b'A'),
1419 _ => panic!("unexpected non-hex character after \\x"),
1420 };
1421 ch += match b1 {
1422 b'0'..=b'9' => b1 - b'0',
1423 b'a'..=b'f' => 10 + (b1 - b'a'),
1424 b'A'..=b'F' => 10 + (b1 - b'A'),
1425 _ => panic!("unexpected non-hex character after \\x"),
1426 };
1427 (ch, &s[2..])
1428 }
1429
1430 fn backslash_u(mut s: &str) -> (char, &str) {
1431 if byte(s, 0) != b'{' {
1432 panic!("{}", "expected { after \\u");
1433 }
1434 s = &s[1..];
1435
1436 let mut ch = 0;
1437 let mut digits = 0;
1438 loop {
1439 let b = byte(s, 0);
1440 let digit = match b {
1441 b'0'..=b'9' => b - b'0',
1442 b'a'..=b'f' => 10 + b - b'a',
1443 b'A'..=b'F' => 10 + b - b'A',
1444 b'_' if digits > 0 => {
1445 s = &s[1..];
1446 continue;
1447 }
1448 b'}' if digits == 0 => panic!("invalid empty unicode escape"),
1449 b'}' => break,
1450 _ => panic!("unexpected non-hex character after \\u"),
1451 };
1452 if digits == 6 {
1453 panic!("overlong unicode escape (must have at most 6 hex digits)");
1454 }
1455 ch *= 0x10;
1456 ch += u32::from(digit);
1457 digits += 1;
1458 s = &s[1..];
1459 }
1460 assert!(byte(s, 0) == b'}');
1461 s = &s[1..];
1462
1463 if let Some(ch) = char::from_u32(ch) {
1464 (ch, s)
1465 } else {
1466 panic!("character code {:x} is not a valid unicode character", ch);
1467 }
1468 }
1469
1470 // Returns base 10 digits and suffix.
1471 pub(crate) fn parse_lit_int(mut s: &str) -> Option<(Box<str>, Box<str>)> {
1472 let negative = byte(s, 0) == b'-';
1473 if negative {
1474 s = &s[1..];
1475 }
1476
1477 let base = match (byte(s, 0), byte(s, 1)) {
1478 (b'0', b'x') => {
1479 s = &s[2..];
1480 16
1481 }
1482 (b'0', b'o') => {
1483 s = &s[2..];
1484 8
1485 }
1486 (b'0', b'b') => {
1487 s = &s[2..];
1488 2
1489 }
1490 (b'0'..=b'9', _) => 10,
1491 _ => return None,
1492 };
1493
1494 let mut value = BigInt::new();
1495 let mut has_digit = false;
1496 'outer: loop {
1497 let b = byte(s, 0);
1498 let digit = match b {
1499 b'0'..=b'9' => b - b'0',
1500 b'a'..=b'f' if base > 10 => b - b'a' + 10,
1501 b'A'..=b'F' if base > 10 => b - b'A' + 10,
1502 b'_' => {
1503 s = &s[1..];
1504 continue;
1505 }
1506 // If looking at a floating point literal, we don't want to
1507 // consider it an integer.
1508 b'.' if base == 10 => return None,
1509 b'e' | b'E' if base == 10 => {
1510 let mut has_exp = false;
1511 for (i, b) in s[1..].bytes().enumerate() {
1512 match b {
1513 b'_' => {}
1514 b'-' | b'+' => return None,
1515 b'0'..=b'9' => has_exp = true,
1516 _ => {
1517 let suffix = &s[1 + i..];
1518 if has_exp && crate::ident::xid_ok(suffix) {
1519 return None;
1520 } else {
1521 break 'outer;
1522 }
1523 }
1524 }
1525 }
1526 if has_exp {
1527 return None;
1528 } else {
1529 break;
1530 }
1531 }
1532 _ => break,
1533 };
1534
1535 if digit >= base {
1536 return None;
1537 }
1538
1539 has_digit = true;
1540 value *= base;
1541 value += digit;
1542 s = &s[1..];
1543 }
1544
1545 if !has_digit {
1546 return None;
1547 }
1548
1549 let suffix = s;
1550 if suffix.is_empty() || crate::ident::xid_ok(suffix) {
1551 let mut repr = value.to_string();
1552 if negative {
1553 repr.insert(0, '-');
1554 }
1555 Some((repr.into_boxed_str(), suffix.to_owned().into_boxed_str()))
1556 } else {
1557 None
1558 }
1559 }
1560
1561 // Returns base 10 digits and suffix.
1562 pub(crate) fn parse_lit_float(input: &str) -> Option<(Box<str>, Box<str>)> {
1563 // Rust's floating point literals are very similar to the ones parsed by
1564 // the standard library, except that rust's literals can contain
1565 // ignorable underscores. Let's remove those underscores.
1566
1567 let mut bytes = input.to_owned().into_bytes();
1568
1569 let start = (*bytes.first()? == b'-') as usize;
1570 match bytes.get(start)? {
1571 b'0'..=b'9' => {}
1572 _ => return None,
1573 }
1574
1575 let mut read = start;
1576 let mut write = start;
1577 let mut has_dot = false;
1578 let mut has_e = false;
1579 let mut has_sign = false;
1580 let mut has_exponent = false;
1581 while read < bytes.len() {
1582 match bytes[read] {
1583 b'_' => {
1584 // Don't increase write
1585 read += 1;
1586 continue;
1587 }
1588 b'0'..=b'9' => {
1589 if has_e {
1590 has_exponent = true;
1591 }
1592 bytes[write] = bytes[read];
1593 }
1594 b'.' => {
1595 if has_e || has_dot {
1596 return None;
1597 }
1598 has_dot = true;
1599 bytes[write] = b'.';
1600 }
1601 b'e' | b'E' => {
1602 match bytes[read + 1..]
1603 .iter()
1604 .find(|b| **b != b'_')
1605 .unwrap_or(&b'\0')
1606 {
1607 b'-' | b'+' | b'0'..=b'9' => {}
1608 _ => break,
1609 }
1610 if has_e {
1611 if has_exponent {
1612 break;
1613 } else {
1614 return None;
1615 }
1616 }
1617 has_e = true;
1618 bytes[write] = b'e';
1619 }
1620 b'-' | b'+' => {
1621 if has_sign || has_exponent || !has_e {
1622 return None;
1623 }
1624 has_sign = true;
1625 if bytes[read] == b'-' {
1626 bytes[write] = bytes[read];
1627 } else {
1628 // Omit '+'
1629 read += 1;
1630 continue;
1631 }
1632 }
1633 _ => break,
1634 }
1635 read += 1;
1636 write += 1;
1637 }
1638
1639 if has_e && !has_exponent {
1640 return None;
1641 }
1642
1643 let mut digits = String::from_utf8(bytes).unwrap();
1644 let suffix = digits.split_off(read);
1645 digits.truncate(write);
1646 if suffix.is_empty() || crate::ident::xid_ok(&suffix) {
1647 Some((digits.into_boxed_str(), suffix.into_boxed_str()))
1648 } else {
1649 None
1650 }
1651 }
1652}
1653