1use crate::{arena::Handle, FastHashMap, FastHashSet};
2use std::borrow::Cow;
3use std::hash::{Hash, Hasher};
4
5pub type EntryPointIndex = u16;
6const SEPARATOR: char = '_';
7
8#[derive(Debug, Eq, Hash, PartialEq)]
9pub enum NameKey {
10 Constant(Handle<crate::Constant>),
11 GlobalVariable(Handle<crate::GlobalVariable>),
12 Type(Handle<crate::Type>),
13 StructMember(Handle<crate::Type>, u32),
14 Function(Handle<crate::Function>),
15 FunctionArgument(Handle<crate::Function>, u32),
16 FunctionLocal(Handle<crate::Function>, Handle<crate::LocalVariable>),
17 EntryPoint(EntryPointIndex),
18 EntryPointLocal(EntryPointIndex, Handle<crate::LocalVariable>),
19 EntryPointArgument(EntryPointIndex, u32),
20}
21
22#[derive(Default)]
25pub struct Namer {
26 unique: FastHashMap<String, u32>,
28 keywords: FastHashSet<&'static str>,
29 keywords_case_insensitive: FastHashSet<AsciiUniCase<&'static str>>,
30 reserved_prefixes: Vec<&'static str>,
31}
32
33impl Namer {
34 fn sanitize<'s>(&self, string: &'s str) -> Cow<'s, str> {
45 let string = string
46 .trim_start_matches(|c: char| c.is_numeric())
47 .trim_end_matches(SEPARATOR);
48
49 let base = if !string.is_empty()
50 && !string.contains("__")
51 && string
52 .chars()
53 .all(|c: char| c.is_ascii_alphanumeric() || c == '_')
54 {
55 Cow::Borrowed(string)
56 } else {
57 let mut filtered = string
58 .chars()
59 .filter(|&c| c.is_ascii_alphanumeric() || c == '_')
60 .fold(String::new(), |mut s, c| {
61 if s.ends_with('_') && c == '_' {
62 return s;
63 }
64 s.push(c);
65 s
66 });
67 let stripped_len = filtered.trim_end_matches(SEPARATOR).len();
68 filtered.truncate(stripped_len);
69 if filtered.is_empty() {
70 filtered.push_str("unnamed");
71 }
72 Cow::Owned(filtered)
73 };
74
75 for prefix in &self.reserved_prefixes {
76 if base.starts_with(prefix) {
77 return format!("gen_{base}").into();
78 }
79 }
80
81 base
82 }
83
84 pub fn call(&mut self, label_raw: &str) -> String {
95 use std::fmt::Write as _; let base = self.sanitize(label_raw);
98 debug_assert!(!base.is_empty() && !base.ends_with(SEPARATOR));
99
100 match self.unique.get_mut(base.as_ref()) {
106 Some(count) => {
107 *count += 1;
108 let mut suffixed = base.into_owned();
110 write!(suffixed, "{}{}", SEPARATOR, *count).unwrap();
111 suffixed
112 }
113 None => {
114 let mut suffixed = base.to_string();
115 if base.ends_with(char::is_numeric)
116 || self.keywords.contains(base.as_ref())
117 || self
118 .keywords_case_insensitive
119 .contains(&AsciiUniCase(base.as_ref()))
120 {
121 suffixed.push(SEPARATOR);
122 }
123 debug_assert!(!self.keywords.contains::<str>(&suffixed));
124 self.unique.insert(base.into_owned(), 0);
127 suffixed
128 }
129 }
130 }
131
132 pub fn call_or(&mut self, label: &Option<String>, fallback: &str) -> String {
133 self.call(match *label {
134 Some(ref name) => name,
135 None => fallback,
136 })
137 }
138
139 fn namespace(&mut self, capacity: usize, body: impl FnOnce(&mut Self)) {
145 let fresh = FastHashMap::with_capacity_and_hasher(capacity, Default::default());
146 let outer = std::mem::replace(&mut self.unique, fresh);
147 body(self);
148 self.unique = outer;
149 }
150
151 pub fn reset(
152 &mut self,
153 module: &crate::Module,
154 reserved_keywords: &[&'static str],
155 extra_reserved_keywords: &[&'static str],
156 reserved_keywords_case_insensitive: &[&'static str],
157 reserved_prefixes: &[&'static str],
158 output: &mut FastHashMap<NameKey, String>,
159 ) {
160 self.reserved_prefixes.clear();
161 self.reserved_prefixes.extend(reserved_prefixes.iter());
162
163 self.unique.clear();
164 self.keywords.clear();
165 self.keywords.extend(reserved_keywords.iter());
166 self.keywords.extend(extra_reserved_keywords.iter());
167
168 debug_assert!(reserved_keywords_case_insensitive
169 .iter()
170 .all(|s| s.is_ascii()));
171 self.keywords_case_insensitive.clear();
172 self.keywords_case_insensitive.extend(
173 reserved_keywords_case_insensitive
174 .iter()
175 .map(|string| (AsciiUniCase(*string))),
176 );
177
178 let mut temp = String::new();
179
180 for (ty_handle, ty) in module.types.iter() {
181 let ty_name = self.call_or(&ty.name, "type");
182 output.insert(NameKey::Type(ty_handle), ty_name);
183
184 if let crate::TypeInner::Struct { ref members, .. } = ty.inner {
185 self.namespace(members.len(), |namer| {
187 for (index, member) in members.iter().enumerate() {
188 let name = namer.call_or(&member.name, "member");
189 output.insert(NameKey::StructMember(ty_handle, index as u32), name);
190 }
191 })
192 }
193 }
194
195 for (ep_index, ep) in module.entry_points.iter().enumerate() {
196 let ep_name = self.call(&ep.name);
197 output.insert(NameKey::EntryPoint(ep_index as _), ep_name);
198 for (index, arg) in ep.function.arguments.iter().enumerate() {
199 let name = self.call_or(&arg.name, "param");
200 output.insert(
201 NameKey::EntryPointArgument(ep_index as _, index as u32),
202 name,
203 );
204 }
205 for (handle, var) in ep.function.local_variables.iter() {
206 let name = self.call_or(&var.name, "local");
207 output.insert(NameKey::EntryPointLocal(ep_index as _, handle), name);
208 }
209 }
210
211 for (fun_handle, fun) in module.functions.iter() {
212 let fun_name = self.call_or(&fun.name, "function");
213 output.insert(NameKey::Function(fun_handle), fun_name);
214 for (index, arg) in fun.arguments.iter().enumerate() {
215 let name = self.call_or(&arg.name, "param");
216 output.insert(NameKey::FunctionArgument(fun_handle, index as u32), name);
217 }
218 for (handle, var) in fun.local_variables.iter() {
219 let name = self.call_or(&var.name, "local");
220 output.insert(NameKey::FunctionLocal(fun_handle, handle), name);
221 }
222 }
223
224 for (handle, var) in module.global_variables.iter() {
225 let name = self.call_or(&var.name, "global");
226 output.insert(NameKey::GlobalVariable(handle), name);
227 }
228
229 for (handle, constant) in module.constants.iter() {
230 let label = match constant.name {
231 Some(ref name) => name,
232 None => {
233 use std::fmt::Write;
234 temp.clear();
236 write!(temp, "const_{}", output[&NameKey::Type(constant.ty)]).unwrap();
237 &temp
238 }
239 };
240 let name = self.call(label);
241 output.insert(NameKey::Constant(handle), name);
242 }
243 }
244}
245
246struct AsciiUniCase<S: AsRef<str> + ?Sized>(S);
248
249impl<S: AsRef<str>> PartialEq<Self> for AsciiUniCase<S> {
250 #[inline]
251 fn eq(&self, other: &Self) -> bool {
252 self.0.as_ref().eq_ignore_ascii_case(other.0.as_ref())
253 }
254}
255
256impl<S: AsRef<str>> Eq for AsciiUniCase<S> {}
257
258impl<S: AsRef<str>> Hash for AsciiUniCase<S> {
259 #[inline]
260 fn hash<H: Hasher>(&self, hasher: &mut H) {
261 for byte in self
262 .0
263 .as_ref()
264 .as_bytes()
265 .iter()
266 .map(|b| b.to_ascii_lowercase())
267 {
268 hasher.write_u8(byte);
269 }
270 }
271}
272
273#[test]
274fn test() {
275 let mut namer = Namer::default();
276 assert_eq!(namer.call("x"), "x");
277 assert_eq!(namer.call("x"), "x_1");
278 assert_eq!(namer.call("x1"), "x1_");
279 assert_eq!(namer.call("__x"), "_x");
280 assert_eq!(namer.call("1___x"), "_x_1");
281}