Skip to content

Commit 8e41809

Browse files
committed
Add syntax extensions for lowercasing/uppercasing strings (Fixes #16607)
1 parent 05df25f commit 8e41809

File tree

3 files changed

+86
-0
lines changed

3 files changed

+86
-0
lines changed

src/libstd/macros.rs

+28
Original file line numberDiff line numberDiff line change
@@ -582,6 +582,34 @@ pub mod builtin {
582582
#[macro_export]
583583
macro_rules! stringify( ($t:tt) => ({ /* compiler built-in */ }) )
584584

585+
/// A macro which converts its argument to a lowercase string at compile time
586+
/// Useful for lowercasing stringified idents in macros
587+
///
588+
/// # Example
589+
///
590+
/// ```
591+
/// let lower1 = to_lower!(stringify!(FooBar))
592+
/// let lower2 = to_lower!("FooBar")
593+
/// assert_eq!(lower1, "foobar");
594+
/// assert_eq!(lower2, "foobar");
595+
/// ```
596+
#[macro_export]
597+
macro_rules! to_lower( ($t:tt) => ({ /* compiler built-in */ }) )
598+
599+
/// A macro which converts its argument to an uppercase string at compile time
600+
/// Useful for lowercasing stringified idents in macros
601+
///
602+
/// # Example
603+
///
604+
/// ```
605+
/// let upper1 = to_upper!(stringify!(FooBar))
606+
/// let upper2 = to_upper!("FooBar")
607+
/// assert_eq!(upper1, "FOOBAR");
608+
/// assert_eq!(upper2, "FOOBAR");
609+
/// ```
610+
#[macro_export]
611+
macro_rules! to_upper( ($t:tt) => ({ /* compiler built-in */ }) )
612+
585613
/// Includes a utf8-encoded file as a string.
586614
///
587615
/// This macro will yield an expression of type `&'static str` which is the

src/libsyntax/ext/base.rs

+6
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,12 @@ fn initial_syntax_expander_table() -> SyntaxEnv {
401401
syntax_expanders.insert(intern("stringify"),
402402
builtin_normal_expander(
403403
ext::source_util::expand_stringify));
404+
syntax_expanders.insert(intern("to_lower"),
405+
builtin_normal_expander(
406+
ext::source_util::expand_lower));
407+
syntax_expanders.insert(intern("to_upper"),
408+
builtin_normal_expander(
409+
ext::source_util::expand_upper));
404410
syntax_expanders.insert(intern("include"),
405411
builtin_normal_expander(
406412
ext::source_util::expand_include));

src/libsyntax/ext/source_util.rs

+52
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,58 @@ pub fn expand_stringify(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
6868
token::intern_and_get_ident(s.as_slice())))
6969
}
7070

71+
pub fn expand_lower(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
72+
-> Box<base::MacResult> {
73+
expand_cased(cx, sp, tts, |c| { c.to_lowercase() })
74+
}
75+
76+
pub fn expand_upper(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
77+
-> Box<base::MacResult> {
78+
expand_cased(cx, sp, tts, |c| { c.to_uppercase() })
79+
}
80+
81+
fn expand_cased(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree], transform: |char| -> char)
82+
-> Box<base::MacResult> {
83+
let es = match base::get_exprs_from_tts(cx, sp, tts) {
84+
Some(e) => e,
85+
None => return base::DummyResult::expr(sp)
86+
};
87+
88+
let mut it = es.iter();
89+
let res = match it.next() {
90+
// FIXME (#13910): nested matches are necessary to get through Gc<>
91+
Some(expr) => match expr.node {
92+
ast::ExprLit(ref lit) => match lit.node {
93+
ast::LitStr(ref s, _) => Some((s, lit.span)),
94+
_ => {
95+
cx.span_err(expr.span, "expected a string literal");
96+
None
97+
}
98+
},
99+
_ => {
100+
cx.span_err(expr.span, "expected a string literal");
101+
None
102+
}
103+
},
104+
None => {
105+
cx.span_err(sp, "expected 1 argument, found 0");
106+
None
107+
}
108+
};
109+
match (res, it.count()) {
110+
(Some((s, span)), 0) => {
111+
let new_s = s.get().chars().map(transform).collect::<String>();
112+
base::MacExpr::new(cx.expr_str(span, token::intern_and_get_ident(new_s.as_slice())))
113+
}
114+
(_, rest) => {
115+
if rest > 0 {
116+
cx.span_err(sp, format!("expected 1 argument, found {}", rest+1).as_slice());
117+
}
118+
base::DummyResult::expr(sp)
119+
}
120+
}
121+
}
122+
71123
pub fn expand_mod(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
72124
-> Box<base::MacResult> {
73125
base::check_zero_tts(cx, sp, tts, "module_path!");

0 commit comments

Comments
 (0)