//! This module contains unit tests for the abnf grammar parser. use super::*; #[test] fn test_skip_by() -> Result<(), Box> { let string = std::iter::repeat('a') .take(10) .chain(std::iter::repeat('哈').take(10)) .chain(" \t\n".chars()) .collect::(); let mut s: &str = string.as_str(); dbg!(&s); let alpha_len = skip_by(s, is_alpha); assert_eq!(alpha_len, 10); s = &s[alpha_len..]; dbg!(&s); let not_newline_len = skip_by(s, is_not_newline); // 哈 takes 3 bytes, so the count should be 30 plus two bytes for // the space and the tab. assert_eq!(not_newline_len, 32); Ok(()) } #[test] fn test_skip_c_nl() -> Result<(), Box> { let string = std::iter::repeat(' ') .take(10) .chain(std::iter::repeat('哈').take(10)) .chain(" \t\n".chars()) .collect::(); let s = string.as_str(); dbg!(s); let c_nl = skip_c_nl(s); assert!(c_nl.is_none()); let new_string = std::iter::once(';') .chain(string.chars()) .chain(string.chars()) .collect::(); let s = new_string.as_str(); dbg!(s); let c_nl = skip_c_nl(s); assert_eq!(c_nl, Some(string.len() + 1)); let c_nl = skip_c_nl("\n"); assert_eq!(c_nl, Some(1)); Ok(()) } #[test] fn test_skip_c_wsp() -> Result<(), Box> { let mut string: String = std::iter::repeat(' ') .take(10) .chain(std::iter::repeat('\t').take(10)) .collect(); string.extend( std::iter::once(';') .chain( std::iter::repeat(std::iter::once('\n').chain(std::iter::once(';'))) .take(10) .flatten(), ) .chain(std::iter::once('\n')) .chain(std::iter::repeat('a').take(10)), ); let string = string; dbg!(&string); let skip_c_wsp_size = skip_c_wsp(&string); assert_eq!(skip_c_wsp_size, string.len() - 10); Ok(()) } #[test] fn test_parse_repetition() -> Result<(), Box> { let strs = ["*A", "0*A", "*0A", "2*A", "*2A", "5*10A", "A"]; let results = strs.map(parse_repetition_spec); dbg!(&results); let answers = [ Some((1, RepetitionSpec::Star)), Some((2, RepetitionSpec::Min(0))), Some((2, RepetitionSpec::Max(0))), Some((2, RepetitionSpec::Min(2))), Some((2, RepetitionSpec::Max(2))), Some((4, RepetitionSpec::MinMax(5, 10))), None, ]; for (result, answer) in results.iter().zip(answers.iter()) { assert_eq!(result, answer); } Ok(()) } use super::Error; #[test] fn test_parse_rulename() -> Result<(), Box> { let s = "NONTERMINAL"; assert!(matches!(parse_rulename(s), Err(Error::HangingRuleName(0)))); let s = " NONTERMINAL = "; assert!(matches!(parse_rulename(s), Ok(0))); let s = "NONTERMINAL = "; assert!(matches!(parse_rulename(s), Ok(11))); Ok(()) } #[test] fn test_parse_numeric() -> Result<(), Box> { let s = "haha"; assert!(matches!( parse_numeric(s, Radix::Binary), Err(Error::InvalidDigit(0)) )); let s = "124"; assert!(matches!( parse_numeric(s, Radix::Binary), Err(Error::InvalidCharacter(1)) )); let s = "32"; assert!(matches!( parse_numeric(s, Radix::Decimal), Ok((2, IntermediateConstruct::Terminal(string))) if string == " ".to_string() )); let s = "32.65.66.66.65"; assert!(matches!( parse_numeric(s, Radix::Decimal), Ok((14, IntermediateConstruct::Terminal(string))) if string == " ABBA".to_string() )); let s = "32-"; assert!(matches!( parse_numeric(s, Radix::Decimal), Err(Error::HangingNumeric(2)) )); let s = "32-124"; assert!(matches!( parse_numeric(s, Radix::Decimal), Ok((6, IntermediateConstruct::Range(from, to))) if from == ' ' && to == '|', )); Ok(()) } #[test] fn test_parse_alternation() -> Result<(), Box> { let s = "0*10nonterminal\n"; let (len, alt) = parse_alternation(s, 0)?; assert_eq!(len, s.len()); let regex = alt.regex; println!("regex = {regex}"); assert_eq!( format!("{regex}"), "((0(0(0(0(0(0(0(0(0(0)?)?)?)?)?)?)?)?)?)?)" ); let s = "0*10nonterminal ( *nonterminal nonterminal / [ nonterminal ] 1*5nonterminal )\n"; let (len, alt) = parse_alternation(s, 0)?; assert_eq!(len, s.len()); let regex = alt.regex; println!("regex = {regex}"); assert_eq!( format!("{regex}"), "((0(0(0(0(0(0(0(0(0(0)?)?)?)?)?)?)?)?)?)?((1)*2|((3))?4(4(4(4(4)?)?)?)?))" ); let s = "5nonterminal\n"; let (len, alt) = parse_alternation(s, 0)?; assert_eq!(len, s.len()); let regex = alt.regex; println!("regex = {regex}"); assert_eq!(format!("{regex}"), "(00000)"); Ok(()) } #[test] fn test_parse_onerule() -> Result<(), Box> { let s = "start = 1*10nonterminal [ nonterminal ] / 1*nonterminal\n"; let (len, (name, data, regex, extra_p)) = parse_one_rule(s, 0)?; println!("name = {name}"); print!("data = "); for (index, non_terminal) in data.iter().enumerate() { print!("({index}, {non_terminal})"); } println!(); println!("regex = {regex}"); println!("extra_p = {extra_p}"); assert_eq!(len, s.len()); assert_eq!(name, "start".to_string()); let answers = ["nnonterminal", "nnonterminal", "nnonterminal"]; assert_eq!(data.len(), answers.len()); for (tnt, answer) in data.into_iter().zip(answers) { assert_eq!(format!("{tnt}"), answer.to_string()); } assert_eq!( format!("{regex}"), "(0(0(0(0(0(0(0(0(0(0)?)?)?)?)?)?)?)?)?((1))?|(2)+)".to_string() ); assert!(!extra_p); Ok(()) } #[test] fn test_parse_grammar() -> Result<(), Box> { let grammar_file_name = "abnf grammars/test.abnf"; let file_content = std::fs::read_to_string(grammar_file_name)?; let grammar: Grammar = file_content.parse()?; println!("print grammar:"); println!("{grammar}"); Ok(()) }