1 use proc_macro::TokenStream;
3 use std::collections::HashSet;
4 use syn::parse::{Parse, ParseStream, Result};
5 use syn::{braced, parse_macro_input, Ident, LitStr, Token};
8 syn::custom_keyword!(Keywords);
9 syn::custom_keyword!(Symbols);
17 impl Parse for Keyword {
18 fn parse(input: ParseStream<'_>) -> Result<Self> {
19 let name = input.parse()?;
20 input.parse::<Token![:]>()?;
21 let value = input.parse()?;
22 input.parse::<Token![,]>()?;
24 Ok(Keyword { name, value })
30 value: Option<LitStr>,
33 impl Parse for Symbol {
34 fn parse(input: ParseStream<'_>) -> Result<Self> {
35 let name = input.parse()?;
36 let value = match input.parse::<Token![:]>() {
37 Ok(_) => Some(input.parse()?),
40 input.parse::<Token![,]>()?;
42 Ok(Symbol { name, value })
46 /// A type used to greedily parse another type until the input is empty.
47 struct List<T>(Vec<T>);
49 impl<T: Parse> Parse for List<T> {
50 fn parse(input: ParseStream<'_>) -> Result<Self> {
51 let mut list = Vec::new();
52 while !input.is_empty() {
53 list.push(input.parse()?);
60 keywords: List<Keyword>,
61 symbols: List<Symbol>,
64 impl Parse for Input {
65 fn parse(input: ParseStream<'_>) -> Result<Self> {
66 input.parse::<kw::Keywords>()?;
68 braced!(content in input);
69 let keywords = content.parse()?;
71 input.parse::<kw::Symbols>()?;
73 braced!(content in input);
74 let symbols = content.parse()?;
76 Ok(Input { keywords, symbols })
80 pub fn symbols(input: TokenStream) -> TokenStream {
81 let input = parse_macro_input!(input as Input);
83 let mut keyword_stream = quote! {};
84 let mut symbols_stream = quote! {};
85 let mut digits_stream = quote! {};
86 let mut prefill_stream = quote! {};
87 let mut counter = 0u32;
88 let mut keys = HashSet::<String>::new();
89 let mut prev_key: Option<String> = None;
90 let mut errors = Vec::<String>::new();
92 let mut check_dup = |str: &str, errors: &mut Vec<String>| {
93 if !keys.insert(str.to_string()) {
94 errors.push(format!("Symbol `{}` is duplicated", str));
98 let mut check_order = |str: &str, errors: &mut Vec<String>| {
99 if let Some(ref prev_str) = prev_key {
101 errors.push(format!("Symbol `{}` must precede `{}`", str, prev_str));
104 prev_key = Some(str.to_string());
107 // Generate the listed keywords.
108 for keyword in &input.keywords.0 {
109 let name = &keyword.name;
110 let value = &keyword.value;
111 check_dup(&value.value(), &mut errors);
112 prefill_stream.extend(quote! {
115 keyword_stream.extend(quote! {
116 #[allow(non_upper_case_globals)]
117 pub const #name: Symbol = Symbol::new(#counter);
122 // Generate the listed symbols.
123 for symbol in &input.symbols.0 {
124 let name = &symbol.name;
125 let value = match &symbol.value {
126 Some(value) => value.value(),
127 None => name.to_string(),
129 check_dup(&value, &mut errors);
130 check_order(&name.to_string(), &mut errors);
131 prefill_stream.extend(quote! {
134 symbols_stream.extend(quote! {
135 #[allow(rustc::default_hash_types)]
136 #[allow(non_upper_case_globals)]
137 pub const #name: Symbol = Symbol::new(#counter);
142 // Generate symbols for the strings "0", "1", ..., "9".
144 let n = n.to_string();
145 check_dup(&n, &mut errors);
146 prefill_stream.extend(quote! {
149 digits_stream.extend(quote! {
150 Symbol::new(#counter),
155 if !errors.is_empty() {
156 for error in errors.into_iter() {
157 eprintln!("error: {}", error)
159 panic!("errors in `Keywords` and/or `Symbols`");
162 let tt = TokenStream::from(quote! {
163 macro_rules! keywords {
169 macro_rules! define_symbols {
173 #[allow(non_upper_case_globals)]
174 pub const digits_array: &[Symbol; 10] = &[
181 pub fn fresh() -> Self {
189 // To see the generated code generated, uncomment this line, recompile, and
190 // run the resulting output through `rustfmt`.
191 //eprintln!("{}", tt);