Refactor Variant to work with char. Add function to select variant.

This commit is contained in:
Lyle Mantooth 2024-02-06 09:34:56 -05:00
parent 0c3d77b90b
commit ef9b4f3769
Signed by: IslandUsurper
GPG key ID: 6DB52EAE123A5789

View file

@ -14,22 +14,22 @@ struct Translation<'a> {
#[derive(Clone)] #[derive(Clone)]
/// Holds the set of delimiters that should be removed from the MML to select a variant. /// Holds the set of delimiters that should be removed from the MML to select a variant.
pub struct Variant<'v> { pub struct Variant {
keep_delim: &'v str, keep_delim: char,
remove_delims: HashSet<&'v str>, remove_delims: HashSet<char>,
} }
impl<'v> Variant<'v> { impl Variant {
/// Get the set of character delimiters to remove from MML for this variant. /// Get the set of character delimiters to remove from MML for this variant.
pub fn ignores(&self) -> &HashSet<&str> { pub fn ignores(&self) -> &HashSet<char> {
&self.remove_delims &self.remove_delims
} }
} }
/// The list of all variants defined in an MML file. /// The list of all variants defined in an MML file.
pub struct VariantList<'v> { pub struct VariantList<'v> {
all_delims: HashSet<&'v str>, all_delims: HashSet<char>,
variants: BTreeMap<&'v str, Variant<'v>>, variants: BTreeMap<&'v str, Variant>,
} }
impl<'v> VariantList<'v> { impl<'v> VariantList<'v> {
@ -43,14 +43,16 @@ impl<'v> VariantList<'v> {
fn finish(mut self) -> Self { fn finish(mut self) -> Self {
for (_, v) in self.variants.iter_mut() { for (_, v) in self.variants.iter_mut() {
v.remove_delims = self.all_delims.clone(); v.remove_delims = self.all_delims.clone();
v.remove_delims.remove(v.keep_delim); v.remove_delims.remove(&v.keep_delim);
} }
if self.variants.is_empty() { if self.variants.is_empty() {
self.variants.insert( self.variants.insert(
"_default_", "_default_",
Variant { Variant {
keep_delim: "", // Not that we expect to find NUL bytes, but effectively we will remove all
// delimiters with this variant.
keep_delim: '\0',
remove_delims: self.all_delims.clone(), remove_delims: self.all_delims.clone(),
}, },
); );
@ -115,17 +117,17 @@ pub fn get_variants<'a>(input: &'a str, sfx_mode: SfxMode) -> VariantList<'a> {
if let Some((first, second)) = l.trim().rsplit_once(char::is_whitespace) { if let Some((first, second)) = l.trim().rsplit_once(char::is_whitespace) {
use SfxMode::*; use SfxMode::*;
match sfx_mode { match sfx_mode {
Off => state.all_delims.insert(first), Off => state.all_delims.insert(first_char(first)),
On => state.all_delims.insert(second), On => state.all_delims.insert(first_char(second)),
}; };
} }
} else if let Some(l) = line.strip_prefix("#VARIANT") { } else if let Some(l) = line.strip_prefix("#VARIANT") {
let (first, second) = { let (first, second) = {
let rest = l.trim(); let rest = l.trim();
if let Some((f, s)) = rest.rsplit_once(' ') { if let Some((f, s)) = rest.rsplit_once(' ') {
(f, s) (first_char(f), s)
} else { } else {
(rest, "_default_") (first_char(rest), "_default_")
} }
}; };
@ -153,3 +155,42 @@ pub fn get_variants<'a>(input: &'a str, sfx_mode: SfxMode) -> VariantList<'a> {
}) })
.finish() .finish()
} }
fn first_char(s: &str) -> char {
s.chars().next().unwrap_or('\0')
}
pub fn select_variant<'i>(input: &'i str, var: &'i Variant) -> String {
let mut output = input.to_string();
// Workspace for converting char to &str in the loop.
let mut buf = [0; 4];
// Set of chars to find in `input`.
let charlist: Vec<char> = var
.remove_delims
.iter()
.copied()
.chain([var.keep_delim])
.collect();
let mut needles = input.rmatch_indices(&charlist[..]).peekable();
while let Some((idx, c)) = needles.next() {
match c {
// Remove the char if it is the `keep_delim`.
d if var.keep_delim.encode_utf8(&mut buf) == d => {
// `replace_range` doesn't make a full copy of the string the way `remove` does.
output.replace_range(idx..=idx, "");
}
// Anything else will be in `remove_delim`, so find a matching pair and remove
// them and everything between them.
d => match needles.peek() {
Some(&(idx_1, d2)) if d == d2 => {
needles.next();
output.replace_range(idx_1..=idx, "");
}
_ => {}
},
}
}
output
}