summaryrefslogtreecommitdiffstats
path: root/third_party/rust/cssparser-macros/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/cssparser-macros/lib.rs')
-rw-r--r--third_party/rust/cssparser-macros/lib.rs186
1 files changed, 186 insertions, 0 deletions
diff --git a/third_party/rust/cssparser-macros/lib.rs b/third_party/rust/cssparser-macros/lib.rs
new file mode 100644
index 0000000000..64d749f04b
--- /dev/null
+++ b/third_party/rust/cssparser-macros/lib.rs
@@ -0,0 +1,186 @@
+/* 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;
+
+use proc_macro::TokenStream;
+
+#[proc_macro]
+pub fn _cssparser_internal_max_len(input: TokenStream) -> TokenStream {
+ struct Input {
+ max_length: usize,
+ }
+
+ 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 })
+ }
+ }
+
+ let Input { max_length } = syn::parse_macro_input!(input);
+ quote::quote!(
+ pub(super) const MAX_LENGTH: usize = #max_length;
+ )
+ .into()
+}
+
+fn get_byte_from_expr_lit(expr: &syn::Expr) -> u8 {
+ match *expr {
+ syn::Expr::Lit(syn::ExprLit { ref lit, .. }) => {
+ if let syn::Lit::Byte(ref byte) = *lit {
+ byte.value()
+ } else {
+ panic!("Found a pattern that wasn't a byte")
+ }
+ }
+ _ => 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 {
+ &syn::Pat::Lit(syn::PatLit { ref expr, .. }) => {
+ let value = get_byte_from_expr_lit(expr);
+ if table[value as usize] == 0 {
+ table[value as usize] = case_id;
+ }
+ }
+ &syn::Pat::Range(syn::PatRange { ref lo, ref hi, .. }) => {
+ let lo = get_byte_from_expr_lit(lo);
+ let hi = get_byte_from_expr_lit(hi);
+ 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()
+}