diff --git a/calc_engine.py b/calc_engine.py index 4db0f4b..c45e121 100644 --- a/calc_engine.py +++ b/calc_engine.py @@ -1,142 +1,17 @@ import ptoken -import tokenizer -import copy -from collections import deque +import parser 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(tokens, True) +def calculate_results(node): -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 + if type(node) is parser.Num: + return float(node.value) - 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 + left = calculate_results(node.left) + right = calculate_results(node.right) + return operate(left, right, node.op) def operate(n1, n2, tokenType): # Utility function to handle dealing with the various mathmatical operations that the engine can process. @@ -161,14 +36,3 @@ def operate(n1, n2, tokenType): 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() diff --git a/parser.py b/parser.py index 84c4615..080a2d3 100644 --- a/parser.py +++ b/parser.py @@ -1,7 +1,6 @@ import ptoken as token import tokenizer import uuid -import calc_engine from collections import deque class Op: @@ -20,55 +19,63 @@ class Num: def __str__(self): return self.value +class Variable: + def __init__(self, token): + self.symbol = token.value + self.value = None + + def __str__(self): + if self.value == None: + return self.symbol + else: + return str(self.value) + class Parser: def __init__(self, tokens): + if len(tokens) == 0: + raise ValueError("Token list can't be empty") self.tokens = tokens - self.current_token = self.tokens.popleft() + self.current_token = None + self.advance_current_token() + self.ast = self.generate_ast() - - def eat(self, token_type): - if self.current_token.type != token_type: - print("grammar") + def advance_current_token(self): + if len(self.tokens) > 0: self.current_token = self.tokens.popleft() def factor(self): - node = self.current_token - #print(node) if self.current_token.type == token.TokenType.constant: - self.eat(token.TokenType.constant) - return Num(node) + node = Num(self.current_token) + self.advance_current_token() + return node + elif self.current_token.type == token.TokenType.variable: + node = Variable(self.current_token) + self.advance_current_token() + return node elif self.current_token.type == token.TokenType.exp_start: - self.eat(token.TokenType.exp_start) + self.advance_current_token() # ( node = self.topLevel() - self.eat(token.TokenType.exp_end) + self.advance_current_token() # ) return node def lowLevel(self): node = self.factor() while self.current_token.type == token.TokenType.power: - t = self.current_token - - if t.type == token.TokenType.power: - self.eat(token.TokenType.power) + tmp = self.current_token + self.advance_current_token() + node = Op(left=node, op=tmp.type, right=self.factor()) - node = Op(left=node, op=t.type, right=self.factor()) - return node def midLevel(self): node = self.lowLevel() while self.current_token.type in (token.TokenType.multiply, token.TokenType.divide): - t = self.current_token - - if t.type == token.TokenType.multiply: - self.eat(token.TokenType.multiply) - elif t.type == token.TokenType.divide: - self.eat(token.TokenType.divide) - - node = Op(left=node, op=t.type, right=self.lowLevel()) + tmp = self.current_token + self.advance_current_token() + node = Op(left=node, op=tmp.type, right=self.lowLevel()) return node @@ -76,109 +83,11 @@ class Parser: node = self.midLevel() while self.current_token.type in (token.TokenType.add, token.TokenType.subtract): - t = self.current_token - - if t.type == token.TokenType.add: - self.eat(token.TokenType.add) - elif t.type == token.TokenType.subtract: - self.eat(token.TokenType.subtract) - - node = Op(left=node, op=t.type, right=self.midLevel()) + tmp = self.current_token + self.advance_current_token() + node = Op(left=node, op=tmp.type, right=self.midLevel()) return node - def parse(self): - #print("C: %s" %(self.current_token)) + def generate_ast(self): return self.topLevel() - - def print_tree(root): - depth = Parser.get_tree_diameter(root) - for i in range(depth): - print("%s" %("".rjust(depth - i, ' ')), end = "" ) - Parser.print_height(root, i) - print() - - def print_height(node, level): - if node == None: - return - - if level == 0: - print("%s " %(str(node)), end = "") - else: - Parser.print_height(node.left, level - 1) - Parser.print_height(node.right, level - 1) - - def get_tree_diameter(node): - if type(node) is Num: - return 0 - - if node == None: - return 0 - - return 1 + max(Parser.get_tree_diameter(node.left), Parser.get_tree_diameter(node.right)) - - def p(node): - - if type(node) is Num: - #print("%s " %(node), end ="") - return - - else: - #Parser.p(node.left) - print(node) - #Parser.p(node.right) - - Parser.p(node.left) - Parser.p(node.right) - -def calc(node): - - if type(node) is Num: - return float(node.value) - - left = calc(node.left) - right = calc(node.right) - - return operate(left, right, node.op) - -debug = False -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 == token.TokenType.add: - if debug: print("Adding %f and %f to get %f." %(n1, n2, n1 + n2)) - return n1 + n2 - elif tokenType == token.TokenType.subtract: - if debug: print("Subtracting %f and %f to get %f." %(n1, n2, n1 - n2)) - return n1 - n2 - elif tokenType == token.TokenType.multiply: - if debug: print("Multiplying %f and %f to get %f" %(n1, n2, n1 * n2)) - return n1 * n2 - elif tokenType == token.TokenType.divide: - if debug: print("Dividing %f and %f to get %f" %(n1, n2, n1 / n2)) - return n1 / n2 - elif tokenType == token.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) - -# k(((1+i)^n - 1)/i) -#t = Add() -#t.add_child(Constant(4)) -#t.add_child(Sub()) -#t.left.add_child(Sub()) - -tokens = tokenizer.get_tokens_from_expression_string("5*2^(2+1)-(1+5)-2^5+5*(2/2*3+5)^2") -#tokens.reverse() -#print(calc_engine.calculate_results(tokens)) -#tokens = tokenizer.get_tokens_from_expression_string("200((1-(1.08)^(-12))/0.08)") -#print(calc_engine.calculate_results(tokens)) -#tokens = tokenizer.get_tokens_from_expression_string("1+6 / 2") -n = Parser(tokens) -s = n.parse() -#Parser.p(s) -print(calc(s))