diff options
author | JSDurand <mmemmew@gmail.com> | 2023-07-08 12:30:21 +0800 |
---|---|---|
committer | JSDurand <mmemmew@gmail.com> | 2023-07-08 12:31:13 +0800 |
commit | 9a317e56f8a6126583f7d0c431bf878d9b1fe7b1 (patch) | |
tree | 7bb6004196b38446a5ab0cb3a0ab642d35f113e9 /grammar/src/abnf/tests.rs | |
parent | 691f969eb104fa3d4c2a1667693fd0382eb9d6b5 (diff) |
Finished the Emacs binding.
Now the binding part is finished.
What remains is a bug encountered when planting a fragment to the
forest which intersects a packed node, which would lead to invalid
forests. This will also cause problem when planting a packed
fragment, but until now my testing grammars do not produce packed
fragments, so this problem is not encountered yet.
I am still figuring out efficient ways to solve this problem.
Diffstat (limited to 'grammar/src/abnf/tests.rs')
-rw-r--r-- | grammar/src/abnf/tests.rs | 291 |
1 files changed, 291 insertions, 0 deletions
diff --git a/grammar/src/abnf/tests.rs b/grammar/src/abnf/tests.rs new file mode 100644 index 0000000..67dfaaa --- /dev/null +++ b/grammar/src/abnf/tests.rs @@ -0,0 +1,291 @@ +//! This module contains unit tests for the abnf grammar parser. + +use super::*; + +#[test] +fn test_skip_by() -> Result<(), Box<dyn std::error::Error>> { + let string = std::iter::repeat('a') + .take(10) + .chain(std::iter::repeat('哈').take(10)) + .chain(" \t\n".chars()) + .collect::<String>(); + + 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<dyn std::error::Error>> { + let string = std::iter::repeat(' ') + .take(10) + .chain(std::iter::repeat('哈').take(10)) + .chain(" \t\n".chars()) + .collect::<String>(); + + 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::<String>(); + + 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<dyn std::error::Error>> { + 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<dyn std::error::Error>> { + 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<dyn std::error::Error>> { + 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<dyn std::error::Error>> { + 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<dyn std::error::Error>> { + 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<dyn std::error::Error>> { + 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<dyn std::error::Error>> { + 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(()) +} |