1extern crate proc_macro;
6
7use proc_macro::TokenStream;
8
9fn get_byte_from_lit(lit: &syn::Lit) -> u8 {
10 if let syn::Lit::Byte(ref byte) = *lit {
11 byte.value()
12 } else {
13 panic!("Found a pattern that wasn't a byte")
14 }
15}
16
17fn get_byte_from_expr_lit(expr: &syn::Expr) -> u8 {
18 match *expr {
19 syn::Expr::Lit(syn::ExprLit { ref lit, .. }) => get_byte_from_lit(lit),
20 _ => unreachable!(),
21 }
22}
23
24fn parse_pat_to_table<'a>(
26 pat: &'a syn::Pat,
27 case_id: u8,
28 wildcard: &mut Option<&'a syn::Ident>,
29 table: &mut [Option<u8>; 256],
30) {
31 match pat {
32 syn::Pat::Lit(syn::PatLit { ref lit, .. }) => {
33 let value = get_byte_from_lit(lit);
34 table[value as usize].get_or_insert(case_id);
35 }
36 syn::Pat::Range(syn::PatRange {
37 ref start, ref end, ..
38 }) => {
39 let lo = get_byte_from_expr_lit(start.as_ref().unwrap());
40 let hi = get_byte_from_expr_lit(end.as_ref().unwrap());
41 for value in lo..hi {
42 table[value as usize].get_or_insert(case_id);
43 }
44 table[hi as usize].get_or_insert(case_id);
45 }
46 syn::Pat::Wild(_) => {
47 for byte in table.iter_mut() {
48 byte.get_or_insert(case_id);
49 }
50 }
51 syn::Pat::Ident(syn::PatIdent { ref ident, .. }) => {
52 assert_eq!(*wildcard, None);
53 *wildcard = Some(ident);
54 for byte in table.iter_mut() {
55 byte.get_or_insert(case_id);
56 }
57 }
58 syn::Pat::Or(syn::PatOr { ref cases, .. }) => {
59 for case in cases {
60 parse_pat_to_table(case, case_id, wildcard, table);
61 }
62 }
63 _ => {
64 panic!("Unexpected or unsupported pattern: {:?}. Buggy code ?", pat);
65 }
66 }
67}
68
69#[proc_macro]
83pub fn match_byte(input: TokenStream) -> TokenStream {
84 use syn::spanned::Spanned;
85 struct MatchByte {
86 expr: syn::Expr,
87 arms: Vec<syn::Arm>,
88 }
89
90 impl syn::parse::Parse for MatchByte {
91 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
92 Ok(MatchByte {
93 expr: {
94 let expr = input.parse()?;
95 input.parse::<syn::Token![,]>()?;
96 expr
97 },
98 arms: {
99 let mut arms = Vec::new();
100 while !input.is_empty() {
101 let arm = input.call(syn::Arm::parse)?;
102 assert!(
103 arm.attrs.is_empty(),
104 "match_byte doesn't support attributes"
105 );
106 arms.push(arm);
107 }
108 arms
109 },
110 })
111 }
112 }
113 let MatchByte { expr, arms } = syn::parse_macro_input!(input);
114
115 let mut cases = Vec::new();
116 let mut table = [None; 256];
117 let mut match_body = Vec::new();
118 let mut wildcard = None;
119 for (i, ref arm) in arms.iter().enumerate() {
120 let case_id = i as isize;
121 let name = syn::Ident::new(&format!("Case{case_id}"), arm.span());
122 let pat = &arm.pat;
123 parse_pat_to_table(pat, i as u8, &mut wildcard, &mut table);
124
125 cases.push(quote::quote!(#name = #case_id));
126 let body = &arm.body;
127 match_body.push(quote::quote!(Case::#name => { #body }))
128 }
129
130 let en = quote::quote!(enum Case {
131 #(#cases),*
132 });
133
134 let mut table_content = Vec::new();
135 for (byte, entry) in table.iter().enumerate() {
136 let case_id = match entry {
137 Some(id) => id,
138 None => panic!("Uncovered byte {:?} (add a wildcard pattern?)", byte),
139 };
140 let name: syn::Path = syn::parse_str(&format!("Case::Case{case_id}")).unwrap();
141 table_content.push(name);
142 }
143 let table = quote::quote!(static __CASES: [Case; 256] = [#(#table_content),*];);
144
145 if let Some(binding) = wildcard {
146 quote::quote!({ #en #table let #binding = #expr; match __CASES[#binding as usize] { #(#match_body),* }})
147 } else {
148 quote::quote!({ #en #table match __CASES[#expr as usize] { #(#match_body),* }})
149 }.into()
150}