midenc_hir/ir/
ident.rs

1use alloc::format;
2use core::{
3    cmp::Ordering,
4    fmt,
5    hash::{Hash, Hasher},
6    str::FromStr,
7};
8
9use anyhow::anyhow;
10
11use super::{
12    interner::{symbols, Symbol},
13    SourceSpan, Spanned,
14};
15use crate::{
16    define_attr_type,
17    formatter::{self, PrettyPrint},
18};
19
20/// Represents a globally-unique module/function name pair, with corresponding source spans.
21#[derive(Copy, Clone, PartialEq, Eq, Hash, Spanned)]
22pub struct FunctionIdent {
23    pub module: Ident,
24    #[span]
25    pub function: Ident,
26}
27define_attr_type!(FunctionIdent);
28impl FunctionIdent {
29    pub fn display(&self) -> impl fmt::Display + '_ {
30        use crate::formatter::*;
31
32        flatten(
33            const_text(self.module.as_str())
34                + const_text("::")
35                + const_text(self.function.as_str()),
36        )
37    }
38}
39impl FromStr for FunctionIdent {
40    type Err = anyhow::Error;
41
42    fn from_str(s: &str) -> Result<Self, Self::Err> {
43        match s.rsplit_once("::") {
44            Some((ns, id)) => {
45                let module = Ident::with_empty_span(Symbol::intern(ns));
46                let function = Ident::with_empty_span(Symbol::intern(id));
47                Ok(Self { module, function })
48            }
49            None => Err(anyhow!(
50                "invalid function name, expected fully-qualified identifier, e.g. \
51                 'std::math::u64::checked_add'"
52            )),
53        }
54    }
55}
56impl fmt::Debug for FunctionIdent {
57    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
58        f.debug_struct("FunctionIdent")
59            .field("module", &self.module.name)
60            .field("function", &self.function.name)
61            .finish()
62    }
63}
64impl fmt::Display for FunctionIdent {
65    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
66        self.pretty_print(f)
67    }
68}
69impl PrettyPrint for FunctionIdent {
70    fn render(&self) -> formatter::Document {
71        use crate::formatter::*;
72
73        flatten(const_text("(") + display(self.module) + const_text(" ") + display(self.function))
74    }
75}
76impl PartialOrd for FunctionIdent {
77    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
78        Some(self.cmp(other))
79    }
80}
81impl Ord for FunctionIdent {
82    fn cmp(&self, other: &Self) -> Ordering {
83        self.module.cmp(&other.module).then(self.function.cmp(&other.function))
84    }
85}
86
87/// Represents an identifier in the IR.
88///
89/// An identifier is some string, along with an associated source span
90#[derive(Copy, Clone, Eq, Spanned)]
91pub struct Ident {
92    pub name: Symbol,
93    #[span]
94    pub span: SourceSpan,
95}
96define_attr_type!(Ident);
97impl Default for Ident {
98    fn default() -> Self {
99        Self {
100            name: symbols::Empty,
101            span: SourceSpan::UNKNOWN,
102        }
103    }
104}
105impl FromStr for Ident {
106    type Err = core::convert::Infallible;
107
108    fn from_str(name: &str) -> Result<Self, Self::Err> {
109        Ok(Self::from(name))
110    }
111}
112impl<'a> From<&'a str> for Ident {
113    fn from(name: &'a str) -> Self {
114        Self::with_empty_span(Symbol::intern(name))
115    }
116}
117impl From<Symbol> for Ident {
118    #[inline]
119    fn from(sym: Symbol) -> Self {
120        Self::with_empty_span(sym)
121    }
122}
123impl From<Ident> for Symbol {
124    #[inline]
125    fn from(id: Ident) -> Self {
126        id.as_symbol()
127    }
128}
129impl Ident {
130    #[inline]
131    pub const fn new(name: Symbol, span: SourceSpan) -> Ident {
132        Ident { name, span }
133    }
134
135    #[inline]
136    pub const fn with_empty_span(name: Symbol) -> Ident {
137        Ident::new(name, SourceSpan::UNKNOWN)
138    }
139
140    #[inline]
141    pub fn as_str(self) -> &'static str {
142        self.name.as_str()
143    }
144
145    #[inline(always)]
146    pub fn as_symbol(self) -> Symbol {
147        self.name
148    }
149
150    // An identifier can be unquoted if is composed of any sequence of printable
151    // ASCII characters, except whitespace, quotation marks, comma, semicolon, or brackets
152    pub fn requires_quoting(&self) -> bool {
153        self.as_str().contains(|c| match c {
154            c if c.is_ascii_control() => true,
155            ' ' | '\'' | '"' | ',' | ';' | '[' | ']' => true,
156            c if c.is_ascii_graphic() => false,
157            _ => true,
158        })
159    }
160}
161impl AsRef<str> for Ident {
162    #[inline(always)]
163    fn as_ref(&self) -> &str {
164        self.as_str()
165    }
166}
167impl alloc::borrow::Borrow<Symbol> for Ident {
168    #[inline]
169    fn borrow(&self) -> &Symbol {
170        &self.name
171    }
172}
173impl alloc::borrow::Borrow<str> for Ident {
174    #[inline]
175    fn borrow(&self) -> &str {
176        self.as_str()
177    }
178}
179impl Ord for Ident {
180    #[inline]
181    fn cmp(&self, other: &Self) -> Ordering {
182        self.as_str().cmp(other.as_str())
183    }
184}
185impl PartialOrd for Ident {
186    #[inline]
187    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
188        Some(self.cmp(other))
189    }
190}
191impl PartialEq for Ident {
192    #[inline]
193    fn eq(&self, rhs: &Self) -> bool {
194        self.name == rhs.name
195    }
196}
197impl PartialEq<Symbol> for Ident {
198    #[inline]
199    fn eq(&self, rhs: &Symbol) -> bool {
200        self.name.eq(rhs)
201    }
202}
203impl PartialEq<str> for Ident {
204    fn eq(&self, rhs: &str) -> bool {
205        self.name.as_str() == rhs
206    }
207}
208impl Hash for Ident {
209    fn hash<H: Hasher>(&self, state: &mut H) {
210        self.name.hash(state);
211    }
212}
213impl fmt::Debug for Ident {
214    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
215        self.pretty_print(f)
216    }
217}
218impl fmt::Display for Ident {
219    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
220        self.pretty_print(f)
221    }
222}
223impl PrettyPrint for Ident {
224    fn render(&self) -> formatter::Document {
225        use crate::formatter::*;
226
227        if self.requires_quoting() {
228            text(format!("\"{}\"", self.as_str().escape_default()))
229        } else {
230            text(format!("#{}", self.as_str()))
231        }
232    }
233}