gregex_macros/
lib.rs

1#[doc = include_str!("../README.md")]
2#[cfg(not(doctest))]
3extern crate proc_macro;
4
5use proc_macro::TokenStream;
6use quote::quote;
7use syn::{parse_macro_input, Expr, ExprLit, ExprMacro, Lit};
8
9#[proc_macro]
10pub fn dot(input: TokenStream) -> TokenStream {
11    let inputs = parse_macro_input!(input with syn::punctuated::Punctuated::<Expr, syn::Token![,]>::parse_terminated);
12
13    let nodes = inputs.iter().map(|expr| {
14        match expr {
15            Expr::Macro(ExprMacro { mac, .. }) => {
16                // Handle procedural macro
17                quote! { #mac }
18            }
19            Expr::Lit(ExprLit { lit, .. }) => match lit {
20                Lit::Char(c) => {
21                    let count = gregex_logic::TERMINAL_COUNT
22                        .fetch_add(1, core::sync::atomic::Ordering::SeqCst);
23                    quote! {
24                        gregex_logic::translation::node::Node::Terminal(#c, #count)
25                    }
26                }
27                _ => panic!("Unsupported literal type"),
28            },
29            _ => panic!("Unsupported input type"),
30        }
31    });
32
33    // Generate the code for concatenating nodes
34    let mut iter = nodes.into_iter();
35    let first = iter.next().expect("The input is empty");
36    let operations = iter.fold(first, |left, right| {
37        quote! {
38            gregex_logic::translation::node::Node::Operation(
39                gregex_logic::translation::operator::Operator::Concat,
40                Box::new(#left),
41                Some(Box::new(#right))
42            )
43        }
44    });
45
46    // Generate the final token stream
47    let gen = quote! {
48        #operations
49    };
50
51    gen.into()
52}
53
54#[proc_macro]
55pub fn or(input: TokenStream) -> TokenStream {
56    let inputs = parse_macro_input!(input with syn::punctuated::Punctuated::<Expr, syn::Token![,]>::parse_terminated);
57
58    let nodes = inputs.iter().map(|expr| {
59        match expr {
60            Expr::Macro(ExprMacro { mac, .. }) => {
61                // Handle procedural macro
62                quote! { #mac }
63            }
64            Expr::Lit(ExprLit { lit, .. }) => match lit {
65                Lit::Char(c) => {
66                    let count = gregex_logic::TERMINAL_COUNT
67                        .fetch_add(1, core::sync::atomic::Ordering::SeqCst);
68                    quote! {
69                        gregex_logic::translation::node::Node::Terminal(#c, #count)
70                    }
71                }
72                _ => panic!("Unsupported literal type"),
73            },
74            _ => panic!("Unsupported input type"),
75        }
76    });
77
78    // Generate the code for concatenating nodes
79    let mut iter = nodes.into_iter();
80    let first = iter.next().expect("The input is empty");
81    let operations = iter.fold(first, |left, right| {
82        quote! {
83            gregex_logic::translation::node::Node::Operation(
84                gregex_logic::translation::operator::Operator::Or,
85                Box::new(#left),
86                Some(Box::new(#right))
87            )
88        }
89    });
90
91    // Generate the final token stream
92    let gen = quote! {
93        #operations
94    };
95
96    gen.into()
97}
98
99#[proc_macro]
100pub fn star(input: TokenStream) -> TokenStream {
101    let expr = parse_macro_input!(input as Expr);
102
103    let node = match expr {
104        Expr::Macro(ExprMacro { mac, .. }) => {
105            // Handle procedural macro
106            quote! { #mac }
107        }
108        Expr::Lit(ExprLit { lit, .. }) => match lit {
109            Lit::Char(c) => {
110                let count =
111                    gregex_logic::TERMINAL_COUNT.fetch_add(1, core::sync::atomic::Ordering::SeqCst);
112                quote! {
113                    gregex_logic::translation::node::Node::Terminal(#c, #count)
114                }
115            }
116            _ => panic!("Unsupported literal type"),
117        },
118        _ => panic!("Unsupported input type"),
119    };
120
121    // Generate the code for the star operation
122    let operation = quote! {
123        gregex_logic::translation::node::Node::Operation(
124            gregex_logic::translation::operator::Operator::Production,
125            Box::new(#node),
126            None
127        )
128    };
129
130    // Generate the final token stream
131    let gen = quote! {
132        #operation
133    };
134
135    gen.into()
136}
137
138#[proc_macro]
139pub fn regex(input: TokenStream) -> TokenStream {
140    let expr = parse_macro_input!(input as Expr);
141
142    // Convert the input expression into a Node structure
143    let node = match expr {
144        Expr::Macro(ExprMacro { mac, .. }) => {
145            // Handle procedural macro
146            quote! { #mac }
147        }
148        Expr::Lit(ExprLit { lit, .. }) => match lit {
149            Lit::Char(c) => {
150                let count =
151                    gregex_logic::TERMINAL_COUNT.fetch_add(1, core::sync::atomic::Ordering::SeqCst);
152                quote! {
153                    gregex_logic::translation::node::Node::Terminal(#c, #count)
154                }
155            }
156            _ => panic!("Unsupported literal type"),
157        },
158        _ => panic!("Unsupported input type"),
159    };
160
161    // Generate the code to convert the Node into a Regex
162    let gen = quote! {
163        {
164            let regex_tree = #node;
165            let prefix_set = gregex_logic::translation::node::prefix_set(&regex_tree);
166            let suffix_set = gregex_logic::translation::node::suffix_set(&regex_tree);
167            let factors_set = gregex_logic::translation::node::factors_set(&regex_tree);
168            gregex_logic::nfa::NFA::set_to_nfa(&prefix_set, &suffix_set, &factors_set)
169        }
170    };
171
172    gen.into()
173}