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, rename: Option>, 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(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(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 { 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) { self.rename = Some(new_name.clone()); } pub fn intern(&self) -> ArcIntern { 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(&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) } }