More useful literal api
diff --git a/src/attr.rs b/src/attr.rs
index 96d96e5..58405b6 100644
--- a/src/attr.rs
+++ b/src/attr.rs
@@ -95,10 +95,7 @@
return Some(MetaItem::NameValue(MetaNameValue {
ident: *name,
eq_token: Token,
- lit: Lit {
- value: LitKind::Other(lit.clone()),
- span: tts[1].span,
- },
+ lit: Lit::new(lit.clone(), tts[1].span),
}));
}
}
@@ -113,10 +110,7 @@
match tts[0].kind {
TokenNode::Literal(ref lit) => {
- let lit = Lit {
- value: LitKind::Other(lit.clone()),
- span: tts[0].span,
- };
+ let lit = Lit::new(lit.clone(), tts[0].span);
Some((NestedMetaItem::Literal(lit), &tts[1..]))
}
@@ -128,10 +122,7 @@
let pair = MetaNameValue {
ident: Ident::new(sym.as_str(), tts[0].span),
eq_token: Token,
- lit: Lit {
- value: LitKind::Other(lit.clone()),
- span: tts[2].span,
- },
+ lit: Lit::new(lit.clone(), tts[2].span),
};
return Some((MetaItem::NameValue(pair).into(), &tts[3..]));
}
@@ -328,7 +319,7 @@
use cursor::Cursor;
use parse_error;
use synom::PResult;
- use proc_macro2::{Spacing, Span, TokenNode, TokenTree};
+ use proc_macro2::{Spacing, Span, TokenNode, TokenTree, Literal};
fn eq(span: Span) -> TokenTree {
TokenTree {
@@ -441,7 +432,7 @@
Ok((
TokenTree {
span: span,
- kind: TokenNode::Literal(lit),
+ kind: TokenNode::Literal(Literal::string(&string)),
},
rest,
))
@@ -458,6 +449,7 @@
mod printing {
use super::*;
use quote::{ToTokens, Tokens};
+ use proc_macro2::Literal;
impl ToTokens for Attribute {
fn to_tokens(&self, tokens: &mut Tokens) {
@@ -465,9 +457,11 @@
if self.is_sugared_doc {
if let Some(MetaItem::NameValue(ref pair)) = self.meta_item() {
if pair.ident == "doc" {
- let value = pair.lit.value.to_string();
- if value.starts_with('/') {
- pair.lit.to_tokens(tokens);
+ if let Lit::Str(ref comment) = pair.lit {
+ tokens.append(TokenTree {
+ span: comment.span,
+ kind: TokenNode::Literal(Literal::doccomment(&comment.value())),
+ });
return;
}
}
@@ -480,7 +474,7 @@
}
self.bracket_token.surround(tokens, |tokens| {
self.path.to_tokens(tokens);
- tokens.append_all(&self.tts.clone().into_iter().collect::<Vec<_>>());
+ self.tts.to_tokens(tokens);
});
}
}
diff --git a/src/expr.rs b/src/expr.rs
index bf4bdb8..13c462a 100644
--- a/src/expr.rs
+++ b/src/expr.rs
@@ -796,7 +796,7 @@
use ty::parsing::ty_no_eq_after;
#[cfg(feature = "full")]
- use proc_macro2::{Span, TokenStream};
+ use proc_macro2::TokenStream;
use synom::Synom;
use cursor::Cursor;
#[cfg(feature = "full")]
@@ -1467,12 +1467,8 @@
ExprMethodCall {
attrs: Vec::new(),
// this expr will get overwritten after being returned
- receiver: Box::new(Expr::Lit(ExprLit {
- attrs: Vec::new(),
- lit: Lit {
- span: Span::default(),
- value: LitKind::Bool(false),
- },
+ receiver: Box::new(Expr::Verbatim(ExprVerbatim {
+ tts: TokenStream::empty(),
})),
method: method,
@@ -2372,10 +2368,10 @@
#[cfg(feature = "full")]
impl Synom for Index {
named!(parse -> Self, do_parse!(
- lit: syn!(Lit) >>
+ lit: syn!(LitInt) >>
({
- if let Ok(i) = lit.value.to_string().parse() {
- Index { index: i, span: lit.span }
+ if let IntSuffix::None = lit.suffix() {
+ Index { index: lit.value() as u32, span: lit.span }
} else {
return parse_error();
}
diff --git a/src/gen/fold.rs b/src/gen/fold.rs
index 7a94df8..c9f349e 100644
--- a/src/gen/fold.rs
+++ b/src/gen/fold.rs
@@ -255,6 +255,22 @@
fn fold_lifetime_def(&mut self, i: LifetimeDef) -> LifetimeDef { fold_lifetime_def(self, i) }
# [ cfg ( any ( feature = "full" , feature = "derive" ) ) ]
fn fold_lit(&mut self, i: Lit) -> Lit { fold_lit(self, i) }
+# [ cfg ( any ( feature = "full" , feature = "derive" ) ) ]
+fn fold_lit_bool(&mut self, i: LitBool) -> LitBool { fold_lit_bool(self, i) }
+# [ cfg ( any ( feature = "full" , feature = "derive" ) ) ]
+fn fold_lit_byte(&mut self, i: LitByte) -> LitByte { fold_lit_byte(self, i) }
+# [ cfg ( any ( feature = "full" , feature = "derive" ) ) ]
+fn fold_lit_byte_str(&mut self, i: LitByteStr) -> LitByteStr { fold_lit_byte_str(self, i) }
+# [ cfg ( any ( feature = "full" , feature = "derive" ) ) ]
+fn fold_lit_char(&mut self, i: LitChar) -> LitChar { fold_lit_char(self, i) }
+# [ cfg ( any ( feature = "full" , feature = "derive" ) ) ]
+fn fold_lit_float(&mut self, i: LitFloat) -> LitFloat { fold_lit_float(self, i) }
+# [ cfg ( any ( feature = "full" , feature = "derive" ) ) ]
+fn fold_lit_int(&mut self, i: LitInt) -> LitInt { fold_lit_int(self, i) }
+# [ cfg ( any ( feature = "full" , feature = "derive" ) ) ]
+fn fold_lit_str(&mut self, i: LitStr) -> LitStr { fold_lit_str(self, i) }
+# [ cfg ( any ( feature = "full" , feature = "derive" ) ) ]
+fn fold_lit_verbatim(&mut self, i: LitVerbatim) -> LitVerbatim { fold_lit_verbatim(self, i) }
# [ cfg ( any ( feature = "full" , feature = "derive" ) ) ] # [ cfg ( feature = "full" ) ]
fn fold_local(&mut self, i: Local) -> Local { fold_local(self, i) }
# [ cfg ( any ( feature = "full" , feature = "derive" ) ) ]
@@ -406,15 +422,29 @@
}
-pub fn fold_ident<V: Folder + ?Sized>(_visitor: &mut V, mut _i: Ident) -> Ident {
- _i.span = _visitor.fold_span(_i.span);
- _i
+macro_rules! fold_span_only {
+ ($f:ident : $t:ident) => {
+ pub fn $f<V: Folder + ?Sized>(_visitor: &mut V, mut _i: $t) -> $t {
+ _i.span = _visitor.fold_span(_i.span);
+ _i
+ }
+ }
}
-pub fn fold_lifetime<V: Folder + ?Sized>(_visitor: &mut V, mut _i: Lifetime) -> Lifetime {
- _i.span = _visitor.fold_span(_i.span);
- _i
-}
+fold_span_only!(fold_ident: Ident);
+fold_span_only!(fold_lifetime: Lifetime);
+#[cfg(any(feature = "full", feature = "derive"))]
+fold_span_only!(fold_lit_byte: LitByte);
+#[cfg(any(feature = "full", feature = "derive"))]
+fold_span_only!(fold_lit_byte_str: LitByteStr);
+#[cfg(any(feature = "full", feature = "derive"))]
+fold_span_only!(fold_lit_char: LitChar);
+#[cfg(any(feature = "full", feature = "derive"))]
+fold_span_only!(fold_lit_float: LitFloat);
+#[cfg(any(feature = "full", feature = "derive"))]
+fold_span_only!(fold_lit_int: LitInt);
+#[cfg(any(feature = "full", feature = "derive"))]
+fold_span_only!(fold_lit_str: LitStr);
# [ cfg ( any ( feature = "full" , feature = "derive" ) ) ]
pub fn fold_abi<V: Folder + ?Sized>(_visitor: &mut V, _i: Abi) -> Abi {
@@ -1959,11 +1989,63 @@
}
# [ cfg ( any ( feature = "full" , feature = "derive" ) ) ]
pub fn fold_lit<V: Folder + ?Sized>(_visitor: &mut V, _i: Lit) -> Lit {
- Lit {
+ match _i {
+ Lit::Str(_binding_0, ) => {
+ Lit::Str (
+ _visitor.fold_lit_str(_binding_0),
+ )
+ }
+ Lit::ByteStr(_binding_0, ) => {
+ Lit::ByteStr (
+ _visitor.fold_lit_byte_str(_binding_0),
+ )
+ }
+ Lit::Byte(_binding_0, ) => {
+ Lit::Byte (
+ _visitor.fold_lit_byte(_binding_0),
+ )
+ }
+ Lit::Char(_binding_0, ) => {
+ Lit::Char (
+ _visitor.fold_lit_char(_binding_0),
+ )
+ }
+ Lit::Int(_binding_0, ) => {
+ Lit::Int (
+ _visitor.fold_lit_int(_binding_0),
+ )
+ }
+ Lit::Float(_binding_0, ) => {
+ Lit::Float (
+ _visitor.fold_lit_float(_binding_0),
+ )
+ }
+ Lit::Bool(_binding_0, ) => {
+ Lit::Bool (
+ _visitor.fold_lit_bool(_binding_0),
+ )
+ }
+ Lit::Verbatim(_binding_0, ) => {
+ Lit::Verbatim (
+ _visitor.fold_lit_verbatim(_binding_0),
+ )
+ }
+ }
+}
+# [ cfg ( any ( feature = "full" , feature = "derive" ) ) ]
+pub fn fold_lit_bool<V: Folder + ?Sized>(_visitor: &mut V, _i: LitBool) -> LitBool {
+ LitBool {
value: _i . value,
span: _visitor.fold_span(_i . span),
}
}
+# [ cfg ( any ( feature = "full" , feature = "derive" ) ) ]
+pub fn fold_lit_verbatim<V: Folder + ?Sized>(_visitor: &mut V, _i: LitVerbatim) -> LitVerbatim {
+ LitVerbatim {
+ token: _i . token,
+ span: _visitor.fold_span(_i . span),
+ }
+}
# [ cfg ( any ( feature = "full" , feature = "derive" ) ) ] # [ cfg ( feature = "full" ) ]
pub fn fold_local<V: Folder + ?Sized>(_visitor: &mut V, _i: Local) -> Local {
Local {
diff --git a/src/gen/visit.rs b/src/gen/visit.rs
index b9338a5..aaf9377 100644
--- a/src/gen/visit.rs
+++ b/src/gen/visit.rs
@@ -252,6 +252,22 @@
fn visit_lifetime_def(&mut self, i: &'ast LifetimeDef) { visit_lifetime_def(self, i) }
# [ cfg ( any ( feature = "full" , feature = "derive" ) ) ]
fn visit_lit(&mut self, i: &'ast Lit) { visit_lit(self, i) }
+# [ cfg ( any ( feature = "full" , feature = "derive" ) ) ]
+fn visit_lit_bool(&mut self, i: &'ast LitBool) { visit_lit_bool(self, i) }
+# [ cfg ( any ( feature = "full" , feature = "derive" ) ) ]
+fn visit_lit_byte(&mut self, i: &'ast LitByte) { visit_lit_byte(self, i) }
+# [ cfg ( any ( feature = "full" , feature = "derive" ) ) ]
+fn visit_lit_byte_str(&mut self, i: &'ast LitByteStr) { visit_lit_byte_str(self, i) }
+# [ cfg ( any ( feature = "full" , feature = "derive" ) ) ]
+fn visit_lit_char(&mut self, i: &'ast LitChar) { visit_lit_char(self, i) }
+# [ cfg ( any ( feature = "full" , feature = "derive" ) ) ]
+fn visit_lit_float(&mut self, i: &'ast LitFloat) { visit_lit_float(self, i) }
+# [ cfg ( any ( feature = "full" , feature = "derive" ) ) ]
+fn visit_lit_int(&mut self, i: &'ast LitInt) { visit_lit_int(self, i) }
+# [ cfg ( any ( feature = "full" , feature = "derive" ) ) ]
+fn visit_lit_str(&mut self, i: &'ast LitStr) { visit_lit_str(self, i) }
+# [ cfg ( any ( feature = "full" , feature = "derive" ) ) ]
+fn visit_lit_verbatim(&mut self, i: &'ast LitVerbatim) { visit_lit_verbatim(self, i) }
# [ cfg ( any ( feature = "full" , feature = "derive" ) ) ] # [ cfg ( feature = "full" ) ]
fn visit_local(&mut self, i: &'ast Local) { visit_local(self, i) }
# [ cfg ( any ( feature = "full" , feature = "derive" ) ) ]
@@ -1542,9 +1558,73 @@
}
# [ cfg ( any ( feature = "full" , feature = "derive" ) ) ]
pub fn visit_lit<'ast, V: Visitor<'ast> + ?Sized>(_visitor: &mut V, _i: &'ast Lit) {
+ match *_i {
+ Lit::Str(ref _binding_0, ) => {
+ _visitor.visit_lit_str(_binding_0);
+ }
+ Lit::ByteStr(ref _binding_0, ) => {
+ _visitor.visit_lit_byte_str(_binding_0);
+ }
+ Lit::Byte(ref _binding_0, ) => {
+ _visitor.visit_lit_byte(_binding_0);
+ }
+ Lit::Char(ref _binding_0, ) => {
+ _visitor.visit_lit_char(_binding_0);
+ }
+ Lit::Int(ref _binding_0, ) => {
+ _visitor.visit_lit_int(_binding_0);
+ }
+ Lit::Float(ref _binding_0, ) => {
+ _visitor.visit_lit_float(_binding_0);
+ }
+ Lit::Bool(ref _binding_0, ) => {
+ _visitor.visit_lit_bool(_binding_0);
+ }
+ Lit::Verbatim(ref _binding_0, ) => {
+ _visitor.visit_lit_verbatim(_binding_0);
+ }
+ }
+}
+# [ cfg ( any ( feature = "full" , feature = "derive" ) ) ]
+pub fn visit_lit_bool<'ast, V: Visitor<'ast> + ?Sized>(_visitor: &mut V, _i: &'ast LitBool) {
// Skipped field _i . value;
_visitor.visit_span(& _i . span);
}
+# [ cfg ( any ( feature = "full" , feature = "derive" ) ) ]
+pub fn visit_lit_byte<'ast, V: Visitor<'ast> + ?Sized>(_visitor: &mut V, _i: &'ast LitByte) {
+ // Skipped field _i . token;
+ _visitor.visit_span(& _i . span);
+}
+# [ cfg ( any ( feature = "full" , feature = "derive" ) ) ]
+pub fn visit_lit_byte_str<'ast, V: Visitor<'ast> + ?Sized>(_visitor: &mut V, _i: &'ast LitByteStr) {
+ // Skipped field _i . token;
+ _visitor.visit_span(& _i . span);
+}
+# [ cfg ( any ( feature = "full" , feature = "derive" ) ) ]
+pub fn visit_lit_char<'ast, V: Visitor<'ast> + ?Sized>(_visitor: &mut V, _i: &'ast LitChar) {
+ // Skipped field _i . token;
+ _visitor.visit_span(& _i . span);
+}
+# [ cfg ( any ( feature = "full" , feature = "derive" ) ) ]
+pub fn visit_lit_float<'ast, V: Visitor<'ast> + ?Sized>(_visitor: &mut V, _i: &'ast LitFloat) {
+ // Skipped field _i . token;
+ _visitor.visit_span(& _i . span);
+}
+# [ cfg ( any ( feature = "full" , feature = "derive" ) ) ]
+pub fn visit_lit_int<'ast, V: Visitor<'ast> + ?Sized>(_visitor: &mut V, _i: &'ast LitInt) {
+ // Skipped field _i . token;
+ _visitor.visit_span(& _i . span);
+}
+# [ cfg ( any ( feature = "full" , feature = "derive" ) ) ]
+pub fn visit_lit_str<'ast, V: Visitor<'ast> + ?Sized>(_visitor: &mut V, _i: &'ast LitStr) {
+ // Skipped field _i . token;
+ _visitor.visit_span(& _i . span);
+}
+# [ cfg ( any ( feature = "full" , feature = "derive" ) ) ]
+pub fn visit_lit_verbatim<'ast, V: Visitor<'ast> + ?Sized>(_visitor: &mut V, _i: &'ast LitVerbatim) {
+ // Skipped field _i . token;
+ _visitor.visit_span(& _i . span);
+}
# [ cfg ( any ( feature = "full" , feature = "derive" ) ) ] # [ cfg ( feature = "full" ) ]
pub fn visit_local<'ast, V: Visitor<'ast> + ?Sized>(_visitor: &mut V, _i: &'ast Local) {
for it in & _i . attrs { _visitor.visit_attribute(it) };
diff --git a/src/gen/visit_mut.rs b/src/gen/visit_mut.rs
index 77556bd..a15936d 100644
--- a/src/gen/visit_mut.rs
+++ b/src/gen/visit_mut.rs
@@ -252,6 +252,22 @@
fn visit_lifetime_def_mut(&mut self, i: &mut LifetimeDef) { visit_lifetime_def_mut(self, i) }
# [ cfg ( any ( feature = "full" , feature = "derive" ) ) ]
fn visit_lit_mut(&mut self, i: &mut Lit) { visit_lit_mut(self, i) }
+# [ cfg ( any ( feature = "full" , feature = "derive" ) ) ]
+fn visit_lit_bool_mut(&mut self, i: &mut LitBool) { visit_lit_bool_mut(self, i) }
+# [ cfg ( any ( feature = "full" , feature = "derive" ) ) ]
+fn visit_lit_byte_mut(&mut self, i: &mut LitByte) { visit_lit_byte_mut(self, i) }
+# [ cfg ( any ( feature = "full" , feature = "derive" ) ) ]
+fn visit_lit_byte_str_mut(&mut self, i: &mut LitByteStr) { visit_lit_byte_str_mut(self, i) }
+# [ cfg ( any ( feature = "full" , feature = "derive" ) ) ]
+fn visit_lit_char_mut(&mut self, i: &mut LitChar) { visit_lit_char_mut(self, i) }
+# [ cfg ( any ( feature = "full" , feature = "derive" ) ) ]
+fn visit_lit_float_mut(&mut self, i: &mut LitFloat) { visit_lit_float_mut(self, i) }
+# [ cfg ( any ( feature = "full" , feature = "derive" ) ) ]
+fn visit_lit_int_mut(&mut self, i: &mut LitInt) { visit_lit_int_mut(self, i) }
+# [ cfg ( any ( feature = "full" , feature = "derive" ) ) ]
+fn visit_lit_str_mut(&mut self, i: &mut LitStr) { visit_lit_str_mut(self, i) }
+# [ cfg ( any ( feature = "full" , feature = "derive" ) ) ]
+fn visit_lit_verbatim_mut(&mut self, i: &mut LitVerbatim) { visit_lit_verbatim_mut(self, i) }
# [ cfg ( any ( feature = "full" , feature = "derive" ) ) ] # [ cfg ( feature = "full" ) ]
fn visit_local_mut(&mut self, i: &mut Local) { visit_local_mut(self, i) }
# [ cfg ( any ( feature = "full" , feature = "derive" ) ) ]
@@ -1542,9 +1558,73 @@
}
# [ cfg ( any ( feature = "full" , feature = "derive" ) ) ]
pub fn visit_lit_mut<V: VisitorMut + ?Sized>(_visitor: &mut V, _i: &mut Lit) {
+ match *_i {
+ Lit::Str(ref mut _binding_0, ) => {
+ _visitor.visit_lit_str_mut(_binding_0);
+ }
+ Lit::ByteStr(ref mut _binding_0, ) => {
+ _visitor.visit_lit_byte_str_mut(_binding_0);
+ }
+ Lit::Byte(ref mut _binding_0, ) => {
+ _visitor.visit_lit_byte_mut(_binding_0);
+ }
+ Lit::Char(ref mut _binding_0, ) => {
+ _visitor.visit_lit_char_mut(_binding_0);
+ }
+ Lit::Int(ref mut _binding_0, ) => {
+ _visitor.visit_lit_int_mut(_binding_0);
+ }
+ Lit::Float(ref mut _binding_0, ) => {
+ _visitor.visit_lit_float_mut(_binding_0);
+ }
+ Lit::Bool(ref mut _binding_0, ) => {
+ _visitor.visit_lit_bool_mut(_binding_0);
+ }
+ Lit::Verbatim(ref mut _binding_0, ) => {
+ _visitor.visit_lit_verbatim_mut(_binding_0);
+ }
+ }
+}
+# [ cfg ( any ( feature = "full" , feature = "derive" ) ) ]
+pub fn visit_lit_bool_mut<V: VisitorMut + ?Sized>(_visitor: &mut V, _i: &mut LitBool) {
// Skipped field _i . value;
_visitor.visit_span_mut(& mut _i . span);
}
+# [ cfg ( any ( feature = "full" , feature = "derive" ) ) ]
+pub fn visit_lit_byte_mut<V: VisitorMut + ?Sized>(_visitor: &mut V, _i: &mut LitByte) {
+ // Skipped field _i . token;
+ _visitor.visit_span_mut(& mut _i . span);
+}
+# [ cfg ( any ( feature = "full" , feature = "derive" ) ) ]
+pub fn visit_lit_byte_str_mut<V: VisitorMut + ?Sized>(_visitor: &mut V, _i: &mut LitByteStr) {
+ // Skipped field _i . token;
+ _visitor.visit_span_mut(& mut _i . span);
+}
+# [ cfg ( any ( feature = "full" , feature = "derive" ) ) ]
+pub fn visit_lit_char_mut<V: VisitorMut + ?Sized>(_visitor: &mut V, _i: &mut LitChar) {
+ // Skipped field _i . token;
+ _visitor.visit_span_mut(& mut _i . span);
+}
+# [ cfg ( any ( feature = "full" , feature = "derive" ) ) ]
+pub fn visit_lit_float_mut<V: VisitorMut + ?Sized>(_visitor: &mut V, _i: &mut LitFloat) {
+ // Skipped field _i . token;
+ _visitor.visit_span_mut(& mut _i . span);
+}
+# [ cfg ( any ( feature = "full" , feature = "derive" ) ) ]
+pub fn visit_lit_int_mut<V: VisitorMut + ?Sized>(_visitor: &mut V, _i: &mut LitInt) {
+ // Skipped field _i . token;
+ _visitor.visit_span_mut(& mut _i . span);
+}
+# [ cfg ( any ( feature = "full" , feature = "derive" ) ) ]
+pub fn visit_lit_str_mut<V: VisitorMut + ?Sized>(_visitor: &mut V, _i: &mut LitStr) {
+ // Skipped field _i . token;
+ _visitor.visit_span_mut(& mut _i . span);
+}
+# [ cfg ( any ( feature = "full" , feature = "derive" ) ) ]
+pub fn visit_lit_verbatim_mut<V: VisitorMut + ?Sized>(_visitor: &mut V, _i: &mut LitVerbatim) {
+ // Skipped field _i . token;
+ _visitor.visit_span_mut(& mut _i . span);
+}
# [ cfg ( any ( feature = "full" , feature = "derive" ) ) ] # [ cfg ( feature = "full" ) ]
pub fn visit_local_mut<V: VisitorMut + ?Sized>(_visitor: &mut V, _i: &mut Local) {
for it in & mut _i . attrs { _visitor.visit_attribute_mut(it) };
diff --git a/src/lib.rs b/src/lib.rs
index eb3eb1e..31d7512 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -82,7 +82,8 @@
#[cfg(any(feature = "full", feature = "derive"))]
mod lit;
#[cfg(any(feature = "full", feature = "derive"))]
-pub use lit::{Lit, LitKind};
+pub use lit::{Lit, LitStr, LitByteStr, LitByte, LitChar, LitInt, LitFloat, LitBool, LitVerbatim,
+ StrStyle, IntSuffix, FloatSuffix};
#[cfg(any(feature = "full", feature = "derive"))]
mod mac;
diff --git a/src/lit.rs b/src/lit.rs
index 23f8a3a..cf1e062 100644
--- a/src/lit.rs
+++ b/src/lit.rs
@@ -1,96 +1,264 @@
-use std::fmt;
+use proc_macro2::{Literal, Span, Term, TokenNode, TokenTree};
+use std::str;
+
+#[cfg(feature = "extra-traits")]
use std::hash::{Hash, Hasher};
-use proc_macro2::{self, Literal, Span, Term, TokenNode, TokenTree};
+ast_enum_of_structs! {
+ pub enum Lit {
+ /// A string literal (`"foo"`)
+ pub Str(LitStr #manual_extra_traits {
+ token: Literal,
+ pub span: Span,
+ }),
-#[derive(Clone)]
-pub struct Lit {
- pub value: LitKind,
- pub span: Span,
+ /// A byte string (`b"foo"`)
+ pub ByteStr(LitByteStr #manual_extra_traits {
+ token: Literal,
+ pub span: Span,
+ }),
+
+ /// A byte char (`b'f'`)
+ pub Byte(LitByte #manual_extra_traits {
+ token: Literal,
+ pub span: Span,
+ }),
+
+ /// A character literal (`'a'`)
+ pub Char(LitChar #manual_extra_traits {
+ token: Literal,
+ pub span: Span,
+ }),
+
+ /// An integer literal (`1`)
+ ///
+ /// Holds up to 64 bits of data. Use `LitVerbatim` for any larger
+ /// integer literal.
+ pub Int(LitInt #manual_extra_traits {
+ token: Literal,
+ pub span: Span,
+ }),
+
+ /// A float literal (`1f64` or `1E10f64`)
+ ///
+ /// Must be finite. May not be infinte or NaN.
+ pub Float(LitFloat #manual_extra_traits {
+ token: Literal,
+ pub span: Span,
+ }),
+
+ /// A boolean literal
+ pub Bool(LitBool #manual_extra_traits {
+ pub value: bool,
+ pub span: Span,
+ }),
+
+ pub Verbatim(LitVerbatim #manual_extra_traits {
+ pub token: Literal,
+ pub span: Span,
+ }),
+ }
}
-#[derive(Clone)]
-pub enum LitKind {
- Bool(bool),
- Other(Literal),
+impl LitStr {
+ pub fn new(value: &str, span: Span) -> Self {
+ LitStr {
+ token: Literal::string(value),
+ span: span,
+ }
+ }
+
+ pub fn value(&self) -> String {
+ value::parse_lit_str(&self.token.to_string())
+ }
}
-impl Lit {
- pub fn into_token_tree(self) -> TokenTree {
- let kind = match self.value {
- LitKind::Bool(true) => TokenNode::Term(Term::intern("true")),
- LitKind::Bool(false) => TokenNode::Term(Term::intern("false")),
- LitKind::Other(l) => TokenNode::Literal(l),
- };
- proc_macro2::TokenTree {
- span: self.span,
- kind: kind,
+impl LitByteStr {
+ pub fn new(value: &[u8], span: Span) -> Self {
+ LitByteStr {
+ token: Literal::byte_string(value),
+ span: span,
+ }
+ }
+
+ pub fn value(&self) -> Vec<u8> {
+ value::parse_lit_byte_str(&self.token.to_string())
+ }
+}
+
+impl LitByte {
+ pub fn new(value: u8, span: Span) -> Self {
+ LitByte {
+ token: Literal::byte_char(value),
+ span: span,
+ }
+ }
+
+ pub fn value(&self) -> u8 {
+ value::parse_lit_byte(&self.token.to_string())
+ }
+}
+
+impl LitChar {
+ pub fn new(value: char, span: Span) -> Self {
+ LitChar {
+ token: Literal::character(value),
+ span: span,
+ }
+ }
+
+ pub fn value(&self) -> char {
+ value::parse_lit_char(&self.token.to_string())
+ }
+}
+
+impl LitInt {
+ pub fn new(value: u64, suffix: IntSuffix, span: Span) -> Self {
+ LitInt {
+ token: match suffix {
+ IntSuffix::Isize => Literal::isize(value as isize),
+ IntSuffix::I8 => Literal::i8(value as i8),
+ IntSuffix::I16 => Literal::i16(value as i16),
+ IntSuffix::I32 => Literal::i32(value as i32),
+ IntSuffix::I64 => Literal::i64(value as i64),
+ IntSuffix::I128 => value::to_literal(&format!("{}i128", value)),
+ IntSuffix::Usize => Literal::usize(value as usize),
+ IntSuffix::U8 => Literal::u8(value as u8),
+ IntSuffix::U16 => Literal::u16(value as u16),
+ IntSuffix::U32 => Literal::u32(value as u32),
+ IntSuffix::U64 => Literal::u64(value),
+ IntSuffix::U128 => value::to_literal(&format!("{}u128", value)),
+ IntSuffix::None => Literal::integer(value as i64),
+ },
+ span: span,
+ }
+ }
+
+ 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
+ }
+}
+
+impl LitFloat {
+ pub fn new(value: f64, suffix: FloatSuffix, span: Span) -> Self {
+ LitFloat {
+ token: match suffix {
+ FloatSuffix::F32 => Literal::f32(value as f32),
+ FloatSuffix::F64 => Literal::f64(value),
+ FloatSuffix::None => Literal::float(value),
+ },
+ span: span,
+ }
+ }
+
+ 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
+ }
+}
+
+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);
+ }
}
}
}
-impl fmt::Display for Lit {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- fmt::Display::fmt(&self.value, f)
+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! {
+ pub enum StrStyle #no_visit {
+ /// A regular string, like `"foo"`
+ Cooked,
+ /// A raw string, like `r##"foo"##`
+ ///
+ /// The unsigned integer is the number of `#` symbols used.
+ Raw(usize),
}
}
-impl fmt::Debug for Lit {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- fmt::Display::fmt(&self.value, f)
+ast_enum! {
+ pub enum IntSuffix #no_visit {
+ I8,
+ I16,
+ I32,
+ I64,
+ I128,
+ Isize,
+ U8,
+ U16,
+ U32,
+ U64,
+ U128,
+ Usize,
+ None,
}
}
-impl PartialEq for Lit {
- fn eq(&self, other: &Lit) -> bool {
- self.value == other.value
- }
-}
-
-impl Eq for Lit {}
-
-impl Hash for Lit {
- fn hash<H: Hasher>(&self, hasher: &mut H) {
- self.value.hash(hasher)
- }
-}
-
-impl fmt::Display for LitKind {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- match *self {
- LitKind::Bool(b) => b.fmt(f),
- LitKind::Other(ref l) => l.fmt(f),
- }
- }
-}
-
-impl fmt::Debug for LitKind {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- match *self {
- LitKind::Bool(b) => b.fmt(f),
- LitKind::Other(ref l) => fmt::Display::fmt(l, f),
- }
- }
-}
-
-impl PartialEq for LitKind {
- fn eq(&self, other: &LitKind) -> bool {
- match (self, other) {
- (&LitKind::Bool(b1), &LitKind::Bool(b2)) => b1 == b2,
- (&LitKind::Other(ref l1), &LitKind::Other(ref l2)) => l1.to_string() == l2.to_string(),
- _ => false,
- }
- }
-}
-
-impl Eq for LitKind {}
-
-impl Hash for LitKind {
- fn hash<H: Hasher>(&self, hasher: &mut H) {
- match *self {
- LitKind::Bool(b) => (0u8, b).hash(hasher),
- LitKind::Other(ref l) => (1u8, l.to_string()).hash(hasher),
- }
+ast_enum! {
+ pub enum FloatSuffix #no_visit {
+ F32,
+ F64,
+ None,
}
}
@@ -105,31 +273,21 @@
impl Synom for Lit {
fn parse(input: Cursor) -> PResult<Self> {
match input.literal() {
- Some((span, lit, rest)) => Ok((
- Lit {
- span: span,
- value: LitKind::Other(lit),
- },
- rest,
- )),
+ Some((span, lit, rest)) => Ok((Lit::new(lit, span), rest)),
_ => match input.term() {
- Some((span, term, rest)) => {
- let kind = if term.as_str() == "true" {
- LitKind::Bool(true)
- } else if term.as_str() == "false" {
- LitKind::Bool(false)
- } else {
- return parse_error();
- };
-
- Ok((
- Lit {
- span: span,
- value: kind,
+ Some((span, term, rest)) => Ok((
+ Lit::Bool(LitBool {
+ value: if term.as_str() == "true" {
+ true
+ } else if term.as_str() == "false" {
+ false
+ } else {
+ return parse_error();
},
- rest,
- ))
- }
+ span: span,
+ }),
+ rest,
+ )),
_ => parse_error(),
},
}
@@ -139,6 +297,126 @@
Some("literal")
}
}
+
+ impl_synom!(LitStr "string literal" switch!(
+ syn!(Lit),
+ Lit::Str(lit) => value!(lit)
+ |
+ _ => reject!()
+ ));
+
+ impl_synom!(LitByteStr "byte string literal" switch!(
+ syn!(Lit),
+ Lit::ByteStr(lit) => value!(lit)
+ |
+ _ => reject!()
+ ));
+
+ impl_synom!(LitByte "byte literal" switch!(
+ syn!(Lit),
+ Lit::Byte(lit) => value!(lit)
+ |
+ _ => reject!()
+ ));
+
+ impl_synom!(LitChar "character literal" switch!(
+ syn!(Lit),
+ Lit::Char(lit) => value!(lit)
+ |
+ _ => reject!()
+ ));
+
+ impl_synom!(LitInt "integer literal" switch!(
+ syn!(Lit),
+ Lit::Int(lit) => value!(lit)
+ |
+ _ => reject!()
+ ));
+
+ impl_synom!(LitFloat "floating point literal" switch!(
+ syn!(Lit),
+ Lit::Float(lit) => value!(lit)
+ |
+ _ => reject!()
+ ));
+
+ impl_synom!(LitBool "boolean literal" switch!(
+ syn!(Lit),
+ Lit::Bool(lit) => value!(lit)
+ |
+ _ => reject!()
+ ));
+
+ impl Lit {
+ pub fn new(token: Literal, span: Span) -> Self {
+ let value = token.to_string();
+
+ match value::byte(&value, 0) {
+ b'"' | b'r' => return Lit::Str(LitStr {
+ token: token,
+ span: span,
+ }),
+ b'b' => match value::byte(&value, 1) {
+ b'"' | b'r' => return Lit::ByteStr(LitByteStr {
+ token: token,
+ span: span,
+ }),
+ b'\'' => return Lit::Byte(LitByte {
+ token: token,
+ span: span,
+ }),
+ _ => {}
+ }
+ b'\'' => return Lit::Char(LitChar {
+ token: token,
+ span: span,
+ }),
+ b'0'...b'9' => if number_is_int(&value) {
+ return Lit::Int(LitInt {
+ token: token,
+ span: span,
+ });
+ } else if number_is_float(&value) {
+ return Lit::Float(LitFloat {
+ token: token,
+ span: span,
+ });
+ } else {
+ // number overflow
+ return Lit::Verbatim(LitVerbatim {
+ token: token,
+ span: span,
+ });
+ }
+ _ => if value == "true" || value == "false" {
+ return Lit::Bool(LitBool {
+ value: value == "true",
+ span: 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')
+ }
+ }
}
#[cfg(feature = "printing")]
@@ -146,9 +424,488 @@
use super::*;
use quote::{ToTokens, Tokens};
- impl ToTokens for Lit {
+ impl ToTokens for LitStr {
fn to_tokens(&self, tokens: &mut Tokens) {
- self.clone().into_token_tree().to_tokens(tokens)
+ tokens.append(TokenTree {
+ span: self.span,
+ kind: TokenNode::Literal(self.token.clone()),
+ });
+ }
+ }
+
+ impl ToTokens for LitByteStr {
+ fn to_tokens(&self, tokens: &mut Tokens) {
+ tokens.append(TokenTree {
+ span: self.span,
+ kind: TokenNode::Literal(self.token.clone()),
+ });
+ }
+ }
+
+ impl ToTokens for LitByte {
+ fn to_tokens(&self, tokens: &mut Tokens) {
+ tokens.append(TokenTree {
+ span: self.span,
+ kind: TokenNode::Literal(self.token.clone()),
+ });
+ }
+ }
+
+ impl ToTokens for LitChar {
+ fn to_tokens(&self, tokens: &mut Tokens) {
+ tokens.append(TokenTree {
+ span: self.span,
+ kind: TokenNode::Literal(self.token.clone()),
+ });
+ }
+ }
+
+ impl ToTokens for LitInt {
+ fn to_tokens(&self, tokens: &mut Tokens) {
+ tokens.append(TokenTree {
+ span: self.span,
+ kind: TokenNode::Literal(self.token.clone()),
+ });
+ }
+ }
+
+ impl ToTokens for LitFloat {
+ fn to_tokens(&self, tokens: &mut Tokens) {
+ tokens.append(TokenTree {
+ span: self.span,
+ kind: TokenNode::Literal(self.token.clone()),
+ });
+ }
+ }
+
+ impl ToTokens for LitBool {
+ fn to_tokens(&self, tokens: &mut Tokens) {
+ tokens.append(TokenTree {
+ span: self.span,
+ kind: TokenNode::Term(Term::intern(if self.value {
+ "true"
+ } else {
+ "false"
+ })),
+ });
+ }
+ }
+
+ impl ToTokens for LitVerbatim {
+ fn to_tokens(&self, tokens: &mut Tokens) {
+ tokens.append(TokenTree {
+ span: self.span,
+ kind: TokenNode::Literal(self.token.clone()),
+ });
+ }
+ }
+}
+
+mod value {
+ use super::*;
+ use std::char;
+ use std::ops::{Index, RangeFrom};
+ use proc_macro2::TokenStream;
+
+ /// 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!(),
+ }
+ }
+
+ 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(byte as u32).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!(),
+ }
+ }
+
+ 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(byte as u32).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(byte as u32).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 += 0x1 * 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 += (b - b'0') as u32;
+ s = &s[1..];
+ }
+ b'a'...b'f' => {
+ ch *= 0x10;
+ ch += (10 + b - b'a') as u32;
+ s = &s[1..];
+ }
+ b'A'...b'F' => {
+ ch *= 0x10;
+ ch += (10 + b - b'A') as u32;
+ 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' => (b - b'0') as u64,
+ b'a'...b'f' if base > 10 => 10 + (b - b'a') as u64,
+ b'A'...b'F' if base > 10 => 10 + (b - b'A') as u64,
+ 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
+ } else 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(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().kind {
+ TokenNode::Literal(l) => l,
+ _ => unreachable!(),
}
}
}
diff --git a/src/macros.rs b/src/macros.rs
index 273db09..d8fb4b8 100644
--- a/src/macros.rs
+++ b/src/macros.rs
@@ -44,7 +44,7 @@
macro_rules! ast_enum {
(
$(#[$enum_attr:meta])*
- pub enum $name:ident { $($variants:tt)* }
+ pub enum $name:ident $(# $tags:ident)* { $($variants:tt)* }
) => (
$(#[$enum_attr])*
#[cfg_attr(feature = "extra-traits", derive(Debug, Eq, PartialEq, Hash))]
@@ -164,7 +164,7 @@
($($rest:tt)*) => (ast_struct! { $($rest)* });
}
-#[cfg(all(feature = "full", feature = "parsing"))]
+#[cfg(feature = "parsing")]
macro_rules! impl_synom {
($t:ident $description:tt $($parser:tt)+) => {
impl Synom for $t {