Permalink
Cannot retrieve contributors at this time
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
lsbasi/part2/calc2.py
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
152 lines (122 sloc)
4.42 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Token types | |
# EOF (end-of-file) token is used to indicate that | |
# there is no more input left for lexical analysis | |
INTEGER, PLUS, MINUS, EOF = 'INTEGER', 'PLUS', 'MINUS', 'EOF' | |
class Token(object): | |
def __init__(self, type, value): | |
# token type: INTEGER, PLUS, MINUS, or EOF | |
self.type = type | |
# token value: non-negative integer value, '+', '-', or None | |
self.value = value | |
def __str__(self): | |
"""String representation of the class instance. | |
Examples: | |
Token(INTEGER, 3) | |
Token(PLUS '+') | |
""" | |
return 'Token({type}, {value})'.format( | |
type=self.type, | |
value=repr(self.value) | |
) | |
def __repr__(self): | |
return self.__str__() | |
class Interpreter(object): | |
def __init__(self, text): | |
# client string input, e.g. "3 + 5", "12 - 5", etc | |
self.text = text | |
# self.pos is an index into self.text | |
self.pos = 0 | |
# current token instance | |
self.current_token = None | |
self.current_char = self.text[self.pos] | |
def error(self): | |
raise Exception('Error parsing input') | |
def advance(self): | |
"""Advance the 'pos' pointer and set the 'current_char' variable.""" | |
self.pos += 1 | |
if self.pos > len(self.text) - 1: | |
self.current_char = None # Indicates end of input | |
else: | |
self.current_char = self.text[self.pos] | |
def skip_whitespace(self): | |
while self.current_char is not None and self.current_char.isspace(): | |
self.advance() | |
def integer(self): | |
"""Return a (multidigit) integer consumed from the input.""" | |
result = '' | |
while self.current_char is not None and self.current_char.isdigit(): | |
result += self.current_char | |
self.advance() | |
return int(result) | |
def get_next_token(self): | |
"""Lexical analyzer (also known as scanner or tokenizer) | |
This method is responsible for breaking a sentence | |
apart into tokens. | |
""" | |
while self.current_char is not None: | |
if self.current_char.isspace(): | |
self.skip_whitespace() | |
continue | |
if self.current_char.isdigit(): | |
return Token(INTEGER, self.integer()) | |
if self.current_char == '+': | |
self.advance() | |
return Token(PLUS, '+') | |
if self.current_char == '-': | |
self.advance() | |
return Token(MINUS, '-') | |
self.error() | |
return Token(EOF, None) | |
def eat(self, token_type): | |
# compare the current token type with the passed token | |
# type and if they match then "eat" the current token | |
# and assign the next token to the self.current_token, | |
# otherwise raise an exception. | |
if self.current_token.type == token_type: | |
self.current_token = self.get_next_token() | |
else: | |
self.error() | |
def expr(self): | |
"""Parser / Interpreter | |
expr -> INTEGER PLUS INTEGER | |
expr -> INTEGER MINUS INTEGER | |
""" | |
# set current token to the first token taken from the input | |
self.current_token = self.get_next_token() | |
# we expect the current token to be an integer | |
left = self.current_token | |
self.eat(INTEGER) | |
# we expect the current token to be either a '+' or '-' | |
op = self.current_token | |
if op.type == PLUS: | |
self.eat(PLUS) | |
else: | |
self.eat(MINUS) | |
# we expect the current token to be an integer | |
right = self.current_token | |
self.eat(INTEGER) | |
# after the above call the self.current_token is set to | |
# EOF token | |
# at this point either the INTEGER PLUS INTEGER or | |
# the INTEGER MINUS INTEGER sequence of tokens | |
# has been successfully found and the method can just | |
# return the result of adding or subtracting two integers, | |
# thus effectively interpreting client input | |
if op.type == PLUS: | |
result = left.value + right.value | |
else: | |
result = left.value - right.value | |
return result | |
def main(): | |
while True: | |
try: | |
text = input('calc> ') | |
except EOFError: | |
break | |
if not text: | |
continue | |
interpreter = Interpreter(text) | |
result = interpreter.expr() | |
print(result) | |
if __name__ == '__main__': | |
main() |