From 37ed2ab91038567bafe3fd2e545c7d1631ff2ab0 Mon Sep 17 00:00:00 2001 From: Dan Aloni Date: Tue, 10 Apr 2018 02:08:47 +0300 Subject: Macros: Add a 'literal' fragment specifier Implements RFC 1576. See: https://github.com/rust-lang/rfcs/blob/master/text/1576-macros-literal-matcher.md Changes are mostly in libsyntax, docs, and tests. Feature gate is enabled for 1.27.0. Many thanks to Vadim Petrochenkov for following through code reviews and suggestions. Example: ````rust macro_rules! test_literal { ($l:literal) => { println!("literal: {}", $l); }; ($e:expr) => { println!("expr: {}", $e); }; } fn main() { let a = 1; test_literal!(a); test_literal!(2); test_literal!(-3); } ``` Output: ``` expr: 1 literal: 2 literal: -3 ``` --- src/libsyntax/parse/parser.rs | 18 +++++++++--------- src/libsyntax/parse/token.rs | 21 ++++++++++++++++++++- 2 files changed, 29 insertions(+), 10 deletions(-) (limited to 'src/libsyntax/parse') diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 49b30c6f460..3f0df6d055b 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -115,7 +115,7 @@ macro_rules! maybe_whole_expr { ($p:expr) => { if let token::Interpolated(nt) = $p.token.clone() { match nt.0 { - token::NtExpr(ref e) => { + token::NtExpr(ref e) | token::NtLiteral(ref e) => { $p.bump(); return Ok((*e).clone()); } @@ -1823,7 +1823,7 @@ impl<'a> Parser<'a> { pub fn parse_lit_token(&mut self) -> PResult<'a, LitKind> { let out = match self.token { token::Interpolated(ref nt) => match nt.0 { - token::NtExpr(ref v) => match v.node { + token::NtExpr(ref v) | token::NtLiteral(ref v) => match v.node { ExprKind::Lit(ref lit) => { lit.node.clone() } _ => { return self.unexpected_last(&self.token); } }, @@ -1862,7 +1862,7 @@ impl<'a> Parser<'a> { } /// matches '-' lit | lit (cf. ast_validation::AstValidator::check_expr_within_pat) - pub fn parse_pat_literal_maybe_minus(&mut self) -> PResult<'a, P> { + pub fn parse_literal_maybe_minus(&mut self) -> PResult<'a, P> { maybe_whole_expr!(self); let minus_lo = self.span; @@ -2407,10 +2407,10 @@ impl<'a> Parser<'a> { hi = pth.span; ex = ExprKind::Path(None, pth); } else { - match self.parse_lit() { - Ok(lit) => { - hi = lit.span; - ex = ExprKind::Lit(P(lit)); + match self.parse_literal_maybe_minus() { + Ok(expr) => { + hi = expr.span; + ex = expr.node.clone(); } Err(mut err) => { self.cancel(&mut err); @@ -3724,7 +3724,7 @@ impl<'a> Parser<'a> { let hi = self.prev_span; Ok(self.mk_expr(lo.to(hi), ExprKind::Path(qself, path), ThinVec::new())) } else { - self.parse_pat_literal_maybe_minus() + self.parse_literal_maybe_minus() } } @@ -3914,7 +3914,7 @@ impl<'a> Parser<'a> { } } else { // Try to parse everything else as literal with optional minus - match self.parse_pat_literal_maybe_minus() { + match self.parse_literal_maybe_minus() { Ok(begin) => { if self.eat(&token::DotDotDot) { let end = self.parse_pat_range_end()?; diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index 938711ca1d4..6bcc1b0f026 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -280,7 +280,12 @@ impl Token { Lifetime(..) | // labeled loop Pound => true, // expression attributes Interpolated(ref nt) => match nt.0 { - NtIdent(..) | NtExpr(..) | NtBlock(..) | NtPath(..) | NtLifetime(..) => true, + NtLiteral(..) | + NtIdent(..) | + NtExpr(..) | + NtBlock(..) | + NtPath(..) | + NtLifetime(..) => true, _ => false, }, _ => false, @@ -324,6 +329,18 @@ impl Token { } } + /// Returns `true` if the token is any literal, a minus (which can follow a literal, + /// for example a '-42', or one of the boolean idents). + pub fn can_begin_literal_or_bool(&self) -> bool { + match *self { + Literal(..) => true, + BinOp(Minus) => true, + Ident(ident, false) if ident.name == keywords::True.name() => true, + Ident(ident, false) if ident.name == keywords::False.name() => true, + _ => false, + } + } + /// Returns an identifier if this token is an identifier. pub fn ident(&self) -> Option<(ast::Ident, /* is_raw */ bool)> { match *self { @@ -672,6 +689,7 @@ pub enum Nonterminal { NtTy(P), NtIdent(ast::Ident, /* is_raw */ bool), NtLifetime(ast::Ident), + NtLiteral(P), /// Stuff inside brackets for attributes NtMeta(ast::MetaItem), NtPath(ast::Path), @@ -713,6 +731,7 @@ impl fmt::Debug for Nonterminal { NtExpr(..) => f.pad("NtExpr(..)"), NtTy(..) => f.pad("NtTy(..)"), NtIdent(..) => f.pad("NtIdent(..)"), + NtLiteral(..) => f.pad("NtLiteral(..)"), NtMeta(..) => f.pad("NtMeta(..)"), NtPath(..) => f.pad("NtPath(..)"), NtTT(..) => f.pad("NtTT(..)"), -- cgit 1.4.1-3-g733a5