general: add 'destructive parsing' (kinda what we were doing in my.core.konsume) to my.experimental
also some cleanup for my.codeforces and my.topcoder
This commit is contained in:
parent
4e13779ed5
commit
26d627e8d1
4 changed files with 183 additions and 97 deletions
60
my/experimental/destructive_parsing.py
Normal file
60
my/experimental/destructive_parsing.py
Normal file
|
@ -0,0 +1,60 @@
|
|||
from dataclasses import dataclass
|
||||
from typing import Any, Iterator, List, Tuple
|
||||
|
||||
from my.core import assert_never
|
||||
from my.core.compat import NoneType
|
||||
|
||||
|
||||
# TODO Popper? not sure
|
||||
@dataclass
|
||||
class Helper:
|
||||
manager: 'Manager'
|
||||
item: Any # todo realistically, list or dict? could at least type as indexable or something
|
||||
path: Tuple[str, ...]
|
||||
|
||||
def pop_if_primitive(self, *keys: str) -> None:
|
||||
"""
|
||||
The idea that primitive TODO
|
||||
"""
|
||||
item = self.item
|
||||
for k in keys:
|
||||
v = item[k]
|
||||
if isinstance(v, (str, bool, float, int, NoneType)):
|
||||
item.pop(k) # todo kinda unfortunate to get dict item twice.. but not sure if can avoid?
|
||||
|
||||
def check(self, key: str, expected: Any) -> None:
|
||||
actual = self.item.pop(key)
|
||||
assert actual == expected, (key, actual, expected)
|
||||
|
||||
def zoom(self, key: str) -> 'Helper':
|
||||
return self.manager.helper(item=self.item.pop(key), path=self.path + (key,))
|
||||
|
||||
|
||||
def is_empty(x) -> bool:
|
||||
if isinstance(x, dict):
|
||||
return len(x) == 0
|
||||
elif isinstance(x, list):
|
||||
return all(map(is_empty, x))
|
||||
else:
|
||||
assert_never(x)
|
||||
|
||||
|
||||
class Manager:
|
||||
def __init__(self) -> None:
|
||||
self.helpers: List[Helper] = []
|
||||
|
||||
def helper(self, item: Any, *, path: Tuple[str, ...] = ()) -> Helper:
|
||||
res = Helper(manager=self, item=item, path=path)
|
||||
self.helpers.append(res)
|
||||
return res
|
||||
|
||||
def check(self) -> Iterator[Exception]:
|
||||
remaining = []
|
||||
for h in self.helpers:
|
||||
# TODO recursively check it's primitive?
|
||||
if is_empty(h.item):
|
||||
continue
|
||||
remaining.append((h.path, h.item))
|
||||
if len(remaining) == 0:
|
||||
return
|
||||
yield RuntimeError(f'Unparsed items remaining: {remaining}')
|
Loading…
Add table
Add a link
Reference in a new issue