You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
153 lines
4.4 KiB
Python
153 lines
4.4 KiB
Python
import re
|
|
from parse import *
|
|
|
|
# List of valid operators
|
|
opers = ['+', '-', '*', '/', '=']
|
|
|
|
# Regular expression that checks for valid characters in an expression
|
|
valid_chars = '[ 0-9a-z.\(\)+-\/*=]'
|
|
|
|
variables = {}
|
|
|
|
def print_error(error_code):
|
|
|
|
# List of error codes:
|
|
# 1 - Invalid characters found in expression
|
|
# 2 - Unclosed parantheses
|
|
# 3 - Two operators next to each other
|
|
# 4 - Wrong number formatting (multiple periods)
|
|
|
|
match error_code:
|
|
case 1:
|
|
print("You have invalid characters in your expression.")
|
|
case 2:
|
|
print("You have an unclosed parantheses in your expression.")
|
|
case 3:
|
|
print("You have two operators next to each other.")
|
|
case 4:
|
|
print("One of your values is improperly formatted.")
|
|
case 5:
|
|
print("Uninitialized variable.")
|
|
|
|
def check_errors(expr):
|
|
expr_small = expr.replace(" ", "") # Remove spaces from the string, to make it easier to parse
|
|
|
|
# Check if number of opening parantheses is equal to number of closing parantheses
|
|
num_open_pars = 0
|
|
num_close_pars = 0
|
|
for index,val in enumerate(expr_small):
|
|
if not re.match(valid_chars, val):
|
|
return 1
|
|
|
|
num_open_pars = num_open_pars+1 if val == '(' else num_open_pars
|
|
num_close_pars = num_close_pars+1 if val == ')' else num_close_pars
|
|
|
|
if val in opers:
|
|
if expr_small[index + 1] in opers: # Two consecutive operators
|
|
return 3
|
|
|
|
if val == '.':
|
|
if not expr_small[index + 1].isdigit(): # If you have a period, you must have a number after it
|
|
return 4
|
|
|
|
|
|
if num_open_pars != num_close_pars:
|
|
return 2
|
|
|
|
|
|
|
|
expr = parse(expr)
|
|
for val in expr:
|
|
if val.count('.') > 1: # A value can have at most 1 period
|
|
return 4
|
|
if val.isalpha() and not val in variables and val != expr[0]: # If the token is a string, and isn't in the dictionary, and isn't the variable at index 0 (e.g. 'x' in 'x = 4')
|
|
return 5
|
|
return 0
|
|
|
|
|
|
def evaluate(subexpr): # Evaluate a tokenized expression, that contains no parantheses
|
|
|
|
subexpr = [element for element in subexpr if element != ''] # Remove empty characters in the expression
|
|
|
|
print(subexpr)
|
|
|
|
for index, val in enumerate(subexpr): # Replace variables with their values
|
|
if str(val).isalpha():
|
|
subexpr[index] = variables[val]
|
|
|
|
print(subexpr)
|
|
|
|
if (len(subexpr) == 1):
|
|
return float(subexpr[0])
|
|
|
|
if '/' in subexpr:
|
|
index = subexpr.index('/')
|
|
subexpr[index] = float(subexpr[index-1]) / float(subexpr[index+1])
|
|
subexpr[index-1] = ''
|
|
subexpr[index+1] = ''
|
|
elif '*' in subexpr:
|
|
index = subexpr.index('*')
|
|
subexpr[index] = float(subexpr[index-1]) * float(subexpr[index+1])
|
|
subexpr[index-1] = ''
|
|
subexpr[index+1] = ''
|
|
elif '+' in subexpr or '-' in subexpr: # Addition and subtraction have the same precedence
|
|
index_plus, index_minus = float('inf'), float('inf') # Set both values to infinity
|
|
if '+' in subexpr:
|
|
index_plus = subexpr.index('+')
|
|
if '-' in subexpr:
|
|
index_minus = subexpr.index('-')
|
|
|
|
index = index_plus if index_plus < index_minus else index_minus # Set the index to the index of the operator that occurs first
|
|
subexpr[index] = float(subexpr[index-1]) + float(subexpr[index+1]) if index_plus < index_minus else float(subexpr[index-1]) - float(subexpr[index+1]) # If addition occured first, add the previous and next tokens. If subtraction occured first, subtract them.
|
|
subexpr[index-1] = ''
|
|
subexpr[index+1] = ''
|
|
|
|
print(subexpr)
|
|
|
|
return evaluate(subexpr)
|
|
|
|
def find_inner(subexpr):
|
|
print("expr: " + subexpr)
|
|
|
|
subexpr = parse(subexpr)
|
|
|
|
for index,val in enumerate(subexpr):
|
|
if '(' in val or ')' in val:
|
|
subexpr[index] = find_inner(val[1:len(val)-1])
|
|
|
|
|
|
subexpr_string = ''.join(subexpr)
|
|
print("New expr: " + subexpr_string)
|
|
if not '(' in subexpr_string and not ')' in subexpr_string:
|
|
return str(evaluate(subexpr))
|
|
|
|
|
|
|
|
def main():
|
|
while True:
|
|
variable = ''
|
|
expr = input()
|
|
errno = check_errors(expr)
|
|
if errno != 0:
|
|
print_error(errno)
|
|
continue # If an error was generated, print an error message and continue on to the next iteration of the loop
|
|
|
|
expr_tokenized = parse(expr)
|
|
|
|
if expr_tokenized[0].isalpha() and expr_tokenized[1] == '=': # If the expression assigns a value to a variable
|
|
variable = expr_tokenized[0] # The first token is the variable
|
|
|
|
expr_tokenized.pop(0) # Remove the first and second tokens
|
|
expr_tokenized.pop(0)
|
|
|
|
expr = find_inner(''.join(expr_tokenized))
|
|
variables.update({variable: expr})
|
|
|
|
else:
|
|
expr = find_inner(expr)
|
|
|
|
print(expr)
|
|
print(variables)
|
|
|
|
main()
|