Masked to string

This commit is contained in:
AlexanderHD27
2025-06-09 21:12:02 +02:00
parent 65c6b69b50
commit 2286ce4eec
3 changed files with 133 additions and 7 deletions

View File

@@ -0,0 +1,114 @@
use crate::configotron::fsm::TransitionMask;
pub fn mask_to_string(mask: TransitionMask) -> String {
let mut last_bit: bool = false;
let mut range_start: Option<u32> = None;
let mut parts: Vec<String> = Vec::new();
fn num_to_string(i: u8) -> String {
match i {
0x00 => "NUL".to_string(),
0x09 => "TAB".to_string(),
0x1B => "ESC".to_string(),
0x20 => "SP".to_string(),
0x7F => "DEL".to_string(),
0x21..=0x7E => format!("\'{}\'", (i as char).to_string()),
_ => format!("0x{:02X}", i),
}
}
for i in 0..=0x7F {
let current_bit = (mask & (1 << i)) > 0;
match (current_bit, last_bit, range_start) {
(true, false, None) => {
// Start of a new range
range_start = Some(i);
last_bit = true;
}
(true, true, Some(_)) => {
// Continue current range
last_bit = true;
}
(false, true, Some(start)) => {
// End of a range
if start == i - 1 {
parts.push(num_to_string(start as u8));
} else if start == i - 2 {
parts.push(num_to_string(start as u8));
parts.push(num_to_string((i - 1) as u8));
} else {
parts.push(format!("{}..{}", num_to_string(start as u8), num_to_string((i - 1) as u8)));
}
range_start = None;
last_bit = false;
}
(false, false, None) => {
// Nothing to do
last_bit = false;
}
_ => {}
}
}
match (last_bit, range_start) {
(_, None) => {},
(_, Some(start)) => {
if start == 0x7f {
parts.push(num_to_string(start as u8));
} else if start == 0x7e {
parts.push(num_to_string(start as u8));
parts.push(num_to_string((0x7f) as u8));
} else {
parts.push(format!("{}..{}", num_to_string(start as u8), num_to_string((0x7f - 1) as u8)));
}
}
}
parts.join(",")
}
#[cfg(test)]
mod test {
use crate::configotron::fsm::{display::mask_to_string, masks::{mask_all, mask_char, mask_char_range}};
#[test]
fn mask_to_string_test() {
mask_to_string(mask_char('\x7F'));
assert_eq!(mask_to_string(mask_char('A')), "'A'".to_string());
assert_eq!(mask_to_string(mask_all()), "NUL..'~'".to_string());
assert_eq!(mask_to_string(mask_char('z')), "'z'".to_string());
assert_eq!(mask_to_string(mask_char('0')), "'0'".to_string());
assert_eq!(mask_to_string(mask_char_range('0', '9')), "'0'..'9'".to_string());
assert_eq!(mask_to_string(mask_char_range('x', 'z')), "'x'..'z'".to_string());
assert_eq!(mask_to_string(mask_char('a') | mask_char('c')), "'a','c'".to_string());
assert_eq!(mask_to_string(mask_char('a') | mask_char('b') | mask_char('c')), "'a'..'c'".to_string());
assert_eq!(mask_to_string(mask_char_range('a', 'c') | mask_char('e')), "'a'..'c','e'".to_string());
assert_eq!(mask_to_string(mask_char('\t')), "TAB".to_string());
assert_eq!(mask_to_string(mask_char('\x7F')), "DEL".to_string());
assert_eq!(mask_to_string(mask_char('\x00')), "NUL".to_string());
assert_eq!(mask_to_string(mask_char_range('A', 'A')), "'A'".to_string());
assert_eq!(mask_to_string(mask_char(' ')), "SP".to_string());
assert_eq!(mask_to_string(mask_char('\x1B')), "ESC".to_string());
assert_eq!(
mask_to_string(
mask_char('a')
| mask_char('c')
| mask_char_range('e', 'g')
| mask_char('i')
| mask_char_range('k', 'm')
| mask_char('o')
| mask_char('q')
| mask_char('s')
| mask_char('u')
| mask_char('w')
| mask_char('y')
| mask_char('z')
),
"'a','c','e'..'g','i','k'..'m','o','q','s','u','w','y','z'".to_string()
);
}
}

View File

@@ -36,7 +36,7 @@ pub fn mask_number_range(start: u8, end: u8) -> TransitionMask {
mask
}
pub fn mask_number_char(c: char) -> TransitionMask {
pub fn mask_char(c: char) -> TransitionMask {
if c.is_ascii() && (c as u8) <= 0x7F {
1 << (c as u8)
} else {
@@ -48,6 +48,18 @@ pub fn mask_all() -> TransitionMask {
u128::MAX
}
pub fn mask_char_range(start: char, end: char) -> TransitionMask {
let mut mask: TransitionMask = 0;
let start_u8 = start as u32;
let end_u8 = end as u32;
for c in start_u8..=end_u8 {
if c <= 0x7F {
mask |= 1 << (c as u8);
}
}
mask
}
#[cfg(test)]
mod test {
use crate::configotron::fsm::masks::decompose_transition;

View File

@@ -155,7 +155,7 @@ pub fn run_fsm(fsm: &FiniteStateMachine, input: &String) -> StateType {
#[cfg(test)]
mod test {
use crate::configotron::fsm::{masks::{mask_all, mask_number_char}, run_fsm, Action, FSMBuilder, StateType};
use crate::configotron::fsm::{masks::{mask_all, mask_char}, run_fsm, Action, FSMBuilder, StateType};
#[test]
pub fn basic_fsm_build() {
@@ -181,13 +181,13 @@ mod test {
// u5: * -> u5
// Error/Default: z3
fsm_builder.add_link(&z0, &z1, Action::None, mask_number_char('a')).unwrap();
fsm_builder.add_link(&z0, &z2, Action::None, mask_number_char('b')).unwrap();
fsm_builder.add_link(&z0, &z1, Action::None, mask_char('a')).unwrap();
fsm_builder.add_link(&z0, &z2, Action::None, mask_char('b')).unwrap();
fsm_builder.add_link(&z1, &z1, Action::None, mask_number_char('a')).unwrap();
fsm_builder.add_link(&z1, &z2, Action::None, mask_number_char('b')).unwrap();
fsm_builder.add_link(&z1, &z1, Action::None, mask_char('a')).unwrap();
fsm_builder.add_link(&z1, &z2, Action::None, mask_char('b')).unwrap();
fsm_builder.add_link(&z2, &z2, Action::None, mask_number_char('a')).unwrap();
fsm_builder.add_link(&z2, &z2, Action::None, mask_char('a')).unwrap();
let res_fsm = fsm_builder.finish(&z3, Action::None, &z0);
assert!(res_fsm.is_ok());