1use proc_macro::TokenStream;
4use quote::{ quote, ToTokens };
5use indexmap::IndexMap;
6
7struct ArgInfo {
8 funcarg_ident: syn::Ident,
9 funcarg_def: syn::PatType,
10 keyarg_name: Option<String>,
12 is_optional: bool,
13 is_flag: bool,
14 raw_type: proc_macro2::TokenStream,
15 value_converter: proc_macro2::TokenStream
16}
17
18fn tokens_equal(
19 a: &(impl Clone + IntoIterator<Item = proc_macro2::TokenTree>),
20 b: impl IntoIterator<Item = proc_macro2::TokenTree>
21) -> bool
22{
23 use proc_macro2::*;
24 let a = a.clone().into_iter().collect::<Vec<_>>();
25 let b = b.into_iter().collect::<Vec<_>>();
26
27 if a.len() != b.len() { return false; }
28 for (ai, bi) in a.iter().zip(b.iter()) {
29 match (ai, bi) {
30 (TokenTree::Ident(ia), TokenTree::Ident(ib)) if ia == ib => { },
31 (TokenTree::Punct(pa), TokenTree::Punct(pb))
32 if pa.as_char() == pb.as_char() => { },
33 _ => return false
34 }
35 }
36 true
37}
38
39fn tokens_match(
40 a: &(impl Clone + IntoIterator<Item = proc_macro2::TokenTree>),
41 l: impl IntoIterator<Item = proc_macro2::TokenTree>,
42 r: impl IntoIterator<Item = proc_macro2::TokenTree>
43) -> Option<proc_macro2::TokenStream>
44{
45 use proc_macro2::*;
46 let mut a = a.clone().into_iter().collect::<Vec<_>>();
47 let l = l.into_iter().collect::<Vec<_>>();
48 let r = r.into_iter().collect::<Vec<_>>();
49
50 if a.len() < l.len() + r.len() { return None; }
51 for i in 0..l.len() {
52 match (&a[i], &l[i]) {
53 (TokenTree::Ident(ia), TokenTree::Ident(ib)) if ia == ib => { },
54 (TokenTree::Punct(pa), TokenTree::Punct(pb))
55 if pa.as_char() == pb.as_char() => { },
56 _ => return None,
57 }
58 }
59 for i in 0..r.len() {
60 match (&a[a.len() - r.len() + i], &r[i]) {
61 (TokenTree::Ident(ia), TokenTree::Ident(ib)) if ia == ib => { },
62 (TokenTree::Punct(pa), TokenTree::Punct(pb))
63 if pa.as_char() == pb.as_char() => { },
64 _ => return None,
65 }
66 }
67
68 let ret = a.splice(l.len()..(a.len() - r.len()), []);
69 Some(ret.into_iter().collect())
70}
71
72#[proc_macro_attribute]
73pub fn molt_argparse(args: TokenStream, decl: TokenStream) -> TokenStream {
74 let mut args = syn::parse_macro_input!(args as syn::LitStr).value()
76 .split_whitespace()
77 .map(|s| s.to_string())
78 .collect::<Vec<String>>();
79 let decl = syn::parse_macro_input!(decl as syn::ItemFn);
80 let mut funcargs: IndexMap<String, (syn::PatType, syn::PatIdent)> =
84 decl.sig.inputs.iter().map(|fnarg| match fnarg {
85 syn::FnArg::Typed(typ) => match &*typ.pat {
86 syn::Pat::Ident(patident) => {
87 (patident.ident.to_string(),
88 (typ.clone(), patident.clone()))
89 }
90 _ => panic!("unexpected arg def")
91 },
92 syn::FnArg::Receiver(_) =>
93 panic!("self argument is unsupported by molt_argparse")
94 })
95 .collect();
96
97 let contextdecl = {
98 if args[0].starts_with("{") {
99 let s = args.remove(0);
100 let p = &s[1..s.len() - 1];
101 let (pattype, _) = funcargs.remove(p).expect("context arg not found");
102 quote! {
103 let #pattype = _interp.context(_ctxid);
104 }
105 }
106 else { quote! { } }
107 };
108
109 let args = args.into_iter().map(|arg| {
110 let (funcarg_name, keyarg_name) = match arg.starts_with("-") {
111 true => match arg.find(':') {
112 Some(i) => (arg[i + 1..].to_string(),
113 Some(arg[0..i].to_string())),
114 None => (arg[1..].to_string(), Some(arg))
115 }
116 false => (arg, None)
117 };
118 let (pt, pi) = funcargs.remove(&funcarg_name)
119 .expect("arg not found");
120
121 let type_tokens = pt.ty.to_token_stream();
122 let (raw_type, is_optional) = match tokens_match(
123 &type_tokens,
124 quote!{ Option< }, quote!{ > }
125 ) {
126 Some(ts) => (ts, true),
127 None => (type_tokens, false)
128 };
129
130 let raw_type_vec = raw_type.clone().into_iter().collect::<Vec<_>>();
131
132 let is_flag = keyarg_name.is_some() &&
133 !is_optional &&
134 tokens_equal(&raw_type, quote!{ bool });
135
136 let value_converter = {
137 if tokens_equal(&raw_type_vec, quote!{ bool }) {
138 quote!{ _molt_v.as_bool().ok() }
139 }
140 else if tokens_equal(&raw_type_vec, quote!{ &str }) {
141 quote!{ Some(_molt_v.as_str()) }
142 }
143 else if let Some(_) = tokens_match(
144 &raw_type_vec,
145 quote!{ &mut }, quote!{ }
146 ) {
147 panic!("mutable reference is not allowed in molt_argparse");
148 }
149 else if let Some(t) = tokens_match(
150 &raw_type_vec,
151 quote!{ &Vec< }, quote!{ > }
152 ) {
153 quote!{ _molt_v.as_other::<molt_argparse::WrapVec<#t>>()
154 .map(|v| &v.0) }
155 }
156 else if let Some(t) = tokens_match(
157 &raw_type_vec,
158 quote!{ Vec< }, quote!{ > }
159 ) {
160 quote!{ _molt_v.as_other::<molt_argparse::WrapVec<#t>>()
161 .map(|v| v.0.clone()) }
162 }
163 else if let Some(v) = tokens_match(
164 &raw_type_vec,
165 quote!{ & }, quote!{ }
166 ) {
167 quote!{ _molt_v.as_other::<#v>().map(|r| r.as_ref()) }
168 }
169 else if let Some(v) = tokens_match(
170 &raw_type_vec,
171 quote!{ Rc< }, quote!{ > }
172 ) {
173 quote!{ _molt_v.as_other::<#v>() }
174 }
175 else {
176 quote!{ _molt_v.as_other::<#(#raw_type_vec)*>()
177 .map(|r| r.as_ref().clone()) }
178 }
179 };
180
181 ArgInfo {
182 funcarg_ident: pi.ident,
183 funcarg_def: pt,
184 keyarg_name,
185 is_optional,
186 is_flag,
187 raw_type,
188 value_converter
189 }
190 }).collect::<Vec<_>>();
191
192 assert_eq!(funcargs.len(), 0, "excess funcargs");
193
194 let argdecls = args.iter().map(|ai| {
195 let ident = &ai.funcarg_ident;
196 let typ = &ai.raw_type;
197 match ai.is_flag {
198 true => quote! { let mut #ident: bool = false; },
199 false => quote! { let mut #ident: Option<#typ> = None; }
200 }
201 });
202
203 let argpostprocess = args.iter().filter(|ai| !ai.is_optional && !ai.is_flag).map(|ai| {
204 let ident = &ai.funcarg_ident;
205 let error_info = format!("argparse: {} is required.", ident);
206 quote! {
207 let #ident = match #ident {
208 Some(#ident) => #ident,
209 None => { return molt_ng::molt_err!(#error_info); }
210 };
211 }
212 });
213
214 let argfixmatch = args.iter().filter(|ai| ai.keyarg_name.is_none()).enumerate().map(|(i, ai)| {
215 let ident = &ai.funcarg_ident;
216 let value_converter = &ai.value_converter;
217 quote! {
218 #i => {
219 if let Some(_v) = #value_converter {
220 #ident = Some(_v);
221 _molt_argparse_i += 1;
222 continue;
223 }
224 }
225 }
226 });
227
228 let argkeymatch = args.iter().filter(|ai| ai.keyarg_name.is_some()).map(|ai| {
229 let keyarg_name = ai.keyarg_name.as_ref().unwrap().as_str();
230 let ident = &ai.funcarg_ident;
231 let value_converter = &ai.value_converter;
232 match ai.is_flag {
233 true => quote! {
234 #keyarg_name => {
235 #ident = true;
236 Ok(true)
237 }
238 },
239 false => quote! {
240 #keyarg_name => {
241 if let Some(_molt_v) = _molt_argparse_vals.next() {
242 if let Some(_v) = #value_converter {
243 #ident = Some(_v);
244 Ok(true)
245 }
246 else {
247 return molt_ng::molt_err!(
250 "argparse: {} does not accept {}",
251 #keyarg_name, _molt_v.as_str());
252 }
253 }
254 else {
255 return molt_ng::molt_err!(
256 "argparse: expected value after {}",
257 #keyarg_name);
258 }
259 }
260 }
261 }
262 });
263
264 let orig_args = args.iter().map(|ai| ai.funcarg_ident.clone());
265 let orig_arg_defs = args.iter().map(|ai| ai.funcarg_def.clone());
266 let orig_stmts = &decl.block.stmts;
267 let (orig_ret_type, is_ret_empty) = match &decl.sig.output {
268 syn::ReturnType::Default => (quote!{ () }, true),
269 syn::ReturnType::Type(_, typ) => (typ.to_token_stream().into(), false)
270 };
271 let (orig_raw_rettype, orig_ret_is_option) = {
272 if let Some(v) = tokens_match(
273 &orig_ret_type, quote!{ Option< }, quote!{ > }
274 ) {
275 (v, true)
276 }
277 else { (orig_ret_type.clone(), false) }
278 };
279
280 let retstmt = {
281 if is_ret_empty {
282 quote!{ molt_ng::Value::empty() }
283 }
284 else if tokens_equal(&orig_raw_rettype, quote!{ &str }) {
285 quote!{ molt_ng::Value::from(_ret) }
286 }
287 else if tokens_equal(&orig_raw_rettype, quote!{ String }) {
288 quote!{ molt_ng::Value::from(_ret) }
289 }
290 else if let Some(t) = tokens_match(
291 &orig_raw_rettype, quote!{ Vec< }, quote!{ > }
292 ) {
293 quote!{ molt_ng::Value::from_other(
294 molt_argparse::WrapVec<#t>(_ret)) }
295 }
296 else {
297 quote!{ molt_ng::Value::from_other(_ret) }
298 }
299 };
300 let retstmt = match orig_ret_is_option {
301 true => quote!{
302 match _ret {
303 Some(_ret) => Ok(#retstmt),
304 None => Err(molt_ng::Exception::molt_err(molt_ng::Value::empty()))
305 }
306 },
307 false => quote!{ Ok(#retstmt) }
308 };
309
310 let parser = quote!{
311 fn parser_function(_interp: &mut molt_ng::Interp, _ctxid: molt_ng::ContextID, _argv: &[molt_ng::Value]) -> molt_ng::MoltResult {
312 #contextdecl
313 #(#argdecls)*
314
315 let mut _molt_argparse_i: usize = 0;
316 let mut _molt_argparse_vals = _argv.iter();
317 let _ = _molt_argparse_vals.next().unwrap(); let mut _molt_f_parse_key =
319 |_molt_key: &str, _molt_argparse_vals: &mut std::slice::Iter<molt_ng::Value>| ->
320 std::result::Result<bool, molt_ng::Exception>
321 {
322 match _molt_key {
323 #(#argkeymatch,)*
324 _ => Ok(false)
325 }
326 };
327 while let Some(_molt_v) = _molt_argparse_vals.next() {
328 if let Some(_molt_v_str) = _molt_v.try_as_str() {
329 if _molt_f_parse_key(_molt_v_str, &mut _molt_argparse_vals)? {
330 continue;
331 }
332 }
333 match _molt_argparse_i {
334 #(#argfixmatch,)*
335 _ => { }
336 }
337 let s = _molt_v.as_str();
338 if !_molt_f_parse_key(s, &mut _molt_argparse_vals)? {
339 return molt_ng::molt_err!("argparse: stray {:?}", s);
340 }
341 }
342 #(#argpostprocess)*
343
344 let _ret = (|#(#orig_arg_defs),*| -> #orig_ret_type {
345 #(#orig_stmts)*
346 })(#(#orig_args),*);
347 #retstmt
348 }
349 };
350 let mut decl = decl;
353 let mut parser = syn::parse2::<syn::ItemFn>(parser).unwrap();
354
355 std::mem::swap(&mut decl.sig.inputs, &mut parser.sig.inputs);
356 std::mem::swap(&mut decl.sig.output, &mut parser.sig.output);
357 std::mem::swap(&mut decl.block, &mut parser.block);
358
359 let decl = decl.to_token_stream();
360 decl.into()
362}