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> {
44 let string = string
45 .trim_start_matches(|c: char| c.is_numeric())
46 .trim_end_matches(SEPARATOR);
47
48 let base = if !string.is_empty()
49 && string
50 .chars()
51 .all(|c: char| c.is_ascii_alphanumeric() || c == '_')
52 {
53 Cow::Borrowed(string)
54 } else {
55 let mut filtered = string
56 .chars()
57 .filter(|&c| c.is_ascii_alphanumeric() || c == '_')
58 .collect::<String>();
59 let stripped_len = filtered.trim_end_matches(SEPARATOR).len();
60 filtered.truncate(stripped_len);
61 if filtered.is_empty() {
62 filtered.push_str("unnamed");
63 }
64 Cow::Owned(filtered)
65 };
66
67 for prefix in &self.reserved_prefixes {
68 if base.starts_with(prefix) {
69 return format!("gen_{base}").into();
70 }
71 }
72
73 base
74 }
75
76 pub fn call(&mut self, label_raw: &str) -> String {
87 use std::fmt::Write as _; let base = self.sanitize(label_raw);
90 debug_assert!(!base.is_empty() && !base.ends_with(SEPARATOR));
91
92 match self.unique.get_mut(base.as_ref()) {
98 Some(count) => {
99 *count += 1;
100 let mut suffixed = base.into_owned();
102 write!(suffixed, "{}{}", SEPARATOR, *count).unwrap();
103 suffixed
104 }
105 None => {
106 let mut suffixed = base.to_string();
107 if base.ends_with(char::is_numeric)
108 || self.keywords.contains(base.as_ref())
109 || self
110 .keywords_case_insensitive
111 .contains(&AsciiUniCase(base.as_ref()))
112 {
113 suffixed.push(SEPARATOR);
114 }
115 debug_assert!(!self.keywords.contains::<str>(&suffixed));
116 self.unique.insert(base.into_owned(), 0);
119 suffixed
120 }
121 }
122 }
123
124 pub fn call_or(&mut self, label: &Option<String>, fallback: &str) -> String {
125 self.call(match *label {
126 Some(ref name) => name,
127 None => fallback,
128 })
129 }
130
131 fn namespace(&mut self, capacity: usize, body: impl FnOnce(&mut Self)) {
137 let fresh = FastHashMap::with_capacity_and_hasher(capacity, Default::default());
138 let outer = std::mem::replace(&mut self.unique, fresh);
139 body(self);
140 self.unique = outer;
141 }
142
143 pub fn reset(
144 &mut self,
145 module: &crate::Module,
146 reserved_keywords: &[&'static str],
147 extra_reserved_keywords: &[&'static str],
148 reserved_keywords_case_insensitive: &[&'static str],
149 reserved_prefixes: &[&'static str],
150 output: &mut FastHashMap<NameKey, String>,
151 ) {
152 self.reserved_prefixes.clear();
153 self.reserved_prefixes.extend(reserved_prefixes.iter());
154
155 self.unique.clear();
156 self.keywords.clear();
157 self.keywords.extend(reserved_keywords.iter());
158 self.keywords.extend(extra_reserved_keywords.iter());
159
160 debug_assert!(reserved_keywords_case_insensitive
161 .iter()
162 .all(|s| s.is_ascii()));
163 self.keywords_case_insensitive.clear();
164 self.keywords_case_insensitive.extend(
165 reserved_keywords_case_insensitive
166 .iter()
167 .map(|string| (AsciiUniCase(*string))),
168 );
169
170 let mut temp = String::new();
171
172 for (ty_handle, ty) in module.types.iter() {
173 let ty_name = self.call_or(&ty.name, "type");
174 output.insert(NameKey::Type(ty_handle), ty_name);
175
176 if let crate::TypeInner::Struct { ref members, .. } = ty.inner {
177 self.namespace(members.len(), |namer| {
179 for (index, member) in members.iter().enumerate() {
180 let name = namer.call_or(&member.name, "member");
181 output.insert(NameKey::StructMember(ty_handle, index as u32), name);
182 }
183 })
184 }
185 }
186
187 for (ep_index, ep) in module.entry_points.iter().enumerate() {
188 let ep_name = self.call(&ep.name);
189 output.insert(NameKey::EntryPoint(ep_index as _), ep_name);
190 for (index, arg) in ep.function.arguments.iter().enumerate() {
191 let name = self.call_or(&arg.name, "param");
192 output.insert(
193 NameKey::EntryPointArgument(ep_index as _, index as u32),
194 name,
195 );
196 }
197 for (handle, var) in ep.function.local_variables.iter() {
198 let name = self.call_or(&var.name, "local");
199 output.insert(NameKey::EntryPointLocal(ep_index as _, handle), name);
200 }
201 }
202
203 for (fun_handle, fun) in module.functions.iter() {
204 let fun_name = self.call_or(&fun.name, "function");
205 output.insert(NameKey::Function(fun_handle), fun_name);
206 for (index, arg) in fun.arguments.iter().enumerate() {
207 let name = self.call_or(&arg.name, "param");
208 output.insert(NameKey::FunctionArgument(fun_handle, index as u32), name);
209 }
210 for (handle, var) in fun.local_variables.iter() {
211 let name = self.call_or(&var.name, "local");
212 output.insert(NameKey::FunctionLocal(fun_handle, handle), name);
213 }
214 }
215
216 for (handle, var) in module.global_variables.iter() {
217 let name = self.call_or(&var.name, "global");
218 output.insert(NameKey::GlobalVariable(handle), name);
219 }
220
221 for (handle, constant) in module.constants.iter() {
222 let label = match constant.name {
223 Some(ref name) => name,
224 None => {
225 use std::fmt::Write;
226 temp.clear();
228 write!(temp, "const_{}", output[&NameKey::Type(constant.ty)]).unwrap();
229 &temp
230 }
231 };
232 let name = self.call(label);
233 output.insert(NameKey::Constant(handle), name);
234 }
235 }
236}
237
238struct AsciiUniCase<S: AsRef<str> + ?Sized>(S);
240
241impl<S: AsRef<str>> PartialEq<Self> for AsciiUniCase<S> {
242 #[inline]
243 fn eq(&self, other: &Self) -> bool {
244 self.0.as_ref().eq_ignore_ascii_case(other.0.as_ref())
245 }
246}
247
248impl<S: AsRef<str>> Eq for AsciiUniCase<S> {}
249
250impl<S: AsRef<str>> Hash for AsciiUniCase<S> {
251 #[inline]
252 fn hash<H: Hasher>(&self, hasher: &mut H) {
253 for byte in self
254 .0
255 .as_ref()
256 .as_bytes()
257 .iter()
258 .map(|b| b.to_ascii_lowercase())
259 {
260 hasher.write_u8(byte);
261 }
262 }
263}
264
265#[test]
266fn test() {
267 let mut namer = Namer::default();
268 assert_eq!(namer.call("x"), "x");
269 assert_eq!(namer.call("x"), "x_1");
270 assert_eq!(namer.call("x1"), "x1_");
271}