]> git.lizzy.rs Git - rust.git/blob - src/librustc_macros/src/symbols.rs
move raw span to tt reader
[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!(Other);
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(Ident);
37
38 impl Parse for Symbol {
39     fn parse(input: ParseStream<'_>) -> Result<Self> {
40         let ident: Ident = input.parse()?;
41         input.parse::<Token![,]>()?;
42
43         Ok(Symbol(ident))
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::Other>()?;
73         let content;
74         braced!(content in input);
75         let symbols = content.parse()?;
76
77         Ok(Input {
78             keywords,
79             symbols,
80         })
81     }
82 }
83
84 pub fn symbols(input: TokenStream) -> TokenStream {
85     let input = parse_macro_input!(input as Input);
86
87     let mut keyword_stream = quote! {};
88     let mut symbols_stream = quote! {};
89     let mut prefill_stream = quote! {};
90     let mut from_str_stream = quote! {};
91     let mut counter = 0u32;
92     let mut keys = HashSet::<String>::new();
93
94     let mut check_dup = |str: &str| {
95         if !keys.insert(str.to_string()) {
96             panic!("Symbol `{}` is duplicated", str);
97         }
98     };
99
100     for keyword in &input.keywords.0 {
101         let name = &keyword.name;
102         let value = &keyword.value;
103         check_dup(&value.value());
104         prefill_stream.extend(quote! {
105             #value,
106         });
107         keyword_stream.extend(quote! {
108             pub const #name: Keyword = Keyword {
109                 ident: Ident::with_empty_ctxt(super::Symbol::new(#counter))
110             };
111         });
112         from_str_stream.extend(quote! {
113             #value => Ok(#name),
114         });
115         counter += 1;
116     }
117
118     for symbol in &input.symbols.0 {
119         let value = &symbol.0;
120         let value_str = value.to_string();
121         check_dup(&value_str);
122         prefill_stream.extend(quote! {
123             #value_str,
124         });
125         symbols_stream.extend(quote! {
126             pub const #value: Symbol = Symbol::new(#counter);
127         });
128         counter += 1;
129     }
130
131     TokenStream::from(quote! {
132         macro_rules! keywords {
133             () => {
134                 #keyword_stream
135
136                 impl std::str::FromStr for Keyword {
137                     type Err = ();
138
139                     fn from_str(s: &str) -> Result<Self, ()> {
140                         match s {
141                             #from_str_stream
142                             _ => Err(()),
143                         }
144                     }
145                 }
146             }
147         }
148
149         macro_rules! symbols {
150             () => {
151                 #symbols_stream
152             }
153         }
154
155         impl Interner {
156             pub fn fresh() -> Self {
157                 Interner::prefill(&[
158                     #prefill_stream
159                 ])
160             }
161         }
162     })
163 }