match_string_macros/
lib.rs1use proc_macro::TokenStream;
2use quote::quote;
3use syn::parse::{Parse, ParseStream};
4use syn::{Expr, Ident, Token, parse_macro_input};
5
6struct MatchesInput {
7 reference: Expr,
8 _arrow: Token![=>],
9 pattern: PatternExpr,
10}
11
12struct PatternExpr {
13 kind: PatternKind,
14}
15
16enum PatternKind {
17 Lit(syn::Lit),
18 Ident(Ident),
19 Tuple(Vec<PatternExpr>),
20 Or(Vec<PatternExpr>),
21 Many(Box<PatternExpr>),
22 Some(Box<PatternExpr>),
23 Sep(Box<PatternExpr>, Box<PatternExpr>),
24 Sep1(Box<PatternExpr>, Box<PatternExpr>),
25 To(Ident, Box<PatternExpr>),
26}
27
28impl Parse for MatchesInput {
29 fn parse(input: ParseStream) -> syn::Result<Self> {
30 let reference: Expr = input.parse()?;
31 let _arrow: Token![=>] = input.parse()?;
32 let pattern = input.parse::<PatternExpr>()?;
33 Ok(MatchesInput {
34 reference,
35 _arrow,
36 pattern,
37 })
38 }
39}
40
41impl Parse for PatternExpr {
42 fn parse(input: ParseStream) -> syn::Result<Self> {
43 parse_seq_expr(input)
44 }
45}
46
47fn parse_seq_expr(input: ParseStream) -> syn::Result<PatternExpr> {
48 let mut terms = Vec::new();
49 loop {
50 terms.push(parse_or_expr(input)?);
51 if input.peek(Token![,]) {
52 input.parse::<Token![,]>()?;
53 } else {
54 break;
55 }
56 }
57 if terms.len() == 1 {
58 Ok(terms.into_iter().next().unwrap())
59 } else {
60 Ok(PatternExpr {
61 kind: PatternKind::Tuple(terms),
62 })
63 }
64}
65
66fn parse_or_expr(input: ParseStream) -> syn::Result<PatternExpr> {
67 let mut terms = Vec::new();
68 loop {
69 terms.push(parse_and_expr(input)?);
70 if input.peek(Token![/]) {
71 input.parse::<Token![/]>()?;
72 } else {
73 break;
74 }
75 }
76 if terms.len() == 1 {
77 Ok(terms.into_iter().next().unwrap())
78 } else {
79 Ok(PatternExpr {
80 kind: PatternKind::Or(terms),
81 })
82 }
83}
84
85fn parse_and_expr(input: ParseStream) -> syn::Result<PatternExpr> {
86 let expr = parse_term(input)?;
87 if input.peek(syn::token::Bracket) {
89 let content;
90 syn::bracketed!(content in input);
91 let sep = parse_or_expr(&content)?;
92 if input.peek(Token![+]) {
93 input.parse::<Token![+]>()?;
94 return Ok(PatternExpr {
95 kind: PatternKind::Sep(Box::new(expr), Box::new(sep)),
96 });
97 } else if input.peek(Token![*]) {
98 input.parse::<Token![*]>()?;
99 return Ok(PatternExpr {
100 kind: PatternKind::Sep1(Box::new(expr), Box::new(sep)),
101 });
102 } else {
103 return Err(syn::Error::new(
104 input.span(),
105 "expected `+` or `*` after bracketed separator",
106 ));
107 }
108 }
109
110 if input.peek(Token![+]) {
111 input.parse::<Token![+]>()?;
112 Ok(PatternExpr {
113 kind: PatternKind::Many(Box::new(expr)),
114 })
115 } else if input.peek(Token![*]) {
116 input.parse::<Token![*]>()?;
117 Ok(PatternExpr {
118 kind: PatternKind::Some(Box::new(expr)),
119 })
120 } else {
121 Ok(expr)
122 }
123}
124
125fn parse_term(input: ParseStream) -> syn::Result<PatternExpr> {
126 if input.peek(syn::token::Paren) {
127 let content;
128 syn::parenthesized!(content in input);
129 let inner = parse_seq_expr(&content)?;
131 return Ok(inner);
132 }
133
134 if input.peek(Ident) && input.peek2(Token![@]) {
135 let ident: Ident = input.parse()?;
136 input.parse::<Token![@]>()?;
137 let expr = parse_or_expr(input)?;
138 return Ok(PatternExpr {
139 kind: PatternKind::To(ident, Box::new(expr)),
140 });
141 }
142
143 if input.peek(syn::Lit) {
144 let lit: syn::Lit = input.parse()?;
145 return Ok(PatternExpr {
146 kind: PatternKind::Lit(lit),
147 });
148 }
149
150 if input.peek(Ident) {
151 let ident: Ident = input.parse()?;
152 return Ok(PatternExpr {
153 kind: PatternKind::Ident(ident),
154 });
155 }
156
157 Err(syn::Error::new(
158 input.span(),
159 "expected literal, identifier, grouped expression, or to",
160 ))
161}
162
163fn build_pattern_tokens(pattern: &PatternExpr) -> proc_macro2::TokenStream {
164 match &pattern.kind {
165 PatternKind::Lit(lit) => quote! { #lit },
166 PatternKind::Ident(ident) => quote! { #ident },
167 PatternKind::Or(exprs) => {
168 if exprs.is_empty() {
169 panic!("empty or");
170 } else if exprs.len() == 1 {
171 build_pattern_tokens(&exprs[0])
172 } else {
173 let mut tokens = build_pattern_tokens(&exprs[0]);
174 for expr in &exprs[1..] {
175 let inner = build_pattern_tokens(expr);
176 tokens = quote! { Or(#tokens, #inner) };
177 }
178 tokens
179 }
180 }
181 PatternKind::Tuple(exprs) => {
182 if exprs.is_empty() {
183 quote! { () }
184 } else if exprs.len() == 1 {
185 build_pattern_tokens(&exprs[0])
186 } else if exprs.len() == 2 {
187 let inner = exprs.iter().map(build_pattern_tokens);
188 quote! { (#(#inner),*) }
189 } else {
190 let mut tokens = build_pattern_tokens(&exprs[0]);
192 for expr in &exprs[1..] {
193 let inner = build_pattern_tokens(expr);
194 tokens = quote! { (#tokens, #inner) };
195 }
196 tokens
197 }
198 }
199 PatternKind::Many(expr) => {
200 let inner = build_pattern_tokens(expr);
201 quote! { RangeToInclusive { end: #inner } }
202 }
203 PatternKind::Some(expr) => {
204 let inner = build_pattern_tokens(expr);
205 quote! { RangeTo { end: #inner } }
206 }
207 PatternKind::Sep(elem, sep) => {
208 let e = build_pattern_tokens(elem);
209 let s = build_pattern_tokens(sep);
210 quote! { Sep(#s, #e) }
211 }
212 PatternKind::Sep1(elem, sep) => {
213 let e = build_pattern_tokens(elem);
214 let s = build_pattern_tokens(sep);
215 quote! { Sep1(#s, #e) }
216 }
217 PatternKind::To(ident, expr) => {
218 let inner = build_pattern_tokens(expr);
219 quote! { To(#inner, &#ident) }
220 }
221 }
222}
223
224#[proc_macro]
225pub fn matches(item: TokenStream) -> TokenStream {
226 let input = parse_macro_input!(item as MatchesInput);
227
228 let pattern_tokens = build_pattern_tokens(&input.pattern);
229
230 let reference = input.reference;
231
232 let output = quote!({
233 let __pattern = #pattern_tokens;
234 crate::__matches(&__pattern, & #reference)
235 });
236
237 output.into()
238}