2017-02-26 15:10:07 +03:00
|
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
|
|
|
|
extern crate proc_macro;
|
|
|
|
|
2019-12-04 22:54:06 +03:00
|
|
|
use proc_macro::TokenStream;
|
2017-02-26 15:10:07 +03:00
|
|
|
|
2019-12-04 22:54:06 +03:00
|
|
|
#[proc_macro]
|
|
|
|
pub fn _cssparser_internal_max_len(input: TokenStream) -> TokenStream {
|
|
|
|
struct Input {
|
|
|
|
max_length: usize,
|
2017-02-26 15:10:07 +03:00
|
|
|
}
|
|
|
|
|
2019-12-04 22:54:06 +03:00
|
|
|
impl syn::parse::Parse for Input {
|
|
|
|
fn parse(input: syn::parse::ParseStream) -> syn::parse::Result<Self> {
|
|
|
|
let mut max_length = 0;
|
|
|
|
while !input.is_empty() {
|
|
|
|
if input.peek(syn::Token![_]) {
|
|
|
|
input.parse::<syn::Token![_]>().unwrap();
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
let lit: syn::LitStr = input.parse()?;
|
|
|
|
let value = lit.value();
|
|
|
|
if value.to_ascii_lowercase() != value {
|
|
|
|
return Err(syn::Error::new(lit.span(), "must be ASCII-lowercase"));
|
|
|
|
}
|
|
|
|
max_length = max_length.max(value.len());
|
|
|
|
}
|
|
|
|
Ok(Input { max_length })
|
2017-02-26 15:10:07 +03:00
|
|
|
}
|
2017-02-28 20:22:51 +03:00
|
|
|
}
|
2017-02-26 15:10:07 +03:00
|
|
|
|
2019-12-04 22:54:06 +03:00
|
|
|
let Input { max_length } = syn::parse_macro_input!(input);
|
|
|
|
quote::quote!(
|
|
|
|
pub(super) const MAX_LENGTH: usize = #max_length;
|
|
|
|
)
|
|
|
|
.into()
|
2017-02-26 15:10:07 +03:00
|
|
|
}
|
2023-05-16 21:02:52 +03:00
|
|
|
|
2023-06-14 23:50:14 +03:00
|
|
|
fn get_byte_from_lit(lit: &syn::Lit) -> u8 {
|
|
|
|
if let syn::Lit::Byte(ref byte) = *lit {
|
|
|
|
byte.value()
|
|
|
|
} else {
|
|
|
|
panic!("Found a pattern that wasn't a byte")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-16 21:02:52 +03:00
|
|
|
fn get_byte_from_expr_lit(expr: &syn::Expr) -> u8 {
|
|
|
|
match *expr {
|
|
|
|
syn::Expr::Lit(syn::ExprLit { ref lit, .. }) => {
|
2023-06-14 23:50:14 +03:00
|
|
|
get_byte_from_lit(lit)
|
2023-05-16 21:02:52 +03:00
|
|
|
}
|
|
|
|
_ => unreachable!(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Parse a pattern and fill the table accordingly
|
|
|
|
fn parse_pat_to_table<'a>(
|
|
|
|
pat: &'a syn::Pat,
|
|
|
|
case_id: u8,
|
|
|
|
wildcard: &mut Option<&'a syn::Ident>,
|
|
|
|
table: &mut [u8; 256],
|
|
|
|
) {
|
|
|
|
match pat {
|
2023-06-14 23:50:14 +03:00
|
|
|
&syn::Pat::Lit(syn::PatLit { ref lit, .. }) => {
|
|
|
|
let value = get_byte_from_lit(lit);
|
2023-05-16 21:02:52 +03:00
|
|
|
if table[value as usize] == 0 {
|
|
|
|
table[value as usize] = case_id;
|
|
|
|
}
|
|
|
|
}
|
2023-06-14 23:50:14 +03:00
|
|
|
&syn::Pat::Range(syn::PatRange { ref start, ref end, .. }) => {
|
|
|
|
let lo = get_byte_from_expr_lit(&start.as_ref().unwrap());
|
|
|
|
let hi = get_byte_from_expr_lit(&end.as_ref().unwrap());
|
2023-05-16 21:02:52 +03:00
|
|
|
for value in lo..hi {
|
|
|
|
if table[value as usize] == 0 {
|
|
|
|
table[value as usize] = case_id;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if table[hi as usize] == 0 {
|
|
|
|
table[hi as usize] = case_id;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
&syn::Pat::Wild(_) => {
|
|
|
|
for byte in table.iter_mut() {
|
|
|
|
if *byte == 0 {
|
|
|
|
*byte = case_id;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
&syn::Pat::Ident(syn::PatIdent { ref ident, .. }) => {
|
|
|
|
assert_eq!(*wildcard, None);
|
|
|
|
*wildcard = Some(ident);
|
|
|
|
for byte in table.iter_mut() {
|
|
|
|
if *byte == 0 {
|
|
|
|
*byte = case_id;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
&syn::Pat::Or(syn::PatOr { ref cases, .. }) => {
|
|
|
|
for case in cases {
|
|
|
|
parse_pat_to_table(case, case_id, wildcard, table);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
panic!("Unexpected pattern: {:?}. Buggy code ?", pat);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Expand a TokenStream corresponding to the `match_byte` macro.
|
|
|
|
///
|
|
|
|
/// ## Example
|
|
|
|
///
|
|
|
|
/// ```rust
|
|
|
|
/// match_byte! { tokenizer.next_byte_unchecked(),
|
|
|
|
/// b'a'..b'z' => { ... }
|
|
|
|
/// b'0'..b'9' => { ... }
|
|
|
|
/// b'\n' | b'\\' => { ... }
|
|
|
|
/// foo => { ... }
|
|
|
|
/// }
|
|
|
|
/// ```
|
|
|
|
///
|
|
|
|
#[proc_macro]
|
|
|
|
pub fn match_byte(input: TokenStream) -> TokenStream {
|
|
|
|
use syn::spanned::Spanned;
|
|
|
|
struct MatchByte {
|
|
|
|
expr: syn::Expr,
|
|
|
|
arms: Vec<syn::Arm>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl syn::parse::Parse for MatchByte {
|
|
|
|
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
|
|
|
Ok(MatchByte {
|
|
|
|
expr: {
|
|
|
|
let expr = input.parse()?;
|
|
|
|
input.parse::<syn::Token![,]>()?;
|
|
|
|
expr
|
|
|
|
},
|
|
|
|
arms: {
|
|
|
|
let mut arms = Vec::new();
|
|
|
|
while !input.is_empty() {
|
|
|
|
let arm = input.call(syn::Arm::parse)?;
|
|
|
|
assert!(arm.guard.is_none(), "match_byte doesn't support guards");
|
|
|
|
assert!(
|
|
|
|
arm.attrs.is_empty(),
|
|
|
|
"match_byte doesn't support attributes"
|
|
|
|
);
|
|
|
|
arms.push(arm);
|
|
|
|
}
|
|
|
|
arms
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
let MatchByte { expr, arms } = syn::parse_macro_input!(input);
|
|
|
|
|
|
|
|
let mut cases = Vec::new();
|
|
|
|
let mut table = [0u8; 256];
|
|
|
|
let mut match_body = Vec::new();
|
|
|
|
let mut wildcard = None;
|
|
|
|
for (i, ref arm) in arms.iter().enumerate() {
|
|
|
|
let case_id = i + 1;
|
|
|
|
let index = case_id as isize;
|
|
|
|
let name = syn::Ident::new(&format!("Case{}", case_id), arm.span());
|
|
|
|
let pat = &arm.pat;
|
|
|
|
parse_pat_to_table(pat, case_id as u8, &mut wildcard, &mut table);
|
|
|
|
|
|
|
|
cases.push(quote::quote!(#name = #index));
|
|
|
|
let body = &arm.body;
|
|
|
|
match_body.push(quote::quote!(Case::#name => { #body }))
|
|
|
|
}
|
|
|
|
|
|
|
|
let en = quote::quote!(enum Case {
|
|
|
|
#(#cases),*
|
|
|
|
});
|
|
|
|
|
|
|
|
let mut table_content = Vec::new();
|
|
|
|
for entry in table.iter() {
|
|
|
|
let name: syn::Path = syn::parse_str(&format!("Case::Case{}", entry)).unwrap();
|
|
|
|
table_content.push(name);
|
|
|
|
}
|
|
|
|
let table = quote::quote!(static __CASES: [Case; 256] = [#(#table_content),*];);
|
|
|
|
|
|
|
|
if let Some(binding) = wildcard {
|
|
|
|
quote::quote!({ #en #table let #binding = #expr; match __CASES[#binding as usize] { #(#match_body),* }})
|
|
|
|
} else {
|
|
|
|
quote::quote!({ #en #table match __CASES[#expr as usize] { #(#match_body),* }})
|
|
|
|
}.into()
|
|
|
|
}
|