aboutsummaryrefslogtreecommitdiff
path: root/syn/src/lit.rs
diff options
context:
space:
mode:
Diffstat (limited to 'syn/src/lit.rs')
-rw-r--r--syn/src/lit.rs1382
1 files changed, 0 insertions, 1382 deletions
diff --git a/syn/src/lit.rs b/syn/src/lit.rs
deleted file mode 100644
index abc4ec2..0000000
--- a/syn/src/lit.rs
+++ /dev/null
@@ -1,1382 +0,0 @@
-use proc_macro2::{Literal, Span};
-use std::fmt::{self, Display};
-use std::str::{self, FromStr};
-
-#[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 crate::lookahead;
-#[cfg(feature = "parsing")]
-use crate::parse::{Parse, Parser};
-use crate::{Error, 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 #manual_extra_traits {
- /// A UTF-8 string literal: `"foo"`.
- Str(LitStr),
-
- /// A byte string literal: `b"foo"`.
- ByteStr(LitByteStr),
-
- /// 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 infinte 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"`.
- ///
- /// *This type is available if Syn is built with the `"derive"` or
- /// `"full"` feature.*
- pub struct LitStr #manual_extra_traits_debug {
- repr: Box<LitStrRepr>,
- }
-}
-
-#[cfg_attr(feature = "clone-impls", derive(Clone))]
-struct LitStrRepr {
- token: Literal,
- suffix: Box<str>,
-}
-
-ast_struct! {
- /// A byte string literal: `b"foo"`.
- ///
- /// *This type is available if Syn is built with the `"derive"` or
- /// `"full"` feature.*
- pub struct LitByteStr #manual_extra_traits_debug {
- token: Literal,
- }
-}
-
-ast_struct! {
- /// A byte literal: `b'f'`.
- ///
- /// *This type is available if Syn is built with the `"derive"` or
- /// `"full"` feature.*
- pub struct LitByte #manual_extra_traits_debug {
- token: Literal,
- }
-}
-
-ast_struct! {
- /// A character literal: `'a'`.
- ///
- /// *This type is available if Syn is built with the `"derive"` or
- /// `"full"` feature.*
- pub struct LitChar #manual_extra_traits_debug {
- token: Literal,
- }
-}
-
-ast_struct! {
- /// An integer literal: `1` or `1u16`.
- ///
- /// *This type is available if Syn is built with the `"derive"` or
- /// `"full"` feature.*
- pub struct LitInt #manual_extra_traits_debug {
- repr: Box<LitIntRepr>,
- }
-}
-
-#[cfg_attr(feature = "clone-impls", derive(Clone))]
-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 infinte or NaN.
- ///
- /// *This type is available if Syn is built with the `"derive"` or
- /// `"full"` feature.*
- pub struct LitFloat #manual_extra_traits_debug {
- repr: Box<LitFloatRepr>,
- }
-}
-
-#[cfg_attr(feature = "clone-impls", derive(Clone))]
-struct LitFloatRepr {
- token: Literal,
- digits: Box<str>,
- suffix: Box<str>,
-}
-
-ast_struct! {
- /// A boolean literal: `true` or `false`.
- ///
- /// *This type is available if Syn is built with the `"derive"` or
- /// `"full"` feature.*
- pub struct LitBool #manual_extra_traits_debug {
- pub value: bool,
- pub span: Span,
- }
-}
-
-#[cfg(feature = "extra-traits")]
-impl Eq for Lit {}
-
-#[cfg(feature = "extra-traits")]
-impl PartialEq for Lit {
- fn eq(&self, other: &Self) -> bool {
- match (self, other) {
- (Lit::Str(this), Lit::Str(other)) => this == other,
- (Lit::ByteStr(this), Lit::ByteStr(other)) => this == other,
- (Lit::Byte(this), Lit::Byte(other)) => this == other,
- (Lit::Char(this), Lit::Char(other)) => this == other,
- (Lit::Int(this), Lit::Int(other)) => this == other,
- (Lit::Float(this), Lit::Float(other)) => this == other,
- (Lit::Bool(this), Lit::Bool(other)) => this == other,
- (Lit::Verbatim(this), Lit::Verbatim(other)) => this.to_string() == other.to_string(),
- _ => false,
- }
- }
-}
-
-#[cfg(feature = "extra-traits")]
-impl Hash for Lit {
- fn hash<H>(&self, hash: &mut H)
- where
- H: Hasher,
- {
- match self {
- Lit::Str(lit) => {
- hash.write_u8(0);
- lit.hash(hash);
- }
- Lit::ByteStr(lit) => {
- hash.write_u8(1);
- lit.hash(hash);
- }
- Lit::Byte(lit) => {
- hash.write_u8(2);
- lit.hash(hash);
- }
- Lit::Char(lit) => {
- hash.write_u8(3);
- lit.hash(hash);
- }
- Lit::Int(lit) => {
- hash.write_u8(4);
- lit.hash(hash);
- }
- Lit::Float(lit) => {
- hash.write_u8(5);
- lit.hash(hash);
- }
- Lit::Bool(lit) => {
- hash.write_u8(6);
- lit.hash(hash);
- }
- Lit::Verbatim(lit) => {
- hash.write_u8(7);
- lit.to_string().hash(hash);
- }
- }
- }
-}
-
-impl LitStr {
- pub fn new(value: &str, span: Span) -> Self {
- let mut lit = Literal::string(value);
- lit.set_span(span);
- LitStr {
- repr: Box::new(LitStrRepr {
- token: lit,
- suffix: Box::<str>::default(),
- }),
- }
- }
-
- pub fn value(&self) -> String {
- let (value, _) = value::parse_lit_str(&self.repr.token.to_string());
- 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 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
- ///
- /// ```
- /// # 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 &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 mut tokens = crate::parse_str(&self.value())?;
- tokens = respan_token_stream(tokens, self.span());
-
- parser.parse2(tokens)
- }
-
- 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
- }
-}
-
-impl LitByteStr {
- pub fn new(value: &[u8], span: Span) -> Self {
- let mut token = Literal::byte_string(value);
- token.set_span(span);
- LitByteStr { 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 }
- }
-
- 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 }
- }
-
- 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(repr: &str, span: Span) -> Self {
- if let Some((digits, suffix)) = value::parse_lit_int(repr) {
- let mut token = value::to_literal(repr);
- token.set_span(span);
- LitInt {
- repr: Box::new(LitIntRepr {
- token,
- digits,
- suffix,
- }),
- }
- } else {
- panic!("Not an integer literal: `{}`", repr);
- }
- }
-
- 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)
- }
-}
-
-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 {
- if let Some((digits, suffix)) = value::parse_lit_float(repr) {
- let mut token = value::to_literal(repr);
- token.set_span(span);
- LitFloat {
- repr: Box::new(LitFloatRepr {
- token,
- digits,
- suffix,
- }),
- }
- } else {
- panic!("Not a float literal: `{}`", repr);
- }
- }
-
- 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)
- }
-}
-
-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)
- }
-}
-
-#[cfg(feature = "extra-traits")]
-mod debug_impls {
- use super::*;
- use std::fmt::{self, Debug};
-
- 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()
- }
- }
-
- impl Debug for LitByteStr {
- fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
- formatter
- .debug_struct("LitByteStr")
- .field("token", &format_args!("{}", self.token))
- .finish()
- }
- }
-
- impl Debug for LitByte {
- fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
- formatter
- .debug_struct("LitByte")
- .field("token", &format_args!("{}", self.token))
- .finish()
- }
- }
-
- impl Debug for LitChar {
- fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
- formatter
- .debug_struct("LitChar")
- .field("token", &format_args!("{}", self.token))
- .finish()
- }
- }
-
- 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()
- }
- }
-
- 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()
- }
- }
-
- impl Debug for LitBool {
- fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
- formatter
- .debug_struct("LitBool")
- .field("value", &self.value)
- .finish()
- }
- }
-}
-
-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 {}
- }
- };
-}
-
-lit_extra_traits!(LitStr, repr.token);
-lit_extra_traits!(LitByteStr, token);
-lit_extra_traits!(LitByte, token);
-lit_extra_traits!(LitChar, token);
-lit_extra_traits!(LitInt, repr.token);
-lit_extra_traits!(LitFloat, repr.token);
-lit_extra_traits!(LitBool, value);
-
-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),
- }
-}
-
-#[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 crate::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,
- 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.repr.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.repr.token.to_tokens(tokens);
- }
- }
-
- impl ToTokens for LitFloat {
- fn to_tokens(&self, tokens: &mut TokenStream) {
- self.repr.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));
- }
- }
-}
-
-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.
- pub fn new(token: Literal) -> Self {
- 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(LitStrRepr { token, suffix }),
- });
- }
- b'b' => match byte(&repr, 1) {
- b'"' | b'r' => {
- return Lit::ByteStr(LitByteStr { token });
- }
- b'\'' => {
- return Lit::Byte(LitByte { token });
- }
- _ => {}
- },
- b'\'' => {
- return Lit::Char(LitChar { token });
- }
- b'0'..=b'9' | b'-' => {
- if !(repr.ends_with("f32") || repr.ends_with("f64")) {
- 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,
- }),
- });
- }
- }
- b't' | b'f' => {
- if repr == "true" || repr == "false" {
- return Lit::Bool(LitBool {
- value: repr == "true",
- span: token.span(),
- });
- }
- }
- _ => {}
- }
-
- panic!("Unrecognized literal: `{}`", repr);
- }
- }
-
- /// 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')
- }
-
- // 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),
- _ => unreachable!(),
- }
- }
-
- // Clippy false positive
- // https://github.com/rust-lang-nursery/rust-clippy/issues/2329
- #[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 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 <= 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
- }
- };
- 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'"');
- assert_eq!(byte(s, s.len() - pounds - 1), b'"');
- for end in s[s.len() - pounds..].bytes() {
- assert_eq!(end, b'#');
- }
-
- let content = s[pounds + 1..s.len() - pounds - 1]
- .to_owned()
- .into_boxed_str();
- let suffix = Box::<str>::default(); // todo
- (content, suffix)
- }
-
- 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
- #[allow(clippy::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');
- String::from(parse_lit_str_raw(&s[1..]).0).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);
- }
- }
-
- // 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..];
- 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();
- 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;
- }
- // 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 {
- return None;
- }
-
- value *= base;
- value += digit;
- s = &s[1..];
- }
-
- 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 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.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' => {
- if has_e {
- 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
- }
- }
-
- pub fn to_literal(s: &str) -> Literal {
- let stream = s.parse::<TokenStream>().unwrap();
- match stream.into_iter().next().unwrap() {
- TokenTree::Literal(l) => l,
- _ => unreachable!(),
- }
- }
-}