Files
bang/src/syntax/name.rs
2025-11-24 18:31:44 -08:00

157 lines
3.6 KiB
Rust

use crate::syntax::Location;
#[cfg(test)]
use internment::ArcIntern;
use std::cmp;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::sync::atomic::{AtomicU64, Ordering};
static IDENTIFIER_COUNTER: AtomicU64 = AtomicU64::new(0);
#[derive(Clone, Debug)]
pub struct Name {
printable: String,
identifier: u64,
location: Option<Location>,
}
impl cmp::PartialEq for Name {
fn eq(&self, other: &Self) -> bool {
self.identifier == other.identifier
}
}
impl cmp::Eq for Name {}
impl Hash for Name {
fn hash<H: Hasher>(&self, state: &mut H) {
self.identifier.hash(state);
}
}
impl fmt::Display for Name {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}:{}", self.printable, self.identifier)
}
}
impl Name {
pub fn new<S: ToString>(location: Location, s: S) -> Name {
let my_id = IDENTIFIER_COUNTER.fetch_add(1, Ordering::SeqCst);
Name {
printable: s.to_string(),
identifier: my_id,
location: Some(location),
}
}
pub fn gensym(base: &'static str) -> Name {
let formatted = format!("<{base}>");
let my_id = IDENTIFIER_COUNTER.fetch_add(1, Ordering::SeqCst);
Name {
printable: formatted,
identifier: my_id,
location: None,
}
}
pub fn as_printed(&self) -> &str {
self.printable.as_str()
}
pub fn bind_to(&mut self, other: &Name) {
self.identifier = other.identifier;
}
pub fn location(&self) -> Option<&Location> {
self.location.as_ref()
}
}
#[test]
fn equality() {
let file = ArcIntern::new("/foo.bang".into());
let loc1 = Location::new(&file, 0..3);
let loc2 = Location::new(&file, 9..12);
assert_ne!(Name::gensym("x"), Name::gensym("x"));
assert_ne!(Name::new(loc1.clone(), "x"), Name::new(loc1.clone(), "x"));
assert_eq!(
Name {
printable: "x".into(),
identifier: 5,
location: Some(loc1.clone())
},
Name {
printable: "x".into(),
identifier: 5,
location: Some(loc2.clone())
}
);
assert_eq!(
Name {
printable: "x".into(),
identifier: 5,
location: Some(loc1.clone())
},
Name {
printable: "x".into(),
identifier: 5,
location: None
}
);
assert_eq!(
Name {
printable: "x".into(),
identifier: 5,
location: Some(loc1.clone())
},
Name {
printable: "y".into(),
identifier: 5,
location: None
}
);
}
#[test]
fn hashing() {
let file = ArcIntern::new("/foo.bang".into());
let loc1 = Location::new(&file, 0..3);
let loc2 = Location::new(&file, 9..12);
let x1 = Name {
printable: "x".into(),
identifier: 1,
location: Some(loc1),
};
let mut x2 = Name {
printable: "x".into(),
identifier: 2,
location: Some(loc2),
};
let y1 = Name {
printable: "y".into(),
identifier: 1,
location: None,
};
let run_hash = |name: &Name| {
let mut hash = std::hash::DefaultHasher::new();
name.hash(&mut hash);
hash.finish()
};
let hash_x1 = run_hash(&x1);
let hash_x2 = run_hash(&x2);
let hash_y1 = run_hash(&y1);
assert_ne!(hash_x1, hash_x2);
assert_eq!(hash_x1, hash_y1);
x2.bind_to(&x1);
let rehashed_x2 = run_hash(&x2);
assert_eq!(hash_x1, rehashed_x2);
}