Files
LiberPrayground/RuneText.py
2021-01-30 21:15:01 +01:00

250 lines
8.2 KiB
Python
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
white_rune = {'': ' ', '': '.', '': ',', '': ';', '': '#'}
white_text = {v: k for k, v in white_rune.items()}
alphabet = [ # Using last value for display. Custom added: V
(2, '', ['F']), (3, '', ['V', 'U']), (5, '', ['TH']), (7, '', ['O']),
(11, '', ['R']), (13, '', ['K', 'C']), (17, '', ['G']),
(19, '', ['W']), (23, '', ['H']), (29, '', ['N']), (31, '', ['I']),
(37, '', ['J']), (41, '', ['EO']), (43, '', ['P']), (47, '', ['X']),
(53, '', ['Z', 'S']), (59, '', ['T']), (61, '', ['B']),
(67, '', ['E']), (71, '', ['M']), (73, '', ['L']),
(79, '', ['ING', 'NG']), (83, '', ['OE']), (89, '', ['D']),
(97, '', ['A']), (101, '', ['AE']), (103, '', ['Y']),
(107, '', ['IO', 'IA']), (109, '', ['EA'])
]
text_map = {t: r for _, r, ta in alphabet for t in ta}
rune_map = {r: t for _, r, ta in alphabet for t in ta}
index_map = [r for _, r, _ in alphabet] # array already sorted
primes_map = {r: p for p, r, _ in alphabet}
# del alphabet # used in playground for GP display
#########################################
# Rune : Stores a single rune. Incl. text, prime, index, and kind
#########################################
class Rune(object):
def __init__(self, r=None, t=None, i=None, p=None):
self._rune = r
self._text = t
self._index = i
self._prime = p
self._kind = None # one of: r n s l w
def __repr__(self):
return f'<{self._rune}, {self._text}, {self._index}, {self._prime}>'
@property
def rune(self):
if self._rune is None:
self._rune = index_map[self._index] if self._index < 29 else ''
return self._rune
@property
def text(self, sameWhitespace=False):
if self._text is None:
if sameWhitespace:
self._text = rune_map.get(self.rune, ' ')
else:
r = self.rune
self._text = rune_map.get(r, white_rune.get(r, r))
return self._text
@property
def index(self):
if self._index is None:
r = self._rune
self._index = index_map.index(r) if r in index_map else 29
return self._index
@property
def prime(self):
if self._prime is None:
self._prime = primes_map.get(self.rune, 0)
return self._prime
@property
def kind(self):
if self._kind is None:
x = self.rune
if x in rune_map:
self._kind = 'r' # rune
elif x == '':
self._kind = 's' # paragraph, but treat as sentence
elif x == '':
self._kind = 's' # sentence
elif x == '\n' or x == '\r':
self._kind = 'l' # line end
elif x in '1234567890':
self._kind = 'n' # number
else:
self._kind = 'w' # whitespace (explicitly not n or s)
return self._kind
def __add__(self, o):
if isinstance(o, Rune):
o = o.index
if self.index == 29 or o == 29:
return self
return Rune(i=(self.index + o) % 29)
def __sub__(self, o):
if isinstance(o, Rune):
o = o.index
if self.index == 29 or o == 29:
return self
return Rune(i=(self.index - o) % 29)
def __radd__(self, o):
return self if self.index == 29 else Rune(i=(o + self.index) % 29)
def __rsub__(self, o):
return self if self.index == 29 else Rune(i=(o - self.index) % 29)
def __invert__(self):
return self if self.index == 29 else Rune(i=28 - self.index)
#########################################
# RuneText : Stores multiple Rune objects. Allows arithmetic operations
#########################################
class RuneText(object):
def __init__(self, anything):
self._rune_sum = None
if not anything:
self._data = []
elif isinstance(anything, list):
if len(anything) > 0 and isinstance(anything[0], Rune):
self._data = anything
else:
self._data = [Rune(i=x) for x in anything]
else:
txt = anything.strip()
if not txt:
self._data = []
elif txt[0] in rune_map or txt[0] in white_rune:
self._data = [Rune(r=x) for x in txt]
else:
try:
self._data = [
Rune(i=int(x)) for x in txt.strip('[]').split(',')
]
except ValueError:
self._data = self.from_text(txt)
self._data_len = len(self._data)
def __len__(self):
return self._data_len
def trim(self, maxlen):
if self._data_len > maxlen:
if self._rune_sum > 0:
self._rune_sum -= sum(x.prime for x in self._data[maxlen:])
self._data = self._data[:maxlen]
self._data_len = maxlen
@classmethod
def from_text(self, text):
res = []
text = text.strip().upper().replace('QU', 'CW')
tlen = len(text)
skip = 0
for i in range(tlen):
if skip:
skip -= 1
continue
char = text[i]
rune = None
wspace = white_text.get(char, None)
if wspace is not None:
rune = wspace
elif char in '\"\'\n\r\t1234567890':
rune = char
else:
if char in 'TINEOA' and i + 1 < tlen:
bichar = char + text[i + 1]
rune = text_map.get(bichar, None)
if rune is not None:
char = bichar
skip = 1
elif char == 'I' and i + 2 < tlen:
trichar = bichar + text[i + 2]
rune = text_map.get(trichar, None)
if rune:
char = trichar
skip = 2
if not rune:
rune = text_map.get(char, None)
if not rune:
raise ValueError(f'Unkn0n char: {i} "{char}"')
res.append(Rune(r=rune, t=char))
return res
def as_dict(self):
ret = {'r': '', 't': '', 'i': [], 'p': []}
for x in self._data:
ret['r'] += x.rune
ret['t'] += x.text
ret['i'].append(x.index)
ret['p'].append(x.prime)
return ret
def description(self, count=False, index=True, indexWhitespace=False):
if len(self) == 0:
return None
fmt = '{0} ({1}) {2} ({3})' if count else '{0} {2}'
d = self.as_dict()
if index:
fmt += ' {4}'
if not indexWhitespace:
d['i'] = [x for x in d['i'] if x != 29]
return fmt.format(d['r'], len(d['r']), d['t'], len(d['t']), d['i'])
def zip_sub(self, other):
if len(self) != len(other):
raise IndexError('RuneText length mismatch')
return RuneText([x - y for x, y in zip(self._data, other._data)])
@property
def rune_sum(self):
if self._rune_sum is None:
self._rune_sum = sum(x.prime for x in self._data)
return self._rune_sum
@property
def text(self):
return ''.join(x.text for x in self._data)
@property
def rune(self):
return ''.join(x.rune for x in self._data)
def __getitem__(self, key):
if isinstance(key, str):
return [getattr(x, key) for x in self._data]
else:
return self._data[key]
def __setitem__(self, key, value):
self._data[key] = value
def __add__(self, other):
return RuneText([x + other for x in self._data])
def __sub__(self, other):
return RuneText([x - other for x in self._data])
def __radd__(self, other):
return RuneText([other + x for x in self._data])
def __rsub__(self, other):
return RuneText([other - x for x in self._data])
def __invert__(self):
return RuneText([~x for x in self._data])
def __repr__(self):
return f'RuneText<{len(self._data)}>'