-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcalculator.py
More file actions
153 lines (123 loc) · 3.81 KB
/
Copy pathcalculator.py
File metadata and controls
153 lines (123 loc) · 3.81 KB
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
148
149
150
151
152
153
import sys
import readline
class ParseError(Exception):
pass
def tokenize(expr):
expr = expr.replace(" ", "")
return expr, 0
def peek(expr, pos):
if pos < len(expr):
return expr[pos]
return None
def next_token(expr, pos):
if pos < len(expr):
return expr[pos], pos + 1
return None, pos
def consume(expr, pos, expected):
if peek(expr, pos) == expected:
return next_token(expr, pos)
raise ParseError(f"Expected '{expected}' at position {pos}")
def parse(expr):
expr, pos = tokenize(expr)
node, pos = parse_expr(expr, pos)
if peek(expr, pos) is not None:
raise ParseError(f"Unexpected character '{peek(expr, pos)}' at position {pos}")
return node
def parse_expr(expr, pos):
node, pos = parse_term(expr, pos)
while peek(expr, pos) in ("+", "-"):
op, pos = next_token(expr, pos)
right, pos = parse_term(expr, pos)
node = (op, node, right)
return node, pos
def parse_term(expr, pos):
node, pos = parse_factor(expr, pos)
while peek(expr, pos) in ("*", "/"):
op, pos = next_token(expr, pos)
right, pos = parse_factor(expr, pos)
node = (op, node, right)
return node, pos
def parse_factor(expr, pos):
node, pos = parse_power(expr, pos)
return node, pos
def parse_power(expr, pos):
node, pos = parse_atom(expr, pos)
while peek(expr, pos) == "^":
_, pos = next_token(expr, pos)
right, pos = parse_atom(expr, pos)
node = ("^", node, right)
return node, pos
def parse_atom(expr, pos):
ch = peek(expr, pos)
if ch is None:
raise ParseError("Unexpected end of input")
if ch == "(":
_, pos = next_token(expr, pos)
node, pos = parse_expr(expr, pos)
_, pos = consume(expr, pos, ")")
return node, pos
elif ch in "+-":
op, pos = next_token(expr, pos)
node, pos = parse_atom(expr, pos)
return (op, 0.0, node), pos
else:
return parse_number(expr, pos)
def parse_number(expr, pos):
num_str = ""
dot_seen = False
while True:
ch = peek(expr, pos)
if ch is not None and (ch.isdigit() or (ch == "." and not dot_seen)):
if ch == ".":
dot_seen = True
num_str += ch
pos += 1
else:
break
if not num_str:
raise ParseError(f"Expected number at position {pos}")
return float(num_str), pos
def eval_ast(node):
if isinstance(node, float):
return node
if isinstance(node, tuple):
op = node[0]
if op == "+":
return eval_ast(node[1]) + eval_ast(node[2])
elif op == "-":
return eval_ast(node[1]) - eval_ast(node[2])
elif op == "*":
return eval_ast(node[1]) * eval_ast(node[2])
elif op == "/":
right = eval_ast(node[2])
if right == 0:
raise ZeroDivisionError("Division by zero")
return eval_ast(node[1]) / right
elif op == "^":
return eval_ast(node[1]) ** eval_ast(node[2])
raise ParseError("Invalid syntax tree")
def main():
print("Python Calculator. Type 'exit' or Ctrl+C to quit.")
last_expr = ""
while True:
try:
expr = input("> ")
if expr.strip() == "":
continue
if expr.strip().lower() in ("exit", "quit"):
break
last_expr = expr
ast = parse(expr)
result = eval_ast(ast)
print(result)
except EOFError:
print("")
break
except ParseError as e:
print(f"Parse error: {e}")
except ZeroDivisionError as e:
print(f"Math error: {e}")
except Exception as e:
print(f"Error: {e}")
if __name__ == "__main__":
main()