From 5abefd8dbee111c3ef87e6c62a9330b1d30cd5ea Mon Sep 17 00:00:00 2001 From: Ruediger Ludwig Date: Fri, 9 Dec 2022 18:32:36 +0100 Subject: [PATCH] added lazy to parser --- advent/parser/parser.py | 22 ++++++++++++++++++++ advent/parser/test_parser.py | 40 ++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) diff --git a/advent/parser/parser.py b/advent/parser/parser.py index 3398307..146c8c0 100644 --- a/advent/parser/parser.py +++ b/advent/parser/parser.py @@ -114,6 +114,10 @@ class P(Generic[T]): def surround(self, other: P[Any]) -> P[T]: return P.map3(other, self, other, lambda _1, v, _2: v) + def some_lazy(self) -> P[list[T]]: + return P._fix(lambda p: self.bind( + lambda x: P.either(P.pure([]), p).fmap(lambda ys: [x] + ys))) + def some(self) -> P[list[T]]: return P._fix(lambda p: self.bind( lambda x: P.either(p, P.pure([])).fmap(lambda ys: [x] + ys))) @@ -121,12 +125,18 @@ class P(Generic[T]): def many(self) -> P[list[T]]: return P.either(self.some(), P.pure([])) + def many_lazy(self) -> P[list[T]]: + return P.either(P.pure([]), self.some_lazy()) + def satisfies(self, pred: Callable[[T], bool]) -> P[T]: return self.bind(lambda v: P.pure(v) if pred(v) else P.fail()) def optional(self) -> P[T | None]: return P.either(self, P.pure(None)) + def optional_lazy(self) -> P[T | None]: + return P.either(P.pure(None), self) + def times(self, *, max: int | None = None, min: int | None = None, exact: int | None = None) -> P[list[T]]: match (exact, min, max): @@ -139,6 +149,18 @@ class P(Generic[T]): case _: raise Exception("Choose exactly one of exact, min or max") + def times_lazy(self, *, max: int | None = None, min: int | None = None, + exact: int | None = None) -> P[list[T]]: + match (exact, min, max): + case (int(e), None, None): + return self.many_lazy().satisfies(lambda lst: len(lst) == e) + case (None, int(mn), None): + return self.many_lazy().satisfies(lambda lst: len(lst) >= mn) + case (None, None, int(mx)): + return self.many_lazy().satisfies(lambda lst: len(lst) <= mx) + case _: + raise Exception("Choose exactly one of exact, min or max") + def sep_by(self, sep: P[Any]) -> P[list[T]]: return P.map2(self, P.snd(sep, self).many(), lambda f, r: [f] + r) diff --git a/advent/parser/test_parser.py b/advent/parser/test_parser.py index 61d6a2c..6c5fe07 100644 --- a/advent/parser/test_parser.py +++ b/advent/parser/test_parser.py @@ -205,3 +205,43 @@ def test_times_max(): expected = [['a', 'a'], ['a'], []] result = list(parser.parse_multi(input)) assert result == expected + + +def test_some_lazy(): + input = 'aa' + parser = P.is_char('a').some_lazy() + expected = [['a'], ['a', 'a']] + result = list(parser.parse_multi(input)) + assert result == expected + + +def test_many_lazy(): + input = 'aa' + parser = P.is_char('a').many_lazy() + expected = [[], ['a'], ['a', 'a']] + result = list(parser.parse_multi(input)) + assert result == expected + + +def test_times_lazy_exact(): + input = 'aaa' + parser = P.is_char('a').times_lazy(exact=2) + expected = [['a', 'a']] + result = list(parser.parse_multi(input)) + assert result == expected + + +def test_times_lazy_min(): + input = 'aaa' + parser = P.is_char('a').times_lazy(min=2) + expected = [['a', 'a'], ['a', 'a', 'a']] + result = list(parser.parse_multi(input)) + assert result == expected + + +def test_times_lazy_max(): + input = 'aaa' + parser = P.is_char('a').times_lazy(max=2) + expected = [[], ['a'], ['a', 'a']] + result = list(parser.parse_multi(input)) + assert result == expected