1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
use ahash::AHashMap;
#[cfg(debug_assertions)]
use ahash::AHashSet;
use quote::quote;
use syn::{spanned::Spanned, Error, Expr, ExprArray, ExprTuple, Lit};
pub(super) fn alphabet_match_inner(input: ExprArray) -> syn::Result<proc_macro2::TokenStream> {
let mut value_to_keys = AHashMap::new();
let mut keys_all = Vec::new();
let mut unknown_lang = None;
// Parse the input array of tuples
for tuple in input.elems {
match tuple {
Expr::Tuple(ExprTuple { elems, .. }) => {
// We assume the key can be any expression, so we just clone it
let key = &elems[0];
// if !strict (or skips other chars for that key)
if elems.get(2).is_none() {
keys_all.push(key.clone());
}
// Handle the array of values associated with the key
match &elems[1] {
Expr::Array(ExprArray { elems: values, .. }) => {
#[cfg(debug_assertions)]
let mut values_set: AHashSet<char> = AHashSet::new();
for val in values {
let value = match val {
Expr::Lit(expr_lit) => match &expr_lit.lit {
Lit::Char(lit_char) => lit_char.value(),
v => {
return Err(Error::new(
v.span(),
"Expected char literal for value",
))
}
},
v => {
return Err(Error::new(
v.span(),
"Expected char literal for value",
))
}
};
#[cfg(debug_assertions)]
if let Expr::Path(ep) = key {
let ki = &ep.path.segments[1].ident;
if !values_set.insert(value) {
return Err(Error::new(
val.span(),
format!(
"Char literal: {value} was already added for key: {ki}",
),
));
}
}
value_to_keys
.entry(value)
.or_insert_with(Vec::new)
.push(key.clone()); // Clone the key to store it in the map
}
}
v => {
return Err(Error::new(v.span(), "Expected an array of values"));
}
}
}
Expr::Path(p) => {
unknown_lang = Some(p);
}
v => {
return Err(Error::new(v.span(), "Expected a tuple"));
}
}
}
// Generate match arms
let arms = value_to_keys
.iter()
.filter(|(_, k)| k.len() < keys_all.len())
.map(|(value, keys)| {
quote! {
#value => &[#(#keys),*],
}
});
let other = if let Some(l) = unknown_lang {
let chars_other = value_to_keys
.iter()
.filter(|(_, k)| k.len() >= keys_all.len())
.map(|(&c, _)| c)
.collect::<Vec<_>>();
let others = if chars_other.is_empty() {
quote! {}
} else {
quote! { #(#chars_other)|* => &[#(#keys_all),*], }
};
quote! {
#others
_ => &[#l]
}
} else {
quote! { _ => &[#(#keys_all),*] }
};
let chars = value_to_keys.keys().map(|c| quote! { #c });
// Generate the entire match block
let expanded = quote! {{
#[cfg(all(debug_assertions, feature = "test_chars"))]
{
let chars = [#(#chars),*];
test_chars(script, &chars);
}
match ch {
#(#arms)*
#other
}
}};
Ok(expanded)
}