Refactor Registry to take &str, not &LabelName
This commit is contained in:
+29
-16
@@ -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
@@ -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(", ");
|
||||||
|
|||||||
Reference in New Issue
Block a user