228 lines
7.2 KiB
Python
228 lines
7.2 KiB
Python
|
|
|
|
class AbacusCore:
|
|
|
|
def __init__(self):
|
|
self._input_string = ""
|
|
self._vars = dict()
|
|
self._funcs = dict()
|
|
|
|
self._vars['result'] = 0
|
|
|
|
self._operators = ['+', '-', '/', '*']
|
|
self._delimiters = ['(', ')', '[', ']', ';', ' ']
|
|
|
|
def get_vars(self):
|
|
return self._vars
|
|
|
|
def get_funcs(self):
|
|
return self._funcs
|
|
|
|
def add_var(self, name, value):
|
|
name = name.strip()
|
|
if not name:
|
|
raise ValueError("Имя переменной не может быть пустым")
|
|
self._vars[name] = value
|
|
|
|
def add_func(self, name, value):
|
|
name = name.strip()
|
|
if not name:
|
|
raise ValueError("Имя функции не может быть пустым")
|
|
self._funcs[name] = value
|
|
|
|
def parse_input(self, input_str):
|
|
"""
|
|
Execute a multiline input sequentially, line by line.
|
|
|
|
All lines share the same variable/function dictionaries, so a
|
|
variable created on one line is immediately available on the next.
|
|
"""
|
|
if input_str is None:
|
|
raise ValueError("Leere Eingabe")
|
|
|
|
if not input_str.strip():
|
|
raise ValueError("Leere Eingabe")
|
|
|
|
lines = input_str.splitlines()
|
|
results = []
|
|
last_comment = None
|
|
last_name = None
|
|
last_value = None
|
|
|
|
for line in lines:
|
|
if not line.strip():
|
|
continue
|
|
|
|
result = self._parse_line(line)
|
|
|
|
if result is None:
|
|
continue
|
|
|
|
orig, name, value, comment = result
|
|
results.append(result)
|
|
last_name = name
|
|
last_value = value
|
|
last_comment = comment
|
|
|
|
if not results:
|
|
raise ValueError("Leere Eingabe")
|
|
|
|
# The notebook displays the result of the last executable line.
|
|
return input_str, last_name, last_value, last_comment
|
|
|
|
def _parse_line(self, input_str):
|
|
"""Execute exactly one line."""
|
|
orig_input_str = input_str
|
|
input_str = input_str.strip()
|
|
|
|
if not input_str:
|
|
return None
|
|
|
|
input_str, comment = self._get_input_wo_commentar(input_str)
|
|
|
|
if input_str is None or not input_str.strip():
|
|
return None
|
|
|
|
input_str = input_str.strip()
|
|
|
|
chunks = input_str.split("=")
|
|
|
|
if len(chunks) > 2:
|
|
raise ValueError("Mehrfachzuweisung wird nicht unterstützt")
|
|
|
|
if len(chunks) == 2:
|
|
name = chunks[0].strip()
|
|
expression = chunks[1].strip()
|
|
|
|
if not name:
|
|
raise ValueError("Имя переменной не может быть пустым")
|
|
if not expression:
|
|
raise ValueError("Значение переменной не может быть пустым")
|
|
|
|
value = self._with_variable_another_way(expression)
|
|
self._vars[name] = value
|
|
return orig_input_str, name, value, comment
|
|
|
|
chunks = input_str.split(":", 1)
|
|
|
|
if len(chunks) == 2:
|
|
name = chunks[0].strip()
|
|
expression = chunks[1].strip()
|
|
|
|
if not name:
|
|
raise ValueError("Имя функции не может быть пустым")
|
|
if not expression:
|
|
raise ValueError("Тело функции не может быть пустым")
|
|
|
|
self._funcs[name] = expression
|
|
return orig_input_str, name, expression, comment
|
|
|
|
result = self._with_variable_another_way(input_str)
|
|
self._vars["result"] = result
|
|
return orig_input_str, None, result, comment
|
|
|
|
def _with_variable_another_way(self, input_str):
|
|
input_str, comment = self._get_input_wo_commentar(input_str)
|
|
input_str = ''.join(input_str.split()) # entferne alle leerzeichen
|
|
input_str = input_str.replace(',', '.')
|
|
opdel = self._operators + self._delimiters
|
|
chunks = list()
|
|
last_index = 0
|
|
for i, v in enumerate(input_str):
|
|
if v in opdel:
|
|
chunk = input_str[last_index:i]
|
|
chunk = self._is_a_variable_or_func(chunk)
|
|
chunks.append(chunk)
|
|
chunks.append(input_str[i])
|
|
last_index = i+1
|
|
elif i == len(input_str)-1:
|
|
chunk = input_str[last_index:i+1]
|
|
chunk = self._is_a_variable_or_func(chunk)
|
|
chunks.append(chunk)
|
|
return eval(''.join(chunks))
|
|
|
|
def _is_a_variable_or_func(self, chunk):
|
|
chunk = chunk.strip()
|
|
var = self._vars.get(chunk)
|
|
if not var:
|
|
var = self._funcs.get(chunk)
|
|
if var:
|
|
var = self._with_variable_another_way(var)
|
|
if var:
|
|
return str(var)
|
|
else:
|
|
return chunk
|
|
|
|
|
|
|
|
|
|
def _replace_vars(self, input_str):
|
|
'''
|
|
try to find vars and replace them
|
|
:param input_str:
|
|
:return:
|
|
'''
|
|
first_position = None
|
|
new_input_str = ''
|
|
op_del = self._operators + self._delimiters
|
|
if input_str[0] in self._operators:
|
|
input_str = 'result' + input_str
|
|
input_str_len = len(input_str)
|
|
for i, c in enumerate(input_str):
|
|
if c in op_del or i == input_str_len-1: # and first_position is not None:
|
|
if first_position is not None:
|
|
last_position = i
|
|
if i == input_str_len-1:
|
|
last_position += 1
|
|
temp_var = input_str[first_position:last_position]
|
|
var = self._vars.get(temp_var)
|
|
if var is not None:
|
|
new_input_str += str(var)
|
|
else:
|
|
func = self._funcs.get(temp_var)
|
|
if func:
|
|
new_input_str += str(func)
|
|
else:
|
|
raise NotImplemented()
|
|
first_position = None
|
|
if i == input_str_len - 1:
|
|
continue
|
|
new_input_str += input_str[i]
|
|
elif c.isalpha() or c=='_':
|
|
if first_position is None:
|
|
first_position = i
|
|
else:
|
|
new_input_str += input_str[i]
|
|
return new_input_str
|
|
|
|
def _get_input_wo_commentar(self, input_str):
|
|
'''
|
|
Zerlege input in input und kommentar
|
|
:param input_str:
|
|
:return:
|
|
'''
|
|
input_str = input_str.strip()
|
|
comment_index = input_str.find('#')
|
|
if comment_index == 0: # die ganze zeile ist ein kommentar
|
|
return None, input_str
|
|
elif comment_index == -1:
|
|
return input_str, None
|
|
else:
|
|
comment = input_str[comment_index + 1:]
|
|
input_str = input_str[:comment_index]
|
|
return input_str, comment
|
|
|
|
|
|
def _is_new_variable(self, input_str):
|
|
'''
|
|
|
|
'''
|
|
chunks = input_str.split('=')
|
|
chunks_len = len(chunks)
|
|
if chunks_len > 2:
|
|
raise ValueError()
|
|
# chunks.reverse()
|
|
elif chunks_len == 2:
|
|
return chunks[1].strip(), chunks[0].strip()
|
|
else:
|
|
return chunks[0].strip(), None |