forked from keon/algorithms
-
Notifications
You must be signed in to change notification settings - Fork 0
/
math_parser.py
147 lines (122 loc) · 4.08 KB
/
math_parser.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
"""
Contributed by izanbf1803.
Example:
-------------------------------------------------------------------------------------------------
Code:
| exp = "2452 * (3 * 6.5 + 1) * 6 / 235"
| print("Expression:", exp)
| print("Parsed expression:", mp.parse(exp))
| print("Evaluation result:", mp.evaluate(exp))
Output:
| Expression: 2452 * (3 * 6 + 1) * 6 / 235
| Parsed expression: ['2452', '*', '(', '3', '*', '6', '+', '1', ')', '*', '6', '/', '235']
| Evaluation result: 1189.4808510638297
-------------------------------------------------------------------------------------------------
Now added '^' operator for exponents. (by @goswami-rahul)
"""
from collections import deque
import re
numeric_value = re.compile('\d+(\.\d+)?')
__operators__ = "+-/*^"
__parenthesis__ = "()"
__priority__ = {
'+': 0,
'-': 0,
'*': 1,
'/': 1,
'^': 2
}
def is_operator(token):
"""
Check if token it's a operator
token Char: Token
"""
return token in __operators__
def higher_priority(op1, op2):
"""
Check if op1 have higher priority than op2
op1 Char: Operation Token 1
op2 Char: Operation Token 2
"""
return __priority__[op1] >= __priority__[op2]
def calc(n2, n1, operator):
"""
Calculate operation result
n2 Number: Number 2
n1 Number: Number 1
operator Char: Operation to calculate
"""
if operator == '-': return n1 - n2
elif operator == '+': return n1 + n2
elif operator == '*': return n1 * n2
elif operator == '/': return n1 / n2
elif operator == '^': return n1 ** n2
return 0
def apply_operation(op_stack, out_stack):
"""
Apply operation to the first 2 items of the output queue
op_stack Deque (reference)
out_stack Deque (reference)
"""
out_stack.append(calc(out_stack.pop(), out_stack.pop(), op_stack.pop()))
def parse(expression):
"""
Return array of parsed tokens in the expression
expression String: Math expression to parse in infix notation
"""
result = []
current = ""
for i in expression:
if i.isdigit() or i == '.':
current += i
else:
if len(current) > 0:
result.append(current)
current = ""
if i in __operators__ or i in __parenthesis__:
result.append(i)
else:
raise Exception("invalid syntax " + i)
if len(current) > 0:
result.append(current)
return result
def evaluate(expression):
"""
Calculate result of expression
expression String: The expression
type Type (optional): Number type [int, float]
"""
op_stack = deque() # operator stack
out_stack = deque() # output stack (values)
tokens = parse(expression) # calls the function only once!
for token in tokens:
if numeric_value.match(token):
out_stack.append(float(token))
elif token == '(':
op_stack.append(token)
elif token == ')':
while len(op_stack) > 0 and op_stack[-1] != '(':
apply_operation(op_stack, out_stack)
op_stack.pop() # Remove remaining '('
else: # is_operator(token)
while len(op_stack) > 0 and is_operator(op_stack[-1]) and higher_priority(op_stack[-1], token):
apply_operation(op_stack, out_stack)
op_stack.append(token)
while len(op_stack) > 0:
apply_operation(op_stack, out_stack)
return out_stack[-1]
def main():
"""
simple user-interface
"""
print("\t\tCalculator\n\n")
user_input = input("expression or exit: ")
while user_input != "exit":
try:
print("The result is {0}".format(evaluate(user_input)))
except Exception:
print("invalid syntax!")
user_input = input("expression or exit: ")
print("program end")
if __name__ == "__main__":
main()