]> git.lizzy.rs Git - rust.git/blob - src/librustc_macros/src/symbols.rs
Rollup merge of #61048 - wizAmit:feature/nth_back_chunks, r=scottmcm
[rust.git] / src / librustc_macros / src / symbols.rs
1 use proc_macro::TokenStream;
2 use syn::{
3     Token, Ident, LitStr,
4     braced, parse_macro_input,
5 };
6 use syn::parse::{Result, Parse, ParseStream};
7 use syn;
8 use std::collections::HashSet;
9 use quote::quote;
10
11 #[allow(non_camel_case_types)]
12 mod kw {
13     syn::custom_keyword!(Keywords);
14     syn::custom_keyword!(Symbols);
15 }
16
17 struct Keyword {
18     name: Ident,
19     value: LitStr,
20 }
21
22 impl Parse for Keyword {
23     fn parse(input: ParseStream<'_>) -> Result<Self> {
24         let name = input.parse()?;
25         input.parse::<Token![:]>()?;
26         let value = input.parse()?;
27         input.parse::<Token![,]>()?;
28
29         Ok(Keyword {
30             name,
31             value,
32         })
33     }
34 }
35
36 struct Symbol {
37     name: Ident,
38     value: Option<LitStr>,
39 }
40
41 impl Parse for Symbol {
42     fn parse(input: ParseStream<'_>) -> Result<Self> {
43         let name = input.parse()?;
44         let value = match input.parse::<Token![:]>() {
45             Ok(_) => Some(input.parse()?),
46             Err(_) => None,
47         };
48         input.parse::<Token![,]>()?;
49
50         Ok(Symbol {
51             name,
52             value,
53         })
54     }
55 }
56
57 /// A type used to greedily parse another type until the input is empty.
58 struct List<T>(Vec<T>);
59
60 impl<T: Parse> Parse for List<T> {
61     fn parse(input: ParseStream<'_>) -> Result<Self> {
62         let mut list = Vec::new();
63         while !input.is_empty() {
64             list.push(input.parse()?);
65         }
66         Ok(List(list))
67     }
68 }
69
70 struct Input {
71     keywords: List<Keyword>,
72     symbols: List<Symbol>,
73 }
74
75 impl Parse for Input {
76     fn parse(input: ParseStream<'_>) -> Result<Self> {
77         input.parse::<kw::Keywords>()?;
78         let content;
79         braced!(content in input);
80         let keywords = content.parse()?;
81
82         input.parse::<kw::Symbols>()?;
83         let content;
84         braced!(content in input);
85         let symbols = content.parse()?;
86
87         Ok(Input {
88             keywords,
89             symbols,
90         })
91     }
92 }
93
94 pub fn symbols(input: TokenStream) -> TokenStream {
95     let input = parse_macro_input!(input as Input);
96
97     let mut keyword_stream = quote! {};
98     let mut symbols_stream = quote! {};
99     let mut digits_stream = quote! {};
100     let mut prefill_stream = quote! {};
101     let mut counter = 0u32;
102     let mut keys = HashSet::<String>::new();
103
104     let mut check_dup = |str: &str| {
105         if !keys.insert(str.to_string()) {
106             panic!("Symbol `{}` is duplicated", str);
107         }
108     };
109
110     // Generate the listed keywords.
111     for keyword in &input.keywords.0 {
112         let name = &keyword.name;
113         let value = &keyword.value;
114         check_dup(&value.value());
115         prefill_stream.extend(quote! {
116             #value,
117         });
118         keyword_stream.extend(quote! {
119             pub const #name: Symbol = Symbol::new(#counter);
120         });
121         counter += 1;
122     }
123
124     // Generate the listed symbols.
125     for symbol in &input.symbols.0 {
126         let name = &symbol.name;
127         let value = match &symbol.value {
128             Some(value) => value.value(),
129             None => name.to_string(),
130         };
131         check_dup(&value);
132         prefill_stream.extend(quote! {
133             #value,
134         });
135         symbols_stream.extend(quote! {
136             pub const #name: Symbol = Symbol::new(#counter);
137         });
138         counter += 1;
139     }
140
141     // Generate symbols for the strings "0", "1", ..., "9".
142     for n in 0..10 {
143         let n = n.to_string();
144         check_dup(&n);
145         prefill_stream.extend(quote! {
146             #n,
147         });
148         digits_stream.extend(quote! {
149             Symbol::new(#counter),
150         });
151         counter += 1;
152     }
153
154     let tt = TokenStream::from(quote! {
155         macro_rules! keywords {
156             () => {
157                 #keyword_stream
158             }
159         }
160
161         macro_rules! symbols {
162             () => {
163                 #symbols_stream
164
165                 pub const digits_array: &[Symbol; 10] = &[
166                     #digits_stream
167                 ];
168             }
169         }
170
171         impl Interner {
172             pub fn fresh() -> Self {
173                 Interner::prefill(&[
174                     #prefill_stream
175                 ])
176             }
177         }
178     });
179
180     // To see the generated code generated, uncomment this line, recompile, and
181     // run the resulting output through `rustfmt`.
182     //eprintln!("{}", tt);
183
184     tt
185 }