1use proc_macro::TokenStream;
2use unscanny::Scanner;
3
4enum Kind<'a> {
6 Display(&'a str),
7 Iterator(&'a str),
8 Call(&'a str),
9}
10
11fn id_start(c: char) -> bool {
13 unicode_xid::UnicodeXID::is_xid_start(c) || c == '_'
14}
15
16fn id_continue(c: char) -> bool {
18 unicode_xid::UnicodeXID::is_xid_continue(c)
19}
20
21fn parse_ident<'a>(scan: &mut Scanner<'a>) -> Option<&'a str> {
23 if scan.at(id_start) {
24 Some(scan.eat_while(id_continue))
25 } else if scan.eat_if('{') {
26 scan.eat_whitespace();
27 let start = scan.cursor();
28 scan.eat_while(id_continue);
29 let ident = scan.from(start);
30 scan.eat_whitespace();
31 scan.expect('}');
32 scan.eat_whitespace();
33 Some(ident)
34 } else {
35 None
36 }
37}
38
39fn parse_next<'a>(scan: &mut Scanner<'a>) -> (&'a str, Option<Kind<'a>>) {
42 let raw = scan.eat_until('$');
43
44 if scan.eat_if('$') {
45 scan.eat_whitespace();
46 let pattern = if let Some(ident) = parse_ident(scan) {
47 Some(Kind::Display(ident))
48 } else if scan.eat_if('!') {
49 scan.eat_whitespace();
50 if let Some(ident) = parse_ident(scan) {
51 Some(Kind::Call(ident))
52 } else {
53 panic!("Unknown pattern $!{}", scan.eat_while(|_| true))
54 }
55 } else if scan.eat_if('(') {
56 let start = scan.cursor();
57 let mut count_delim = 0;
58 while let Some(c) = scan.peek() {
59 match c {
60 ')' if count_delim == 0 => break,
61 ')' => count_delim -= 1,
62 '(' => count_delim += 1,
63 _ => {}
64 }
65 scan.eat();
66 }
67 let inner = scan.from(start);
68 scan.expect(')');
69 scan.eat_whitespace();
70 Some(Kind::Iterator(inner))
71 } else {
72 panic!("Unknown pattern ${}", scan.eat_while(|_| true))
73 };
74
75 let start = scan.cursor();
77 scan.eat_whitespace();
78 if !scan.at("$") {
79 scan.jump(start);
80 }
81
82 (raw, pattern)
83 } else {
84 (raw, None)
85 }
86}
87
88fn ident_in_iterator<'a>(scan: &'a mut Scanner) -> Vec<&'a str> {
90 let start = scan.cursor();
91 let mut idents = Vec::new();
92 while let (_, Some(pat)) = parse_next(scan) {
93 match pat {
94 Kind::Display(ident) | Kind::Call(ident) => idents.push(ident),
95 Kind::Iterator(_) => panic!("nested repetitions are not supported"),
96 }
97 }
98 scan.jump(start);
99 idents
100}
101
102fn gen_str(s: &mut String, out: &str, str: &str) {
104 if !str.is_empty() {
105 s.push_str(out);
106 s.push_str(".write_str(\"");
107 for c in str.chars() {
108 if c == '"' {
109 s.push_str("\\\"")
110 } else {
111 s.push(c)
112 }
113 }
114 s.push_str("\").unwrap();\n");
115 }
116}
117
118fn gen_disp(s: &mut String, out: &str, ident: &str) {
120 s.push_str(out);
121 s.push_str(".write_fmt(format_args!(\"{}\",");
122 s.push_str(ident);
123 s.push_str(")).unwrap();\n");
124}
125
126fn gen_recursive<'a>(scan: &'a mut Scanner, s: &mut String, out: &str) {
128 loop {
129 let (raw, pattern) = parse_next(scan);
130 if raw.is_empty() && pattern.is_none() {
131 break;
132 }
133 gen_str(s, out, raw);
134 if let Some(pattern) = pattern {
135 match pattern {
136 Kind::Display(ident) => gen_disp(s, out, ident),
137 Kind::Call(ident) => {
138 s.push_str("let w = &mut*w;");
139 s.push_str(ident);
140 s.push_str("(&mut *");
141 s.push_str(out);
142 s.push_str(");\n");
143 }
144 Kind::Iterator(inner) => {
145 let mut scan = Scanner::new(inner);
146 let idents = ident_in_iterator(&mut scan);
147 let mut iter = idents.iter();
148 s.push_str("{\nlet iter = ");
149 s.push_str(iter.next().unwrap());
150 s.push_str(".clone()");
151 for item in iter {
152 s.push_str(".zip(");
153 s.push_str(item);
154 s.push_str(".clone())");
155 }
156 s.push_str(";\n");
157 s.push_str("for ");
158 for _ in 1..idents.len() {
159 s.push('(');
160 }
161 let mut iter = idents.iter();
162 s.push_str(iter.next().unwrap());
163 for item in iter {
164 s.push(',');
165 s.push_str(item);
166 s.push(')');
167 }
168 s.push_str(" in iter {\n");
169 gen_recursive(&mut scan, s, out);
170 s.push_str("}\n}\n");
171 }
172 }
173 }
174 }
175}
176
177#[proc_macro]
203pub fn code(pattern: TokenStream) -> TokenStream {
204 let pattern = pattern.to_string();
205 let mut scan = unscanny::Scanner::new(&pattern);
206 scan.eat_whitespace();
207 let out = scan.eat_while(id_continue);
208 scan.eat_whitespace();
209 scan.expect("=>");
210 scan.eat_whitespace();
211
212 let mut s = String::new();
213 s.push('{');
214 gen_recursive(&mut scan, &mut s, out);
215 s.push('}');
216 s.parse().unwrap()
217}