use compact_str::CompactString;
use thiserror::Error;
use crate::{span::Span, typechecker::map_stack::MapStack};
pub const LAST_RESULT_IDENTIFIERS: &[&str] = &["ans", "_"];
#[derive(Debug, Clone, Error, PartialEq, Eq)]
pub enum NameResolutionError {
#[error("Identifier is already in use{}: '{conflicting_identifier}'.",
if let Some(t) = .original_item_type { format!(" by the {t}") } else { "".to_owned() })]
IdentifierClash {
conflicting_identifier: String,
conflict_span: Span,
original_span: Span,
original_item_type: Option<String>,
},
#[error("Reserved identifier")]
ReservedIdentifier(Span),
}
#[derive(Debug, Clone, Default)]
pub struct Namespace {
seen: MapStack<CompactString, (CompactString, Span)>,
}
impl Namespace {
pub(crate) fn save(&mut self) {
self.seen.save()
}
pub(crate) fn restore(&mut self) {
self.seen.restore()
}
pub fn add_identifier_allow_override(
&mut self,
name: CompactString,
span: Span,
item_type: CompactString,
) -> Result<(), NameResolutionError> {
self.add_impl(name, span, item_type, true)
}
pub fn add_identifier(
&mut self,
name: CompactString,
span: Span,
item_type: CompactString,
) -> Result<(), NameResolutionError> {
self.add_impl(name, span, item_type, false)
}
pub fn has_identifier(&self, name: &str) -> bool {
self.seen.contains_key(name)
}
fn add_impl(
&mut self,
name: CompactString,
span: Span,
item_type: CompactString,
allow_override: bool,
) -> Result<(), NameResolutionError> {
if let Some((original_item_type, original_span)) = self.seen.get(&name) {
if original_span == &span {
return Ok(());
}
if allow_override && original_item_type == item_type {
return Ok(());
}
return Err(NameResolutionError::IdentifierClash {
conflicting_identifier: name.to_string(),
conflict_span: span,
original_span: *original_span,
original_item_type: Some(original_item_type.to_string()),
});
}
self.seen.insert(name, (item_type, span));
Ok(())
}
}