diff options
Diffstat (limited to 'rust/vendor/syn-0.15.44/src/lit.rs')
-rw-r--r-- | rust/vendor/syn-0.15.44/src/lit.rs | 1106 |
1 files changed, 1106 insertions, 0 deletions
diff --git a/rust/vendor/syn-0.15.44/src/lit.rs b/rust/vendor/syn-0.15.44/src/lit.rs new file mode 100644 index 0000000..aa6a654 --- /dev/null +++ b/rust/vendor/syn-0.15.44/src/lit.rs @@ -0,0 +1,1106 @@ +use proc_macro2::{Literal, Span}; +use std::str; + +#[cfg(feature = "printing")] +use proc_macro2::Ident; + +#[cfg(feature = "parsing")] +use proc_macro2::TokenStream; + +use proc_macro2::TokenTree; + +#[cfg(feature = "extra-traits")] +use std::hash::{Hash, Hasher}; + +#[cfg(feature = "parsing")] +use lookahead; +#[cfg(feature = "parsing")] +use parse::{Parse, Parser, Result}; + +ast_enum_of_structs! { + /// A Rust literal such as a string or integer or boolean. + /// + /// *This type is available if Syn is built with the `"derive"` or `"full"` + /// feature.* + /// + /// # Syntax tree enum + /// + /// This type is a [syntax tree enum]. + /// + /// [syntax tree enum]: enum.Expr.html#syntax-tree-enums + // + // TODO: change syntax-tree-enum link to an intra rustdoc link, currently + // blocked on https://github.com/rust-lang/rust/issues/62833 + pub enum Lit { + /// A UTF-8 string literal: `"foo"`. + /// + /// *This type is available if Syn is built with the `"derive"` or + /// `"full"` feature.* + pub Str(LitStr #manual_extra_traits { + token: Literal, + }), + + /// A byte string literal: `b"foo"`. + /// + /// *This type is available if Syn is built with the `"derive"` or + /// `"full"` feature.* + pub ByteStr(LitByteStr #manual_extra_traits { + token: Literal, + }), + + /// A byte literal: `b'f'`. + /// + /// *This type is available if Syn is built with the `"derive"` or + /// `"full"` feature.* + pub Byte(LitByte #manual_extra_traits { + token: Literal, + }), + + /// A character literal: `'a'`. + /// + /// *This type is available if Syn is built with the `"derive"` or + /// `"full"` feature.* + pub Char(LitChar #manual_extra_traits { + token: Literal, + }), + + /// An integer literal: `1` or `1u16`. + /// + /// Holds up to 64 bits of data. Use `LitVerbatim` for any larger + /// integer literal. + /// + /// *This type is available if Syn is built with the `"derive"` or + /// `"full"` feature.* + pub Int(LitInt #manual_extra_traits { + token: Literal, + }), + + /// A floating point literal: `1f64` or `1.0e10f64`. + /// + /// Must be finite. May not be infinte or NaN. + /// + /// *This type is available if Syn is built with the `"derive"` or + /// `"full"` feature.* + pub Float(LitFloat #manual_extra_traits { + token: Literal, + }), + + /// A boolean literal: `true` or `false`. + /// + /// *This type is available if Syn is built with the `"derive"` or + /// `"full"` feature.* + pub Bool(LitBool #manual_extra_traits { + pub value: bool, + pub span: Span, + }), + + /// A raw token literal not interpreted by Syn, possibly because it + /// represents an integer larger than 64 bits. + /// + /// *This type is available if Syn is built with the `"derive"` or + /// `"full"` feature.* + pub Verbatim(LitVerbatim #manual_extra_traits { + pub token: Literal, + }), + } +} + +impl LitStr { + pub fn new(value: &str, span: Span) -> Self { + let mut lit = Literal::string(value); + lit.set_span(span); + LitStr { token: lit } + } + + pub fn value(&self) -> String { + value::parse_lit_str(&self.token.to_string()) + } + + /// 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 + /// + /// ```edition2018 + /// use proc_macro2::Span; + /// use syn::{Attribute, Error, Ident, Lit, Meta, MetaNameValue, 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); + /// } + /// + /// match attr.parse_meta()? { + /// Meta::NameValue(MetaNameValue { lit: Lit::Str(lit_str), .. }) => { + /// lit_str.parse().map(Some) + /// } + /// _ => { + /// let message = "expected #[path = \"...\"]"; + /// Err(Error::new_spanned(attr, message)) + /// } + /// } + /// } + /// ``` + #[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 + /// + /// ```edition2018 + /// # 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")] + 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 token { + TokenTree::Group(ref mut g) => { + let stream = respan_token_stream(g.stream().clone(), span); + *g = Group::new(g.delimiter(), stream); + g.set_span(span); + } + ref mut other => other.set_span(span), + } + token + } + + // Parse string literal into a token stream with every span equal to the + // original literal's span. + let mut tokens = ::parse_str(&self.value())?; + tokens = respan_token_stream(tokens, self.span()); + + parser.parse2(tokens) + } + + pub fn span(&self) -> Span { + self.token.span() + } + + pub fn set_span(&mut self, span: Span) { + self.token.set_span(span) + } +} + +impl LitByteStr { + pub fn new(value: &[u8], span: Span) -> Self { + let mut token = Literal::byte_string(value); + token.set_span(span); + LitByteStr { token: token } + } + + pub fn value(&self) -> Vec<u8> { + value::parse_lit_byte_str(&self.token.to_string()) + } + + pub fn span(&self) -> Span { + self.token.span() + } + + pub fn set_span(&mut self, span: Span) { + self.token.set_span(span) + } +} + +impl LitByte { + pub fn new(value: u8, span: Span) -> Self { + let mut token = Literal::u8_suffixed(value); + token.set_span(span); + LitByte { token: token } + } + + pub fn value(&self) -> u8 { + value::parse_lit_byte(&self.token.to_string()) + } + + pub fn span(&self) -> Span { + self.token.span() + } + + pub fn set_span(&mut self, span: Span) { + self.token.set_span(span) + } +} + +impl LitChar { + pub fn new(value: char, span: Span) -> Self { + let mut token = Literal::character(value); + token.set_span(span); + LitChar { token: token } + } + + pub fn value(&self) -> char { + value::parse_lit_char(&self.token.to_string()) + } + + pub fn span(&self) -> Span { + self.token.span() + } + + pub fn set_span(&mut self, span: Span) { + self.token.set_span(span) + } +} + +impl LitInt { + pub fn new(value: u64, suffix: IntSuffix, span: Span) -> Self { + let mut token = match suffix { + IntSuffix::Isize => Literal::isize_suffixed(value as isize), + IntSuffix::I8 => Literal::i8_suffixed(value as i8), + IntSuffix::I16 => Literal::i16_suffixed(value as i16), + IntSuffix::I32 => Literal::i32_suffixed(value as i32), + IntSuffix::I64 => Literal::i64_suffixed(value as i64), + IntSuffix::I128 => value::to_literal(&format!("{}i128", value)), + IntSuffix::Usize => Literal::usize_suffixed(value as usize), + IntSuffix::U8 => Literal::u8_suffixed(value as u8), + IntSuffix::U16 => Literal::u16_suffixed(value as u16), + IntSuffix::U32 => Literal::u32_suffixed(value as u32), + IntSuffix::U64 => Literal::u64_suffixed(value), + IntSuffix::U128 => value::to_literal(&format!("{}u128", value)), + IntSuffix::None => Literal::u64_unsuffixed(value), + }; + token.set_span(span); + LitInt { token: token } + } + + pub fn value(&self) -> u64 { + value::parse_lit_int(&self.token.to_string()).unwrap() + } + + pub fn suffix(&self) -> IntSuffix { + let value = self.token.to_string(); + for (s, suffix) in vec![ + ("i8", IntSuffix::I8), + ("i16", IntSuffix::I16), + ("i32", IntSuffix::I32), + ("i64", IntSuffix::I64), + ("i128", IntSuffix::I128), + ("isize", IntSuffix::Isize), + ("u8", IntSuffix::U8), + ("u16", IntSuffix::U16), + ("u32", IntSuffix::U32), + ("u64", IntSuffix::U64), + ("u128", IntSuffix::U128), + ("usize", IntSuffix::Usize), + ] { + if value.ends_with(s) { + return suffix; + } + } + IntSuffix::None + } + + pub fn span(&self) -> Span { + self.token.span() + } + + pub fn set_span(&mut self, span: Span) { + self.token.set_span(span) + } +} + +impl LitFloat { + pub fn new(value: f64, suffix: FloatSuffix, span: Span) -> Self { + let mut token = match suffix { + FloatSuffix::F32 => Literal::f32_suffixed(value as f32), + FloatSuffix::F64 => Literal::f64_suffixed(value), + FloatSuffix::None => Literal::f64_unsuffixed(value), + }; + token.set_span(span); + LitFloat { token: token } + } + + pub fn value(&self) -> f64 { + value::parse_lit_float(&self.token.to_string()) + } + + pub fn suffix(&self) -> FloatSuffix { + let value = self.token.to_string(); + for (s, suffix) in vec![("f32", FloatSuffix::F32), ("f64", FloatSuffix::F64)] { + if value.ends_with(s) { + return suffix; + } + } + FloatSuffix::None + } + + pub fn span(&self) -> Span { + self.token.span() + } + + pub fn set_span(&mut self, span: Span) { + self.token.set_span(span) + } +} + +macro_rules! lit_extra_traits { + ($ty:ident, $field:ident) => { + #[cfg(feature = "extra-traits")] + impl Eq for $ty {} + + #[cfg(feature = "extra-traits")] + impl PartialEq for $ty { + fn eq(&self, other: &Self) -> bool { + self.$field.to_string() == other.$field.to_string() + } + } + + #[cfg(feature = "extra-traits")] + impl Hash for $ty { + fn hash<H>(&self, state: &mut H) + where + H: Hasher, + { + self.$field.to_string().hash(state); + } + } + + #[cfg(feature = "parsing")] + #[doc(hidden)] + #[allow(non_snake_case)] + pub fn $ty(marker: lookahead::TokenMarker) -> $ty { + match marker {} + } + }; +} + +impl LitVerbatim { + pub fn span(&self) -> Span { + self.token.span() + } + + pub fn set_span(&mut self, span: Span) { + self.token.set_span(span) + } +} + +lit_extra_traits!(LitStr, token); +lit_extra_traits!(LitByteStr, token); +lit_extra_traits!(LitByte, token); +lit_extra_traits!(LitChar, token); +lit_extra_traits!(LitInt, token); +lit_extra_traits!(LitFloat, token); +lit_extra_traits!(LitBool, value); +lit_extra_traits!(LitVerbatim, token); + +ast_enum! { + /// The style of a string literal, either plain quoted or a raw string like + /// `r##"data"##`. + /// + /// *This type is available if Syn is built with the `"derive"` or `"full"` + /// feature.* + pub enum StrStyle #no_visit { + /// An ordinary string like `"data"`. + Cooked, + /// A raw string like `r##"data"##`. + /// + /// The unsigned integer is the number of `#` symbols used. + Raw(usize), + } +} + +ast_enum! { + /// The suffix on an integer literal if any, like the `u8` in `127u8`. + /// + /// *This type is available if Syn is built with the `"derive"` or `"full"` + /// feature.* + pub enum IntSuffix #no_visit { + I8, + I16, + I32, + I64, + I128, + Isize, + U8, + U16, + U32, + U64, + U128, + Usize, + None, + } +} + +ast_enum! { + /// The suffix on a floating point literal if any, like the `f32` in + /// `1.0f32`. + /// + /// *This type is available if Syn is built with the `"derive"` or `"full"` + /// feature.* + pub enum FloatSuffix #no_visit { + F32, + F64, + None, + } +} + +#[cfg(feature = "parsing")] +#[doc(hidden)] +#[allow(non_snake_case)] +pub fn Lit(marker: lookahead::TokenMarker) -> Lit { + match marker {} +} + +#[cfg(feature = "parsing")] +pub mod parsing { + use super::*; + use parse::{Parse, ParseStream, Result}; + + 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)); + } + while let Some((ident, rest)) = cursor.ident() { + let value = if ident == "true" { + true + } else if ident == "false" { + false + } else { + break; + }; + let lit_bool = LitBool { + value: value, + span: ident.span(), + }; + return Ok((Lit::Bool(lit_bool), rest)); + } + Err(cursor.error("expected literal")) + }) + } + } + + impl Parse for LitStr { + fn parse(input: ParseStream) -> Result<Self> { + let head = input.fork(); + match input.parse()? { + Lit::Str(lit) => Ok(lit), + _ => Err(head.error("expected string literal")), + } + } + } + + impl Parse for LitByteStr { + fn parse(input: ParseStream) -> Result<Self> { + let head = input.fork(); + match input.parse()? { + Lit::ByteStr(lit) => Ok(lit), + _ => Err(head.error("expected byte string literal")), + } + } + } + + impl Parse for LitByte { + fn parse(input: ParseStream) -> Result<Self> { + let head = input.fork(); + match input.parse()? { + Lit::Byte(lit) => Ok(lit), + _ => Err(head.error("expected byte literal")), + } + } + } + + impl Parse for LitChar { + fn parse(input: ParseStream) -> Result<Self> { + let head = input.fork(); + match input.parse()? { + Lit::Char(lit) => Ok(lit), + _ => Err(head.error("expected character literal")), + } + } + } + + impl Parse for LitInt { + fn parse(input: ParseStream) -> Result<Self> { + let head = input.fork(); + match input.parse()? { + Lit::Int(lit) => Ok(lit), + _ => Err(head.error("expected integer literal")), + } + } + } + + impl Parse for LitFloat { + fn parse(input: ParseStream) -> Result<Self> { + let head = input.fork(); + match input.parse()? { + Lit::Float(lit) => Ok(lit), + _ => Err(head.error("expected floating point literal")), + } + } + } + + impl Parse for LitBool { + fn parse(input: ParseStream) -> Result<Self> { + let head = input.fork(); + match input.parse()? { + Lit::Bool(lit) => Ok(lit), + _ => Err(head.error("expected boolean literal")), + } + } + } +} + +#[cfg(feature = "printing")] +mod printing { + use super::*; + use proc_macro2::TokenStream; + use quote::{ToTokens, TokenStreamExt}; + + impl ToTokens for LitStr { + fn to_tokens(&self, tokens: &mut TokenStream) { + self.token.to_tokens(tokens); + } + } + + impl ToTokens for LitByteStr { + fn to_tokens(&self, tokens: &mut TokenStream) { + self.token.to_tokens(tokens); + } + } + + impl ToTokens for LitByte { + fn to_tokens(&self, tokens: &mut TokenStream) { + self.token.to_tokens(tokens); + } + } + + impl ToTokens for LitChar { + fn to_tokens(&self, tokens: &mut TokenStream) { + self.token.to_tokens(tokens); + } + } + + impl ToTokens for LitInt { + fn to_tokens(&self, tokens: &mut TokenStream) { + self.token.to_tokens(tokens); + } + } + + impl ToTokens for LitFloat { + fn to_tokens(&self, tokens: &mut TokenStream) { + self.token.to_tokens(tokens); + } + } + + impl ToTokens for LitBool { + fn to_tokens(&self, tokens: &mut TokenStream) { + let s = if self.value { "true" } else { "false" }; + tokens.append(Ident::new(s, self.span)); + } + } + + impl ToTokens for LitVerbatim { + fn to_tokens(&self, tokens: &mut TokenStream) { + self.token.to_tokens(tokens); + } + } +} + +mod value { + use super::*; + use proc_macro2::TokenStream; + use std::char; + use std::ops::{Index, RangeFrom}; + + impl Lit { + /// Interpret a Syn literal from a proc-macro2 literal. + /// + /// Not all proc-macro2 literals are valid Syn literals. In particular, + /// doc comments are considered by proc-macro2 to be literals but in Syn + /// they are [`Attribute`]. + /// + /// [`Attribute`]: crate::Attribute + /// + /// # Panics + /// + /// Panics if the input is a doc comment literal. + pub fn new(token: Literal) -> Self { + let value = token.to_string(); + + match value::byte(&value, 0) { + b'"' | b'r' => return Lit::Str(LitStr { token: token }), + b'b' => match value::byte(&value, 1) { + b'"' | b'r' => return Lit::ByteStr(LitByteStr { token: token }), + b'\'' => return Lit::Byte(LitByte { token: token }), + _ => {} + }, + b'\'' => return Lit::Char(LitChar { token: token }), + b'0'...b'9' => { + if number_is_int(&value) { + return Lit::Int(LitInt { token: token }); + } else if number_is_float(&value) { + return Lit::Float(LitFloat { token: token }); + } else { + // number overflow + return Lit::Verbatim(LitVerbatim { token: token }); + } + } + _ => { + if value == "true" || value == "false" { + return Lit::Bool(LitBool { + value: value == "true", + span: token.span(), + }); + } + } + } + + panic!("Unrecognized literal: {}", value); + } + } + + fn number_is_int(value: &str) -> bool { + if number_is_float(value) { + false + } else { + value::parse_lit_int(value).is_some() + } + } + + fn number_is_float(value: &str) -> bool { + if value.contains('.') { + true + } else if value.starts_with("0x") || value.ends_with("size") { + false + } else { + value.contains('e') || value.contains('E') + } + } + + /// Get the byte at offset idx, or a default of `b'\0'` if we're looking + /// past the end of the input buffer. + pub 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') + } + + pub fn parse_lit_str(s: &str) -> String { + match byte(s, 0) { + b'"' => parse_lit_str_cooked(s), + b'r' => parse_lit_str_raw(s), + _ => unreachable!(), + } + } + + // Clippy false positive + // https://github.com/rust-lang-nursery/rust-clippy/issues/2329 + #[cfg_attr(feature = "cargo-clippy", allow(needless_continue))] + fn parse_lit_str_cooked(mut s: &str) -> String { + assert_eq!(byte(s, 0), b'"'); + s = &s[1..]; + + let mut out = 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 <= 0x80, "Invalid \\x byte in string literal"); + char::from_u32(u32::from(byte)).unwrap() + } + b'u' => { + let (chr, rest) = backslash_u(s); + s = rest; + chr + } + b'n' => '\n', + b'r' => '\r', + b't' => '\t', + b'\\' => '\\', + b'0' => '\0', + b'\'' => '\'', + b'"' => '"', + b'\r' | b'\n' => loop { + let ch = next_chr(s); + if ch.is_whitespace() { + s = &s[ch.len_utf8()..]; + } else { + continue 'outer; + } + }, + b => panic!("unexpected byte {:?} after \\ character in byte literal", 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 + } + }; + out.push(ch); + } + + assert_eq!(s, "\""); + out + } + + fn parse_lit_str_raw(mut s: &str) -> String { + 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'"'); + assert_eq!(byte(s, s.len() - pounds - 1), b'"'); + for end in s[s.len() - pounds..].bytes() { + assert_eq!(end, b'#'); + } + + s[pounds + 1..s.len() - pounds - 1].to_owned() + } + + pub fn parse_lit_byte_str(s: &str) -> Vec<u8> { + 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!(), + } + } + + // Clippy false positive + // https://github.com/rust-lang-nursery/rust-clippy/issues/2329 + #[cfg_attr(feature = "cargo-clippy", allow(needless_continue))] + fn parse_lit_byte_str_cooked(mut s: &str) -> Vec<u8> { + 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 s = s.as_bytes(); + + let mut out = Vec::new(); + 'outer: loop { + let byte = match byte(s, 0) { + b'"' => break, + b'\\' => { + let b = byte(s, 1); + s = &s[2..]; + match b { + b'x' => { + let (b, rest) = backslash_x(s); + s = 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(s, 0); + let ch = char::from_u32(u32::from(byte)).unwrap(); + if ch.is_whitespace() { + s = &s[1..]; + } else { + continue 'outer; + } + }, + b => panic!("unexpected byte {:?} after \\ character in byte literal", b), + } + } + b'\r' => { + assert_eq!(byte(s, 1), b'\n', "Bare CR not allowed in string"); + s = &s[2..]; + b'\n' + } + b => { + s = &s[1..]; + b + } + }; + out.push(byte); + } + + assert_eq!(s, b"\""); + out + } + + fn parse_lit_byte_str_raw(s: &str) -> Vec<u8> { + assert_eq!(byte(s, 0), b'b'); + parse_lit_str_raw(&s[1..]).into_bytes() + } + + pub fn parse_lit_byte(s: &str) -> u8 { + 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 s = s[2..].as_bytes(); + + let b = match byte(s, 0) { + b'\\' => { + let b = byte(s, 1); + s = &s[2..]; + match b { + b'x' => { + let (b, rest) = backslash_x(s); + s = 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", b), + } + } + b => { + s = &s[1..]; + b + } + }; + + assert_eq!(byte(s, 0), b'\''); + b + } + + pub fn parse_lit_char(mut s: &str) -> char { + 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 <= 0x80, "Invalid \\x byte in string literal"); + char::from_u32(u32::from(byte)).unwrap() + } + b'u' => { + let (chr, rest) = backslash_u(s); + s = rest; + chr + } + b'n' => '\n', + b'r' => '\r', + b't' => '\t', + b'\\' => '\\', + b'0' => '\0', + b'\'' => '\'', + b'"' => '"', + b => panic!("unexpected byte {:?} after \\ character in byte literal", b), + } + } + _ => { + let ch = next_chr(s); + s = &s[ch.len_utf8()..]; + ch + } + }; + assert_eq!(s, "\'", "Expected end of char literal"); + ch + } + + 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(mut s: &str) -> (char, &str) { + if byte(s, 0) != b'{' { + panic!("expected {{ after \\u"); + } + s = &s[1..]; + + let mut ch = 0; + for _ in 0..6 { + let b = byte(s, 0); + match b { + b'0'...b'9' => { + ch *= 0x10; + ch += u32::from(b - b'0'); + s = &s[1..]; + } + b'a'...b'f' => { + ch *= 0x10; + ch += u32::from(10 + b - b'a'); + s = &s[1..]; + } + b'A'...b'F' => { + ch *= 0x10; + ch += u32::from(10 + b - b'A'); + s = &s[1..]; + } + b'}' => break, + _ => panic!("unexpected non-hex character after \\u"), + } + } + 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); + } + } + + pub fn parse_lit_int(mut s: &str) -> Option<u64> { + 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, + _ => unreachable!(), + }; + + let mut value = 0u64; + loop { + let b = byte(s, 0); + let digit = match b { + b'0'...b'9' => u64::from(b - b'0'), + b'a'...b'f' if base > 10 => 10 + u64::from(b - b'a'), + b'A'...b'F' if base > 10 => 10 + u64::from(b - b'A'), + b'_' => { + s = &s[1..]; + continue; + } + // NOTE: Looking at a floating point literal, we don't want to + // consider these integers. + b'.' if base == 10 => return None, + b'e' | b'E' if base == 10 => return None, + _ => break, + }; + + if digit >= base { + panic!("Unexpected digit {:x} out of base range", digit); + } + + value = match value.checked_mul(base) { + Some(value) => value, + None => return None, + }; + value = match value.checked_add(digit) { + Some(value) => value, + None => return None, + }; + s = &s[1..]; + } + + Some(value) + } + + pub fn parse_lit_float(input: &str) -> f64 { + // 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 mut write = 0; + for read in 0..bytes.len() { + if bytes[read] == b'_' { + continue; // Don't increase write + } + if write != read { + let x = bytes[read]; + bytes[write] = x; + } + write += 1; + } + bytes.truncate(write); + let input = String::from_utf8(bytes).unwrap(); + let end = input.find('f').unwrap_or_else(|| input.len()); + input[..end].parse().unwrap() + } + + pub fn to_literal(s: &str) -> Literal { + let stream = s.parse::<TokenStream>().unwrap(); + match stream.into_iter().next().unwrap() { + TokenTree::Literal(l) => l, + _ => unreachable!(), + } + } +} |