175 lines
5.7 KiB
Python
175 lines
5.7 KiB
Python
import ptoken
|
|
import tokenizer
|
|
import copy
|
|
from collections import deque
|
|
|
|
debug = False
|
|
|
|
def calculate_results(tokens):
|
|
# Main entry into the calc_engine.
|
|
# Returns the result of arthmetic as described by the token list.
|
|
return process_tokens(convert_to_deque(tokens), True)
|
|
|
|
def process_tokens(tokens, top_level = False):
|
|
pending_operations = []
|
|
running_total = 0
|
|
# Flag that inidicates a high priority set of mathmatical operations have occurred.
|
|
# High priority (mulitplation, division, power for instance) act as a pivot on which we can
|
|
# determine whether or not lower priority operations can safely be performed without violating
|
|
# the order of operations.
|
|
p_ops_ran = False
|
|
|
|
while True:
|
|
if not tokens:
|
|
break
|
|
|
|
token = tokens.popleft()
|
|
|
|
if not top_level and token.type == ptoken.TokenType.exp_end:
|
|
if tokens:
|
|
look_ahead = tokens[0]
|
|
|
|
if look_ahead.type == ptoken.TokenType.power:
|
|
tokens.popleft()
|
|
two_ahead = tokens.popleft()
|
|
|
|
tmp = process_tokens(convert_to_deque(pending_operations))
|
|
|
|
if two_ahead.type == ptoken.TokenType.exp_start:
|
|
two_ahead = ptoken.Token(process_tokens(tokens), ptoken.TokenType.constant)
|
|
|
|
return operate(tmp, two_ahead.value, look_ahead.type)
|
|
|
|
break
|
|
|
|
if token.type == ptoken.TokenType.exp_start:
|
|
tmp = process_tokens(tokens)
|
|
|
|
pending_operations.append(ptoken.Token(tmp, ptoken.TokenType.constant))
|
|
|
|
elif token.type == ptoken.TokenType.power:
|
|
look_ahead = tokens.popleft()
|
|
|
|
#if look_ahead.type == ptoken.TokenType.exp_start:
|
|
# look_ahead = ptoken.Token(process_tokens(tokens), ptoken.TokenType.constant)
|
|
|
|
pending_operations.append(ptoken.Token(operate(pending_operations.pop().value, look_ahead.value, token.type), ptoken.TokenType.constant))
|
|
|
|
elif token.type == ptoken.TokenType.add or token.type == ptoken.TokenType.subtract:
|
|
if pending_operations and p_ops_ran:
|
|
tmp = convert_to_deque(pending_operations)
|
|
previous_token = ptoken.Token(handle_pending(tmp), ptoken.TokenType.constant)
|
|
pending_operations = []
|
|
pending_operations.append(previous_token)
|
|
pending_operations.append(token)
|
|
p_ops_ran = False
|
|
|
|
elif token.type == ptoken.TokenType.constant:
|
|
pending_operations.append(token)
|
|
p_ops_ran = False
|
|
|
|
elif token.type == ptoken.TokenType.multiply or token.type == ptoken.TokenType.divide:
|
|
look_behind = pending_operations.pop()
|
|
look_ahead = tokens.popleft()
|
|
two_ahead = tokens[0]
|
|
|
|
if look_ahead.type == ptoken.TokenType.exp_start:
|
|
look_ahead = ptoken.Token(process_tokens(tokens), ptoken.TokenType.constant)
|
|
|
|
if two_ahead.type == ptoken.TokenType.power:
|
|
tokens.popleft()
|
|
three_ahead = tokens.popleft()
|
|
|
|
if three_ahead.type != ptoken.TokenType.exp_start:
|
|
look_ahead = ptoken.Token(operate(look_ahead.value, three_ahead.value, two_ahead.type), ptoken.TokenType.constant)
|
|
else:
|
|
tmp = process_tokens(tokens)
|
|
look_ahead = ptoken.Token(operate(look_ahead.value, tmp, two_ahead.type), ptoken.TokenType.constant)
|
|
|
|
running_total = operate(look_behind.value, look_ahead.value, token.type)
|
|
|
|
pending_operations.append(ptoken.Token(running_total, ptoken.TokenType.constant))
|
|
p_ops_ran = True
|
|
|
|
#tokenizer.print_token_list(pending_operations)
|
|
|
|
if pending_operations:
|
|
if len(pending_operations) == 1:
|
|
return float(pending_operations.pop().value)
|
|
return handle_pending(convert_to_deque(pending_operations))
|
|
|
|
return running_total
|
|
#handle add/sub
|
|
def handle_pending(tokens):
|
|
# Effectively, this function handles adding and subtracting as all higher priority mathmatical operations
|
|
# would have already been processed.
|
|
# Returns the result of the addition or subtraction of the provided token list.
|
|
running_total = 0
|
|
previous_token = None
|
|
|
|
if len(tokens) == 1:
|
|
return float(tokens[0].value)
|
|
|
|
while True:
|
|
if not tokens:
|
|
break
|
|
|
|
tmp_token = tokens.popleft()
|
|
|
|
if tmp_token.type == ptoken.TokenType.constant:
|
|
previous_token = tmp_token
|
|
elif tmp_token.type == ptoken.TokenType.add or tmp_token.type == ptoken.TokenType.subtract:
|
|
look_ahead = tokens.popleft()
|
|
|
|
running_total = operate(previous_token.value, look_ahead.value, tmp_token.type)
|
|
|
|
previous_token = ptoken.Token(running_total, ptoken.TokenType.constant)
|
|
|
|
return running_total
|
|
|
|
def convert_to_deque(tokens):
|
|
# Converts a list into a Deque collection object.
|
|
# Returns a Deque object with the contents of the supplied list or array.
|
|
deq = deque()
|
|
|
|
for tk in tokens:
|
|
deq.append(tk)
|
|
|
|
return deq
|
|
|
|
|
|
def operate(n1, n2, tokenType):
|
|
# Utility function to handle dealing with the various mathmatical operations that the engine can process.
|
|
# Returns the result of any one of five mathmatical operations, else throws an error for unrecognized operations.
|
|
n1 = float(n1)
|
|
n2 = float(n2)
|
|
|
|
if tokenType == ptoken.TokenType.add:
|
|
if debug: print("Adding %f and %f to get %f." %(n1, n2, n1 + n2))
|
|
return n1 + n2
|
|
elif tokenType == ptoken.TokenType.subtract:
|
|
if debug: print("Subtracting %f and %f to get %f." %(n1, n2, n1 - n2))
|
|
return n1 - n2
|
|
elif tokenType == ptoken.TokenType.multiply:
|
|
if debug: print("Multiplying %f and %f to get %f" %(n1, n2, n1 * n2))
|
|
return n1 * n2
|
|
elif tokenType == ptoken.TokenType.divide:
|
|
if debug: print("Dividing %f and %f to get %f" %(n1, n2, n1 / n2))
|
|
return n1 / n2
|
|
elif tokenType == ptoken.TokenType.power:
|
|
if debug: print("Raising %f to the power of %f to get %f" %(n1, n2, n1**n2))
|
|
return n1 ** n2
|
|
else:
|
|
raise TypeError("Invalid operator value " + str(tokenType) + ".", tokenType)
|
|
|
|
def print_t(tokens):
|
|
# Prints to standard out a flattened representation of a Deque object.
|
|
tmp = copy.deepcopy(tokens)
|
|
|
|
while True:
|
|
if not tmp:
|
|
break
|
|
token = tmp.popleft()
|
|
print("%s" %(token.value), end = "")
|
|
print()
|