-// Copyright 2018 Syn Developers
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-use proc_macro2::{Literal, Span};
-use std::str;
-
+#[cfg(feature = "parsing")]
+use crate::lookahead;
+#[cfg(feature = "parsing")]
+use crate::parse::{Parse, Parser};
+use crate::{Error, Result};
#[cfg(feature = "printing")]
use proc_macro2::Ident;
-
#[cfg(feature = "parsing")]
use proc_macro2::TokenStream;
-#[cfg(feature = "parsing")]
-use Error;
-
use proc_macro2::TokenTree;
-
+use proc_macro2::{Literal, Span};
+use std::fmt::{self, Display};
#[cfg(feature = "extra-traits")]
use std::hash::{Hash, Hasher};
-
-#[cfg(feature = "parsing")]
-use lookahead;
-#[cfg(feature = "parsing")]
-use parse::Parse;
+use std::str::{self, FromStr};
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
+ /// [syntax tree enum]: crate::Expr#syntax-tree-enums
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,
- }),
+ Str(LitStr),
/// 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,
- }),
+ ByteStr(LitByteStr),
/// 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,
- }),
+ Byte(LitByte),
/// 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,
- }),
+ Char(LitChar),
/// 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,
- }),
+ Int(LitInt),
/// 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,
- }),
+ Float(LitFloat),
/// 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,
- }),
+ 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 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 lit = Literal::string(value);
- lit.set_span(span);
- LitStr { token: lit }
+ 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 {
- value::parse_lit_str(&self.token.to_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.
/// # Example
///
/// ```
- /// # extern crate proc_macro2;
- /// # extern crate syn;
- /// #
/// use proc_macro2::Span;
/// use syn::{Attribute, Error, Ident, Lit, Meta, MetaNameValue, Path, Result};
///
/// lit_str.parse().map(Some)
/// }
/// _ => {
- /// let error_span = attr.bracket_token.span;
/// let message = "expected #[path = \"...\"]";
- /// Err(Error::new(error_span, message))
+ /// Err(Error::new_spanned(attr, message))
/// }
/// }
/// }
/// ```
#[cfg(feature = "parsing")]
- pub fn parse<T: Parse>(&self) -> Result<T, Error> {
- use proc_macro2::Group;
+ #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
+ pub fn parse<T: Parse>(&self) -> Result<T> {
+ self.parse_with(T::parse)
+ }
- // Parse string literal into a token stream with every span equal to the
- // original literal's span.
- fn spanned_tokens(s: &LitStr) -> Result<TokenStream, Error> {
- let stream = ::parse_str(&s.value())?;
- Ok(respan_token_stream(stream, s.span()))
- }
+ /// 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(doc_cfg, 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 {
// 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);
+ match &mut token {
+ TokenTree::Group(g) => {
+ let stream = respan_token_stream(g.stream(), span);
*g = Group::new(g.delimiter(), stream);
g.set_span(span);
}
- ref mut other => other.set_span(span),
+ other => other.set_span(span),
}
token
}
- spanned_tokens(self).and_then(::parse2)
+ // Parse string literal into a token stream with every span equal to the
+ // original literal's span.
+ let mut tokens = crate::parse_str(&self.value())?;
+ tokens = respan_token_stream(tokens, self.span());
+
+ parser.parse2(tokens)
}
pub fn span(&self) -> Span {
- self.token.span()
+ self.repr.token.span()
}
pub fn set_span(&mut self, span: Span) {
- self.token.set_span(span)
+ self.repr.token.set_span(span)
+ }
+
+ pub fn suffix(&self) -> &str {
+ &self.repr.suffix
}
}
pub fn new(value: &[u8], span: Span) -> Self {
let mut token = Literal::byte_string(value);
token.set_span(span);
- LitByteStr { token: token }
+ LitByteStr {
+ repr: Box::new(LitRepr {
+ token,
+ suffix: Box::<str>::default(),
+ }),
+ }
}
pub fn value(&self) -> Vec<u8> {
- value::parse_lit_byte_str(&self.token.to_string())
+ let repr = self.repr.token.to_string();
+ let (value, _suffix) = value::parse_lit_byte_str(&repr);
+ value
}
pub fn span(&self) -> Span {
- self.token.span()
+ self.repr.token.span()
}
pub fn set_span(&mut self, span: Span) {
- self.token.set_span(span)
+ self.repr.token.set_span(span)
+ }
+
+ pub fn suffix(&self) -> &str {
+ &self.repr.suffix
}
}
pub fn new(value: u8, span: Span) -> Self {
let mut token = Literal::u8_suffixed(value);
token.set_span(span);
- LitByte { token: token }
+ LitByte {
+ repr: Box::new(LitRepr {
+ token,
+ suffix: Box::<str>::default(),
+ }),
+ }
}
pub fn value(&self) -> u8 {
- value::parse_lit_byte(&self.token.to_string())
+ let repr = self.repr.token.to_string();
+ let (value, _suffix) = value::parse_lit_byte(&repr);
+ value
}
pub fn span(&self) -> Span {
- self.token.span()
+ self.repr.token.span()
}
pub fn set_span(&mut self, span: Span) {
- self.token.set_span(span)
+ self.repr.token.set_span(span)
+ }
+
+ pub fn suffix(&self) -> &str {
+ &self.repr.suffix
}
}
pub fn new(value: char, span: Span) -> Self {
let mut token = Literal::character(value);
token.set_span(span);
- LitChar { token: token }
+ LitChar {
+ repr: Box::new(LitRepr {
+ token,
+ suffix: Box::<str>::default(),
+ }),
+ }
}
pub fn value(&self) -> char {
- value::parse_lit_char(&self.token.to_string())
+ let repr = self.repr.token.to_string();
+ let (value, _suffix) = value::parse_lit_char(&repr);
+ value
}
pub fn span(&self) -> Span {
- self.token.span()
+ self.repr.token.span()
}
pub fn set_span(&mut self, span: Span) {
- self.token.set_span(span)
+ self.repr.token.set_span(span)
+ }
+
+ pub fn suffix(&self) -> &str {
+ &self.repr.suffix
}
}
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),
+ 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 = match value::to_literal(repr, &digits, &suffix) {
+ Some(token) => token,
+ None => panic!("Unsupported integer literal: `{}`", repr),
};
+
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;
- }
+ LitInt {
+ repr: Box::new(LitIntRepr {
+ token,
+ digits,
+ suffix,
+ }),
}
- IntSuffix::None
+ }
+
+ 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.token.span()
+ self.repr.token.span()
}
pub fn set_span(&mut self, span: Span) {
- self.token.set_span(span)
+ self.repr.token.set_span(span)
+ }
+}
+
+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(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),
+ 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 = match value::to_literal(repr, &digits, &suffix) {
+ Some(token) => token,
+ None => panic!("Unsupported float literal: `{}`", repr),
};
+
token.set_span(span);
- LitFloat { token: token }
+ 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 value(&self) -> f64 {
- value::parse_lit_float(&self.token.to_string())
+ 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) -> 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;
+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);
}
- FloatSuffix::None
+ }
+}
+
+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.token.span()
+ self.span
}
pub fn set_span(&mut self, span: Span) {
- self.token.set_span(span)
+ self.span = span;
+ }
+}
+
+#[cfg(feature = "extra-traits")]
+mod debug_impls {
+ use super::*;
+ use std::fmt::{self, Debug};
+
+ #[cfg_attr(doc_cfg, doc(cfg(feature = "extra-traits")))]
+ impl Debug for LitStr {
+ fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ formatter
+ .debug_struct("LitStr")
+ .field("token", &format_args!("{}", self.repr.token))
+ .finish()
+ }
+ }
+
+ #[cfg_attr(doc_cfg, doc(cfg(feature = "extra-traits")))]
+ impl Debug for LitByteStr {
+ fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ formatter
+ .debug_struct("LitByteStr")
+ .field("token", &format_args!("{}", self.repr.token))
+ .finish()
+ }
+ }
+
+ #[cfg_attr(doc_cfg, doc(cfg(feature = "extra-traits")))]
+ impl Debug for LitByte {
+ fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ formatter
+ .debug_struct("LitByte")
+ .field("token", &format_args!("{}", self.repr.token))
+ .finish()
+ }
+ }
+
+ #[cfg_attr(doc_cfg, doc(cfg(feature = "extra-traits")))]
+ impl Debug for LitChar {
+ fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ formatter
+ .debug_struct("LitChar")
+ .field("token", &format_args!("{}", self.repr.token))
+ .finish()
+ }
+ }
+
+ #[cfg_attr(doc_cfg, doc(cfg(feature = "extra-traits")))]
+ impl Debug for LitInt {
+ fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ formatter
+ .debug_struct("LitInt")
+ .field("token", &format_args!("{}", self.repr.token))
+ .finish()
+ }
+ }
+
+ #[cfg_attr(doc_cfg, doc(cfg(feature = "extra-traits")))]
+ impl Debug for LitFloat {
+ fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ formatter
+ .debug_struct("LitFloat")
+ .field("token", &format_args!("{}", self.repr.token))
+ .finish()
+ }
+ }
+
+ #[cfg_attr(doc_cfg, doc(cfg(feature = "extra-traits")))]
+ impl Debug for LitBool {
+ fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ formatter
+ .debug_struct("LitBool")
+ .field("value", &self.value)
+ .finish()
+ }
+ }
+}
+
+#[cfg(feature = "clone-impls")]
+#[cfg_attr(doc_cfg, 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(doc_cfg, 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(doc_cfg, 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, $field:ident) => {
- #[cfg(feature = "extra-traits")]
- impl Eq for $ty {}
+ ($ty:ident) => {
+ #[cfg(feature = "clone-impls")]
+ #[cfg_attr(doc_cfg, doc(cfg(feature = "clone-impls")))]
+ impl Clone for $ty {
+ fn clone(&self) -> Self {
+ $ty {
+ repr: self.repr.clone(),
+ }
+ }
+ }
#[cfg(feature = "extra-traits")]
+ #[cfg_attr(doc_cfg, doc(cfg(feature = "extra-traits")))]
impl PartialEq for $ty {
fn eq(&self, other: &Self) -> bool {
- self.$field.to_string() == other.$field.to_string()
+ self.repr.token.to_string() == other.repr.token.to_string()
}
}
#[cfg(feature = "extra-traits")]
+ #[cfg_attr(doc_cfg, doc(cfg(feature = "extra-traits")))]
impl Hash for $ty {
fn hash<H>(&self, state: &mut H)
where
H: Hasher,
{
- self.$field.to_string().hash(state);
+ self.repr.token.to_string().hash(state);
}
}
};
}
-impl LitVerbatim {
- pub fn span(&self) -> Span {
- self.token.span()
- }
+lit_extra_traits!(LitStr);
+lit_extra_traits!(LitByteStr);
+lit_extra_traits!(LitByte);
+lit_extra_traits!(LitChar);
+lit_extra_traits!(LitInt);
+lit_extra_traits!(LitFloat);
- pub fn set_span(&mut self, span: Span) {
- self.token.set_span(span)
- }
+#[cfg(feature = "parsing")]
+#[doc(hidden)]
+#[allow(non_snake_case)]
+pub fn LitBool(marker: lookahead::TokenMarker) -> LitBool {
+ match marker {}
}
-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,
}
}
-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)]
#[cfg(feature = "parsing")]
pub mod parsing {
use super::*;
- use parse::{Parse, ParseStream, Result};
+ use crate::buffer::Cursor;
+ use crate::parse::{Parse, ParseStream, Result};
+ use proc_macro2::Punct;
+ #[cfg_attr(doc_cfg, 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));
}
- 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));
+
+ 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) {
+ if let Some(mut token) = value::to_literal(&repr, &digits, &suffix) {
+ 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 = value::to_literal(&repr, &digits, &suffix)?;
+ token.set_span(span);
+ Some((
+ Lit::Float(LitFloat {
+ repr: Box::new(LitFloatRepr {
+ token,
+ digits,
+ suffix,
+ }),
+ }),
+ rest,
+ ))
+ }
+
+ #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
impl Parse for LitStr {
fn parse(input: ParseStream) -> Result<Self> {
let head = input.fork();
- match input.parse()? {
- Lit::Str(lit) => Ok(lit),
+ match input.parse() {
+ Ok(Lit::Str(lit)) => Ok(lit),
_ => Err(head.error("expected string literal")),
}
}
}
+ #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
impl Parse for LitByteStr {
fn parse(input: ParseStream) -> Result<Self> {
let head = input.fork();
- match input.parse()? {
- Lit::ByteStr(lit) => Ok(lit),
+ match input.parse() {
+ Ok(Lit::ByteStr(lit)) => Ok(lit),
_ => Err(head.error("expected byte string literal")),
}
}
}
+ #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
impl Parse for LitByte {
fn parse(input: ParseStream) -> Result<Self> {
let head = input.fork();
- match input.parse()? {
- Lit::Byte(lit) => Ok(lit),
+ match input.parse() {
+ Ok(Lit::Byte(lit)) => Ok(lit),
_ => Err(head.error("expected byte literal")),
}
}
}
+ #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
impl Parse for LitChar {
fn parse(input: ParseStream) -> Result<Self> {
let head = input.fork();
- match input.parse()? {
- Lit::Char(lit) => Ok(lit),
+ match input.parse() {
+ Ok(Lit::Char(lit)) => Ok(lit),
_ => Err(head.error("expected character literal")),
}
}
}
+ #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
impl Parse for LitInt {
fn parse(input: ParseStream) -> Result<Self> {
let head = input.fork();
- match input.parse()? {
- Lit::Int(lit) => Ok(lit),
+ match input.parse() {
+ Ok(Lit::Int(lit)) => Ok(lit),
_ => Err(head.error("expected integer literal")),
}
}
}
+ #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
impl Parse for LitFloat {
fn parse(input: ParseStream) -> Result<Self> {
let head = input.fork();
- match input.parse()? {
- Lit::Float(lit) => Ok(lit),
+ match input.parse() {
+ Ok(Lit::Float(lit)) => Ok(lit),
_ => Err(head.error("expected floating point literal")),
}
}
}
+ #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
impl Parse for LitBool {
fn parse(input: ParseStream) -> Result<Self> {
let head = input.fork();
- match input.parse()? {
- Lit::Bool(lit) => Ok(lit),
+ match input.parse() {
+ Ok(Lit::Bool(lit)) => Ok(lit),
_ => Err(head.error("expected boolean literal")),
}
}
use proc_macro2::TokenStream;
use quote::{ToTokens, TokenStreamExt};
+ #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))]
impl ToTokens for LitStr {
fn to_tokens(&self, tokens: &mut TokenStream) {
- self.token.to_tokens(tokens);
+ self.repr.token.to_tokens(tokens);
}
}
+ #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))]
impl ToTokens for LitByteStr {
fn to_tokens(&self, tokens: &mut TokenStream) {
- self.token.to_tokens(tokens);
+ self.repr.token.to_tokens(tokens);
}
}
+ #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))]
impl ToTokens for LitByte {
fn to_tokens(&self, tokens: &mut TokenStream) {
- self.token.to_tokens(tokens);
+ self.repr.token.to_tokens(tokens);
}
}
+ #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))]
impl ToTokens for LitChar {
fn to_tokens(&self, tokens: &mut TokenStream) {
- self.token.to_tokens(tokens);
+ self.repr.token.to_tokens(tokens);
}
}
+ #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))]
impl ToTokens for LitInt {
fn to_tokens(&self, tokens: &mut TokenStream) {
- self.token.to_tokens(tokens);
+ self.repr.token.to_tokens(tokens);
}
}
+ #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))]
impl ToTokens for LitFloat {
fn to_tokens(&self, tokens: &mut TokenStream) {
- self.token.to_tokens(tokens);
+ self.repr.token.to_tokens(tokens);
}
}
+ #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))]
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 crate::bigint::BigInt;
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`]: struct.Attribute.html
- ///
- /// # 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 }),
+ let repr = token.to_string();
+
+ match byte(&repr, 0) {
+ 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'"' | b'r' => {
+ let (_, suffix) = parse_lit_byte_str(&repr);
+ return Lit::ByteStr(LitByteStr {
+ repr: Box::new(LitRepr { token, suffix }),
+ });
+ }
+ b'\'' => {
+ let (_, suffix) = parse_lit_byte(&repr);
+ return Lit::Byte(LitByte {
+ repr: Box::new(LitRepr { token, suffix }),
+ });
+ }
_ => {}
},
- 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 });
+ b'\'' => {
+ let (_, suffix) = parse_lit_char(&repr);
+ return Lit::Char(LitChar {
+ repr: Box::new(LitRepr { token, suffix }),
+ });
+ }
+ b'0'..=b'9' | b'-' => {
+ if let Some((digits, suffix)) = parse_lit_int(&repr) {
+ return Lit::Int(LitInt {
+ repr: Box::new(LitIntRepr {
+ token,
+ digits,
+ suffix,
+ }),
+ });
+ }
+ if let Some((digits, suffix)) = parse_lit_float(&repr) {
+ return Lit::Float(LitFloat {
+ repr: Box::new(LitFloatRepr {
+ token,
+ digits,
+ suffix,
+ }),
+ });
}
}
- _ => {
- if value == "true" || value == "false" {
+ b't' | b'f' => {
+ if repr == "true" || repr == "false" {
return Lit::Bool(LitBool {
- value: value == "true",
+ value: repr == "true",
span: token.span(),
});
}
}
+ _ => {}
}
- panic!("Unrecognized literal: {}", value);
+ panic!("Unrecognized literal: `{}`", repr);
}
- }
- fn number_is_int(value: &str) -> bool {
- if number_is_float(value) {
- false
- } else {
- value::parse_lit_int(value).is_some()
+ pub fn suffix(&self) -> &str {
+ match self {
+ Lit::Str(lit) => lit.suffix(),
+ Lit::ByteStr(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(_) => "",
+ }
}
- }
- 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')
+ pub fn span(&self) -> Span {
+ match self {
+ Lit::Str(lit) => lit.span(),
+ Lit::ByteStr(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::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),
+ }
}
}
s.chars().next().unwrap_or('\0')
}
- pub fn parse_lit_str(s: &str) -> String {
+ // Returns (content, suffix).
+ pub 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),
// 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 {
+ #[allow(clippy::needless_continue)]
+ fn parse_lit_str_cooked(mut s: &str) -> (Box<str>, Box<str>) {
assert_eq!(byte(s, 0), b'"');
s = &s[1..];
- let mut out = String::new();
+ let mut content = String::new();
'outer: loop {
let ch = match byte(s, 0) {
b'"' => break,
ch
}
};
- out.push(ch);
+ content.push(ch);
}
- assert_eq!(s, "\"");
- out
+ 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) -> String {
+ fn parse_lit_str_raw(mut s: &str) -> (Box<str>, Box<str>) {
assert_eq!(byte(s, 0), b'r');
s = &s[1..];
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() {
+ let close = s.rfind('"').unwrap();
+ for end in s[close + 1..close + 1 + pounds].bytes() {
assert_eq!(end, b'#');
}
- s[pounds + 1..s.len() - pounds - 1].to_owned()
+ let content = s[pounds + 1..close].to_owned().into_boxed_str();
+ let suffix = s[close + 1 + pounds..].to_owned().into_boxed_str();
+ (content, suffix)
}
- pub fn parse_lit_byte_str(s: &str) -> Vec<u8> {
+ // Returns (content, suffix).
+ pub 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),
// 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> {
+ #[allow(clippy::needless_continue)]
+ 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 s = s.as_bytes();
+ let mut v = s.as_bytes();
let mut out = Vec::new();
'outer: loop {
- let byte = match byte(s, 0) {
+ let byte = match byte(v, 0) {
b'"' => break,
b'\\' => {
- let b = byte(s, 1);
- s = &s[2..];
+ let b = byte(v, 1);
+ v = &v[2..];
match b {
b'x' => {
- let (b, rest) = backslash_x(s);
- s = rest;
+ let (b, rest) = backslash_x(v);
+ v = rest;
b
}
b'n' => b'\n',
b'\'' => b'\'',
b'"' => b'"',
b'\r' | b'\n' => loop {
- let byte = byte(s, 0);
+ let byte = byte(v, 0);
let ch = char::from_u32(u32::from(byte)).unwrap();
if ch.is_whitespace() {
- s = &s[1..];
+ v = &v[1..];
} else {
continue 'outer;
}
}
}
b'\r' => {
- assert_eq!(byte(s, 1), b'\n', "Bare CR not allowed in string");
- s = &s[2..];
+ assert_eq!(byte(v, 1), b'\n', "Bare CR not allowed in string");
+ v = &v[2..];
b'\n'
}
b => {
- s = &s[1..];
+ v = &v[1..];
b
}
};
out.push(byte);
}
- assert_eq!(s, b"\"");
- out
+ 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> {
+ fn parse_lit_byte_str_raw(s: &str) -> (Vec<u8>, Box<str>) {
assert_eq!(byte(s, 0), b'b');
- parse_lit_str_raw(&s[1..]).into_bytes()
+ let (value, suffix) = parse_lit_str_raw(&s[1..]);
+ (String::from(value).into_bytes(), suffix)
}
- pub fn parse_lit_byte(s: &str) -> u8 {
+ // Returns (value, suffix).
+ pub 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 s = s[2..].as_bytes();
+ let mut v = s[2..].as_bytes();
- let b = match byte(s, 0) {
+ let b = match byte(v, 0) {
b'\\' => {
- let b = byte(s, 1);
- s = &s[2..];
+ let b = byte(v, 1);
+ v = &v[2..];
match b {
b'x' => {
- let (b, rest) = backslash_x(s);
- s = rest;
+ let (b, rest) = backslash_x(v);
+ v = rest;
b
}
b'n' => b'\n',
}
}
b => {
- s = &s[1..];
+ v = &v[1..];
b
}
};
- assert_eq!(byte(s, 0), b'\'');
- b
+ assert_eq!(byte(v, 0), b'\'');
+ let suffix = s[s.len() - v.len() + 1..].to_owned().into_boxed_str();
+ (b, suffix)
}
- pub fn parse_lit_char(mut s: &str) -> char {
+ // Returns (value, suffix).
+ pub fn parse_lit_char(mut s: &str) -> (char, Box<str>) {
assert_eq!(byte(s, 0), b'\'');
s = &s[1..];
ch
}
};
- assert_eq!(s, "\'", "Expected end of char literal");
- 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)
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'),
+ 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'),
+ 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");
+ panic!("{}", "expected { after \\u");
}
s = &s[1..];
let mut ch = 0;
- for _ in 0..6 {
+ let mut digits = 0;
+ loop {
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');
+ 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..];
}
}
- pub fn parse_lit_int(mut s: &str) -> Option<u64> {
+ // Returns base 10 digits and suffix.
+ pub 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..];
s = &s[2..];
2
}
- (b'0'...b'9', _) => 10,
- _ => unreachable!(),
+ (b'0'..=b'9', _) => 10,
+ _ => return None,
};
- let mut value = 0u64;
- loop {
+ let mut value = BigInt::new();
+ 'outer: 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'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;
}
- // NOTE: Looking at a floating point literal, we don't want to
- // consider these integers.
+ // 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 => 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 {
- panic!("Unexpected digit {:x} out of base range", digit);
+ return None;
}
- value = match value.checked_mul(base) {
- Some(value) => value,
- None => return None,
- };
- value = match value.checked_add(digit) {
- Some(value) => value,
- None => return None,
- };
+ value *= base;
+ value += digit;
s = &s[1..];
}
- Some(value)
+ 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
+ }
}
- pub fn parse_lit_float(input: &str) -> f64 {
+ // Returns base 10 digits and suffix.
+ pub 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 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;
+
+ let start = (*bytes.get(0)? == 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;
}
- bytes.truncate(write);
- let input = String::from_utf8(bytes).unwrap();
- let end = input.find('f').unwrap_or_else(|| input.len());
- input[..end].parse().unwrap()
+
+ 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
+ }
}
- pub fn to_literal(s: &str) -> Literal {
- let stream = s.parse::<TokenStream>().unwrap();
- match stream.into_iter().next().unwrap() {
- TokenTree::Literal(l) => l,
- _ => unreachable!(),
+ pub fn to_literal(repr: &str, digits: &str, suffix: &str) -> Option<Literal> {
+ if repr.starts_with('-') {
+ let f64_parse_finite = || digits.parse().ok().filter(|x: &f64| x.is_finite());
+ let f32_parse_finite = || digits.parse().ok().filter(|x: &f32| x.is_finite());
+ if suffix == "f64" {
+ f64_parse_finite().map(Literal::f64_suffixed)
+ } else if suffix == "f32" {
+ f32_parse_finite().map(Literal::f32_suffixed)
+ } else if suffix == "i64" {
+ digits.parse().ok().map(Literal::i64_suffixed)
+ } else if suffix == "i32" {
+ digits.parse().ok().map(Literal::i32_suffixed)
+ } else if suffix == "i16" {
+ digits.parse().ok().map(Literal::i16_suffixed)
+ } else if suffix == "i8" {
+ digits.parse().ok().map(Literal::i8_suffixed)
+ } else if !suffix.is_empty() {
+ None
+ } else if digits.contains('.') {
+ f64_parse_finite().map(Literal::f64_unsuffixed)
+ } else {
+ digits.parse().ok().map(Literal::i64_unsuffixed)
+ }
+ } else {
+ let stream = repr.parse::<TokenStream>().unwrap();
+ match stream.into_iter().next().unwrap() {
+ TokenTree::Literal(l) => Some(l),
+ _ => unreachable!(),
+ }
}
}
}