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