nom_both_macros/
lib.rs

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}