#[cfg(feature = "parsing")] usecrate::lookahead; #[cfg(feature = "parsing")] usecrate::parse::{Parse, Parser}; usecrate::{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] pubenum 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 floating point literal: `1f64` or `1.0e10f64`. /// /// Must be finite. May not be infinite or NaN. pubstruct LitFloat {
repr: Box<LitFloatRepr>,
}
}
pubfn 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")))] pubfn 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")))] pubfn 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(); letmut 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),
));
}
/// 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 }) /// } /// } /// ``` pubfn base10_parse<N>(&self) -> Result<N> where
N: FromStr,
N::Err: Display,
{ self.base10_digits()
.parse()
.map_err(|err| Error::new(self.span(), err))
}
/// The style of a string literal, either plain quoted or a raw string like /// `r##"data"##`. #[doc(hidden)] // https://github.com/dtolnay/syn/issues/1566 pubenum StrStyle { /// An ordinary string like `"data"`.
Cooked, /// A raw string like `r##"data"##`. /// /// The unsigned integer is the number of `#` symbols used.
Raw(usize),
}
mod value { usecrate::bigint::BigInt; usecrate::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. pubfn new(token: Literal) -> Self { let repr = token.to_string();
/// 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
}
}
// We're going to want to have slices which don't respect codepoint boundaries. letmut v = s[2..].as_bytes();
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
}
};
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..];
letmut ch = 0; letmut 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..];
iflet 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,
};
letmut value = BigInt::new(); letmut 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 => { letmut 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) { letmut 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.
letmut bytes = input.to_owned().into_bytes();
let start = (*bytes.first()? == b'-') as usize; match bytes.get(start)? {
b'0'..=b'9' => {}
_ => return None,
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.