1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
extern crate proc_macro;
use std::mem;
use proc_macro2::{Group, Ident, Punct, Spacing, TokenStream, TokenTree};
#[proc_macro]
pub fn caesar(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input = TokenStream::from(input);
let output: TokenStream = decode_stream(input);
proc_macro::TokenStream::from(output)
}
fn decode_stream(ts: TokenStream) -> TokenStream {
ts.into_iter().map(decode_tree).collect()
}
fn decode_tree(tt: TokenTree) -> TokenTree {
match tt {
TokenTree::Group(g) => {
let mut dg = Group::new(g.delimiter(), decode_stream(g.stream()));
dg.set_span(g.span());
TokenTree::Group(dg)
}
TokenTree::Ident(i) => {
let gi = Ident::new(&caesar_decode(&i.to_string()), i.span());
TokenTree::Ident(gi)
}
TokenTree::Punct(..) | TokenTree::Literal(..) => tt,
}
}
fn caesar_decode(s: &str) -> String {
return s.chars().map(decode_char).collect();
fn decode_char(c: char) -> char {
match c {
'a'..='z' => rot(c, 'a'),
'A'..='Z' => rot(c, 'A'),
_ => c,
}
}
fn rot(c: char, base: char) -> char {
std::char::from_u32(((c as u32 - base as u32) + 13) % 26 + base as u32).unwrap()
}
}
#[test]
fn decoding_works() {
assert_eq!(
&caesar_decode("Ornhgvshy vf orggre guna htyl."),
"Beautiful is better than ugly."
)
}
#[proc_macro]
pub fn mirror(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input = TokenStream::from(input);
let output: TokenStream = mirror_stream(input);
proc_macro::TokenStream::from(output)
}
fn mirror_stream(ts: TokenStream) -> TokenStream {
let mut spacing = Spacing::Alone;
ts.into_iter()
.map(|tt| match tt {
TokenTree::Group(g) => {
let mut mg = Group::new(g.delimiter(), mirror_stream(g.stream()));
mg.set_span(g.span());
TokenTree::Group(mg)
}
TokenTree::Punct(p) => {
let c = mirror_char(p.as_char());
let spacing = mem::replace(&mut spacing, p.spacing());
let mut mp = Punct::new(c, spacing);
mp.set_span(p.span());
TokenTree::Punct(mp)
}
TokenTree::Ident(..) | TokenTree::Literal(..) => tt,
})
.collect::<Vec<_>>()
.into_iter()
.rev()
.collect()
}
fn mirror_char(c: char) -> char {
match c {
'<' => '>',
'>' => '<',
_ => c,
}
}