AbacusNG/abacus.py

271 lines
9.0 KiB
Python

import mathematik
class AbacusMultipleAssignmentException(Exception):
pass
class AbacusNameAssignmentException(Exception):
pass
class AbacusWrongAssignmentException(Exception):
pass
class AbacusEmptyAssignmentException(Exception):
pass
class AbacusDataModel:
step_no = 0
def __init__(self, input_string, chunks, chunks_values, value, comment, step_no=None):
self._input_string = input_string
self._chunks = chunks
self._chunks_values = chunks_values
self._value = value
self._comment = comment
if step_no is not None:
self._step_no = step_no
else:
self._step_no = AbacusDataModel._get_next_step_no()
@classmethod
def _get_next_step_no(cls):
cls.step_no += 1
return cls.step_no
@property
def value(self):
return self._value
@property
def input_string(self):
return self._input_string
@property
def chunks(self):
return self._chunks
@property
def chunks_values(self):
return self._chunks_values
@property
def comment(self):
return self._comment
def __repr__(self):
return self._step_no, self._input_string, str(self._chunks), str(self._chunks_values), self._value, self._comment
class Abacus:
stop_operators = [':', '=']
operators = ['+', '-', '*', '/', ',']
brackets = ['(', ')']
comment = '#'
math_operator = '>'
def __init__(self):
self._vars = {'result': '0', 'cba': 'result+20+add'}
self._formel = {'add': 'result+10', 'abc': 'cba-result'} # dict()
def get_vars(self):
return self._vars
def get_var_by_name(self, name):
return self._vars.get(name, None)
def get_formel_by_name(self, name):
return self._formel.get(name, None)
def set_var(self, name, value):
if name in Abacus.stop_operators or name in Abacus.operators or name in Abacus.comment or name in Abacus.brackets:
raise AbacusNameAssignmentException('Name cannot be a operator')
res = self.get_formel_by_name(name)
if res:
raise AbacusNameAssignmentException('Name {} already exists'.format(name))
self._vars[name] = str(value)
def set_formel(self, name, value):
if name in Abacus.stop_operators or name in Abacus.operators or name in Abacus.comment or name in Abacus.brackets:
raise AbacusNameAssignmentException('Name cannot be a operator')
res = self.get_var_by_name(name)
if res:
raise AbacusNameAssignmentException('Name {} already exists'.format(name))
self._formel[name] = str(value)
@classmethod
def calculate(cls, input_string):
try:
return eval(input_string)
except Exception as e:
return e
@classmethod
def clean_input(cls, input_string):
input_string = str(input_string)
# input_string = input_string.replace(' ', '')
# input_string = input_string.replace('.', '')
input_string = input_string.replace(',', '.')
input_string = input_string.replace(';', ',')
return input_string.strip()
@classmethod
def split_chunks(cls, input_string):
chunks = list()
last_pos = 0
cnt_stop = 0
for i, c in enumerate(input_string):
if c == cls.comment:
if last_pos != i:
chunks.append(input_string[last_pos:i].strip())
chunks.append(input_string[i:].strip())
return chunks
if c in cls.stop_operators and cnt_stop == 0:
chunks.append(input_string[last_pos:i].strip())
chunks.append(input_string[i])
last_pos = i+1
cnt_stop += 1
elif c in cls.stop_operators and cnt_stop > 0:
raise AbacusMultipleAssignmentException('Multiple assignments not supported')
if c in cls.operators or c in cls.brackets:
if i != last_pos:
chunks.append(input_string[last_pos:i].strip())
chunks.append(input_string[i].strip())
last_pos = i+1
elif i == len(input_string)-1: # das letzte zeichen
chunks.append(input_string[last_pos:i+1].strip())
return chunks
@classmethod
def validate_chunks(cls, chunks):
input_str = ''.join(chunks)
if input_str[-1] in cls.operators:
raise AbacusWrongAssignmentException('Last character cannot be a operator')
for i, chunk in enumerate(chunks):
if chunk in cls.stop_operators and i != 1:
raise AbacusWrongAssignmentException('Wrong assignment expression')
@classmethod
def replace_operator_to_math(cls, input_string):
return input_string.replace(cls.math_operator, 'mathematik.')
def process_chunks(self, chunks):
chunks = chunks[:]
for i, chunk in enumerate(chunks):
res = self.get_var_by_name(chunk)
if res is not None:
chunks[i] = res
else:
res = self.get_formel_by_name(chunk)
if res is not None:
cchunks = Abacus.split_chunks(res)
res_var = self.process_chunks(cchunks)
for i in res_var:
chunks.insert()[i] = res_var # TODO: Unboxing die Liste und integration in die bestehende
return chunks
def _replace_formel_and_vars(self, input_str):
chunks = Abacus.split_chunks(input_str)
for i, chunk in enumerate(chunks):
res = self.get_var_by_name(chunk)
if res is not None:
chunks[i] = self._replace_formel_and_vars(res)
else:
res = self.get_formel_by_name(chunk)
if res is not None:
chunks[i] = self._replace_formel_and_vars(res)
return ''.join(chunks)
def process_input(self, input_str):
input_string = Abacus.clean_input(input_str)
input_string = self.replace_operator_to_math(input_string)
if len(input_string) == 0:
raise AbacusEmptyAssignmentException('Empty assignment expression')
chunks = Abacus.split_chunks(input_string)
comment, chunks = Abacus.get_comment_and_rest(chunks)
Abacus.validate_chunks(chunks)
formel, var_chunks = Abacus.get_function_assignment_from_chunks(chunks)
if formel is not None:
self.set_formel(formel, ''.join(var_chunks))
clean_chunks = self._replace_formel_and_vars(''.join(var_chunks))
value = Abacus.calculate(clean_chunks)
return input_string, chunks, clean_chunks, value, comment
else:
var, formel_chunks = Abacus.get_var_assignment_from_chunks(chunks)
if var is not None:
self.set_var(var, ''.join(formel_chunks))
clean_chunks = self._replace_formel_and_vars(''.join(formel_chunks))
value = Abacus.calculate(clean_chunks)
return input_string, chunks, clean_chunks, value, comment
else:
clean_chunks = self._replace_formel_and_vars(''.join(chunks))
value = Abacus.calculate(clean_chunks)
return input_string, chunks, clean_chunks, value, comment
@classmethod
def get_var_assignment_from_chunks(cls, chunks):
chunks = chunks[:]
chunks = ''.join(chunks).split(cls.stop_operators[1])
if len(chunks) > 2:
raise AbacusMultipleAssignmentException('Multiple assignments not supported')
if len(chunks) == 2:
return chunks[0], chunks[1:]
return None, None
@classmethod
def get_function_assignment_from_chunks(cls, chunks):
chunks = chunks[:]
chunks = ''.join(chunks).split(cls.stop_operators[0])
if len(chunks) > 2:
raise AbacusMultipleAssignmentException('Multiple assignments not supported')
if len(chunks) == 2:
return chunks[0], chunks[1:]
return None, None
@classmethod
def get_comment_from_chunks(cls, chunks):
for chunk in chunks:
chunk = str(chunk)
if chunk.startswith(cls.comment):
chunk = chunk[1:]
return chunk.strip()
return None
@classmethod
def get_chunks_without_comment(cls, chunks):
for i, chunk in enumerate(chunks):
chunk = str(chunk)
if chunk.startswith(cls.comment):
return chunks[:i]
return chunks
@classmethod
def get_comment_and_rest(cls, chunks):
comment = cls.get_comment_from_chunks(chunks)
rest = cls.get_chunks_without_comment(chunks)
return comment, rest
if __name__ == '__main__':
input_string = ['10+5', 'bbc:(10+10)', 'bba=(bbc+20)', 'bba//bbc']
steps = list()
ab = Abacus()
for i_s in input_string:
res = ab.process_input(i_s)
adm = AbacusDataModel(*res)
steps.append(adm)
for s in steps:
s.__repr__()