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 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
//! Provides the alphabet!() macro. It can be used to create const alphabets easily. //! //! Usually you would have to write alphabets in a cumbersome way: //! ``` //! const HEX: [char; 16] = ['0', '1', '2', '3', //! '4', '5', '6', '7', //! '8', '9', 'a', 'b', //! 'c', 'd', 'e', 'f']; //! //! assert_eq!(HEX.len(), 16); //! assert_eq!(HEX[5], '5'); //! assert_eq!(HEX[10], 'a'); //! ``` //! //! But with the alphabet!() macro this can be done way easier. //! ``` //! use alphabet_macro::alphabet; //! //! alphabet!(HEX = "0123456789abcdef"); //! //! assert_eq!(HEX.len(), 16); //! assert_eq!(HEX[5], '5'); //! assert_eq!(HEX[10], 'a'); //! ``` //! //! The alphabet!() macro expands to the snippet above, while being easier to read, write and understand. use proc_macro::TokenStream; use quote::quote; use syn::{parse, self}; struct Alphabet { meta: Option<Vec<syn::Attribute>>, vis: syn::Visibility, ident: syn::Ident, contents: String } /// Declares a constant alphabet. You can choose any valid identifier as the name for the alphabet. /// /// # Examples /// /// Basic usage: /// ``` /// use alphabet_macro::alphabet; /// /// alphabet!(BINARY = "01"); /// alphabet!(ENGLISH = "abcdefghijklmnopqrstuvwxyz"); /// alphabet!(GERMAN = "aäbcdefghijklmnoöpqrstuüvwxyzß"); /// alphabet!(HEBREW = "אבגדהוזחטיכלמנסעפצקרשת"); /// /// assert_eq!(BINARY.len(), 2); /// assert_eq!(ENGLISH.len(), 26); /// assert_eq!(GERMAN.len(), 30); /// assert_eq!(HEBREW.len(), 22); /// ``` /// /// You can also specify a visibility for the generated alphabet: /// ``` /// use alphabet_macro::alphabet; /// /// alphabet!(pub BINARY = "01"); /// ``` /// /// You can pass attributes to the generated alphabet as well. This includes doc comments, which internally use attributes: /// ``` /// use alphabet_macro::alphabet; /// /// alphabet! { /// /// An alphabet for binary strings. /// pub BINARY = "01"; /// } /// ``` /// /// # Syntax /// /// Alphabets follow the following syntax, with the nonterminal `alphabet` being the contents of the alphabet! macro: /// ```bnf /// alphabet ::= attribute* visibility? identifier "=" literal-string ";"? /// ``` #[proc_macro] pub fn alphabet(input: TokenStream) -> TokenStream { let Alphabet { meta, vis, ident, contents } = syn::parse_macro_input!(input as Alphabet); let chars: Vec<char> = contents.chars().collect(); let alphabet_len = chars.len(); TokenStream::from(match meta { None => quote! { #vis const #ident: [char; #alphabet_len] = [#(#chars),*]; }, Some(meta) => quote! { #(#meta)* #vis const #ident: [char; #alphabet_len] = [#(#chars),*]; } }) } impl syn::parse::Parse for Alphabet { fn parse(buf: parse::ParseStream) -> syn::Result<Self> { let meta; if buf.peek(syn::Token![#]) { // #foo[bar] // ^^^^^^^^^ meta = Some(buf.call(syn::Attribute::parse_outer)?); } else { meta = None; } let vis; if buf.peek(syn::Token![pub]) || buf.peek(syn::Token![crate]) { // alphabet!(pub FOO = "abcdef0123456789"); // ^^^ vis = buf.parse::<syn::Visibility>()?; } else { vis = syn::Visibility::Inherited; } // alphabet!(FOO = "abcdef0123456789"); // ^^^ let ident: syn::Ident = buf.parse()?; // alphabet!(FOO = "abcdef0123456789"); // ^ buf.parse::<syn::Token![=]>()?; // alphabet!(FOO = "abcdef0123456789"); // ^^^^^^^^^^^^^^^^^^ let contents: syn::LitStr = buf.parse()?; let contents = contents.value(); // Allow an optional semicolon. Makes most sense when used in {}-form: // alphabet! { // pub ENGLISH = "abcdefghijklmnopqrstuvwxyz"; // } if buf.peek(syn::Token![;]) { buf.parse::<syn::Token![;]>()?; } Ok(Alphabet { meta, vis, ident, contents }) } }