added nicer ÜParserInput

This commit is contained in:
Ruediger Ludwig 2022-12-12 18:28:16 +01:00
parent 5c0183ab93
commit 6a7e34e322
3 changed files with 112 additions and 102 deletions

View file

@ -8,7 +8,8 @@ I use python 3.11 without any libraries beyond the standard.
| Day | Time | Rank | Score | Time | Rank | Score | | 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 | | 10 | 00:38:16 | 7637 | 0 | 01:16:55 | 7961 | 0 |
| 9 | 00:54:18 | 7719 | 0 | 01:07:37 | 4901 | 0 | | 9 | 00:54:18 | 7719 | 0 | 01:07:37 | 4901 | 0 |
| 8 | 00:41:51 | 7831 | 0 | 00:59:27 | 6325 | 0 | | 8 | 00:41:51 | 7831 | 0 | 00:59:27 | 6325 | 0 |

View file

@ -1,11 +1,12 @@
from __future__ import annotations from __future__ import annotations
from dataclasses import dataclass, field
from functools import reduce from functools import reduce
from itertools import chain 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 import unicodedata
from advent.parser.parser_input import AllowedParserInput, ParserInput, create_parser_input
from .result import Result from .result import Result
T = TypeVar('T') T = TypeVar('T')
@ -17,93 +18,6 @@ T5 = TypeVar('T5')
TR = TypeVar('TR') 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]] ParserResult = Iterator[tuple[ParserInput, T]]
ParserFunc = Callable[[ParserInput], ParserResult[T]] ParserFunc = Callable[[ParserInput], ParserResult[T]]
@ -112,26 +26,20 @@ class P(Generic[T]):
def __init__(self, func: ParserFunc[T]): def __init__(self, func: ParserFunc[T]):
self.func = func self.func = func
def parse(self, input: str | Iterator[str]) -> Result[T]: def parse(self, input: AllowedParserInput) -> Result[T]:
if isinstance(input, str): parser_input = create_parser_input(input)
parser_input = SimpleParserInput(input, 0)
else:
parser_input = IteratorParserInput(StringDispenser(input), 0)
all_results = self.func(parser_input) all_results = self.func(parser_input)
try: try:
_, result = next(all_results) _, result = next(all_results)
return Result.of(result) return Result.of(result)
except StopIteration: except StopIteration:
return Result.fail("No result") return Result.fail("No result")
def parse_multi(self, input: str | Iterator[str]) -> Iterator[T]: def parse_multi(self, input: AllowedParserInput) -> Iterator[T]:
if isinstance(input, str): parser_input = create_parser_input(input)
parser_input = SimpleParserInput(input, 0)
else:
parser_input = IteratorParserInput(StringDispenser(input), 0)
all_results = self.func(parser_input) all_results = self.func(parser_input)
return (v for _, v in all_results) return (v for _, v in all_results)
@classmethod @classmethod

View file

@ -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)