From 0397a11f1a1f1c63f3d983b2ddda08569f1e3acd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=B6rnquist?= <hugo@lysator.liu.se> Date: Sat, 16 Sep 2023 02:44:19 +0200 Subject: [PATCH] Add more tests. --- tests/test_parser_combinator.py | 191 ++++++++++++++++++++++++++++++++ tests/test_util.py | 14 +++ 2 files changed, 205 insertions(+) create mode 100644 tests/test_parser_combinator.py create mode 100644 tests/test_util.py diff --git a/tests/test_parser_combinator.py b/tests/test_parser_combinator.py new file mode 100644 index 0000000..c718b45 --- /dev/null +++ b/tests/test_parser_combinator.py @@ -0,0 +1,191 @@ +from muppet.parser_combinator import ( + MatchLiteral, + MatchObject, + ParseDirective, + ParseError, + ParserCombinator, + all_, + char, + complement, + count, + digit, + hexdig, + line_comment, + many, + many1, + name, + nop, + not_, + optional, + s, + tag, + ws, + delimited, + discard, + space, +) + + +def lit(x): + return MatchLiteral(matched=x) + + +def test_partial(): + data = "123 Hello" + + parser = ParserCombinator(data) + + assert [lit("123")] == parser.get(many(digit)) + assert " Hello" == parser.remaining() + + +def test_char(): + parser = ParserCombinator("Hello!") + assert [lit('H')] == parser.get(char) + assert [lit('e')] == parser.get(char) + assert "llo!" == parser.remaining() + + +def test_nop(): + parser = ParserCombinator("Hello!") + assert [] == parser.get(nop) + assert "Hello!" == parser.remaining() + + +def test_digit(): + p1 = ParserCombinator("123") + assert [lit('1')] == p1.get(digit) + p2 = ParserCombinator("Hello") + try: + p2.get(digit) + assert False, "Parser should have failed, but didn't" + except ParseError: + assert "Hello" == p2.remaining() + + +def test_consume(): + p = ParserCombinator("Hello") + try: + p.get([char, digit]) + assert False, "Parser should have failed, but didn't" + except ParseError: + assert "ello" == p.remaining() + + +def test_hexdig(): + pass + + +def test_space(): + pass + +# -------------------------------------------------- + + +def test_many(): + p1 = ParserCombinator("Hello, World!") + + assert [lit("Hello, World!")] == p1.get(many(char)) + + p2 = ParserCombinator("") + assert [] == p2.get(many(char)) + + +def test_many1(): + p1 = ParserCombinator("Hello, World!") + + assert [lit("Hello, World!")] == p1.get(many1(char)) + + p2 = ParserCombinator("") + try: + p2.get(many1(char)) + assert False, "Parser should have failed, but didn't" + except ParseError: + assert True + + +def test_count(): + p1 = ParserCombinator("ABCDE") + assert [lit("A")] == p1.get(count(s("A"), 1, 3)) + + p2 = ParserCombinator("AAAAA") + assert [lit("AAA")] == p2.get(count(s("A"), 1, 3)) + + p3 = ParserCombinator("BBBBB") + assert [] == p3.get(count(s("A"), 3)) + + p4 = ParserCombinator("AAAAA") + assert [lit("AAA")] == p4.get(count(s("A"), 3)) + + +def test_optional(): + p1 = ParserCombinator("ABC") + assert [lit("A")] == p1.get(optional(s("A"))) + assert "BC" == p1.remaining() + + p2 = ParserCombinator("ABC") + assert [] == p2.get(optional(s("B"))) + assert "ABC" == p2.remaining() + + +def test_ws(): + p1 = ParserCombinator("Hello") + assert [] == p1.get(ws) + + p2 = ParserCombinator("\t \n\r Hello") + assert [lit("\t \n\r ")] == p2.get(ws) + + +def test_discard(): + p1 = ParserCombinator("Hello") + assert [] == p1.get(discard(s("He"))) + assert "llo" == p1.remaining() + + p2 = ParserCombinator("Hello!") + assert [lit("ll")] == p2.get(discard(s("He")) & s("ll") & discard(s("o"))) + assert "!" == p2.remaining() + + +def handle_int(xs: list[MatchObject]) -> list[int]: + """Convert matched to an integer.""" + return [int(xs[0].matched)] + + +def test_delimited(): + number = many(digit) @ handle_int + + p1 = ParserCombinator("1,20,2") + assert [1, lit(","), 20, lit(","), 2] == p1.get(delimited(s(","), number)) + + p2 = ParserCombinator("1,20,2") + assert [1, 20, 2] == p2.get(delimited(discard(s(",")), number)) + + +def test_all(): + p1 = ParserCombinator("123") + assert [lit("1")] == p1.get(all_(char, digit)) + + +def test_complement_1(): + p1 = ParserCombinator("Hello, World!") + assert [lit("H")] == p1.get(all_(~ space, char)) + + +def test_complement_2(): + p1 = ParserCombinator("Hello, World!") + assert [lit("Hello,")] == p1.get(many(all_(~ space, char))) + + +def test_complement(): + p = ParserCombinator("Hello") + assert [lit("He")] == p.get(many(complement("l"))) + + +def test_stringifiers(): + assert "'a'" == str(s("a")) + assert "~ 'a'" == repr(~ s("a")) + assert "x" == str(name("x", space & space)) + assert "('a' & 'b')" == str(s('a') & s('b')) + assert "('a' | 'b')" == str(s('a') | s('b')) + assert "char" == str(char) + assert "nop" == str(nop) diff --git a/tests/test_util.py b/tests/test_util.py new file mode 100644 index 0000000..f382835 --- /dev/null +++ b/tests/test_util.py @@ -0,0 +1,14 @@ +from muppet.util import group_by, concatenate + + +def test_group_by(): + groups = group_by(lambda x: x % 3, range(10)) + assert len(groups) == 3 + assert groups[0] == [0, 3, 6, 9] + assert groups[1] == [1, 4, 7] + assert groups[2] == [2, 5, 8] + + +def test_concatenate(): + assert concatenate([[1, 2], [3, 4]]) == [1, 2, 3, 4] + assert concatenate([[1, [2]], [3, 4]]) == [1, [2], 3, 4] -- GitLab