Files
LiberPrayground/LP/RuneText.py
2021-02-12 00:36:01 +01:00

203 lines
6.2 KiB
Python
Executable File

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
from Alphabet import white_rune, white_text, rune_map, text_map
from Rune import Rune
#########################################
# 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)
@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 __len__(self):
return self._data_len
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 __invert__(self):
return RuneText([~x for x in self._data])
def __str__(self):
return f'RuneText<{len(self)}>'
@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)
@property
def index_no_newline(self):
return [x.index for x in self._data if x.kind != 'l']
@property
def index_no_white(self):
return [x.index for x in self._data if x.index != 29]
@property
def prime(self):
return [x.prime for x in self._data]
@property
def prime_sum(self):
if self._rune_sum is None:
self._rune_sum = sum(self.prime)
return self._rune_sum
@property
def data_clean(self):
return [x if x.kind == 'r' else Rune(i=29)
for x in self._data if x.kind != 'l']
def description(self, count=False, index=True, indexWhitespace=False):
return None if len(self) == 0 else \
self.rune + (f' ({len(self)})' if count else '') + ' - ' + \
self.text + (f' ({len(self.text)})' if count else '') + \
(' - {}'.format(self.index_no_newline if indexWhitespace else
self.index_no_white)
if index else '')
def trim(self, maxlen):
if self._data_len > maxlen:
if self._rune_sum and 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
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)])
# def equal(self, other):
# if len(self) != len(other):
# return False
# return all(x.index == y.index for x, y in zip(self, other))
def enum_words(self): # [(start, end, len), ...] may include \n \r
start = 0
r_pos = 0
word = []
for i, x in enumerate(self._data):
if x.kind == 'r':
r_pos += 1
word.append(x)
elif x.kind == 'l':
continue
else:
if len(word) > 0:
yield start, i, r_pos - len(word), RuneText(word)
word = []
start = i + 1
class RuneTextFile(RuneText):
def __init__(self, file, limit=None):
with open(file, 'r') as f:
super().__init__(f.read()[:limit])
self.inverted = False
self.loaded_file = file
def reopen(self, limit=None):
ret = RuneTextFile(self.loaded_file, limit)
if self.inverted:
ret.invert()
return ret
def invert(self):
self.inverted = not self.inverted
self._rune_sum = None
self._data = [~x for x in self._data]
def __str__(self):
return '@file: {} ({} bytes), inverted: {}'.format(
self.loaded_file, len(self._data), self.inverted)
if __name__ == '__main__':
x = RuneText('Hi there. And welc\nome, to my "world";')
for a, z, r_pos, word in x.enum_words():
print((a, z), r_pos, word.text)
y = RuneTextFile(file='../_input.txt')
print(y.loaded_file)
print(y.prime_sum)
print(y)