From 6a7e34e3227fd5f80f9bb60d0dc194b567d0b38c Mon Sep 17 00:00:00 2001 From: Ruediger Ludwig Date: Mon, 12 Dec 2022 18:28:16 +0100 Subject: [PATCH] =?UTF-8?q?added=20nicer=20=C3=9CParserInput?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 3 +- advent/parser/parser.py | 110 +++------------------------------- advent/parser/parser_input.py | 101 +++++++++++++++++++++++++++++++ 3 files changed, 112 insertions(+), 102 deletions(-) create mode 100644 advent/parser/parser_input.py diff --git a/README.md b/README.md index d8dc2e0..59fe1af 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,8 @@ I use python 3.11 without any libraries beyond the standard. | Day | Time | Rank | Score | Time | Rank | Score | | --- | --------- | ----- | ----- | -------- | ----- | ----- | -| 11 | 00:56:44 | 5414 | 0 | 02:42:24 | 7558 | 0 | +| 12 | 01:43:14 | 6571 | 0 | 01:51:15 | 6246 | 0 | +| 11 | 00:56:44 | 5414 | 0 | 02:42:24 | 7558 | 0 | | 10 | 00:38:16 | 7637 | 0 | 01:16:55 | 7961 | 0 | | 9 | 00:54:18 | 7719 | 0 | 01:07:37 | 4901 | 0 | | 8 | 00:41:51 | 7831 | 0 | 00:59:27 | 6325 | 0 | diff --git a/advent/parser/parser.py b/advent/parser/parser.py index d55fdfb..dc88d2f 100644 --- a/advent/parser/parser.py +++ b/advent/parser/parser.py @@ -1,11 +1,12 @@ from __future__ import annotations -from dataclasses import dataclass, field from functools import reduce from itertools import chain -from typing import Any, Callable, Generic, Iterator, Protocol, Self, TypeVar, overload +from typing import Any, Callable, Generic, Iterator, Self, TypeVar, overload import unicodedata +from advent.parser.parser_input import AllowedParserInput, ParserInput, create_parser_input + from .result import Result T = TypeVar('T') @@ -17,93 +18,6 @@ T5 = TypeVar('T5') TR = TypeVar('TR') -class ParserInput(Protocol): - def step(self) -> tuple[Self, str]: - ... - - def has_data(self) -> bool: - ... - - -@dataclass(slots=True, frozen=True) -class SimpleParserInput: - input: str - start: int - - def step(self) -> tuple[Self, str]: - if self.start >= len(self.input): - raise Exception("Already at End of Input") - - return SimpleParserInput(self.input, self.start + 1), self.input[self.start] - - def has_data(self) -> bool: - return self.start < len(self.input) - - def __repr__(self) -> str: - if self.start == 0: - return f'->[{self.input}]' - if self.start >= len(self.input): - return f'{self.input}' - if self.start < 3: - return f'{self.input[0:self.start-1]}->[{self.input[self.start:]}]' - return f'{self.input[self.start-3:self.start-1]}->[{self.input[self.start:]}]' - - -@dataclass(slots=True) -class StringDispenser: - lines: Iterator[str] - input: str = field(default="", init=False) - length: int = field(default=0, init=False) - - def read_more(self): - try: - part = next(self.lines) - if self.input: - self.input = f"{self.input}\n{part}" - else: - self.input = part - self.length = len(self.input) - return True - except StopIteration: - return False - - def get_str(self, pos: int) -> str | None: - assert pos >= 0 - if pos < self.length: - return self.input[pos] - elif pos == self.length and pos != 0: - return "\n" - elif self.read_more(): - return self.get_str(pos) - else: - return None - - def has_more(self, pos: int) -> bool: - assert pos >= 0 - if pos <= self.length: - return True - elif self.read_more(): - return self.has_more(pos) - else: - return False - - -@dataclass(slots=True, frozen=True) -class IteratorParserInput: - dispenser: StringDispenser - start: int - - def step(self) -> tuple[Self, str]: - char = self.dispenser.get_str(self.start) - if char is None: - raise Exception("Already at End of Input") - - return IteratorParserInput(self.dispenser, self.start + 1), char - - def has_data(self) -> bool: - return self.dispenser.has_more(self.start) - - ParserResult = Iterator[tuple[ParserInput, T]] ParserFunc = Callable[[ParserInput], ParserResult[T]] @@ -112,26 +26,20 @@ class P(Generic[T]): def __init__(self, func: ParserFunc[T]): self.func = func - def parse(self, input: str | Iterator[str]) -> Result[T]: - if isinstance(input, str): - parser_input = SimpleParserInput(input, 0) - else: - parser_input = IteratorParserInput(StringDispenser(input), 0) - + def parse(self, input: AllowedParserInput) -> Result[T]: + parser_input = create_parser_input(input) all_results = self.func(parser_input) + try: _, result = next(all_results) return Result.of(result) except StopIteration: return Result.fail("No result") - def parse_multi(self, input: str | Iterator[str]) -> Iterator[T]: - if isinstance(input, str): - parser_input = SimpleParserInput(input, 0) - else: - parser_input = IteratorParserInput(StringDispenser(input), 0) - + def parse_multi(self, input: AllowedParserInput) -> Iterator[T]: + parser_input = create_parser_input(input) all_results = self.func(parser_input) + return (v for _, v in all_results) @classmethod diff --git a/advent/parser/parser_input.py b/advent/parser/parser_input.py new file mode 100644 index 0000000..6a53869 --- /dev/null +++ b/advent/parser/parser_input.py @@ -0,0 +1,101 @@ + +from __future__ import annotations + +from dataclasses import dataclass, field +from typing import Iterator, Protocol, Self + +AllowedParserInput = str | Iterator[str] + + +def create_parser_input(input: AllowedParserInput) -> ParserInput: + if isinstance(input, str): + return SimpleParserInput(input, 0) + else: + return IteratorParserInput(StringDispenser(input), 0) + + +class ParserInput(Protocol): + def step(self) -> tuple[Self, str]: + ... + + def has_data(self) -> bool: + ... + + +@dataclass(slots=True, frozen=True) +class SimpleParserInput: + input: str + start: int + + def step(self) -> tuple[Self, str]: + if self.start >= len(self.input): + raise Exception("Already at End of Input") + + return SimpleParserInput(self.input, self.start + 1), self.input[self.start] + + def has_data(self) -> bool: + return self.start < len(self.input) + + def __repr__(self) -> str: + if self.start == 0: + return f'->[{self.input}]' + if self.start >= len(self.input): + return f'{self.input}' + if self.start < 3: + return f'{self.input[0:self.start-1]}->[{self.input[self.start:]}]' + return f'{self.input[self.start-3:self.start-1]}->[{self.input[self.start:]}]' + + +@dataclass(slots=True) +class StringDispenser: + lines: Iterator[str] + input: str = field(default="", init=False) + length: int = field(default=0, init=False) + + def read_more(self): + try: + part = next(self.lines) + if self.input: + self.input = f"{self.input}\n{part}" + else: + self.input = part + self.length = len(self.input) + return True + except StopIteration: + return False + + def get_str(self, pos: int) -> str | None: + assert pos >= 0 + if pos < self.length: + return self.input[pos] + elif pos == self.length and pos != 0: + return "\n" + elif self.read_more(): + return self.get_str(pos) + else: + return None + + def has_more(self, pos: int) -> bool: + assert pos >= 0 + if pos <= self.length: + return True + elif self.read_more(): + return self.has_more(pos) + else: + return False + + +@dataclass(slots=True, frozen=True) +class IteratorParserInput: + dispenser: StringDispenser + start: int + + def step(self) -> tuple[Self, str]: + char = self.dispenser.get_str(self.start) + if char is None: + raise Exception("Already at End of Input") + + return IteratorParserInput(self.dispenser, self.start + 1), char + + def has_data(self) -> bool: + return self.dispenser.has_more(self.start)