added nicer ÜParserInput
This commit is contained in:
parent
5c0183ab93
commit
6a7e34e322
3 changed files with 112 additions and 102 deletions
|
|
@ -8,6 +8,7 @@ I use python 3.11 without any libraries beyond the standard.
|
|||
|
||||
| Day | Time | Rank | Score | Time | Rank | Score |
|
||||
| --- | --------- | ----- | ----- | -------- | ----- | ----- |
|
||||
| 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 |
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
101
advent/parser/parser_input.py
Normal file
101
advent/parser/parser_input.py
Normal 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)
|
||||
Loading…
Add table
Add a link
Reference in a new issue