#!/usr/bin/python3 # =================================================================== # evaluate a numeric expression # display the results or an error message # # THIS IS CURRENTLY AN INCOMPLETE WORK IN PROGRESS # # Last Modified: 10/15/2020 # =================================================================== import copy import sys import re from collections import deque import numeric_expression_tokenizer as netok # ------------------------------------------------------------------- # ---- fix/modify unary operator tokens in token list # ---- # ---- this is to distinguish them from regular +/- binary operators. # ---- unary operators follow binary operators or they are the first # ---- token in the token list. # ---- # ---- change '+' to 'u+' # ---- change '-' to 'u-' # ------------------------------------------------------------------- def fix_unary_operators(tok_list): idx = 0 # token list index list_len = len(tok_list) # token list length # ---- these operators can proceed a unary operator proceed_ops = ['-', '+', '%', '*', '/', '(', '**' ] # ---- look at each token in the token list while idx < list_len: # ---- first token in the list? if idx == 0: if tok_list[idx] == '-': tok_list[idx] = 'u-' elif tok_list[idx] == '+': tok_list[idx] = 'u+' idx += 1 continue # ---- is the current token a unary operator (+/-)? if tok_list[idx] == '-' or tok_list[idx] == '+': # ---- does it need fixing? if tok_list[idx-1] in proceed_ops: # ---- fix it if tok_list[idx] == '+': tok_list[idx] = 'u+' elif tok_list[idx] == '-': tok_list[idx] = 'u-' idx += 1 return True # ------------------------------------------------------------------- # ---- test if a key exists in a dictionary # ------------------------------------------------------------------- def check_key(dict,key): if key in dict: return True return False # ------------------------------------------------------------------- # ---- evaluate the numeric expression's token list # ------------------------------------------------------------------- def evaluate(tok_list): results = 0 # expression results o_stack = deque() # operator stack (LIFO) v_stack = deque() # value stack (LIFO) # ---- operators ops = ['-', '+', '%', '*', '/', '(', ')', '**', 'u+', 'u-' ] u_ops = [ 'u-', 'u+' ] b_ops = ['-', '+', '%', '*', '/', '(', ')', '**' ] # ---- operator precedence (higher precedence, higher number) op_prec = { '-' : 1, '+' : 1, '*' : 2, '/' : 2, '%' : 2, 'u+' : 3, 'u-' : 3, '**' : 4, '(' : 5, ')' : 5 } # --------------------------------------------------------------- # display operator and value stack (LIFO) # --------------------------------------------------------------- def display_o_v_stacks(title=None): # ---- get stack lengths o_len = len(o_stack) v_len = len(v_stack) # ---- display stacks side by side if title: print(title) print('O-Stack V-Stack') for i in range(max(o_len,v_len)): if i < o_len: o = o_stack[i] else: o = '' if i < v_len: v = v_stack[i] else: v = '' print(' {:7} {}'.format(o,v)) # --------------------------------------------------------------- # convert string to number # --------------------------------------------------------------- def convert_string_to_number(s): if re.search('\.',s): return float(s) elif re.search('[eE]',s): return float(s) return int(s) # --------------------------------------------------------------- # unary operation # --------------------------------------------------------------- def unary_operation(o,v): n = convert_string_to_number(v) if o == 'u-': return str(-n) return str(n) # --------------------------------------------------------------- # binary operation # --------------------------------------------------------------- def binary_operation(o,a,b): aa = convert_string_to_number(a) bb = convert_string_to_number(b) if o == '-': n = aa - bb elif o == '+': n = aa + bb elif o == '/': n = aa / bb elif o == '*': n = aa * bb elif o == '%': n = aa % bb elif o == '**': n = aa ** bb return str(n) # ---- process token stack for tok in tok_list: if tok in ops: o_stack.appendleft(tok) else: v_stack.appendleft(tok) display_o_v_stacks('---- initial stack state --------') while len(o_stack) > 0: o = o_stack.pop() if o in u_ops and len(v_stack) >= 1: v = v_stack.pop() v = unary_operation(o,v) v_stack.appendleft(v) ##display_o_v_stacks('---- intermediate stack state ---') continue if o in b_ops and len(v_stack) >= 2: b = v_stack.pop() a = v_stack.pop() v = binary_operation(o,a,b) v_stack.appendleft(v) ##display_o_v_stacks('---- intermediate stack state ---') continue print('ERROR processing numeric expression') display_o_v_stacks('Error stack state') return(False,0) display_o_v_stacks('---- final stack state ----------') return (True,v_stack.pop()) # ------------------------------------------------------------------- # ---- main # ------------------------------------------------------------------- if __name__ == '__main__': # --------------------------------------------------------------- # ----running Python3? # --------------------------------------------------------------- def RunningPython3(): if sys.version_info[0] == 3: return True return False # --------------------------------------------------------------- # ---- prompt the user for input # --------------------------------------------------------------- def GetUserInput(prompt,py3): if py3: return input(prompt).strip() else: return raw_input(prompt).strip() # --------------------------------------------------------------- # ---- pause program # --------------------------------------------------------------- def Pause(py3): print('') GetUserInput('Press enter to continue ',py3) # --------------------------------------------------------------- # ---- ask the user for a numeric expression and evaluate it # --------------------------------------------------------------- py3 = RunningPython3() while True: # ---- get user input print() e = GetUserInput('Enter expression: ',py3) if not e: break # ---- tokenize the numeric expression entered by the user (success,token_list) = netok.parse(e) if not success: sys.exit() ##for tok in token_list: ## print('raw tok: {}'.format(tok)) # ---- find and fix unary operators, if any # ---- set unary '-' to 'u-' # ---- set inary '+' to 'u+' if not fix_unary_operators(token_list): sys.exit() # ---- evaluate the numeric expression token list (success,results) = evaluate(token_list) if not success: sys.exit() # ---- display the results print() print('results: {}'.format(results)) Pause(py3) print()