Something went wrong. Try again.
Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863// SPDX-License-Identifier: Apache-2.0 OR MIT
#[cfg(feature = "parsing")]use crate::lookahead;#[cfg(feature = "parsing")]use crate::parse::{Parse, Parser};use crate::{Error, Result};use proc_macro2::{Ident, Literal, Span};#[cfg(feature = "parsing")]use proc_macro2::{TokenStream, TokenTree};use std::ffi::{CStr, CString};use std::fmt::{self, Display};#[cfg(feature = "extra-traits")]use std::hash::{Hash, Hasher};use std::str::{self, FromStr};
ast_enum_of_structs! { /// A Rust literal such as a string or integer or boolean. /// /// # Syntax tree enum /// /// This type is a [syntax tree enum]. /// /// [syntax tree enum]: crate::expr::Expr#syntax-tree-enums #[non_exhaustive] pub enum Lit { /// A UTF-8 string literal: `"foo"`. Str(LitStr),
/// A byte string literal: `b"foo"`. ByteStr(LitByteStr),
/// A nul-terminated C-string literal: `c"foo"`. CStr(LitCStr),
/// A byte literal: `b'f'`. Byte(LitByte),
/// A character literal: `'a'`. Char(LitChar),
/// An integer literal: `1` or `1u16`. Int(LitInt),
/// A floating point literal: `1f64` or `1.0e10f64`. /// /// Must be finite. May not be infinite or NaN. Float(LitFloat),
/// A boolean literal: `true` or `false`. Bool(LitBool),
/// A raw token literal not interpreted by Syn. Verbatim(Literal), }}
ast_struct! { /// A UTF-8 string literal: `"foo"`. pub struct LitStr { repr: Box<LitRepr>, }}
ast_struct! { /// A byte string literal: `b"foo"`. pub struct LitByteStr { repr: Box<LitRepr>, }}
ast_struct! { /// A nul-terminated C-string literal: `c"foo"`. pub struct LitCStr { repr: Box<LitRepr>, }}
ast_struct! { /// A byte literal: `b'f'`. pub struct LitByte { repr: Box<LitRepr>, }}
ast_struct! { /// A character literal: `'a'`. pub struct LitChar { repr: Box<LitRepr>, }}
struct LitRepr { token: Literal, suffix: Box<str>,}
ast_struct! { /// An integer literal: `1` or `1u16`. pub struct LitInt { repr: Box<LitIntRepr>, }}
struct LitIntRepr { token: Literal, digits: Box<str>, suffix: Box<str>,}
ast_struct! { /// A floating point literal: `1f64` or `1.0e10f64`. /// /// Must be finite. May not be infinite or NaN. pub struct LitFloat { repr: Box<LitFloatRepr>, }}
struct LitFloatRepr { token: Literal, digits: Box<str>, suffix: Box<str>,}
ast_struct! { /// A boolean literal: `true` or `false`. pub struct LitBool { pub value: bool, pub span: Span, }}
impl LitStr { pub fn new(value: &str, span: Span) -> Self { let mut token = Literal::string(value); token.set_span(span); LitStr { repr: Box::new(LitRepr { token, suffix: Box::<str>::default(), }), } }
pub fn value(&self) -> String { let repr = self.repr.token.to_string(); let (value, _suffix) = value::parse_lit_str(&repr); String::from(value) }
/// Parse a syntax tree node from the content of this string literal. /// /// All spans in the syntax tree will point to the span of this `LitStr`. /// /// # Example /// /// ``` /// use syn::{Attribute, Error, Expr, Lit, Meta, Path, Result}; /// /// // Parses the path from an attribute that looks like: /// // /// // #[path = "a::b::c"] /// // /// // or returns `None` if the input is some other attribute. /// fn get_path(attr: &Attribute) -> Result<Option<Path>> { /// if !attr.path().is_ident("path") { /// return Ok(None); /// } /// /// if let Meta::NameValue(meta) = &attr.meta { /// if let Expr::Lit(expr) = &meta.value { /// if let Lit::Str(lit_str) = &expr.lit { /// return lit_str.parse().map(Some); /// } /// } /// } /// /// let message = "expected #[path = \"...\"]"; /// Err(Error::new_spanned(attr, message)) /// } /// ``` #[cfg(feature = "parsing")] #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] pub fn parse<T: Parse>(&self) -> Result<T> { self.parse_with(T::parse) }
/// Invoke parser on the content of this string literal. /// /// All spans in the syntax tree will point to the span of this `LitStr`. /// /// # Example /// /// ``` /// # use proc_macro2::Span; /// # use syn::{LitStr, Result}; /// # /// # fn main() -> Result<()> { /// # let lit_str = LitStr::new("a::b::c", Span::call_site()); /// # /// # const IGNORE: &str = stringify! { /// let lit_str: LitStr = /* ... */; /// # }; /// /// // Parse a string literal like "a::b::c" into a Path, not allowing /// // generic arguments on any of the path segments. /// let basic_path = lit_str.parse_with(syn::Path::parse_mod_style)?; /// # /// # Ok(()) /// # } /// ``` #[cfg(feature = "parsing")] #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] pub fn parse_with<F: Parser>(&self, parser: F) -> Result<F::Output> { use proc_macro2::Group;
// Token stream with every span replaced by the given one. fn respan_token_stream(stream: TokenStream, span: Span) -> TokenStream { stream .into_iter() .map(|token| respan_token_tree(token, span)) .collect() }
// Token tree with every span replaced by the given one. fn respan_token_tree(mut token: TokenTree, span: Span) -> TokenTree { match &mut token { TokenTree::Group(g) => { let stream = respan_token_stream(g.stream(), span); *g = Group::new(g.delimiter(), stream); g.set_span(span); } other => other.set_span(span), } token }
// Parse string literal into a token stream with every span equal to the // original literal's span. let span = self.span(); let mut tokens = TokenStream::from_str(&self.value())?; tokens = respan_token_stream(tokens, span);
let result = crate::parse::parse_scoped(parser, span, tokens)?;
let suffix = self.suffix(); if !suffix.is_empty() { return Err(Error::new( self.span(), format!("unexpected suffix `{}` on string literal", suffix), )); }
Ok(result) }
pub fn span(&self) -> Span { self.repr.token.span() }
pub fn set_span(&mut self, span: Span) { self.repr.token.set_span(span); }
pub fn suffix(&self) -> &str { &self.repr.suffix }
pub fn token(&self) -> Literal { self.repr.token.clone() }}
impl LitByteStr { pub fn new(value: &[u8], span: Span) -> Self { let mut token = Literal::byte_string(value); token.set_span(span); LitByteStr { repr: Box::new(LitRepr { token, suffix: Box::<str>::default(), }), } }
pub fn value(&self) -> Vec<u8> { let repr = self.repr.token.to_string(); let (value, _suffix) = value::parse_lit_byte_str(&repr); value }
pub fn span(&self) -> Span { self.repr.token.span() }
pub fn set_span(&mut self, span: Span) { self.repr.token.set_span(span); }
pub fn suffix(&self) -> &str { &self.repr.suffix }
pub fn token(&self) -> Literal { self.repr.token.clone() }}
impl LitCStr { pub fn new(value: &CStr, span: Span) -> Self { let mut token = Literal::c_string(value); token.set_span(span); LitCStr { repr: Box::new(LitRepr { token, suffix: Box::<str>::default(), }), } }
pub fn value(&self) -> CString { let repr = self.repr.token.to_string(); let (value, _suffix) = value::parse_lit_c_str(&repr); value }
pub fn span(&self) -> Span { self.repr.token.span() }
pub fn set_span(&mut self, span: Span) { self.repr.token.set_span(span); }
pub fn suffix(&self) -> &str { &self.repr.suffix }
pub fn token(&self) -> Literal { self.repr.token.clone() }}
impl LitByte { pub fn new(value: u8, span: Span) -> Self { let mut token = Literal::u8_suffixed(value); token.set_span(span); LitByte { repr: Box::new(LitRepr { token, suffix: Box::<str>::default(), }), } }
pub fn value(&self) -> u8 { let repr = self.repr.token.to_string(); let (value, _suffix) = value::parse_lit_byte(&repr); value }
pub fn span(&self) -> Span { self.repr.token.span() }
pub fn set_span(&mut self, span: Span) { self.repr.token.set_span(span); }
pub fn suffix(&self) -> &str { &self.repr.suffix }
pub fn token(&self) -> Literal { self.repr.token.clone() }}
impl LitChar { pub fn new(value: char, span: Span) -> Self { let mut token = Literal::character(value); token.set_span(span); LitChar { repr: Box::new(LitRepr { token, suffix: Box::<str>::default(), }), } }
pub fn value(&self) -> char { let repr = self.repr.token.to_string(); let (value, _suffix) = value::parse_lit_char(&repr); value }
pub fn span(&self) -> Span { self.repr.token.span() }
pub fn set_span(&mut self, span: Span) { self.repr.token.set_span(span); }
pub fn suffix(&self) -> &str { &self.repr.suffix }
pub fn token(&self) -> Literal { self.repr.token.clone() }}
impl LitInt { pub fn new(repr: &str, span: Span) -> Self { let (digits, suffix) = match value::parse_lit_int(repr) { Some(parse) => parse, None => panic!("not an integer literal: `{}`", repr), };
let mut token: Literal = repr.parse().unwrap(); token.set_span(span); LitInt { repr: Box::new(LitIntRepr { token, digits, suffix, }), } }
pub fn base10_digits(&self) -> &str { &self.repr.digits }
/// Parses the literal into a selected number type. /// /// This is equivalent to `lit.base10_digits().parse()` except that the /// resulting errors will be correctly spanned to point to the literal token /// in the macro input. /// /// ``` /// use syn::LitInt; /// use syn::parse::{Parse, ParseStream, Result}; /// /// struct Port { /// value: u16, /// } /// /// impl Parse for Port { /// fn parse(input: ParseStream) -> Result<Self> { /// let lit: LitInt = input.parse()?; /// let value = lit.base10_parse::<u16>()?; /// Ok(Port { value }) /// } /// } /// ``` pub fn base10_parse<N>(&self) -> Result<N> where N: FromStr, N::Err: Display, { self.base10_digits() .parse() .map_err(|err| Error::new(self.span(), err)) }
pub fn suffix(&self) -> &str { &self.repr.suffix }
pub fn span(&self) -> Span { self.repr.token.span() }
pub fn set_span(&mut self, span: Span) { self.repr.token.set_span(span); }
pub fn token(&self) -> Literal { self.repr.token.clone() }}
impl From<Literal> for LitInt { fn from(token: Literal) -> Self { let repr = token.to_string(); if let Some((digits, suffix)) = value::parse_lit_int(&repr) { LitInt { repr: Box::new(LitIntRepr { token, digits, suffix, }), } } else { panic!("not an integer literal: `{}`", repr); } }}
impl Display for LitInt { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { self.repr.token.fmt(formatter) }}
impl LitFloat { pub fn new(repr: &str, span: Span) -> Self { let (digits, suffix) = match value::parse_lit_float(repr) { Some(parse) => parse, None => panic!("not a float literal: `{}`", repr), };
let mut token: Literal = repr.parse().unwrap(); token.set_span(span); LitFloat { repr: Box::new(LitFloatRepr { token, digits, suffix, }), } }
pub fn base10_digits(&self) -> &str { &self.repr.digits }
pub fn base10_parse<N>(&self) -> Result<N> where N: FromStr, N::Err: Display, { self.base10_digits() .parse() .map_err(|err| Error::new(self.span(), err)) }
pub fn suffix(&self) -> &str { &self.repr.suffix }
pub fn span(&self) -> Span { self.repr.token.span() }
pub fn set_span(&mut self, span: Span) { self.repr.token.set_span(span); }
pub fn token(&self) -> Literal { self.repr.token.clone() }}
impl From<Literal> for LitFloat { fn from(token: Literal) -> Self { let repr = token.to_string(); if let Some((digits, suffix)) = value::parse_lit_float(&repr) { LitFloat { repr: Box::new(LitFloatRepr { token, digits, suffix, }), } } else { panic!("not a float literal: `{}`", repr); } }}
impl Display for LitFloat { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { self.repr.token.fmt(formatter) }}
impl LitBool { pub fn new(value: bool, span: Span) -> Self { LitBool { value, span } }
pub fn value(&self) -> bool { self.value }
pub fn span(&self) -> Span { self.span }
pub fn set_span(&mut self, span: Span) { self.span = span; }
pub fn token(&self) -> Ident { let s = if self.value { "true" } else { "false" }; Ident::new(s, self.span) }}
#[cfg(feature = "extra-traits")]mod debug_impls { use crate::lit::{LitBool, LitByte, LitByteStr, LitCStr, LitChar, LitFloat, LitInt, LitStr}; use std::fmt::{self, Debug};
#[cfg_attr(docsrs, doc(cfg(feature = "extra-traits")))] impl Debug for LitStr { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { self.debug(formatter, "LitStr") } }
impl LitStr { pub(crate) fn debug(&self, formatter: &mut fmt::Formatter, name: &str) -> fmt::Result { formatter .debug_struct(name) .field("token", &format_args!("{}", self.repr.token)) .finish() } }
#[cfg_attr(docsrs, doc(cfg(feature = "extra-traits")))] impl Debug for LitByteStr { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { self.debug(formatter, "LitByteStr") } }
impl LitByteStr { pub(crate) fn debug(&self, formatter: &mut fmt::Formatter, name: &str) -> fmt::Result { formatter .debug_struct(name) .field("token", &format_args!("{}", self.repr.token)) .finish() } }
#[cfg_attr(docsrs, doc(cfg(feature = "extra-traits")))] impl Debug for LitCStr { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { self.debug(formatter, "LitCStr") } }
impl LitCStr { pub(crate) fn debug(&self, formatter: &mut fmt::Formatter, name: &str) -> fmt::Result { formatter .debug_struct(name) .field("token", &format_args!("{}", self.repr.token)) .finish() } }
#[cfg_attr(docsrs, doc(cfg(feature = "extra-traits")))] impl Debug for LitByte { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { self.debug(formatter, "LitByte") } }
impl LitByte { pub(crate) fn debug(&self, formatter: &mut fmt::Formatter, name: &str) -> fmt::Result { formatter .debug_struct(name) .field("token", &format_args!("{}", self.repr.token)) .finish() } }
#[cfg_attr(docsrs, doc(cfg(feature = "extra-traits")))] impl Debug for LitChar { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { self.debug(formatter, "LitChar") } }
impl LitChar { pub(crate) fn debug(&self, formatter: &mut fmt::Formatter, name: &str) -> fmt::Result { formatter .debug_struct(name) .field("token", &format_args!("{}", self.repr.token)) .finish() } }
#[cfg_attr(docsrs, doc(cfg(feature = "extra-traits")))] impl Debug for LitInt { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { self.debug(formatter, "LitInt") } }
impl LitInt { pub(crate) fn debug(&self, formatter: &mut fmt::Formatter, name: &str) -> fmt::Result { formatter .debug_struct(name) .field("token", &format_args!("{}", self.repr.token)) .finish() } }
#[cfg_attr(docsrs, doc(cfg(feature = "extra-traits")))] impl Debug for LitFloat { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { self.debug(formatter, "LitFloat") } }
impl LitFloat { pub(crate) fn debug(&self, formatter: &mut fmt::Formatter, name: &str) -> fmt::Result { formatter .debug_struct(name) .field("token", &format_args!("{}", self.repr.token)) .finish() } }
#[cfg_attr(docsrs, doc(cfg(feature = "extra-traits")))] impl Debug for LitBool { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { self.debug(formatter, "LitBool") } }
impl LitBool { pub(crate) fn debug(&self, formatter: &mut fmt::Formatter, name: &str) -> fmt::Result { formatter .debug_struct(name) .field("value", &self.value) .finish() } }}
#[cfg(feature = "clone-impls")]#[cfg_attr(docsrs, doc(cfg(feature = "clone-impls")))]impl Clone for LitRepr { fn clone(&self) -> Self { LitRepr { token: self.token.clone(), suffix: self.suffix.clone(), } }}
#[cfg(feature = "clone-impls")]#[cfg_attr(docsrs, doc(cfg(feature = "clone-impls")))]impl Clone for LitIntRepr { fn clone(&self) -> Self { LitIntRepr { token: self.token.clone(), digits: self.digits.clone(), suffix: self.suffix.clone(), } }}
#[cfg(feature = "clone-impls")]#[cfg_attr(docsrs, doc(cfg(feature = "clone-impls")))]impl Clone for LitFloatRepr { fn clone(&self) -> Self { LitFloatRepr { token: self.token.clone(), digits: self.digits.clone(), suffix: self.suffix.clone(), } }}
macro_rules! lit_extra_traits { ($ty:ident) => { #[cfg(feature = "clone-impls")] #[cfg_attr(docsrs, doc(cfg(feature = "clone-impls")))] impl Clone for $ty { fn clone(&self) -> Self { $ty { repr: self.repr.clone(), } } }
#[cfg(feature = "extra-traits")] #[cfg_attr(docsrs, doc(cfg(feature = "extra-traits")))] impl PartialEq for $ty { fn eq(&self, other: &Self) -> bool { self.repr.token.to_string() == other.repr.token.to_string() } }
#[cfg(feature = "extra-traits")] #[cfg_attr(docsrs, doc(cfg(feature = "extra-traits")))] impl Hash for $ty { fn hash<H>(&self, state: &mut H) where H: Hasher, { self.repr.token.to_string().hash(state); } }
#[cfg(feature = "parsing")] pub_if_not_doc! { #[doc(hidden)] #[allow(non_snake_case)] pub fn $ty(marker: lookahead::TokenMarker) -> $ty { match marker {} } } };}
lit_extra_traits!(LitStr);lit_extra_traits!(LitByteStr);lit_extra_traits!(LitCStr);lit_extra_traits!(LitByte);lit_extra_traits!(LitChar);lit_extra_traits!(LitInt);lit_extra_traits!(LitFloat);
#[cfg(feature = "parsing")]pub_if_not_doc! { #[doc(hidden)] #[allow(non_snake_case)] pub fn LitBool(marker: lookahead::TokenMarker) -> LitBool { match marker {} }}
/// The style of a string literal, either plain quoted or a raw string like/// `r##"data"##`.#[doc(hidden)] // https://github.com/dtolnay/syn/issues/1566pub enum StrStyle { /// An ordinary string like `"data"`. Cooked, /// A raw string like `r##"data"##`. /// /// The unsigned integer is the number of `#` symbols used. Raw(usize),}
#[cfg(feature = "parsing")]pub_if_not_doc! { #[doc(hidden)] #[allow(non_snake_case)] pub fn Lit(marker: lookahead::TokenMarker) -> Lit { match marker {} }}
#[cfg(feature = "parsing")]pub(crate) mod parsing { use crate::buffer::Cursor; use crate::error::Result; use crate::lit::{ value, Lit, LitBool, LitByte, LitByteStr, LitCStr, LitChar, LitFloat, LitFloatRepr, LitInt, LitIntRepr, LitStr, }; use crate::parse::{Parse, ParseStream, Unexpected}; use crate::token::{self, Token}; use proc_macro2::{Literal, Punct, Span}; use std::cell::Cell; use std::rc::Rc;
#[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] impl Parse for Lit { fn parse(input: ParseStream) -> Result<Self> { input.step(|cursor| { if let Some((lit, rest)) = cursor.literal() { return Ok((Lit::new(lit), rest)); }
if let Some((ident, rest)) = cursor.ident() { let value = ident == "true"; if value || ident == "false" { let lit_bool = LitBool { value, span: ident.span(), }; return Ok((Lit::Bool(lit_bool), rest)); } }
if let Some((punct, rest)) = cursor.punct() { if punct.as_char() == '-' { if let Some((lit, rest)) = parse_negative_lit(punct, rest) { return Ok((lit, rest)); } } }
Err(cursor.error("expected literal")) }) } }
fn parse_negative_lit(neg: Punct, cursor: Cursor) -> Option<(Lit, Cursor)> { let (lit, rest) = cursor.literal()?;
let mut span = neg.span(); span = span.join(lit.span()).unwrap_or(span);
let mut repr = lit.to_string(); repr.insert(0, '-');
if let Some((digits, suffix)) = value::parse_lit_int(&repr) { let mut token: Literal = repr.parse().unwrap(); token.set_span(span); return Some(( Lit::Int(LitInt { repr: Box::new(LitIntRepr { token, digits, suffix, }), }), rest, )); }
let (digits, suffix) = value::parse_lit_float(&repr)?; let mut token: Literal = repr.parse().unwrap(); token.set_span(span); Some(( Lit::Float(LitFloat { repr: Box::new(LitFloatRepr { token, digits, suffix, }), }), rest, )) }
#[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] impl Parse for LitStr { fn parse(input: ParseStream) -> Result<Self> { let head = input.fork(); match input.parse() { Ok(Lit::Str(lit)) => Ok(lit), _ => Err(head.error("expected string literal")), } } }
#[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] impl Parse for LitByteStr { fn parse(input: ParseStream) -> Result<Self> { let head = input.fork(); match input.parse() { Ok(Lit::ByteStr(lit)) => Ok(lit), _ => Err(head.error("expected byte string literal")), } } }
#[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] impl Parse for LitCStr { fn parse(input: ParseStream) -> Result<Self> { let head = input.fork(); match input.parse() { Ok(Lit::CStr(lit)) => Ok(lit), _ => Err(head.error("expected C string literal")), } } }
#[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] impl Parse for LitByte { fn parse(input: ParseStream) -> Result<Self> { let head = input.fork(); match input.parse() { Ok(Lit::Byte(lit)) => Ok(lit), _ => Err(head.error("expected byte literal")), } } }
#[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] impl Parse for LitChar { fn parse(input: ParseStream) -> Result<Self> { let head = input.fork(); match input.parse() { Ok(Lit::Char(lit)) => Ok(lit), _ => Err(head.error("expected character literal")), } } }
#[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] impl Parse for LitInt { fn parse(input: ParseStream) -> Result<Self> { let head = input.fork(); match input.parse() { Ok(Lit::Int(lit)) => Ok(lit), _ => Err(head.error("expected integer literal")), } } }
#[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] impl Parse for LitFloat { fn parse(input: ParseStream) -> Result<Self> { let head = input.fork(); match input.parse() { Ok(Lit::Float(lit)) => Ok(lit), _ => Err(head.error("expected floating point literal")), } } }
#[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] impl Parse for LitBool { fn parse(input: ParseStream) -> Result<Self> { let head = input.fork(); match input.parse() { Ok(Lit::Bool(lit)) => Ok(lit), _ => Err(head.error("expected boolean literal")), } } }
fn peek_impl(cursor: Cursor, peek: fn(ParseStream) -> bool) -> bool { let scope = Span::call_site(); let unexpected = Rc::new(Cell::new(Unexpected::None)); let buffer = crate::parse::new_parse_buffer(scope, cursor, unexpected); peek(&buffer) }
macro_rules! impl_token { ($display:literal $name:ty) => { impl Token for $name { fn peek(cursor: Cursor) -> bool { fn peek(input: ParseStream) -> bool { <$name as Parse>::parse(input).is_ok() } peek_impl(cursor, peek) }
fn display() -> &'static str { $display } }
impl token::private::Sealed for $name {} }; }
impl_token!("literal" Lit); impl_token!("string literal" LitStr); impl_token!("byte string literal" LitByteStr); impl_token!("C-string literal" LitCStr); impl_token!("byte literal" LitByte); impl_token!("character literal" LitChar); impl_token!("integer literal" LitInt); impl_token!("floating point literal" LitFloat); impl_token!("boolean literal" LitBool);}
#[cfg(feature = "printing")]mod printing { use crate::lit::{LitBool, LitByte, LitByteStr, LitCStr, LitChar, LitFloat, LitInt, LitStr}; use proc_macro2::TokenStream; use quote::{ToTokens, TokenStreamExt};
#[cfg_attr(docsrs, doc(cfg(feature = "printing")))] impl ToTokens for LitStr { fn to_tokens(&self, tokens: &mut TokenStream) { self.repr.token.to_tokens(tokens); } }
#[cfg_attr(docsrs, doc(cfg(feature = "printing")))] impl ToTokens for LitByteStr { fn to_tokens(&self, tokens: &mut TokenStream) { self.repr.token.to_tokens(tokens); } }
#[cfg_attr(docsrs, doc(cfg(feature = "printing")))] impl ToTokens for LitCStr { fn to_tokens(&self, tokens: &mut TokenStream) { self.repr.token.to_tokens(tokens); } }
#[cfg_attr(docsrs, doc(cfg(feature = "printing")))] impl ToTokens for LitByte { fn to_tokens(&self, tokens: &mut TokenStream) { self.repr.token.to_tokens(tokens); } }
#[cfg_attr(docsrs, doc(cfg(feature = "printing")))] impl ToTokens for LitChar { fn to_tokens(&self, tokens: &mut TokenStream) { self.repr.token.to_tokens(tokens); } }
#[cfg_attr(docsrs, doc(cfg(feature = "printing")))] impl ToTokens for LitInt { fn to_tokens(&self, tokens: &mut TokenStream) { self.repr.token.to_tokens(tokens); } }
#[cfg_attr(docsrs, doc(cfg(feature = "printing")))] impl ToTokens for LitFloat { fn to_tokens(&self, tokens: &mut TokenStream) { self.repr.token.to_tokens(tokens); } }
#[cfg_attr(docsrs, doc(cfg(feature = "printing")))] impl ToTokens for LitBool { fn to_tokens(&self, tokens: &mut TokenStream) { tokens.append(self.token()); } }}
mod value { use crate::bigint::BigInt; use crate::lit::{ Lit, LitBool, LitByte, LitByteStr, LitCStr, LitChar, LitFloat, LitFloatRepr, LitInt, LitIntRepr, LitRepr, LitStr, }; use proc_macro2::{Literal, Span}; use std::ascii; use std::char; use std::ffi::CString; use std::ops::{Index, RangeFrom};
impl Lit { /// Interpret a Syn literal from a proc-macro2 literal. pub fn new(token: Literal) -> Self { let repr = token.to_string();
match byte(&repr, 0) { // "...", r"...", r#"..."# b'"' | b'r' => { let (_, suffix) = parse_lit_str(&repr); return Lit::Str(LitStr { repr: Box::new(LitRepr { token, suffix }), }); } b'b' => match byte(&repr, 1) { // b"...", br"...", br#"...#" b'"' | b'r' => { let (_, suffix) = parse_lit_byte_str(&repr); return Lit::ByteStr(LitByteStr { repr: Box::new(LitRepr { token, suffix }), }); } // b'...' b'\'' => { let (_, suffix) = parse_lit_byte(&repr); return Lit::Byte(LitByte { repr: Box::new(LitRepr { token, suffix }), }); } _ => {} }, // c"...", cr"...", cr#"..."# b'c' => { let (_, suffix) = parse_lit_c_str(&repr); return Lit::CStr(LitCStr { repr: Box::new(LitRepr { token, suffix }), }); } // '...' b'\'' => { let (_, suffix) = parse_lit_char(&repr); return Lit::Char(LitChar { repr: Box::new(LitRepr { token, suffix }), }); } b'0'..=b'9' | b'-' => { // 0, 123, 0xFF, 0o77, 0b11 if let Some((digits, suffix)) = parse_lit_int(&repr) { return Lit::Int(LitInt { repr: Box::new(LitIntRepr { token, digits, suffix, }), }); } // 1.0, 1e-1, 1e+1 if let Some((digits, suffix)) = parse_lit_float(&repr) { return Lit::Float(LitFloat { repr: Box::new(LitFloatRepr { token, digits, suffix, }), }); } } // true, false b't' | b'f' => { if repr == "true" || repr == "false" { return Lit::Bool(LitBool { value: repr == "true", span: token.span(), }); } } b'(' if repr == "(/*ERROR*/)" => return Lit::Verbatim(token), _ => {} }
panic!("unrecognized literal: `{}`", repr); }
pub fn suffix(&self) -> &str { match self { Lit::Str(lit) => lit.suffix(), Lit::ByteStr(lit) => lit.suffix(), Lit::CStr(lit) => lit.suffix(), Lit::Byte(lit) => lit.suffix(), Lit::Char(lit) => lit.suffix(), Lit::Int(lit) => lit.suffix(), Lit::Float(lit) => lit.suffix(), Lit::Bool(_) | Lit::Verbatim(_) => "", } }
pub fn span(&self) -> Span { match self { Lit::Str(lit) => lit.span(), Lit::ByteStr(lit) => lit.span(), Lit::CStr(lit) => lit.span(), Lit::Byte(lit) => lit.span(), Lit::Char(lit) => lit.span(), Lit::Int(lit) => lit.span(), Lit::Float(lit) => lit.span(), Lit::Bool(lit) => lit.span, Lit::Verbatim(lit) => lit.span(), } }
pub fn set_span(&mut self, span: Span) { match self { Lit::Str(lit) => lit.set_span(span), Lit::ByteStr(lit) => lit.set_span(span), Lit::CStr(lit) => lit.set_span(span), Lit::Byte(lit) => lit.set_span(span), Lit::Char(lit) => lit.set_span(span), Lit::Int(lit) => lit.set_span(span), Lit::Float(lit) => lit.set_span(span), Lit::Bool(lit) => lit.span = span, Lit::Verbatim(lit) => lit.set_span(span), } } }
/// Get the byte at offset idx, or a default of `b'\0'` if we're looking /// past the end of the input buffer. pub(crate) fn byte<S: AsRef<[u8]> + ?Sized>(s: &S, idx: usize) -> u8 { let s = s.as_ref(); if idx < s.len() { s[idx] } else { 0 } }
fn next_chr(s: &str) -> char { s.chars().next().unwrap_or('\0') }
// Returns (content, suffix). pub(crate) fn parse_lit_str(s: &str) -> (Box<str>, Box<str>) { match byte(s, 0) { b'"' => parse_lit_str_cooked(s), b'r' => parse_lit_str_raw(s), _ => unreachable!(), } }
fn parse_lit_str_cooked(mut s: &str) -> (Box<str>, Box<str>) { assert_eq!(byte(s, 0), b'"'); s = &s[1..];
let mut content = String::new(); 'outer: loop { let ch = match byte(s, 0) { b'"' => break, b'\\' => { let b = byte(s, 1); s = &s[2..]; match b { b'x' => { let (byte, rest) = backslash_x(s); s = rest; assert!(byte <= 0x7F, "invalid \\x byte in string literal"); char::from_u32(u32::from(byte)).unwrap() } b'u' => { let (ch, rest) = backslash_u(s); s = rest; ch } b'n' => '\n', b'r' => '\r', b't' => '\t', b'\\' => '\\', b'0' => '\0', b'\'' => '\'', b'"' => '"', b'\r' | b'\n' => loop { let b = byte(s, 0); match b { b' ' | b'\t' | b'\n' | b'\r' => s = &s[1..], _ => continue 'outer, } }, b => panic!( "unexpected byte '{}' after \\ character in string literal", ascii::escape_default(b), ), } } b'\r' => { assert_eq!(byte(s, 1), b'\n', "bare CR not allowed in string"); s = &s[2..]; '\n' } _ => { let ch = next_chr(s); s = &s[ch.len_utf8()..]; ch } }; content.push(ch); }
assert!(s.starts_with('"')); let content = content.into_boxed_str(); let suffix = s[1..].to_owned().into_boxed_str(); (content, suffix) }
fn parse_lit_str_raw(mut s: &str) -> (Box<str>, Box<str>) { assert_eq!(byte(s, 0), b'r'); s = &s[1..];
let mut pounds = 0; while byte(s, pounds) == b'#' { pounds += 1; } assert_eq!(byte(s, pounds), b'"'); let close = s.rfind('"').unwrap(); for end in s[close + 1..close + 1 + pounds].bytes() { assert_eq!(end, b'#'); }
let content = s[pounds + 1..close].to_owned().into_boxed_str(); let suffix = s[close + 1 + pounds..].to_owned().into_boxed_str(); (content, suffix) }
// Returns (content, suffix). pub(crate) fn parse_lit_byte_str(s: &str) -> (Vec<u8>, Box<str>) { assert_eq!(byte(s, 0), b'b'); match byte(s, 1) { b'"' => parse_lit_byte_str_cooked(s), b'r' => parse_lit_byte_str_raw(s), _ => unreachable!(), } }
fn parse_lit_byte_str_cooked(mut s: &str) -> (Vec<u8>, Box<str>) { assert_eq!(byte(s, 0), b'b'); assert_eq!(byte(s, 1), b'"'); s = &s[2..];
// We're going to want to have slices which don't respect codepoint boundaries. let mut v = s.as_bytes();
let mut out = Vec::new(); 'outer: loop { let byte = match byte(v, 0) { b'"' => break, b'\\' => { let b = byte(v, 1); v = &v[2..]; match b { b'x' => { let (b, rest) = backslash_x(v); v = rest; b } b'n' => b'\n', b'r' => b'\r', b't' => b'\t', b'\\' => b'\\', b'0' => b'\0', b'\'' => b'\'', b'"' => b'"', b'\r' | b'\n' => loop { let byte = byte(v, 0); if matches!(byte, b' ' | b'\t' | b'\n' | b'\r') { v = &v[1..]; } else { continue 'outer; } }, b => panic!( "unexpected byte '{}' after \\ character in byte-string literal", ascii::escape_default(b), ), } } b'\r' => { assert_eq!(byte(v, 1), b'\n', "bare CR not allowed in string"); v = &v[2..]; b'\n' } b => { v = &v[1..]; b } }; out.push(byte); }
assert_eq!(byte(v, 0), b'"'); let suffix = s[s.len() - v.len() + 1..].to_owned().into_boxed_str(); (out, suffix) }
fn parse_lit_byte_str_raw(s: &str) -> (Vec<u8>, Box<str>) { assert_eq!(byte(s, 0), b'b'); let (value, suffix) = parse_lit_str_raw(&s[1..]); (String::from(value).into_bytes(), suffix) }
// Returns (content, suffix). pub(crate) fn parse_lit_c_str(s: &str) -> (CString, Box<str>) { assert_eq!(byte(s, 0), b'c'); match byte(s, 1) { b'"' => parse_lit_c_str_cooked(s), b'r' => parse_lit_c_str_raw(s), _ => unreachable!(), } }
fn parse_lit_c_str_cooked(mut s: &str) -> (CString, Box<str>) { assert_eq!(byte(s, 0), b'c'); assert_eq!(byte(s, 1), b'"'); s = &s[2..];
// We're going to want to have slices which don't respect codepoint boundaries. let mut v = s.as_bytes();
let mut out = Vec::new(); 'outer: loop { let byte = match byte(v, 0) { b'"' => break, b'\\' => { let b = byte(v, 1); v = &v[2..]; match b { b'x' => { let (b, rest) = backslash_x(v); assert!(b != 0, "\\x00 is not allowed in C-string literal"); v = rest; b } b'u' => { let (ch, rest) = backslash_u(v); assert!(ch != '\0', "\\u{{0}} is not allowed in C-string literal"); v = rest; out.extend_from_slice(ch.encode_utf8(&mut [0u8; 4]).as_bytes()); continue 'outer; } b'n' => b'\n', b'r' => b'\r', b't' => b'\t', b'\\' => b'\\', b'\'' => b'\'', b'"' => b'"', b'\r' | b'\n' => loop { let byte = byte(v, 0); if matches!(byte, b' ' | b'\t' | b'\n' | b'\r') { v = &v[1..]; } else { continue 'outer; } }, b => panic!( "unexpected byte '{}' after \\ character in byte literal", ascii::escape_default(b), ), } } b'\r' => { assert_eq!(byte(v, 1), b'\n', "bare CR not allowed in string"); v = &v[2..]; b'\n' } b => { v = &v[1..]; b } }; out.push(byte); }
assert_eq!(byte(v, 0), b'"'); let suffix = s[s.len() - v.len() + 1..].to_owned().into_boxed_str(); (CString::new(out).unwrap(), suffix) }
fn parse_lit_c_str_raw(s: &str) -> (CString, Box<str>) { assert_eq!(byte(s, 0), b'c'); let (value, suffix) = parse_lit_str_raw(&s[1..]); (CString::new(String::from(value)).unwrap(), suffix) }
// Returns (value, suffix). pub(crate) fn parse_lit_byte(s: &str) -> (u8, Box<str>) { assert_eq!(byte(s, 0), b'b'); assert_eq!(byte(s, 1), b'\'');
// We're going to want to have slices which don't respect codepoint boundaries. let mut v = &s.as_bytes()[2..];
let b = match byte(v, 0) { b'\\' => { let b = byte(v, 1); v = &v[2..]; match b { b'x' => { let (b, rest) = backslash_x(v); v = rest; b } b'n' => b'\n', b'r' => b'\r', b't' => b'\t', b'\\' => b'\\', b'0' => b'\0', b'\'' => b'\'', b'"' => b'"', b => panic!( "unexpected byte '{}' after \\ character in byte literal", ascii::escape_default(b), ), } } b => { v = &v[1..]; b } };
assert_eq!(byte(v, 0), b'\''); let suffix = s[s.len() - v.len() + 1..].to_owned().into_boxed_str(); (b, suffix) }
// Returns (value, suffix). pub(crate) fn parse_lit_char(mut s: &str) -> (char, Box<str>) { assert_eq!(byte(s, 0), b'\''); s = &s[1..];
let ch = match byte(s, 0) { b'\\' => { let b = byte(s, 1); s = &s[2..]; match b { b'x' => { let (byte, rest) = backslash_x(s); s = rest; assert!(byte <= 0x7F, "invalid \\x byte in character literal"); char::from_u32(u32::from(byte)).unwrap() } b'u' => { let (ch, rest) = backslash_u(s); s = rest; ch } b'n' => '\n', b'r' => '\r', b't' => '\t', b'\\' => '\\', b'0' => '\0', b'\'' => '\'', b'"' => '"', b => panic!( "unexpected byte '{}' after \\ character in character literal", ascii::escape_default(b), ), } } _ => { let ch = next_chr(s); s = &s[ch.len_utf8()..]; ch } }; assert_eq!(byte(s, 0), b'\''); let suffix = s[1..].to_owned().into_boxed_str(); (ch, suffix) }
fn backslash_x<S>(s: &S) -> (u8, &S) where S: Index<RangeFrom<usize>, Output = S> + AsRef<[u8]> + ?Sized, { let mut ch = 0; let b0 = byte(s, 0); let b1 = byte(s, 1); ch += 0x10 * match b0 { b'0'..=b'9' => b0 - b'0', b'a'..=b'f' => 10 + (b0 - b'a'), b'A'..=b'F' => 10 + (b0 - b'A'), _ => panic!("unexpected non-hex character after \\x"), }; ch += match b1 { b'0'..=b'9' => b1 - b'0', b'a'..=b'f' => 10 + (b1 - b'a'), b'A'..=b'F' => 10 + (b1 - b'A'), _ => panic!("unexpected non-hex character after \\x"), }; (ch, &s[2..]) }
fn backslash_u<S>(mut s: &S) -> (char, &S) where S: Index<RangeFrom<usize>, Output = S> + AsRef<[u8]> + ?Sized, { if byte(s, 0) != b'{' { panic!("{}", "expected { after \\u"); } s = &s[1..];
let mut ch = 0; let mut digits = 0; loop { let b = byte(s, 0); let digit = match b { b'0'..=b'9' => b - b'0', b'a'..=b'f' => 10 + b - b'a', b'A'..=b'F' => 10 + b - b'A', b'_' if digits > 0 => { s = &s[1..]; continue; } b'}' if digits == 0 => panic!("invalid empty unicode escape"), b'}' => break, _ => panic!("unexpected non-hex character after \\u"), }; if digits == 6 { panic!("overlong unicode escape (must have at most 6 hex digits)"); } ch *= 0x10; ch += u32::from(digit); digits += 1; s = &s[1..]; } assert!(byte(s, 0) == b'}'); s = &s[1..];
if let Some(ch) = char::from_u32(ch) { (ch, s) } else { panic!("character code {:x} is not a valid unicode character", ch); } }
// Returns base 10 digits and suffix. pub(crate) fn parse_lit_int(mut s: &str) -> Option<(Box<str>, Box<str>)> { let negative = byte(s, 0) == b'-'; if negative { s = &s[1..]; }
let base = match (byte(s, 0), byte(s, 1)) { (b'0', b'x') => { s = &s[2..]; 16 } (b'0', b'o') => { s = &s[2..]; 8 } (b'0', b'b') => { s = &s[2..]; 2 } (b'0'..=b'9', _) => 10, _ => return None, };
let mut value = BigInt::new(); let mut has_digit = false; 'outer: loop { let b = byte(s, 0); let digit = match b { b'0'..=b'9' => b - b'0', b'a'..=b'f' if base > 10 => b - b'a' + 10, b'A'..=b'F' if base > 10 => b - b'A' + 10, b'_' => { s = &s[1..]; continue; } // If looking at a floating point literal, we don't want to // consider it an integer. b'.' if base == 10 => return None, b'e' | b'E' if base == 10 => { let mut has_exp = false; for (i, b) in s[1..].bytes().enumerate() { match b { b'_' => {} b'-' | b'+' => return None, b'0'..=b'9' => has_exp = true, _ => { let suffix = &s[1 + i..]; if has_exp && crate::ident::xid_ok(suffix) { return None; } else { break 'outer; } } } } if has_exp { return None; } else { break; } } _ => break, };
if digit >= base { return None; }
has_digit = true; value *= base; value += digit; s = &s[1..]; }
if !has_digit { return None; }
let suffix = s; if suffix.is_empty() || crate::ident::xid_ok(suffix) { let mut repr = value.to_string(); if negative { repr.insert(0, '-'); } Some((repr.into_boxed_str(), suffix.to_owned().into_boxed_str())) } else { None } }
// Returns base 10 digits and suffix. pub(crate) fn parse_lit_float(input: &str) -> Option<(Box<str>, Box<str>)> { // Rust's floating point literals are very similar to the ones parsed by // the standard library, except that rust's literals can contain // ignorable underscores. Let's remove those underscores.
let mut bytes = input.to_owned().into_bytes();
let start = (*bytes.first()? == b'-') as usize; match bytes.get(start)? { b'0'..=b'9' => {} _ => return None, }
let mut read = start; let mut write = start; let mut has_dot = false; let mut has_e = false; let mut has_sign = false; let mut has_exponent = false; while read < bytes.len() { match bytes[read] { b'_' => { // Don't increase write read += 1; continue; } b'0'..=b'9' => { if has_e { has_exponent = true; } bytes[write] = bytes[read]; } b'.' => { if has_e || has_dot { return None; } has_dot = true; bytes[write] = b'.'; } b'e' | b'E' => { match bytes[read + 1..] .iter() .find(|b| **b != b'_') .unwrap_or(&b'\0') { b'-' | b'+' | b'0'..=b'9' => {} _ => break, } if has_e { if has_exponent { break; } else { return None; } } has_e = true; bytes[write] = b'e'; } b'-' | b'+' => { if has_sign || has_exponent || !has_e { return None; } has_sign = true; if bytes[read] == b'-' { bytes[write] = bytes[read]; } else { // Omit '+' read += 1; continue; } } _ => break, } read += 1; write += 1; }
if has_e && !has_exponent { return None; }
let mut digits = String::from_utf8(bytes).unwrap(); let suffix = digits.split_off(read); digits.truncate(write); if suffix.is_empty() || crate::ident::xid_ok(&suffix) { Some((digits.into_boxed_str(), suffix.into_boxed_str())) } else { None } }}