Cleaned up the AST code and updated the calc_engine code.
This commit is contained in:
+7
-143
@@ -1,142 +1,17 @@
|
|||||||
import ptoken
|
import ptoken
|
||||||
import tokenizer
|
import parser
|
||||||
import copy
|
|
||||||
from collections import deque
|
|
||||||
|
|
||||||
debug = False
|
debug = False
|
||||||
|
|
||||||
def calculate_results(tokens):
|
def calculate_results(node):
|
||||||
# Main entry into the calc_engine.
|
|
||||||
# Returns the result of arthmetic as described by the token list.
|
|
||||||
return process_tokens(tokens, True)
|
|
||||||
|
|
||||||
def process_tokens(tokens, top_level = False):
|
if type(node) is parser.Num:
|
||||||
pending_operations = []
|
return float(node.value)
|
||||||
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:
|
left = calculate_results(node.left)
|
||||||
if not tokens:
|
right = calculate_results(node.right)
|
||||||
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
|
|
||||||
|
|
||||||
|
return operate(left, right, node.op)
|
||||||
|
|
||||||
def operate(n1, n2, tokenType):
|
def operate(n1, n2, tokenType):
|
||||||
# Utility function to handle dealing with the various mathmatical operations that the engine can process.
|
# 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
|
return n1 ** n2
|
||||||
else:
|
else:
|
||||||
raise TypeError("Invalid operator value " + str(tokenType) + ".", tokenType)
|
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()
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import ptoken as token
|
import ptoken as token
|
||||||
import tokenizer
|
import tokenizer
|
||||||
import uuid
|
import uuid
|
||||||
import calc_engine
|
|
||||||
from collections import deque
|
from collections import deque
|
||||||
|
|
||||||
class Op:
|
class Op:
|
||||||
@@ -20,55 +19,63 @@ class Num:
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.value
|
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:
|
class Parser:
|
||||||
def __init__(self, tokens):
|
def __init__(self, tokens):
|
||||||
|
if len(tokens) == 0:
|
||||||
|
raise ValueError("Token list can't be empty")
|
||||||
self.tokens = tokens
|
self.tokens = tokens
|
||||||
self.current_token = self.tokens.popleft()
|
self.current_token = None
|
||||||
|
self.advance_current_token()
|
||||||
|
self.ast = self.generate_ast()
|
||||||
|
|
||||||
|
def advance_current_token(self):
|
||||||
def eat(self, token_type):
|
|
||||||
if self.current_token.type != token_type:
|
|
||||||
print("grammar")
|
|
||||||
if len(self.tokens) > 0:
|
if len(self.tokens) > 0:
|
||||||
self.current_token = self.tokens.popleft()
|
self.current_token = self.tokens.popleft()
|
||||||
|
|
||||||
def factor(self):
|
def factor(self):
|
||||||
node = self.current_token
|
|
||||||
#print(node)
|
|
||||||
if self.current_token.type == token.TokenType.constant:
|
if self.current_token.type == token.TokenType.constant:
|
||||||
self.eat(token.TokenType.constant)
|
node = Num(self.current_token)
|
||||||
return Num(node)
|
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:
|
elif self.current_token.type == token.TokenType.exp_start:
|
||||||
self.eat(token.TokenType.exp_start)
|
self.advance_current_token() # (
|
||||||
node = self.topLevel()
|
node = self.topLevel()
|
||||||
self.eat(token.TokenType.exp_end)
|
self.advance_current_token() # )
|
||||||
return node
|
return node
|
||||||
|
|
||||||
def lowLevel(self):
|
def lowLevel(self):
|
||||||
node = self.factor()
|
node = self.factor()
|
||||||
|
|
||||||
while self.current_token.type == token.TokenType.power:
|
while self.current_token.type == token.TokenType.power:
|
||||||
t = self.current_token
|
tmp = self.current_token
|
||||||
|
self.advance_current_token()
|
||||||
if t.type == token.TokenType.power:
|
node = Op(left=node, op=tmp.type, right=self.factor())
|
||||||
self.eat(token.TokenType.power)
|
|
||||||
|
|
||||||
node = Op(left=node, op=t.type, right=self.factor())
|
|
||||||
|
|
||||||
return node
|
return node
|
||||||
|
|
||||||
def midLevel(self):
|
def midLevel(self):
|
||||||
node = self.lowLevel()
|
node = self.lowLevel()
|
||||||
|
|
||||||
while self.current_token.type in (token.TokenType.multiply, token.TokenType.divide):
|
while self.current_token.type in (token.TokenType.multiply, token.TokenType.divide):
|
||||||
t = self.current_token
|
tmp = self.current_token
|
||||||
|
self.advance_current_token()
|
||||||
if t.type == token.TokenType.multiply:
|
node = Op(left=node, op=tmp.type, right=self.lowLevel())
|
||||||
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())
|
|
||||||
|
|
||||||
return node
|
return node
|
||||||
|
|
||||||
@@ -76,109 +83,11 @@ class Parser:
|
|||||||
node = self.midLevel()
|
node = self.midLevel()
|
||||||
|
|
||||||
while self.current_token.type in (token.TokenType.add, token.TokenType.subtract):
|
while self.current_token.type in (token.TokenType.add, token.TokenType.subtract):
|
||||||
t = self.current_token
|
tmp = self.current_token
|
||||||
|
self.advance_current_token()
|
||||||
if t.type == token.TokenType.add:
|
node = Op(left=node, op=tmp.type, right=self.midLevel())
|
||||||
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())
|
|
||||||
|
|
||||||
return node
|
return node
|
||||||
|
|
||||||
def parse(self):
|
def generate_ast(self):
|
||||||
#print("C: %s" %(self.current_token))
|
|
||||||
return self.topLevel()
|
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))
|
|
||||||
|
|||||||
Reference in New Issue
Block a user