diff --git a/src/libstd/macros.rs b/src/libstd/macros.rs index 85e614ab47e3d..2d4a47d3ca802 100644 --- a/src/libstd/macros.rs +++ b/src/libstd/macros.rs @@ -582,6 +582,34 @@ pub mod builtin { #[macro_export] macro_rules! stringify( ($t:tt) => ({ /* compiler built-in */ }) ) + /// A macro which converts its argument to a lowercase string at compile time + /// Useful for lowercasing stringified idents in macros + /// + /// # Example + /// + /// ``` + /// let lower1 = to_lower!(stringify!(FooBar)) + /// let lower2 = to_lower!("FooBar") + /// assert_eq!(lower1, "foobar"); + /// assert_eq!(lower2, "foobar"); + /// ``` + #[macro_export] + macro_rules! to_lower( ($t:tt) => ({ /* compiler built-in */ }) ) + + /// A macro which converts its argument to an uppercase string at compile time + /// Useful for lowercasing stringified idents in macros + /// + /// # Example + /// + /// ``` + /// let upper1 = to_upper!(stringify!(FooBar)) + /// let upper2 = to_upper!("FooBar") + /// assert_eq!(upper1, "FOOBAR"); + /// assert_eq!(upper2, "FOOBAR"); + /// ``` + #[macro_export] + macro_rules! to_upper( ($t:tt) => ({ /* compiler built-in */ }) ) + /// Includes a utf8-encoded file as a string. /// /// This macro will yield an expression of type `&'static str` which is the diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index 01d3920a25463..3e678142b7fb2 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -401,6 +401,12 @@ fn initial_syntax_expander_table() -> SyntaxEnv { syntax_expanders.insert(intern("stringify"), builtin_normal_expander( ext::source_util::expand_stringify)); + syntax_expanders.insert(intern("to_lower"), + builtin_normal_expander( + ext::source_util::expand_lower)); + syntax_expanders.insert(intern("to_upper"), + builtin_normal_expander( + ext::source_util::expand_upper)); syntax_expanders.insert(intern("include"), builtin_normal_expander( ext::source_util::expand_include)); diff --git a/src/libsyntax/ext/source_util.rs b/src/libsyntax/ext/source_util.rs index 703adcbd33552..82c0cf07dd458 100644 --- a/src/libsyntax/ext/source_util.rs +++ b/src/libsyntax/ext/source_util.rs @@ -68,6 +68,58 @@ pub fn expand_stringify(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) token::intern_and_get_ident(s.as_slice()))) } +pub fn expand_lower(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) + -> Box { + expand_cased(cx, sp, tts, |c| { c.to_lowercase() }) +} + +pub fn expand_upper(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) + -> Box { + expand_cased(cx, sp, tts, |c| { c.to_uppercase() }) +} + +fn expand_cased(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree], transform: |char| -> char) + -> Box { + let es = match base::get_exprs_from_tts(cx, sp, tts) { + Some(e) => e, + None => return base::DummyResult::expr(sp) + }; + + let mut it = es.iter(); + let res = match it.next() { + // FIXME (#13910): nested matches are necessary to get through Gc<> + Some(expr) => match expr.node { + ast::ExprLit(ref lit) => match lit.node { + ast::LitStr(ref s, _) => Some((s, lit.span)), + _ => { + cx.span_err(expr.span, "expected a string literal"); + None + } + }, + _ => { + cx.span_err(expr.span, "expected a string literal"); + None + } + }, + None => { + cx.span_err(sp, "expected 1 argument, found 0"); + None + } + }; + match (res, it.count()) { + (Some((s, span)), 0) => { + let new_s = s.get().chars().map(transform).collect::(); + base::MacExpr::new(cx.expr_str(span, token::intern_and_get_ident(new_s.as_slice()))) + } + (_, rest) => { + if rest > 0 { + cx.span_err(sp, format!("expected 1 argument, found {}", rest+1).as_slice()); + } + base::DummyResult::expr(sp) + } + } +} + pub fn expand_mod(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) -> Box { base::check_zero_tts(cx, sp, tts, "module_path!");