1#![recursion_limit = "128"]
2
3extern crate proc_macro;
4
5use crate::proc_macro::TokenStream;
6use quote::{quote, ToTokens};
7use std::str::FromStr;
8use syn::{self, parse_macro_input, parse_quote, AttributeArgs, FnArg, ItemFn, Stmt};
9
10#[proc_macro_attribute]
11pub fn both_parser(attr: TokenStream, item: TokenStream) -> TokenStream {
12 let attr = parse_macro_input!(attr as AttributeArgs);
13 let item = parse_macro_input!(item as ItemFn);
14 impl_both_parser(&attr, &item)
15}
16
17fn impl_both_parser(_attr: &AttributeArgs, item: &ItemFn) -> TokenStream {
18 let body = impl_both_parser_body(&item);
19 let body = parse_macro_input!(body as Stmt);
20
21 let mut item = item.clone();
22
23 item.block.stmts.clear();
24 item.block.stmts.push(body);
25
26 item.into_token_stream().into()
27}
28
29fn impl_both_parser_body(item: &ItemFn) -> TokenStream {
30 let input = if let Some(x) = &item.decl.inputs.first() {
31 match x.value() {
32 FnArg::Captured(arg) => &arg.pat,
33 _ => panic!("function with #[both_parser] must have an argument"),
34 }
35 } else {
36 panic!("function with #[both_parser] must have an argument");
37 };
38
39 let body = item.block.as_ref();
40 let mut body_token = body.into_token_stream().to_string();
41
42 let both_cnt: Vec<&str> = body_token.matches("both").collect();
43 let both_cnt = both_cnt.len();
44
45 let mut replace_parsers = Vec::new();
46 for i in 0..both_cnt {
47 let pos = body_token.find("both").unwrap();
48 let (head, rest) = body_token.split_at(pos);
49 if rest.starts_with("both_opt") {
50 let rest = rest.replacen("both_opt", &format!("b_temporary{}", i), 1);
51 body_token = format!("{}{}", head, rest);
52 replace_parsers.push(("nom_both::some", "nom_both::none"));
53 } else if rest.starts_with("both_alt") {
54 let rest = rest.replacen("both_alt", &format!("b_temporary{}", i), 1);
55 body_token = format!("{}{}", head, rest);
56 replace_parsers.push(("nom_both::alt_left", "nom_both::alt_right"));
57 }
58 }
59
60 let mut gen = quote! {};
61 for i in 0..2_u32.pow(both_cnt as u32) {
62 let mut body_token = body_token.clone();
63 for j in 0..both_cnt {
64 let (p0, p1) = replace_parsers[j];
65 let repl = if ((i >> j) & 1) == 0 { p0 } else { p1 };
66 body_token = body_token.replace(&format!("b_temporary{}", j), repl);
67 }
68 let body_token = format!("{{ {} }}", body_token);
69 let body_token = TokenStream::from_str(&body_token).unwrap();
70 let body_token = parse_macro_input!(body_token as Stmt);
71 gen = quote! {
72 #gen
73 |#input| #body_token,
74 };
75 }
76
77 let gen = quote! {
78 {
79 alt((
80 #gen
81 ))(s)
82 }
83 };
84 gen.into()
85}