1extern crate proc_macro;
6
7use proc_macro::TokenStream;
8
9#[proc_macro]
10pub fn _cssparser_internal_max_len(input: TokenStream) -> TokenStream {
11 struct Input {
12 max_length: usize,
13 }
14
15 impl syn::parse::Parse for Input {
16 fn parse(input: syn::parse::ParseStream) -> syn::parse::Result<Self> {
17 let mut max_length = 0;
18 while !input.is_empty() {
19 if input.peek(syn::Token![_]) {
20 input.parse::<syn::Token![_]>().unwrap();
21 continue;
22 }
23 let lit: syn::LitStr = input.parse()?;
24 let value = lit.value();
25 if value.to_ascii_lowercase() != value {
26 return Err(syn::Error::new(lit.span(), "must be ASCII-lowercase"));
27 }
28 max_length = max_length.max(value.len());
29 }
30 Ok(Input { max_length })
31 }
32 }
33
34 let Input { max_length } = syn::parse_macro_input!(input);
35 quote::quote!(
36 pub(super) const MAX_LENGTH: usize = #max_length;
37 )
38 .into()
39}
40
41fn get_byte_from_lit(lit: &syn::Lit) -> u8 {
42 if let syn::Lit::Byte(ref byte) = *lit {
43 byte.value()
44 } else {
45 panic!("Found a pattern that wasn't a byte")
46 }
47}
48
49fn get_byte_from_expr_lit(expr: &syn::Expr) -> u8 {
50 match *expr {
51 syn::Expr::Lit(syn::ExprLit { ref lit, .. }) => {
52 get_byte_from_lit(lit)
53 }
54 _ => unreachable!(),
55 }
56}
57
58fn parse_pat_to_table<'a>(
60 pat: &'a syn::Pat,
61 case_id: u8,
62 wildcard: &mut Option<&'a syn::Ident>,
63 table: &mut [u8; 256],
64) {
65 match pat {
66 &syn::Pat::Lit(syn::PatLit { ref lit, .. }) => {
67 let value = get_byte_from_lit(lit);
68 if table[value as usize] == 0 {
69 table[value as usize] = case_id;
70 }
71 }
72 &syn::Pat::Range(syn::PatRange { ref start, ref end, .. }) => {
73 let lo = get_byte_from_expr_lit(&start.as_ref().unwrap());
74 let hi = get_byte_from_expr_lit(&end.as_ref().unwrap());
75 for value in lo..hi {
76 if table[value as usize] == 0 {
77 table[value as usize] = case_id;
78 }
79 }
80 if table[hi as usize] == 0 {
81 table[hi as usize] = case_id;
82 }
83 }
84 &syn::Pat::Wild(_) => {
85 for byte in table.iter_mut() {
86 if *byte == 0 {
87 *byte = case_id;
88 }
89 }
90 }
91 &syn::Pat::Ident(syn::PatIdent { ref ident, .. }) => {
92 assert_eq!(*wildcard, None);
93 *wildcard = Some(ident);
94 for byte in table.iter_mut() {
95 if *byte == 0 {
96 *byte = case_id;
97 }
98 }
99 }
100 &syn::Pat::Or(syn::PatOr { ref cases, .. }) => {
101 for case in cases {
102 parse_pat_to_table(case, case_id, wildcard, table);
103 }
104 }
105 _ => {
106 panic!("Unexpected pattern: {:?}. Buggy code ?", pat);
107 }
108 }
109}
110
111#[proc_macro]
125pub fn match_byte(input: TokenStream) -> TokenStream {
126 use syn::spanned::Spanned;
127 struct MatchByte {
128 expr: syn::Expr,
129 arms: Vec<syn::Arm>,
130 }
131
132 impl syn::parse::Parse for MatchByte {
133 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
134 Ok(MatchByte {
135 expr: {
136 let expr = input.parse()?;
137 input.parse::<syn::Token![,]>()?;
138 expr
139 },
140 arms: {
141 let mut arms = Vec::new();
142 while !input.is_empty() {
143 let arm = input.call(syn::Arm::parse)?;
144 assert!(arm.guard.is_none(), "match_byte doesn't support guards");
145 assert!(
146 arm.attrs.is_empty(),
147 "match_byte doesn't support attributes"
148 );
149 arms.push(arm);
150 }
151 arms
152 },
153 })
154 }
155 }
156 let MatchByte { expr, arms } = syn::parse_macro_input!(input);
157
158 let mut cases = Vec::new();
159 let mut table = [0u8; 256];
160 let mut match_body = Vec::new();
161 let mut wildcard = None;
162 for (i, ref arm) in arms.iter().enumerate() {
163 let case_id = i + 1;
164 let index = case_id as isize;
165 let name = syn::Ident::new(&format!("Case{}", case_id), arm.span());
166 let pat = &arm.pat;
167 parse_pat_to_table(pat, case_id as u8, &mut wildcard, &mut table);
168
169 cases.push(quote::quote!(#name = #index));
170 let body = &arm.body;
171 match_body.push(quote::quote!(Case::#name => { #body }))
172 }
173
174 let en = quote::quote!(enum Case {
175 #(#cases),*
176 });
177
178 let mut table_content = Vec::new();
179 for entry in table.iter() {
180 let name: syn::Path = syn::parse_str(&format!("Case::Case{}", entry)).unwrap();
181 table_content.push(name);
182 }
183 let table = quote::quote!(static __CASES: [Case; 256] = [#(#table_content),*];);
184
185 if let Some(binding) = wildcard {
186 quote::quote!({ #en #table let #binding = #expr; match __CASES[#binding as usize] { #(#match_body),* }})
187 } else {
188 quote::quote!({ #en #table match __CASES[#expr as usize] { #(#match_body),* }})
189 }.into()
190}