spade_common/
name.rs

1use std::hash::Hash;
2
3use serde::{de::Visitor, Deserialize, Serialize};
4
5use crate::{
6    interning::INTERNER,
7    location_info::{Loc, WithLocation},
8};
9
10#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
11pub enum Visibility {
12    Implicit,
13    Public,
14    AtLib,
15    AtSelf,
16    AtSuper,
17    // Super-super visibility is an implementation detail, not accessible through Spade syntax.
18    // It arises when enum variants inherit their parent enum's visibility. Because the enum
19    // introduces a new namespace, relative visibility needs to be shifted (`AtSelf` becomes
20    // `AtSuper` and `AtSuper` becomes `AtSuperSuper`).
21    AtSuperSuper,
22}
23
24#[derive(Debug, Clone, Copy, Eq)]
25#[repr(transparent)]
26pub struct Identifier(&'static str);
27
28impl Identifier {
29    pub fn intern(ident: &str) -> Self {
30        Self(INTERNER.intern(ident))
31    }
32
33    pub fn as_str(&self) -> &'static str {
34        self.0
35    }
36}
37
38impl std::fmt::Display for Identifier {
39    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
40        write!(f, "{}", self.0)
41    }
42}
43
44impl PartialEq for Identifier {
45    fn eq(&self, other: &Self) -> bool {
46        ::core::ptr::eq(self.0, other.0)
47    }
48}
49
50impl std::hash::Hash for Identifier {
51    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
52        self.0.as_ptr().hash(state);
53    }
54}
55
56impl Serialize for Identifier {
57    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
58    where
59        S: serde::Serializer,
60    {
61        serializer.serialize_str(self.0)
62    }
63}
64
65impl<'de> Deserialize<'de> for Identifier {
66    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
67    where
68        D: serde::Deserializer<'de>,
69    {
70        struct IdentVisitor;
71        impl<'de> Visitor<'de> for IdentVisitor {
72            type Value = Identifier;
73
74            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
75                formatter.write_str("Identifier")
76            }
77
78            fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
79            where
80                E: serde::de::Error,
81            {
82                Ok(Identifier::intern(v))
83            }
84        }
85
86        deserializer.deserialize_str(IdentVisitor)
87    }
88}
89
90#[derive(Debug)]
91pub enum PathPrefix {
92    FromLib,
93    FromSelf,
94    FromSuper(usize),
95    None,
96}
97
98#[derive(PartialEq, Debug, Clone, Eq, Hash, Serialize, Deserialize)]
99pub enum PathSegment {
100    Named(Loc<Identifier>),
101    Impl(u64),
102    IfT,
103    IfF,
104}
105
106impl PathSegment {
107    pub fn is_named(&self) -> bool {
108        match self {
109            PathSegment::Named(_) => true,
110            PathSegment::Impl(_) | PathSegment::IfT | PathSegment::IfF => false,
111        }
112    }
113
114    pub fn to_named_str(&self) -> Option<&str> {
115        match self {
116            PathSegment::Named(s) => Some(s.0),
117            PathSegment::Impl(_) | PathSegment::IfT | PathSegment::IfF => None,
118        }
119    }
120
121    pub fn loc(&self) -> Loc<()> {
122        match self {
123            PathSegment::Named(ident) => ident.loc(),
124            PathSegment::Impl(_) | PathSegment::IfT | PathSegment::IfF => ().nowhere(),
125        }
126    }
127
128    pub fn unwrap_named(&self) -> &Loc<Identifier> {
129        match self {
130            PathSegment::Named(ident) => ident,
131            PathSegment::Impl(_) | PathSegment::IfT | PathSegment::IfF => {
132                panic!("called `PathSegment::unwrap_named()` on a generated path segment")
133            }
134        }
135    }
136}
137
138impl std::fmt::Display for PathSegment {
139    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
140        match self {
141            PathSegment::Named(ident) => write!(f, "{}", ident),
142            PathSegment::Impl(n) => write!(f, "impl#{n}"),
143            PathSegment::IfT => write!(f, "if#true"),
144            PathSegment::IfF => write!(f, "if#false"),
145        }
146    }
147}
148
149#[derive(PartialEq, Debug, Clone, Eq, Hash, Serialize, Deserialize)]
150pub struct Path(pub Vec<PathSegment>);
151
152impl Path {
153    pub fn to_strings(&self) -> Vec<String> {
154        self.0.iter().map(PathSegment::to_string).collect()
155    }
156
157    pub fn to_named_strs(&self) -> Vec<Option<&str>> {
158        self.0.iter().map(PathSegment::to_named_str).collect()
159    }
160
161    /// Generate a path from a list of strings
162    pub fn from_strs(elems: &[&str]) -> Self {
163        Path(
164            elems
165                .iter()
166                .map(|x| PathSegment::Named(Identifier::intern(x).nowhere()))
167                .collect(),
168        )
169    }
170
171    /// Generate a path from a list of Loc<Identifier>
172    pub fn from_idents(elems: &[&Loc<Identifier>]) -> Self {
173        Path(
174            elems
175                .iter()
176                .map(|x| PathSegment::Named((*x).clone()))
177                .collect(),
178        )
179    }
180
181    pub fn ident(ident: Loc<Identifier>) -> Self {
182        Self(vec![PathSegment::Named(ident)])
183    }
184
185    pub fn ident_with_loc(ident: Loc<Identifier>) -> Loc<Self> {
186        let loc = ident.loc();
187        Self::ident(ident).at_loc(&loc)
188    }
189
190    pub fn push_segment(&self, segment: PathSegment) -> Path {
191        let mut result = self.clone();
192        result.0.push(segment);
193        result
194    }
195
196    pub fn push_ident(&self, ident: Loc<Identifier>) -> Path {
197        let mut result = self.clone();
198        result.0.push(PathSegment::Named(ident));
199        result
200    }
201
202    pub fn pop(&self) -> Self {
203        let mut result = self.clone();
204        result.0.pop().expect("Failed to pop identifier from path");
205        result
206    }
207
208    pub fn join(&self, other: Path) -> Path {
209        let mut result = self.clone();
210        for segment in other.0 {
211            result.0.push(segment);
212        }
213        result
214    }
215
216    pub fn extract_prefix(&self) -> (PathPrefix, Path) {
217        let Some(segment) = self.0.first() else {
218            return (PathPrefix::None, self.clone());
219        };
220
221        let (prefix, count) = match segment.to_named_str() {
222            Some("lib") => (PathPrefix::FromLib, 1),
223            Some("self") => (PathPrefix::FromSelf, 1),
224            Some("super") => {
225                let levels = self
226                    .0
227                    .iter()
228                    .take_while(|s| s.to_named_str() == Some("super"))
229                    .count();
230                (PathPrefix::FromSuper(levels), levels)
231            }
232            _ => (PathPrefix::None, 0),
233        };
234
235        let path_without_prefix = Path(Vec::from(&self.0[count..]));
236        (prefix, path_without_prefix)
237    }
238
239    /// The last element of the path. Panics if the path is empty
240    pub fn tail(&self) -> PathSegment {
241        self.0
242            .last()
243            .expect("Tried getting tail of empty path")
244            .clone()
245    }
246
247    /// Returns the whole path apart from the tail. Panics if the path is empty
248    pub fn prelude(&self) -> Path {
249        Self(self.0[0..self.0.len() - 1].to_owned())
250    }
251
252    pub fn starts_with(&self, other: &Path) -> bool {
253        self.0.iter().zip(&other.0).all(|(l, r)| l == r)
254    }
255}
256
257impl std::fmt::Display for Path {
258    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
259        write!(f, "{}", self.to_strings().join("::"))
260    }
261}
262
263/// Anything named will get assigned a unique name ID during AST lowering in order to avoid caring
264/// about scopes once HIR has been generated. This is the type of those IDs
265///
266/// The associated string is only used for formatting when printing. The hash and eq methods do not
267/// use it
268#[derive(Clone, Serialize, Deserialize)]
269pub struct NameID(pub u64, pub Path);
270
271impl std::cmp::PartialEq for NameID {
272    fn eq(&self, other: &Self) -> bool {
273        self.0 == other.0
274    }
275}
276
277impl std::cmp::Eq for NameID {}
278
279impl std::cmp::PartialOrd for NameID {
280    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
281        Some(self.0.cmp(&other.0))
282    }
283}
284
285impl std::cmp::Ord for NameID {
286    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
287        self.0.cmp(&other.0)
288    }
289}
290
291impl std::hash::Hash for NameID {
292    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
293        self.0.hash(state);
294    }
295}
296
297impl std::fmt::Debug for NameID {
298    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
299        write!(f, "{}#{}", self.1, self.0)
300    }
301}
302impl std::fmt::Display for NameID {
303    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
304        write!(f, "{}", self.1)
305    }
306}
307
308pub mod testutil {
309    use super::*;
310    pub fn name_id(id: u64, name: &str) -> Loc<NameID> {
311        NameID(id, Path::from_strs(&[name])).nowhere()
312    }
313
314    /// Shorthand for creating a name_id with static strs as name
315    pub fn name_id_p(id: u64, name: &[&str]) -> Loc<NameID> {
316        NameID(id, Path::from_strs(name)).nowhere()
317    }
318}