alphabet_macro/lib.rs
1//! Provides the alphabet!() macro. It can be used to create const alphabets easily.
2//!
3//! Usually you would have to write alphabets in a cumbersome way:
4//! ```
5//! const HEX: [char; 16] = ['0', '1', '2', '3',
6//! '4', '5', '6', '7',
7//! '8', '9', 'a', 'b',
8//! 'c', 'd', 'e', 'f'];
9//!
10//! assert_eq!(HEX.len(), 16);
11//! assert_eq!(HEX[5], '5');
12//! assert_eq!(HEX[10], 'a');
13//! ```
14//!
15//! But with the alphabet!() macro this can be done way easier.
16//! ```
17//! use alphabet_macro::alphabet;
18//!
19//! alphabet!(HEX = "0123456789abcdef");
20//!
21//! assert_eq!(HEX.len(), 16);
22//! assert_eq!(HEX[5], '5');
23//! assert_eq!(HEX[10], 'a');
24//! ```
25//!
26//! The alphabet!() macro expands to the snippet above, while being easier to read, write and understand.
27
28use proc_macro::TokenStream;
29use quote::quote;
30use syn::{parse, self};
31
32struct Alphabet {
33 meta: Option<Vec<syn::Attribute>>,
34 vis: syn::Visibility,
35 ident: syn::Ident,
36 contents: String
37}
38
39/// Declares a constant alphabet. You can choose any valid identifier as the name for the alphabet.
40///
41/// # Examples
42///
43/// Basic usage:
44/// ```
45/// use alphabet_macro::alphabet;
46///
47/// alphabet!(BINARY = "01");
48/// alphabet!(ENGLISH = "abcdefghijklmnopqrstuvwxyz");
49/// alphabet!(GERMAN = "aäbcdefghijklmnoöpqrstuüvwxyzß");
50/// alphabet!(HEBREW = "אבגדהוזחטיכלמנסעפצקרשת");
51///
52/// assert_eq!(BINARY.len(), 2);
53/// assert_eq!(ENGLISH.len(), 26);
54/// assert_eq!(GERMAN.len(), 30);
55/// assert_eq!(HEBREW.len(), 22);
56/// ```
57///
58/// You can also specify a visibility for the generated alphabet:
59/// ```
60/// use alphabet_macro::alphabet;
61///
62/// alphabet!(pub BINARY = "01");
63/// ```
64///
65/// You can pass attributes to the generated alphabet as well. This includes doc comments, which internally use attributes:
66/// ```
67/// use alphabet_macro::alphabet;
68///
69/// alphabet! {
70/// /// An alphabet for binary strings.
71/// pub BINARY = "01";
72/// }
73/// ```
74///
75/// # Syntax
76///
77/// Alphabets follow the following syntax, with the nonterminal `alphabet` being the contents of the alphabet! macro:
78/// ```bnf
79/// alphabet ::= attribute* visibility? identifier "=" literal-string ";"?
80/// ```
81#[proc_macro]
82pub fn alphabet(input: TokenStream) -> TokenStream {
83 let Alphabet {
84 meta,
85 vis,
86 ident,
87 contents
88 } = syn::parse_macro_input!(input as Alphabet);
89
90 let chars: Vec<char> = contents.chars().collect();
91 let alphabet_len = chars.len();
92 TokenStream::from(match meta {
93 None => quote! {
94 #vis const #ident: [char; #alphabet_len] = [#(#chars),*];
95 },
96 Some(meta) => quote! {
97 #(#meta)* #vis const #ident: [char; #alphabet_len] = [#(#chars),*];
98 }
99 })
100}
101
102impl syn::parse::Parse for Alphabet {
103 fn parse(buf: parse::ParseStream) -> syn::Result<Self> {
104 let meta;
105 if buf.peek(syn::Token![#]) {
106 // #foo[bar]
107 // ^^^^^^^^^
108 meta = Some(buf.call(syn::Attribute::parse_outer)?);
109 } else {
110 meta = None;
111 }
112
113 let vis;
114 if buf.peek(syn::Token![pub]) || buf.peek(syn::Token![crate]) {
115 // alphabet!(pub FOO = "abcdef0123456789");
116 // ^^^
117 vis = buf.parse::<syn::Visibility>()?;
118 } else {
119 vis = syn::Visibility::Inherited;
120 }
121
122 // alphabet!(FOO = "abcdef0123456789");
123 // ^^^
124 let ident: syn::Ident = buf.parse()?;
125
126 // alphabet!(FOO = "abcdef0123456789");
127 // ^
128 buf.parse::<syn::Token![=]>()?;
129
130 // alphabet!(FOO = "abcdef0123456789");
131 // ^^^^^^^^^^^^^^^^^^
132 let contents: syn::LitStr = buf.parse()?;
133 let contents = contents.value();
134
135 // Allow an optional semicolon. Makes most sense when used in {}-form:
136 // alphabet! {
137 // pub ENGLISH = "abcdefghijklmnopqrstuvwxyz";
138 // }
139 if buf.peek(syn::Token![;]) {
140 buf.parse::<syn::Token![;]>()?;
141 }
142
143 Ok(Alphabet {
144 meta,
145 vis,
146 ident,
147 contents
148 })
149 }
150}