diff options
| author | omagdy7 <omar.professional8777@gmail.com> | 2023-12-04 13:46:11 +0200 |
|---|---|---|
| committer | omagdy7 <omar.professional8777@gmail.com> | 2023-12-04 13:46:11 +0200 |
| commit | 253cebb03b9e630189b644bb1023fef2f506f652 (patch) | |
| tree | 02b6eeb0a81f35cdb2e6dde6fa671100f2b47b18 | |
| parent | ef3ef65e3c6d1cb1b799b8d65847e6f4f228ca3c (diff) | |
| download | rex-253cebb03b9e630189b644bb1023fef2f506f652.tar.xz rex-253cebb03b9e630189b644bb1023fef2f506f652.zip | |
Added a more ergonomic API for constructing Regex
| -rw-r--r-- | src/regex.rs | 139 |
1 files changed, 68 insertions, 71 deletions
diff --git a/src/regex.rs b/src/regex.rs index c2465aa..2ef4c16 100644 --- a/src/regex.rs +++ b/src/regex.rs @@ -2,100 +2,55 @@ macro_rules! Sym { ($c:expr) => { - RegexToken::Symbol($c) + Regex::Symbol($c) }; } macro_rules! Star { ($c:expr) => { - RegexToken::Star(Box::new($c)) + Regex::Star(Box::new($c)) }; } macro_rules! Plus { ($c:expr) => { - RegexToken::Plus(Box::new($c)) + Regex::Plus(Box::new($c)) }; } macro_rules! Concat { ($a:expr, $b:expr) => { - RegexToken::Concat((Box::new($a), Box::new($b))) + Regex::Concat((Box::new($a), Box::new($b))) }; } macro_rules! Union { ($a:expr, $b:expr) => { - RegexToken::Union((Box::new($a), Box::new($b))) + Regex::Union((Box::new($a), Box::new($b))) }; } -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_concat() { - assert_eq!( - Regex::new(String::from("ab")), - Concat!(Sym!('a'), Sym!('b')) - ) - } - - #[test] - fn test_plus() { - assert_eq!( - Regex::new(String::from("(a|b)+c")), - Concat!(Plus!(Union!(Sym!('a'), Sym!('b'))), Sym!('c')) - ) - } - - #[test] - fn test_union() { - assert_eq!( - Regex::new(String::from("(a|b)")), - Union!(Sym!('a'), Sym!('b')) - ) - } - - #[test] - fn test_none() { - assert_eq!(Regex::new(String::from("")), RegexToken::None) - } - - #[test] - fn test_star() { - assert_eq!( - Regex::new(String::from("a*b")), - Concat!(Star!(Sym!('a')), Sym!('b')) - ) - } -} - -type ReToken = Box<RegexToken>; +type RegexToken = Box<Regex>; #[derive(Debug, PartialEq, Clone)] -pub enum RegexToken { +pub enum Regex { Symbol(char), - Concat((ReToken, ReToken)), - Union((ReToken, ReToken)), - Plus(ReToken), - Star(ReToken), + Concat((RegexToken, RegexToken)), + Union((RegexToken, RegexToken)), + Plus(RegexToken), + Star(RegexToken), Dot, None, } -#[derive(Debug, PartialEq)] -pub struct Regex {} - impl Regex { - pub fn new(input: String) -> RegexToken { + pub fn new(input: String) -> Regex { Regex::parse(input) } - fn parse(input: String) -> RegexToken { + fn parse(input: String) -> Regex { if input.is_empty() { - return RegexToken::None; + return Regex::None; } let mut chars = input.chars().peekable(); @@ -105,38 +60,38 @@ impl Regex { } fn parse_expression( - left: &mut RegexToken, + left: &mut Regex, chars: &mut std::iter::Peekable<std::str::Chars>, - ) -> RegexToken { + ) -> Regex { while let Some(&next) = chars.peek() { match next { '|' => { chars.next(); // Consume '|' let right = Self::parse_token(chars); - *left = RegexToken::Union((Box::new(left.clone()), Box::new(right))); + *left = Regex::Union((Box::new(left.clone()), Box::new(right))); } '*' => { chars.next(); // Consume '*' let right = Self::parse_token(chars); - *left = RegexToken::Concat(( - Box::new(RegexToken::Star(Box::new(left.clone()))), + *left = Regex::Concat(( + Box::new(Regex::Star(Box::new(left.clone()))), Box::new(right), )); } '+' => { chars.next(); // Consume '+' let right = Self::parse_token(chars); - *left = RegexToken::Concat(( - Box::new(RegexToken::Plus(Box::new(left.clone()))), + *left = Regex::Concat(( + Box::new(Regex::Plus(Box::new(left.clone()))), Box::new(right), )); } _ => { let right = Self::parse_token(chars); - if let RegexToken::None = right { + if let Regex::None = right { // do nothing } else { - *left = RegexToken::Concat((Box::new(left.clone()), Box::new(right))); + *left = Regex::Concat((Box::new(left.clone()), Box::new(right))); } } } @@ -144,16 +99,58 @@ impl Regex { left.clone() } - fn parse_token(chars: &mut std::iter::Peekable<std::str::Chars>) -> RegexToken { + fn parse_token(chars: &mut std::iter::Peekable<std::str::Chars>) -> Regex { match chars.next() { Some('(') => { let token = Self::parse(chars.collect()); chars.next(); // Skip ')' token } - Some('.') => RegexToken::Dot, + Some('.') => Regex::Dot, Some(c) if c.is_ascii_alphanumeric() => Sym!(c), - _ => RegexToken::None, + _ => Regex::None, } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_concat() { + assert_eq!( + Regex::new(String::from("ab")), + Concat!(Sym!('a'), Sym!('b')) + ) + } + + #[test] + fn test_plus() { + assert_eq!( + Regex::new(String::from("(a|b)+c")), + Concat!(Plus!(Union!(Sym!('a'), Sym!('b'))), Sym!('c')) + ) + } + + #[test] + fn test_union() { + assert_eq!( + Regex::new(String::from("(a|b)")), + Union!(Sym!('a'), Sym!('b')) + ) + } + + #[test] + fn test_none() { + assert_eq!(Regex::new(String::from("")), Regex::None) + } + + #[test] + fn test_star() { + assert_eq!( + Regex::new(String::from("a*b")), + Concat!(Star!(Sym!('a')), Sym!('b')) + ) + } +} |
