microcad_lang/syntax/identifier/
mod.rs1mod identifier_list;
7mod qualified_name;
8
9use derive_more::{Deref, DerefMut};
10pub use identifier_list::*;
11pub use qualified_name::*;
12
13use crate::{parse::*, parser::Parser, src_ref::*, syntax::*, Id};
14
15#[derive(Default, Clone, PartialEq, Eq, PartialOrd, Ord)]
17pub struct Identifier(pub Refer<Id>);
18
19#[derive(Deref, DerefMut, Default)]
20pub(crate) struct IdentifierSet(indexmap::IndexSet<Identifier>);
21
22impl std::iter::FromIterator<Identifier> for IdentifierSet {
23 fn from_iter<T: IntoIterator<Item = Identifier>>(iter: T) -> Self {
24 IdentifierSet(indexmap::IndexSet::from_iter(iter))
25 }
26}
27
28static UNIQUE_ID_NEXT: std::sync::Mutex<usize> = std::sync::Mutex::new(0);
29
30#[derive(Debug, PartialEq, Eq)]
32pub enum Case {
33 Pascal,
35 LowerSnake,
37 UpperSnake,
39 UpperSingleChar,
41 Invalid,
43}
44
45impl Identifier {
46 pub fn none() -> Self {
48 Self(Refer::none("".into()))
49 }
50
51 pub fn unique() -> Self {
55 let mut num = UNIQUE_ID_NEXT
56 .lock()
57 .expect("lock on UNIQUE_ID_NEXT failed");
58 let id = format!("${num}");
59 *num += 1;
60 Identifier::no_ref(&id)
61 }
62
63 pub fn ignore(&self) -> bool {
65 self.0.starts_with("_")
66 }
67
68 pub fn is_super(&self) -> bool {
70 *self.0 == "super"
71 }
72
73 pub fn is_none(&self) -> bool {
75 self.0.src_ref().is_empty() && self.src_ref().is_empty()
76 }
77
78 pub fn no_ref(id: &str) -> Self {
80 Self(Refer::none(id.into()))
81 }
82
83 pub fn id(&self) -> &Id {
85 &self.0.value
86 }
87
88 pub fn len(&self) -> usize {
90 self.0.len()
91 }
92
93 pub fn is_empty(&self) -> bool {
95 self.0.is_empty()
96 }
97
98 pub fn validate(self) -> ParseResult<Self> {
100 Parser::parse_rule(crate::parser::Rule::identifier, self.id().as_str(), 0)
101 }
102
103 pub fn with_prefix(&self, prefix: &QualifiedName) -> QualifiedName {
105 QualifiedName::from(self).with_prefix(prefix)
106 }
107
108 pub fn detect_case(&self) -> Case {
110 let s = &self.0.value;
111
112 if s.is_empty() {
113 return Case::Invalid;
114 }
115
116 if s.len() == 1 {
117 let c = s.chars().next().expect("At least one char");
118 if c.is_ascii_uppercase() {
119 return Case::UpperSingleChar;
120 } else {
121 return Case::Invalid;
122 }
123 }
124
125 let has_underscore = s.contains('_');
126
127 if has_underscore {
128 if s.chars().all(|c| c.is_ascii_uppercase() || c == '_') {
129 return Case::UpperSnake;
130 } else if s.chars().all(|c| c.is_ascii_lowercase() || c == '_') {
131 return Case::LowerSnake;
132 } else {
133 return Case::Invalid;
134 }
135 } else {
136 let mut chars = s.chars();
138 if let Some(first) = chars.next() {
139 if first.is_ascii_uppercase() && chars.all(|c| c.is_ascii_alphanumeric()) {
140 return Case::Pascal;
141 }
142 }
143 }
144
145 Case::Invalid
146 }
147}
148
149impl SrcReferrer for Identifier {
150 fn src_ref(&self) -> SrcRef {
151 self.0.src_ref.clone()
152 }
153}
154
155impl std::hash::Hash for Identifier {
156 fn hash<H: std::hash::Hasher>(&self, hasher: &mut H) {
157 self.0.hash(hasher)
158 }
159}
160
161impl std::str::FromStr for Identifier {
162 type Err = crate::eval::EvalError;
163
164 fn from_str(id: &str) -> Result<Self, Self::Err> {
165 Ok(Identifier::no_ref(id).validate()?)
166 }
167}
168
169impl From<&std::ffi::OsStr> for Identifier {
170 fn from(value: &std::ffi::OsStr) -> Self {
171 Identifier::no_ref(value.to_string_lossy().to_string().as_str())
172 }
173}
174
175impl From<&str> for Identifier {
176 fn from(value: &str) -> Self {
177 Parser::parse_rule(crate::parser::Rule::identifier, value, 0).expect("A valid identifier")
178 }
179}
180
181impl<'a> From<&'a Identifier> for &'a str {
182 fn from(value: &'a Identifier) -> Self {
183 &value.0
184 }
185}
186
187impl std::fmt::Display for Identifier {
188 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
189 if self.is_empty() {
190 write!(f, crate::invalid_no_ansi!(ID))
191 } else {
192 write!(f, "{}", self.0)
193 }
194 }
195}
196
197impl std::fmt::Debug for Identifier {
198 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
199 if self.is_empty() {
200 write!(f, "{}", crate::invalid!(ID))
201 } else {
202 write!(f, "{}", self.0)
203 }
204 }
205}
206
207impl PartialEq<str> for Identifier {
208 fn eq(&self, other: &str) -> bool {
209 *self.0 == other
210 }
211}
212
213impl TreeDisplay for Identifier {
214 fn tree_print(&self, f: &mut std::fmt::Formatter, depth: TreeState) -> std::fmt::Result {
215 writeln!(f, "{:depth$}Identifier: {}", "", self.id())
216 }
217}
218
219impl std::fmt::Display for IdentifierSet {
220 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
221 write!(
222 f,
223 "{}",
224 self.iter()
225 .map(|id| id.to_string())
226 .collect::<Vec<_>>()
227 .join(", ")
228 )
229 }
230}
231
232impl std::fmt::Debug for IdentifierSet {
233 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
234 write!(
235 f,
236 "{}",
237 self.iter()
238 .map(|id| format!("{id:?}"))
239 .collect::<Vec<_>>()
240 .join(", ")
241 )
242 }
243}
244
245#[test]
246fn identifier_comparison() {
247 use crate::syntax::*;
248
249 let id1 = Identifier::no_ref("x");
251 let id2 = Identifier(Refer::new("x".into(), SrcRef::new(0..5, 0, 1, 1)));
252
253 assert!(id1 == id2);
255}
256
257#[test]
258fn identifier_hash() {
259 use crate::syntax::*;
260 use std::hash::{Hash, Hasher};
261
262 let id1 = Identifier(Refer::none("x".into()));
264 let id2 = Identifier(Refer::new("x".into(), SrcRef::new(0..5, 0, 1, 1)));
265
266 let mut hasher = std::hash::DefaultHasher::new();
267 id1.hash(&mut hasher);
268 let hash1 = hasher.finish();
269 let mut hasher = std::hash::DefaultHasher::new();
270 id2.hash(&mut hasher);
271
272 let hash2 = hasher.finish();
273
274 assert_eq!(hash1, hash2);
276}
277
278#[test]
279fn identifier_case() {
280 let detect_case = |s| -> Case { Identifier::no_ref(s).detect_case() };
281
282 assert_eq!(detect_case("PascalCase"), Case::Pascal);
283 assert_eq!(detect_case("lower_snake_case"), Case::LowerSnake);
284 assert_eq!(detect_case("UPPER_SNAKE_CASE"), Case::UpperSnake);
285 assert_eq!(detect_case("notValid123_"), Case::Invalid);
286 assert_eq!(detect_case(""), Case::Invalid);
287 assert_eq!(detect_case("A"), Case::UpperSingleChar); assert_eq!(detect_case("z"), Case::Invalid); assert_eq!(detect_case("_"), Case::Invalid); assert_eq!(detect_case("a_b"), Case::LowerSnake);
291 assert_eq!(detect_case("A_B"), Case::UpperSnake);
292
293 println!("All tests passed.");
294}