1use std::collections::HashMap;
4
5use proc_macro2::{Span, TokenStream};
6use quote::quote;
7use syn::{
8 Expr, Ident, Lit, LitStr, Result, Token, braced,
9 parse::{Parse, ParseStream},
10 punctuated::Punctuated,
11};
12
13pub(super) fn symbols(input: TokenStream) -> TokenStream {
14 let (mut output, errors) = symbols_with_errors(input);
15
16 output.extend(errors.into_iter().map(|e| e.to_compile_error()));
20
21 output
22}
23
24fn symbols_with_errors(input: TokenStream) -> (TokenStream, Vec<syn::Error>) {
25 let mut errors = Errors::default();
26
27 let input: Input = match syn::parse2(input) {
28 Ok(input) => input,
29 Err(e) => {
30 errors.list.push(e);
31 return (TokenStream::default(), errors.list);
32 }
33 };
34
35 let mut prefill_stream = quote! {};
36 let mut entries = Entries::with_capacity(input.keywords.len());
37
38 let mut keyword_stream = quote! {};
40 for keyword in &input.keywords {
41 let name = &keyword.name;
42 let value = &keyword.value;
43 let value_string = value.value();
44 let idx = entries.insert(keyword.name.span(), &value_string, &mut errors);
45 prefill_stream.extend(quote! {
46 #value,
47 });
48 keyword_stream.extend(quote! {
49 pub const #name: Symbol = Symbol::new(rustc_span::symbol::PREDEFINED_SYMBOLS_COUNT + #idx);
50 });
51 }
52
53 let mut symbols_stream = quote! {};
55 let mut prev_key: Option<(Span, String)> = None;
56 let mut check_order = |span: Span, s: &str, errors: &mut Errors| {
57 if let Some((prev_span, ref prev_str)) = prev_key
58 && s < prev_str
59 {
60 errors.error(span, format!("Symbol `{s}` must precede `{prev_str}`"));
61 errors.error(prev_span, format!("location of previous symbol `{prev_str}`"));
62 }
63 prev_key = Some((span, s.to_string()));
64 };
65 for symbol in &input.symbols {
66 let name = &symbol.name;
67 check_order(symbol.name.span(), &name.to_string(), &mut errors);
68 let value = match &symbol.value {
69 Value::SameAsName => name.to_string(),
70 Value::String(lit) => lit.value(),
71 Value::Unsupported(expr) => {
72 errors.list.push(syn::Error::new_spanned(
73 expr,
74 concat!(
75 "unsupported expression for symbol value; implement support for this in ",
76 file!(),
77 ),
78 ));
79 continue;
80 }
81 };
82 let idx = entries.insert(symbol.name.span(), &value, &mut errors);
83
84 prefill_stream.extend(quote! {
85 #value,
86 });
87 symbols_stream.extend(quote! {
88 pub const #name: Symbol = Symbol::new(rustc_span::symbol::PREDEFINED_SYMBOLS_COUNT + #idx);
89 });
90 }
91
92 let predefined_flux_symbols_count = entries.len();
93 let predefined_flux_symbols_count_usize = predefined_flux_symbols_count as usize;
94 let output = quote! {
95 pub const PREDEFINED_FLUX_SYMBOLS_COUNT: u32 = #predefined_flux_symbols_count;
96
97 #[doc(hidden)]
98 #[allow(non_upper_case_globals)]
99 mod kw_generated {
100 use rustc_span::Symbol;
101
102 #keyword_stream
103 }
104
105 #[doc(hidden)]
106 #[allow(non_upper_case_globals)]
107 pub mod sym_generated {
108 use rustc_span::Symbol;
109 #symbols_stream
110 }
111
112 pub const PREDEFINED_FLUX_SYMBOLS: [&'static str; #predefined_flux_symbols_count_usize] =
113 [#prefill_stream];
114 };
115 (output, errors.list)
116}
117
118struct Keyword {
119 name: Ident,
120 value: LitStr,
121}
122
123impl Parse for Keyword {
124 fn parse(input: ParseStream<'_>) -> Result<Self> {
125 let name = input.parse()?;
126 input.parse::<Token![:]>()?;
127 let value = input.parse()?;
128
129 Ok(Keyword { name, value })
130 }
131}
132
133struct Symbol {
134 name: Ident,
135 value: Value,
136}
137
138enum Value {
139 SameAsName,
140 String(LitStr),
141 Unsupported(Expr),
142}
143
144impl Parse for Symbol {
145 fn parse(input: ParseStream<'_>) -> Result<Self> {
146 let name = input.parse()?;
147 let colon_token: Option<Token![:]> = input.parse()?;
148 let value = if colon_token.is_some() { input.parse()? } else { Value::SameAsName };
149
150 Ok(Symbol { name, value })
151 }
152}
153
154impl Parse for Value {
155 fn parse(input: ParseStream<'_>) -> Result<Self> {
156 let expr: Expr = input.parse()?;
157 if let Expr::Lit(expr) = &expr
158 && let Lit::Str(lit) = &expr.lit
159 {
160 Ok(Value::String(lit.clone()))
161 } else {
162 Ok(Value::Unsupported(expr))
163 }
164 }
165}
166
167struct Input {
168 keywords: Punctuated<Keyword, Token![,]>,
169 symbols: Punctuated<Symbol, Token![,]>,
170}
171
172impl Parse for Input {
173 fn parse(input: ParseStream) -> Result<Self> {
174 input.parse::<kw::Keywords>()?;
175 let content;
176 braced!(content in input);
177 let keywords = Punctuated::parse_terminated(&content)?;
178
179 input.parse::<kw::Symbols>()?;
180 let content;
181 braced!(content in input);
182 let symbols = Punctuated::parse_terminated(&content)?;
183
184 Ok(Input { keywords, symbols })
185 }
186}
187
188mod kw {
189 syn::custom_keyword!(Keywords);
190 syn::custom_keyword!(Symbols);
191}
192
193#[derive(Default)]
194struct Errors {
195 list: Vec<syn::Error>,
196}
197
198impl Errors {
199 fn error(&mut self, span: Span, message: String) {
200 self.list.push(syn::Error::new(span, message));
201 }
202}
203
204struct Entries {
205 map: HashMap<String, Predefined>,
206}
207
208impl Entries {
209 fn with_capacity(capacity: usize) -> Self {
210 Entries { map: HashMap::with_capacity(capacity) }
211 }
212
213 fn insert(&mut self, span: Span, s: &str, errors: &mut Errors) -> u32 {
214 if let Some(prev) = self.map.get(s) {
215 errors.error(span, format!("Symbol `{s}` is duplicated"));
216 errors.error(prev.span_of_name, "location of previous definition".to_string());
217 prev.idx
218 } else {
219 let idx = self.len();
220 self.map
221 .insert(s.to_string(), Predefined { idx, span_of_name: span });
222 idx
223 }
224 }
225
226 fn len(&self) -> u32 {
227 u32::try_from(self.map.len()).expect("way too many symbols")
228 }
229}
230
231struct Predefined {
232 idx: u32,
233 span_of_name: Span,
234}