class Expression: def __init__(self): raise NotImplementedError() def Evaluate(self, dictionary): raise NotImplementedError() # NEW code def __repr__(self): klass = self.__class__.__name__ private = '_{0}__'.format(klass) args = [] for name in self.__dict__: if name.startswith(private): value = self.__dict__[name] name = name[len(private):] args.append('{0}={1}'.format(name, repr(value))) return '{0}({1})'.format(klass, ', '.join(args)) # END code class Constant(Expression): def __init__(self, value): self.__value = value def Evaluate(self, dictionary): return self.__value class Variable(Expression): def __init__(self, name): self.__name = name def Evaluate(self, dictionary): if self.__name not in dictionary: raise Exception('Unknown variable: ' + self.__name) return dictionary[self.__name] class Operation(Expression): def __init__(self, left, op, right): self.__left = left self.__op = op self.__right = right def Evaluate(self, dictionary): # NEW code if self.__op == '=': assert isinstance(self.__left, Variable), 'Must Assign to Variable' name = self.__left._Variable__name value = self.__right.Evaluate(dictionary) dictionary[name] = value return value # END code x = self.__left.Evaluate(dictionary) y = self.__right.Evaluate(dictionary) if self.__op == '+': return x + y if self.__op == '-': return x - y if self.__op == '*': return x * y if self.__op == '/': return x / y # NEW code if self.__op == '//': return x // y if self.__op == '\\': return y / x if self.__op == '%': return x % y if self.__op in ('**', '^'): return x ** y if self.__op in ('and', '&&', '&'): return x and y if self.__op in ('or', '||', '|'): return x or y if self.__op == '==': return float(x == y) if self.__op == '!=': return float(x != y) if self.__op == '>': return float(x > y) if self.__op == '<': return float(x < y) if self.__op in ('>=', '=>'): return float(x >= y) if self.__op in ('<=', '=<'): return float(x <= y) # END code raise Exception('Unknown operator: ' + self.__op) # NEW code class Print(Expression): def __init__(self, expression): self.__expression = expression def Evaluate(self, dictionary): value = self.__expression.Evaluate(dictionary) print(value) return value # END code ################################################################################ def run(string, local): # fix string for compatibility on all computer platforms string = string.replace('\r\n', '\n').replace('\r', '\n') lines = tokenize(string) build_operations(lines) evaluate(lines, local) def tokenize(string): lines = [] # replace ';' with line separators string = string.replace(';', '\n') # the string will be evaluate line-by-line for line in string.split('\n'): tokens = [] # ignore empty lines and comments if not line or line[0] == '#': continue # tokens are separated by white-space for token in line.split(): # operations are processed later if token in ('=', '+', '-', '*', '/', '//', '\\', '%', '**', '^', 'and', '&&', '&', 'or', '||', '|', '==', '!=', '>', '<', '>=', '=>', '<=', '=<'): tokens.append(token) else: try: # the token is a constant if it can be converted to a float tokens.append(Constant(float(token))) except: # ... otherwise we assume that it is a variable tokens.append(Variable(token)) lines.append(tokens) return lines def build_operations(lines): # now we work on sorting through operations for line_index, line in enumerate(lines): # assignment is optional on a line if '=' in line: # split on '=' so each section can be processed tokens = split(line) # single variables must be on the left of '=' for section in tokens[:-1]: assert len(section) == 1, 'Must Have Single Token' assert isinstance(section[0], Variable), 'Must Assign to Variable' # construct an operation from the last tokens tokens[-1] = flatten(tokens[-1]) # create as many assignment operations as needed op = Operation(tokens[-2][0], '=', tokens[-1]) for token_index in range(len(tokens) - 3, -1, -1): op = Operation(tokens[token_index][0], '=', op) # replace the line with the final operation lines[line_index] = op else: # no assignment? assume evaluation and printing op = flatten(line) lines[line_index] = Print(op) def split(line): # split the tokens in the line on '=' tokens = [] while '=' in line: index = line.index('=') tokens.append(line[:index]) line = line[index+1:] return tokens + [line] def flatten(tokens): # check for odd number of tokens assert len(tokens) % 2 == 1, 'Must Have Odd Number of Tokens' toggle = True # check the token construction sequence for token in tokens: if toggle: assert isinstance(token, (Constant, Variable)), 'Must Have Constant or Variable' else: assert isinstance(token, str), 'Must Have Operation' toggle = not toggle # if there is only one token, it does not need to be flattened if len(tokens) == 1: return tokens[0] # construct the needed operations starting from the beginning op = Operation(*tokens[:3]) for index in range(3, len(tokens), 2): op = Operation(op, tokens[index], tokens[index+1]) return op def evaluate(lines, local): # evaluate the lines in order with the local dictionary for line in lines: local['_'] = line.Evaluate(local) ################################################################################ def test(): import sys local = {} while True: try: run(input('>>> '), local) except EOFError: return except Exception as err: sys.stderr.write(err.args[0] + '\n') if __name__ == '__main__': test()