-
Notifications
You must be signed in to change notification settings - Fork 13.3k
Add syntax extensions for lowercasing/uppercasing strings (Fixes #16607) #16636
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
#16607 calls them |
Some(e) => e, | ||
None => return base::DummyResult::expr(sp) | ||
}; | ||
match es.move_iter().next() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If multiple expressions are provided, this will happily process the first and ignore the rest. It should expressly test how many expressions it got.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would a test for iter.next().is_some()
be okay style-wise?
The PR currently uses |
} | ||
}, | ||
_ => () | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is hard to read, with all the rightward drift. Ideally you could compress this into a single match
, but I don't think you can match the Gc
in ExprLit
. We can at least get rid of one of the match
levels, and remove the return
from the inside of this big beast:
let s = match es.move_iter().next() {
Some(codemap::Spanned { node: ast::ExprLit(lit), .. }) => match lit.node {
ast::LitStr(ref s, _) => Some(s),
_ => None
},
_ => None
};
match s {
Some(s) => {
let new_s = s.get().chars().map(transform).collect::<String>();
base::MacExpr::new(cx.expr_str(sp, token::intern_and_get_ident(new_s.as_slice())))
}
None => {
cx.span_err(sp, "expected a string literal");
base::DummyResult::expr(sp)
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should also probably pull the span off of the first expression, instead of using the span for the whole macro.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, the Gc
was giving problems. I couldn't find any way of destructuring the @
(I was surprised that syntax still exists)
This seems like a better option, thanks :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried this, there's another Gc
on Expr
, so I had to use the following (one extra match):
let s = match iter.next() {
Some(expr) => {
match *expr {
ast::Expr { node: ast::ExprLit(lit), span: sp2 , ..} => {
sp = sp2;
match lit.node {
ast::LitStr(ref s, _) => Some(s),
_ => None
}
},
_ => None
}
},
_ => None
};
node
doesn't live long enough so I'll have to use s.clone()
if I don't want to nest the matches further. Should I? Seems a bit unnecessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It occurs to me the ref s
isn't going to work, since it's a reference to a moved value. We can skip the move, though, and we can also produce a better error for providing the wrong argument type vs not providing the right number of arguments. Something like:
let mut it = es.iter();
let res = match it.next() {
Some(&codemap::Spanned { node: ast::ExprLit(ref lit), .. }) => match lit.node {
ast::LitStr(ref s, span) => Some((s, span)),
_ => {
cx.span_err(span, "expected a string literal");
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::<String>();
base::MacExpr::new(cx.expr_str(sp, 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)
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is there a codemap::Spanned
there? Shouldn't it be an ast::Ext
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because I was assuming that Expr
was a Spanned<Expr_>
. It actually isn't, which is surprising, and is apparently due to the need for a separate id
field. Anyway, I'm guessing the following should work:
fn expand_cased(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree], transform: |char| -> char)
-> Box<base::MacResult> {
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, span) => Some((s, span)),
_ => {
cx.span_err(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::<String>();
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)
}
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, thanks. Should the sp
in the final MacExpr
be sp
or span
? It might end up with the wrong span on expansion -- I'm not entirely clear on how ExtCtxt
works to be sure of this, and I'd rather not wait for two full builds to see ;)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch, I meant to make that span
. I'll edit my comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There were more issues with span
being taken from the wrong place (should
be lit.span
, not the second value of ExprLit
), but I fixed that.
Thanks for the help!
-Manish Goregaokar
On Thu, Aug 21, 2014 at 4:59 AM, Kevin Ballard notifications@github.com
wrote:
In src/libsyntax/ext/source_util.rs:
- };
- match es.move_iter().next() {
Some(expr) => {
match expr.node {
ast::ExprLit(lit) => match lit.node {
ast::LitStr(ref s, _) => {
return base::MacExpr::new(cx.expr_str(sp,
token::intern_and_get_ident(s.get().chars().map(transform).collect::<String>().as_slice())))
},
_ => ()
},
_ => ()
}
},
_ => ()
- }
Good catch, I meant to make that span. I'll edit my comment.
—
Reply to this email directly or view it on GitHub
https://github.com/rust-lang/rust/pull/16636/files#r16512839.
@SimonSapin Ok, fair enough. |
From chatting on IRC with @Manishearth: either kind of lower casing works for Servo’s use case since they behave the same when the input is within ASCII, which we know because it’s compile time (no arbitrary input from web content). |
@SimonSapin I can add to_ascii_* variants if we want, since all I have to do is add variants of the With some changes (accept a closure that transforms on the whole string isntead of individual chars) I can add titlecasing (and snakecasing) support as well, if these would be useful. |
@Manishearth I don't think there's a need for the |
Updated with the style changes. I'll look into adding |
This is an addition to the prelude for all rust programs in existence, and should be considered with the gravity as such. Currently we require any modifications of the prelude (additions or removals) such as this to go through the RFC process. This is also why we have created a plugin infrastructure for developing extensions such as this out of tree. |
Alright, I'll draft up an RFC when I get time. -Manish Goregaokar On Thu, Aug 21, 2014 at 9:51 PM, Alex Crichton notifications@github.com
|
Alternatively, Servo could define these syntax extensions itself in its |
That would work. I'll have a go. -Manish Goregaokar On Thu, Aug 21, 2014 at 9:57 PM, Simon Sapin notifications@github.com
|
It sounds like this may be able to live outside the compiler in servo, so I'm going to close this for now. Feel free to reopen if any surprises arise! |
The implementation was copied directly from rust-lang/rust#16636 and updated for rust changes, so the credit goes to @Manishearth
The implementation was copied directly from rust-lang/rust#16636 and updated for rust changes, so the credit goes to @Manishearth
The implementation was copied directly from rust-lang/rust#16636 and updated for rust changes, so the credit goes to @Manishearth
The implementation was copied directly from rust-lang/rust#16636 and updated for rust changes, so the credit goes to @Manishearth
The implementation was copied directly from rust-lang/rust#16636 and updated for rust changes, so the credit goes to @Manishearth
…28 (from Swatinem:lowercasegetters); r=Manishearth The implementation was copied directly from rust-lang/rust#16636 and updated for rust changes, so the credit goes to @Manishearth Source-Repo: https://github.com/servo/servo Source-Revision: 902c16497c40684930819693a7e90f0862eb7f56
What's the current status on getting lowercase / uppercase / kebab-case / title-case / etc. variants of strings in macros? I have a lot of use cases for this in various libraries where I often need to create an enum, and then derive both |
You could write a custom proc macro for this and use it.
(One may exist already)
…On Wed, Dec 12, 2018, 6:13 PM Michael Murphy ***@***.*** wrote:
What's the current status on getting lowercase / uppercase / kebab-case /
title-case / etc. variants of strings in macros? I have a lot of use cases
for this in various libraries where I often need to create an enum, and
then derive both impl From<Enum> for &'static str and impl FromStr for
Enum; or when interacting with a JSON API that uses kebab case.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#16636 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/ABivSE8WAx6VNNzxoe6bSj46B9UClqx8ks5u4beugaJpZM4CZb4_>
.
|
…28 (from Swatinem:lowercasegetters); r=Manishearth The implementation was copied directly from rust-lang/rust#16636 and updated for rust changes, so the credit goes to Manishearth Source-Repo: https://github.com/servo/servo Source-Revision: 902c16497c40684930819693a7e90f0862eb7f56 UltraBlame original commit: 1b9c7a69c746676dcf68b519877913b5b2186a56
…28 (from Swatinem:lowercasegetters); r=Manishearth The implementation was copied directly from rust-lang/rust#16636 and updated for rust changes, so the credit goes to Manishearth Source-Repo: https://github.com/servo/servo Source-Revision: 902c16497c40684930819693a7e90f0862eb7f56 UltraBlame original commit: 1b9c7a69c746676dcf68b519877913b5b2186a56
…28 (from Swatinem:lowercasegetters); r=Manishearth The implementation was copied directly from rust-lang/rust#16636 and updated for rust changes, so the credit goes to Manishearth Source-Repo: https://github.com/servo/servo Source-Revision: 902c16497c40684930819693a7e90f0862eb7f56 UltraBlame original commit: 1b9c7a69c746676dcf68b519877913b5b2186a56
Testcase: