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__()