diff --git a/muppet/parser_combinator.py b/muppet/parser_combinator.py index e0d1a3b873bf0c90c21f5d314bda462111e8a8a5..8b58d1c4e545fdd6faa9f175c37281325a45e4d0 100644 --- a/muppet/parser_combinator.py +++ b/muppet/parser_combinator.py @@ -197,26 +197,8 @@ logger = logging.getLogger(__name__) class MatchObject: """A matched item, similar to a regex match.""" - # start: int - # end: int - - matched: Any - - def __init__(self) -> None: - raise TypeError("Can't instanciate MatchObject directly") - - -@dataclass(kw_only=True) -class MatchCompound(MatchObject): - """An advanced matched object.""" - type: str - # matched: list['MatchObject'] - - # def __init__(self, type: str, matched: str | list['MatchObject']): - # self.type = type - # self.matched = matched - # # logger.debug(repr(self)) + matched: Any def __repr__(self) -> str: if self.type: @@ -225,14 +207,6 @@ class MatchCompound(MatchObject): return f'MatchObject({self.matched!r}))' -@dataclass(kw_only=True) -class MatchLiteral(MatchObject): - """A matched string.""" - - def __repr__(self) -> str: - return f's({repr(self.matched)})' - - @dataclass class ParseError(Exception): """ @@ -549,7 +523,7 @@ class CharParser(ParseDirective): def run(self, parser: 'ParserCombinator') -> list[MatchObject]: # noqa: D102 out: list[MatchObject] try: - out = [MatchLiteral(matched=parser._ParserCombinator__source[parser.seek])] + out = [parser._ParserCombinator__source[parser.seek]] except IndexError: raise ParseError("End of string") parser.seek += 1 @@ -650,13 +624,13 @@ class complement(ParseDirective): def run(self, parser: 'ParserCombinator') -> list[MatchObject]: # noqa: D102 match parser.peek(char): - case [MatchLiteral(matched=c)]: + case [str(c)]: if c not in self.chars: return parser.get(char) else: - raise ParseError() - case _: - raise ParseError() + raise ParseError(msg=f"{c} in {self.chars}") + case it: + raise ParseError(msg=f"Parsed item wasn't a char: {it!r}") def __repr__(self) -> str: return f'complement({repr(self.chars)})' @@ -671,7 +645,7 @@ class tag(ParseDirective): def run(self, parser: 'ParserCombinator') -> list[MatchObject]: # noqa: D102 result = parser.get(self.parser) - return [MatchCompound(type=self.tag, matched=result)] + return [MatchObject(type=self.tag, matched=result)] # @dataclass @@ -752,8 +726,7 @@ class ParserCombinator: Optional name of the file being parsed. Only used for debug purposes. """ - # def __init__(self, source: str, file: Optional[str] = None): - def __init__(self, source: str, file: str): + def __init__(self, source: str, file: Optional[str] = None): self.__source = source self.seek = 0 self.file = file @@ -788,7 +761,7 @@ class ParserCombinator: # logger.debug("item: %a", item) out: list[Any] = [] # Step 1: decode our parser, and run it - logger.debug("Running parser %a (%a:%a)", item, self.file, self.seek) + # logger.debug("Running parser %a (%a:%a)", item, self.file, self.seek) try: match item: case [*entries]: @@ -800,7 +773,7 @@ class ParserCombinator: # Always case fold when matching if substr.lower() == s.lower(): self.seek += len(s) - out += [MatchLiteral(matched=s)] + out += [s] else: raise ParseError(f'Expected {item!r}, got {substr!r} (char {self.seek})') @@ -862,17 +835,15 @@ class ParserCombinator: s = '' for entry in entries: match entry: - case MatchLiteral(matched=c): - s += c case str(c): s += c case _: if s != '': - fixed_entries.append(MatchLiteral(matched=s)) + fixed_entries.append(s) fixed_entries.append(entry) s = '' if s != '': - fixed_entries.append(MatchLiteral(matched=s)) + fixed_entries.append(s) return fixed_entries diff --git a/tests/test_parser_combinator.py b/tests/test_parser_combinator.py index c718b45f02fffa7b54ad7b2aa6c5d3502455fce7..19a104ba82842e377dc577dac6c986bd766c72b0 100644 --- a/tests/test_parser_combinator.py +++ b/tests/test_parser_combinator.py @@ -1,5 +1,4 @@ from muppet.parser_combinator import ( - MatchLiteral, MatchObject, ParseDirective, ParseError, @@ -26,23 +25,19 @@ from muppet.parser_combinator import ( ) -def lit(x): - return MatchLiteral(matched=x) - - def test_partial(): data = "123 Hello" parser = ParserCombinator(data) - assert [lit("123")] == parser.get(many(digit)) + assert ["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 ['H'] == parser.get(char) + assert ['e'] == parser.get(char) assert "llo!" == parser.remaining() @@ -54,7 +49,7 @@ def test_nop(): def test_digit(): p1 = ParserCombinator("123") - assert [lit('1')] == p1.get(digit) + assert ['1'] == p1.get(digit) p2 = ParserCombinator("Hello") try: p2.get(digit) @@ -85,7 +80,7 @@ def test_space(): def test_many(): p1 = ParserCombinator("Hello, World!") - assert [lit("Hello, World!")] == p1.get(many(char)) + assert ["Hello, World!"] == p1.get(many(char)) p2 = ParserCombinator("") assert [] == p2.get(many(char)) @@ -94,7 +89,7 @@ def test_many(): def test_many1(): p1 = ParserCombinator("Hello, World!") - assert [lit("Hello, World!")] == p1.get(many1(char)) + assert ["Hello, World!"] == p1.get(many1(char)) p2 = ParserCombinator("") try: @@ -106,21 +101,21 @@ def test_many1(): def test_count(): p1 = ParserCombinator("ABCDE") - assert [lit("A")] == p1.get(count(s("A"), 1, 3)) + assert ["A"] == p1.get(count(s("A"), 1, 3)) p2 = ParserCombinator("AAAAA") - assert [lit("AAA")] == p2.get(count(s("A"), 1, 3)) + assert ["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)) + assert ["AAA"] == p4.get(count(s("A"), 3)) def test_optional(): p1 = ParserCombinator("ABC") - assert [lit("A")] == p1.get(optional(s("A"))) + assert ["A"] == p1.get(optional(s("A"))) assert "BC" == p1.remaining() p2 = ParserCombinator("ABC") @@ -133,7 +128,7 @@ def test_ws(): assert [] == p1.get(ws) p2 = ParserCombinator("\t \n\r Hello") - assert [lit("\t \n\r ")] == p2.get(ws) + assert ["\t \n\r "] == p2.get(ws) def test_discard(): @@ -142,20 +137,20 @@ def test_discard(): assert "llo" == p1.remaining() p2 = ParserCombinator("Hello!") - assert [lit("ll")] == p2.get(discard(s("He")) & s("ll") & discard(s("o"))) + assert ["ll"] == p2.get(discard(s("He")) & s("ll") & discard(s("o"))) assert "!" == p2.remaining() -def handle_int(xs: list[MatchObject]) -> list[int]: +def handle_int(xs: list[str]) -> list[int]: """Convert matched to an integer.""" - return [int(xs[0].matched)] + return [int(xs[0])] def test_delimited(): number = many(digit) @ handle_int p1 = ParserCombinator("1,20,2") - assert [1, lit(","), 20, lit(","), 2] == p1.get(delimited(s(","), number)) + assert [1, ",", 20, ",", 2] == p1.get(delimited(s(","), number)) p2 = ParserCombinator("1,20,2") assert [1, 20, 2] == p2.get(delimited(discard(s(",")), number)) @@ -163,22 +158,25 @@ def test_delimited(): def test_all(): p1 = ParserCombinator("123") - assert [lit("1")] == p1.get(all_(char, digit)) + assert ["1"] == p1.get(all_(char, digit)) def test_complement_1(): p1 = ParserCombinator("Hello, World!") - assert [lit("H")] == p1.get(all_(~ space, char)) + assert ["H"] == p1.get(all_(~ space, char)) def test_complement_2(): p1 = ParserCombinator("Hello, World!") - assert [lit("Hello,")] == p1.get(many(all_(~ space, char))) + assert ["Hello,"] == p1.get(many(all_(~ space, char))) def test_complement(): - p = ParserCombinator("Hello") - assert [lit("He")] == p.get(many(complement("l"))) + p1 = ParserCombinator("Hello") + assert ["H"] == p1.get(complement('e')) + + p2 = ParserCombinator("Hello") + assert ["He"] == p2.get(many(complement("l"))) def test_stringifiers():