summaryrefslogtreecommitdiff
path: root/grammar/src/abnf/tests.rs
diff options
context:
space:
mode:
authorJSDurand <mmemmew@gmail.com>2023-07-08 12:30:21 +0800
committerJSDurand <mmemmew@gmail.com>2023-07-08 12:31:13 +0800
commit9a317e56f8a6126583f7d0c431bf878d9b1fe7b1 (patch)
tree7bb6004196b38446a5ab0cb3a0ab642d35f113e9 /grammar/src/abnf/tests.rs
parent691f969eb104fa3d4c2a1667693fd0382eb9d6b5 (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.rs291
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(())
+}