2from typing
import TypeAlias
4TskvRow: TypeAlias = dict[str, str]
7_EQUALS_OR_BACKSLASH = re.compile(
'=|\\\\')
18def _search_equals_or_backslash(s: str, start: int) -> int:
19 match = _EQUALS_OR_BACKSLASH.search(s, start)
21 return match.start()
if match
else -1
24def _search_backslash(s: str, start: int) -> int:
25 return s.find(
'\\', start)
28def _parse_pair(pair: str) -> tuple[str, str]:
29 search_fn = _search_equals_or_backslash
34 end = search_fn(pair, start)
36 unescaped_part += pair[start:end]
40 search_fn = _search_backslash
41 elif end + 1 != len(pair)
and pair[end + 1]
in _UNESCAPED_CHARS:
42 unescaped_part += _UNESCAPED_CHARS[pair[end + 1]]
45 unescaped_part +=
'\\'
48 end = search_fn(pair, start)
50 unescaped_part += pair[start:]
53 raise RuntimeError(f
'Invalid tskv pair: {pair}')
55 return key, unescaped_part
58def parse_line(line: str) -> TskvRow:
59 parts = line.rstrip(
'\n').split(
'\t')
60 if parts[:1] != [
'tskv']:
61 raise RuntimeError(f
'Invalid tskv line: {line!r}')
62 return dict(map(_parse_pair, parts[1:]))