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