Refactor Registry to take &str, not &LabelName

This commit is contained in:
2025-06-20 01:39:46 -07:00
parent 71906cf4b8
commit 34f808e042
2 changed files with 90 additions and 110 deletions
+29 -16
View File
@@ -17,22 +17,7 @@ pub fn process_labels(board: &mut Board) {
let mut registry = Registry::new(); let mut registry = Registry::new();
resolve_local_labels(&mut stats); resolve_local_labels(&mut stats);
assign_named_labels(&mut stats, &mut registry);
// Replace each label with its sanitized equivalent
for stat in stats.iter_mut() {
for chunk in stat.iter_mut() {
match chunk {
Chunk::Verbatim(_) => {}
Chunk::Label {
name,
is_ref: _,
is_anon: _,
} => {
name.name = registry.sanitize(name).into();
}
}
}
}
// Assign names to anonymous labels // Assign names to anonymous labels
anonymous_forward_pass(&mut stats, &mut registry); anonymous_forward_pass(&mut stats, &mut registry);
@@ -87,6 +72,34 @@ fn resolve_local_labels(stats: &mut [ParsedStat]) {
} }
} }
/// Assign sanitized names to all of the named labels.
fn assign_named_labels(stats: &mut [ParsedStat], registry: &mut Registry) {
for stat in stats.iter_mut() {
for chunk in stat.iter_mut() {
match chunk {
Chunk::Label {
name,
is_ref: _,
is_anon: false,
} => {
let mut full_name = CompactString::const_new("");
if let Some(namespace) = &name.namespace {
full_name.push_str(&namespace);
full_name.push('~');
}
full_name.push_str(&name.name);
if let Some(local) = &name.local {
full_name.push('.');
full_name.push_str(&local);
}
name.name = registry.sanitize(&full_name).into();
}
_ => {}
}
}
}
}
/// Simultaneously: /// Simultaneously:
/// 1. Assign names to anonymous labels. /// 1. Assign names to anonymous labels.
/// 2. Resolve anonymous backward references to their label names. /// 2. Resolve anonymous backward references to their label names.
+61 -94
View File
@@ -2,25 +2,14 @@ use std::collections::{HashMap, HashSet};
use compact_str::CompactString; use compact_str::CompactString;
use super::parse::LabelName;
#[derive(PartialEq, Eq, Hash)] #[derive(PartialEq, Eq, Hash)]
struct LabelId(CompactString); struct LabelId(CompactString);
impl LabelId { impl LabelId {
fn new(label: &LabelName) -> Self { fn new(label: &str) -> Self {
let mut result = CompactString::with_capacity(0); let mut result = CompactString::new(label);
if let Some(namespace) = &label.namespace {
result.push_str(&namespace);
result.push('~');
}
result.push_str(&label.name);
if let Some(local) = &label.local {
result.push('.');
result.push_str(&local);
}
result.make_ascii_lowercase(); result.make_ascii_lowercase();
LabelId(result) Self(result)
} }
} }
@@ -36,11 +25,12 @@ enum Transform {
} }
impl Transform { impl Transform {
fn apply(&self, label: &LabelName) -> CompactString { fn apply(&self, label: &str) -> CompactString {
let preferred = preferred_label_name(label);
match self { match self {
Transform::Preferred => Registry::preferred_name(label), Transform::Preferred => preferred,
Transform::FilteredWithSuffix(suffix) => { Transform::FilteredWithSuffix(suffix) => {
let mut result = Registry::preferred_name(label); let mut result = preferred;
result = Registry::filter_label_chars(&result); result = Registry::filter_label_chars(&result);
result.push_str(&suffix); result.push_str(&suffix);
result result
@@ -66,11 +56,11 @@ impl Registry {
} }
} }
pub fn sanitize(&mut self, label: &LabelName) -> CompactString { pub fn sanitize(&mut self, label: &str) -> CompactString {
// Use existing sanitization if one exists // Use existing sanitization if one exists
let id = LabelId::new(label); let id = LabelId::new(label);
if let Some(transform) = self.label_transforms.get(&id) { if let Some(transform) = self.label_transforms.get(&id) {
return transform.apply(&label); return transform.apply(label);
} }
// Try to use label's preferred name as-is // Try to use label's preferred name as-is
@@ -114,22 +104,6 @@ impl Registry {
result result
} }
fn preferred_name(label: &LabelName) -> CompactString {
let mut result = CompactString::const_new("");
if label.namespace.is_some() || label.local.is_some() {
// Prevent namespaces and local labels from starting with a digit.
// This ensures stuff like `#take gems 100.99orless` won't compile
// to `#take gems 10099orless` (would parse incorrectly).
result.push('_');
}
if let Some(local) = &label.local {
result.push_str(local);
} else {
result.push_str(&label.name);
};
result
}
fn is_valid_label(s: &CompactString) -> bool { fn is_valid_label(s: &CompactString) -> bool {
if s.len() == 0 { if s.len() == 0 {
return false; return false;
@@ -159,6 +133,22 @@ impl Registry {
} }
} }
/// Given an unsanitized full name, generate the first-pick name we'd like to
/// assign this label. (Results returned by this function are subject to veto
/// if they are invalid or already taken.)
fn preferred_label_name(s: &str) -> CompactString {
if let Some((_, suffix)) = s.rsplit_once(|c: char| !c.is_ascii_alphanumeric() && c != '_') {
// Prevent namespaces and local labels from starting with a digit.
// This ensures stuff like `#take gems 100.99orless` won't compile
// to `#take gems 10099orless` (would parse incorrectly).
let mut result = CompactString::const_new("_");
result.push_str(suffix);
result
} else {
CompactString::new(s)
}
}
/// Increments a string through label-safe characters. /// Increments a string through label-safe characters.
/// Characters loop through underscore and the letters a-z. /// Characters loop through underscore and the letters a-z.
/// Additionally, the final character is allowed to loop through 0-9. /// Additionally, the final character is allowed to loop through 0-9.
@@ -166,12 +156,10 @@ fn increment(s: &mut CompactString) {
let original_len = s.len(); let original_len = s.len();
// Find a character we can increment without carry // Find a character we can increment without carry
dbg!(&s);
let mut last_char = s.pop(); let mut last_char = s.pop();
while !(last_char == None || last_char != Some('z')) { while !(last_char == None || last_char != Some('z')) {
last_char = s.pop(); last_char = s.pop();
} }
dbg!(s.len(), last_char);
if let Some(c) = last_char { if let Some(c) = last_char {
// Increment character in order: 0-9, then _, then a-z // Increment character in order: 0-9, then _, then a-z
@@ -204,7 +192,7 @@ mod test {
use compact_str::CompactString; use compact_str::CompactString;
use insta::assert_snapshot; use insta::assert_snapshot;
use crate::labels::{parse::LabelName, sanitize::Registry}; use crate::labels::sanitize::Registry;
use super::increment; use super::increment;
@@ -259,64 +247,47 @@ mod test {
fn test_sanitize_simple() { fn test_sanitize_simple() {
let mut reg = Registry::new(); let mut reg = Registry::new();
let mut results = vec![]; let mut results = vec![];
let simple = |s: &str| -> LabelName {
LabelName {
name: s.into(),
namespace: None,
local: None,
}
};
let full = |ns: &str, name: &str, local: &str| -> LabelName {
let mut result = LabelName {
namespace: None,
name: name.into(),
local: None,
};
if ns.len() > 0 {
result.namespace = Some(ns.into());
}
if local.len() > 0 {
result.local = Some(local.into());
}
result
};
let inputs = [ let inputs = [
simple("foo"), "foo",
full("ns1", "foo", ""), "ns1~foo",
full("ns2", "foo", ""), "ns2~foo",
full("", "foo", "thisloop"), "foo.thisloop",
full("", "foo", "thatloop"), "foo.thatloop",
full("", "bar", "thisloop"), "bar.thisloop",
full("", "bar", "thatloop"), "bar.thatloop",
simple("bar1"), "foo$1.thisloop",
simple("bar2"), "foo$1.thatloop",
simple("bar123"), "bar1",
simple("BAR123"), "bar2",
simple("bar456"), "bar123",
simple("BAR456"), "BAR123",
simple("foo2bar"), "bar456",
"BAR456",
"foo2bar",
]; ];
for label in inputs { for label in inputs {
let sanitized = reg.sanitize(&label); let sanitized = reg.sanitize(label);
assert_eq!(sanitized, reg.sanitize(&label)); assert_eq!(sanitized, reg.sanitize(label));
results.push(format!("{:?} => {}", &label, sanitized)); results.push(format!("{:?} => {}", &label, sanitized));
} }
let result = results.join("\n"); let result = results.join("\n");
assert_snapshot!(result, @r#" assert_snapshot!(result, @r#"
LabelName { namespace: None, name: "foo", local: None } => foo "foo" => foo
LabelName { namespace: Some("ns1"), name: "foo", local: None } => _foo "ns1~foo" => _foo
LabelName { namespace: Some("ns2"), name: "foo", local: None } => _foo0 "ns2~foo" => _foo0
LabelName { namespace: None, name: "foo", local: Some("thisloop") } => _thisloop "foo.thisloop" => _thisloop
LabelName { namespace: None, name: "foo", local: Some("thatloop") } => _thatloop "foo.thatloop" => _thatloop
LabelName { namespace: None, name: "bar", local: Some("thisloop") } => _thisloop0 "bar.thisloop" => _thisloop0
LabelName { namespace: None, name: "bar", local: Some("thatloop") } => _thatloop0 "bar.thatloop" => _thatloop0
LabelName { namespace: None, name: "bar1", local: None } => bar1 "foo$1.thisloop" => _thisloop1
LabelName { namespace: None, name: "bar2", local: None } => bar2 "foo$1.thatloop" => _thatloop1
LabelName { namespace: None, name: "bar123", local: None } => bar_ "bar1" => bar1
LabelName { namespace: None, name: "BAR123", local: None } => BAR_ "bar2" => bar2
LabelName { namespace: None, name: "bar456", local: None } => bar_0 "bar123" => bar_
LabelName { namespace: None, name: "BAR456", local: None } => BAR_0 "BAR123" => BAR_
LabelName { namespace: None, name: "foo2bar", local: None } => foo_bar "bar456" => bar_0
"BAR456" => BAR_0
"foo2bar" => foo_bar
"#); "#);
} }
@@ -324,11 +295,7 @@ mod test {
fn test_gen_anonymous() { fn test_gen_anonymous() {
let mut registry = Registry::new(); let mut registry = Registry::new();
for letter in ["a", "e", "i"] { for letter in ["a", "e", "i"] {
registry.sanitize(&LabelName { registry.sanitize(letter);
namespace: None,
name: letter.into(),
local: None,
});
} }
let result: Vec<_> = (0..10).map(|_| registry.gen_anonymous()).collect(); let result: Vec<_> = (0..10).map(|_| registry.gen_anonymous()).collect();
let result = result.join(", "); let result = result.join(", ");