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