Files
ngr/src/syntax/name.rs

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)
}
}