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