114 lines
3.6 KiB
Rust
114 lines
3.6 KiB
Rust
use crate::syntax::Location;
|
|
use internment::ArcIntern;
|
|
use std::fmt;
|
|
use std::hash::Hash;
|
|
|
|
/// The name of a thing in the source language.
|
|
///
|
|
/// In many ways, you can treat this like a string, but it's a very tricky
|
|
/// string in a couple of ways:
|
|
///
|
|
/// First, it's a string associated with a particular location in the source
|
|
/// file, and you can find out what that source location is relatively easily.
|
|
///
|
|
/// Second, it's a name that retains something of its identity across renaming,
|
|
/// so that you can keep track of what a variables original name was, as well as
|
|
/// what it's new name is if it's been renamed.
|
|
///
|
|
/// Finally, when it comes to equality tests, comparisons, and hashing, `Name`
|
|
/// uses *only* the new name, if the variable has been renamed, or the original
|
|
/// name, if it has not been renamed. It never uses the location. This allows
|
|
/// relatively fast hashing and searching for things like binding sites, as the
|
|
/// value of the binding `Name` will be equal to the bound `Name`, even though
|
|
/// they occur at different locations.
|
|
#[derive(Clone, Debug)]
|
|
pub struct Name {
|
|
name: ArcIntern<String>,
|
|
rename: Option<ArcIntern<String>>,
|
|
location: Location,
|
|
}
|
|
|
|
impl Name {
|
|
/// Create a new name at the given location.
|
|
///
|
|
/// This creates an "original" name, which has not been renamed, at the
|
|
/// given location.
|
|
pub fn new<S: ToString>(n: S, location: Location) -> Name {
|
|
Name {
|
|
name: ArcIntern::new(n.to_string()),
|
|
rename: None,
|
|
location,
|
|
}
|
|
}
|
|
|
|
/// Create a new name with no location information.
|
|
///
|
|
/// This creates an "original" name, which has not been renamed, at the
|
|
/// given location. You should always prefer to use [`Location::new`] if
|
|
/// there is any possible way to get it, because that will be more
|
|
/// helpful to our users.
|
|
pub fn manufactured<S: ToString>(n: S) -> Name {
|
|
Name {
|
|
name: ArcIntern::new(n.to_string()),
|
|
rename: None,
|
|
location: Location::manufactured(),
|
|
}
|
|
}
|
|
|
|
/// Returns a reference to the original name of the variable.
|
|
///
|
|
/// Regardless of whether or not the function has been renamed, this will
|
|
/// return whatever name this variable started with.
|
|
pub fn original_name(&self) -> &str {
|
|
self.name.as_str()
|
|
}
|
|
|
|
/// Returns a reference to the current name of the variable.
|
|
///
|
|
/// If the variable has been renamed, it will return that, otherwise we'll
|
|
/// return the current name.
|
|
pub fn current_name(&self) -> &str {
|
|
self.rename.as_ref().map(|x| x.as_str()).unwrap_or_else(|| self.name.as_str())
|
|
}
|
|
|
|
/// Returns the current name of the variable as an interned string.
|
|
pub fn current_interned(&self) -> &ArcIntern<String> {
|
|
self.rename.as_ref().unwrap_or(&self.name)
|
|
}
|
|
|
|
/// Return the location of this name.
|
|
pub fn location(&self) -> &Location {
|
|
&self.location
|
|
}
|
|
|
|
/// Rename this variable to the given value
|
|
pub fn rename(&mut self, new_name: &ArcIntern<String>) {
|
|
self.rename = Some(new_name.clone());
|
|
}
|
|
|
|
pub fn intern(&self) -> ArcIntern<String> {
|
|
self.current_interned().clone()
|
|
}
|
|
}
|
|
|
|
impl PartialEq for Name {
|
|
fn eq(&self, other: &Self) -> bool {
|
|
self.current_interned() == other.current_interned()
|
|
}
|
|
}
|
|
|
|
impl Eq for Name {}
|
|
|
|
impl Hash for Name {
|
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
|
self.current_interned().hash(state)
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for Name {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
self.current_name().fmt(f)
|
|
}
|
|
}
|
|
|