-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcalc1.py
175 lines (128 loc) · 4.03 KB
/
calc1.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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
'''
An interpreter for a subset of the
Pascal programming language, written in
Python
Inspired by tutorial by Ruslan Spivak
Author: GotoCode
'''
# Token Types
INTEGER = 'INTEGER'
PLUS = 'PLUS'
MINUS = 'MINUS'
EOF = 'EOF'
# A Token is a pair - (type, value)
class Token(object):
def __init__(self, type, value):
self.type = type
self.value = value
def __str__(self):
return 'Token({type}, {value})'.format(type=self.type, value=self.value)
def __repr__(self):
return self.__str__()
# An Interpreter which converts a single-line
# expression into a stream of tokens
class Interpreter(object):
def __init__(self, text):
# input expression
self.text = text
# 'clean up' the input string
self.remove_whitespace()
# pointer to current symbol
self.pos = 0
# most recent token available for processing
self.curr_token = None
def error(self):
raise Exception('Error parsing input...')
def get_next_token(self):
'''
Lexical analyzer which returns a stream of
tokens corresponding to input expression
RETURN: Token object
'''
text = self.text
# if we reached end-of-input, return EOF
if self.pos >= len(text):
return Token(EOF, None)
# convert current (character) symbol to appropriate token
symbol = text[self.pos]
if symbol.isdigit():
# extract multi-digit integer
num_str = ''
while self.pos < len(text) and text[self.pos].isdigit():
num_str += text[self.pos]
self.pos += 1
token = Token(INTEGER, int(num_str))
elif symbol == '+':
token = Token(PLUS, symbol)
self.pos += 1
elif symbol == '-':
token = Token(MINUS, symbol)
self.pos += 1
else:
token = None
if not token:
self.error()
else:
return token
def consume(self, type):
'''
If the given type matches that of the
current token, then consume it, else
raise an error
'''
if type == self.curr_token.type:
self.curr_token = self.get_next_token()
else:
self.error()
def eval(self):
'''
Evaluate the given input expression
'''
self.curr_token = self.get_next_token()
# Check for pattern 'INTEGER PLUS INTEGER'
# first int operand
first = self.curr_token.value
self.consume(INTEGER)
# plus/minus operator
op = self.curr_token
try:
self.consume(PLUS)
except:
self.consume(MINUS)
# second int operand
second = self.curr_token.value
self.consume(INTEGER)
# based on pattern return appropriate evaluation
if op.type == PLUS:
out_val = first + second
else:
out_val = first - second
return out_val
def remove_whitespace(self):
'''
"cleans up" the input string by extracting
only those non-whitespace characters from it
'''
result_text = ''
for i in range(len(self.text)):
if not self.text[i].isspace():
result_text += self.text[i]
self.text = result_text
def main():
'''
Main logic for presenting CLI to user of interpreter
'''
while True:
try:
input_expr = raw_input('calc> ')
except EOFError:
print
break
# ignore any empty lines of input
if not input_expr:
continue
interpreter = Interpreter(input_expr)
result = interpreter.eval()
print result
if __name__ == '__main__':
main()