203 lines
6.3 KiB
Python
Executable File
203 lines
6.3 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 __repr__(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, reverse=False): # [(start, end, len), ...]
|
|
start = 0
|
|
r_pos = 0
|
|
word = []
|
|
for i, x in enumerate(reversed(self._data) if reverse else self._data):
|
|
if x.kind == 'r':
|
|
r_pos += 1
|
|
word.append(x)
|
|
elif x.kind == 'l':
|
|
continue
|
|
else:
|
|
if len(word) > 0: # RuneText may include \n and \r
|
|
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)
|