#! /usr/bin/env python import operator ################################################################################ def evaluate(source, local): "Execute all math operations found in the source." for expression in expressions(source): local['_'] = tokens(expression).evaluate(local) def expressions(source): "Separate expressions and yield each individually." lines = source.replace('\r\n', '\n').replace('\r', '\n').split('\n') uncommented = map(lambda line: line.split('#', 1)[0], lines) for line in uncommented: if line and not line.isspace(): for expression in line.split(';'): yield expression def tokens(string): "Build an expression tree by tokenizing expression." evaluator = _tokens(string) if isinstance(evaluator, Operation) and \ evaluator._Operation__symbol == Operation.ASSIGNMENT: return evaluator return Print(evaluator) def _tokens(string): "Private module function: recursively builds a tree." expression = string.strip() if not expression: raise SyntaxError('empty expression') divisions = Operation.split(expression) if divisions: left, symbol, right = divisions return Operation(_tokens(left), symbol, _tokens(right)) if len(expression.split()) > 1: raise SyntaxError(expression) if expression.startswith('0x'): return Constant(int(expression[2:], 16)) if expression.startswith('0d'): return Constant(int(expression[2:], 10)) if expression.startswith('0o'): return Constant(int(expression[2:], 8)) if expression.startswith('0q'): return Constant(int(expression[2:], 4)) if expression.startswith('0b'): return Constant(int(expression[2:], 2)) if expression.isdigit(): return Constant(int(expression)) if expression.isidentifier(): return Variable(expression) raise SyntaxError(expression) ################################################################################ class Expression: "Abstract class for Expression objects." def __init__(self): "Initialize the Expression object." raise NotImplementedError() def evaluate(self, bindings): "Calculate the value of this object." raise NotImplementedError() def __repr__(self): "Return a representation of this object." klass = self.__class__.__name__ private = '_{}__'.format(klass) args = [] for name in vars(self): if name.startswith(private): key = name[len(private):] value = getattr(self, name) args.append('{}={!r}'.format(key, value)) return '{}({})'.format(klass, ', '.join(args)) ################################################################################ class Constant(Expression): "Class for storing all math constants." def __init__(self, value): "Initialize the Constant object." self.__value = value def evaluate(self, bindings): "Calculate the value of this object." return self.__value ################################################################################ class Variable(Expression): "Class for storing all math variables." def __init__(self, name): "Initialize the Variable object." self.__name = name def evaluate(self, bindings): "Calculate the value of this object." if self.__name not in bindings: raise NameError(self.__name) return bindings[self.__name] ################################################################################ class Operation(Expression): "Class for executing math operations." ASSIGNMENT = '->' OPERATORS = {ASSIGNMENT: lambda a, b: None, 'and': lambda a, b: a and b, 'or': lambda a, b: a or b, '+': operator.add, '-': operator.sub, '*': operator.mul, '/': operator.floordiv, '%': operator.mod, '**': operator.pow, '&': operator.and_, '|': operator.or_, '^': operator.xor, '>>': operator.rshift, '<<': operator.lshift, '==': operator.eq, '!=': operator.ne, '>': operator.gt, '>=': operator.ge, '<': operator.lt, '<=': operator.le} def __init__(self, left, symbol, right): "Initialize the Operation object." self.__left = left self.__symbol = symbol self.__right = right def evaluate(self, bindings): "Calculate the value of this object." if self.__symbol == self.ASSIGNMENT: if not isinstance(self.__right, Variable): raise TypeError(self.__right) key = self.__right._Variable__name value = self.__left.evaluate(bindings) bindings[key] = value return value return self.__operate(bindings) def __operate(self, bindings): "Execute operation defined by symbol." if self.__symbol not in self.OPERATORS: raise SyntaxError(self.__symbol) a = self.__left.evaluate(bindings) b = self.__right.evaluate(bindings) return self.OPERATORS[self.__symbol](a, b) __operators = sorted(OPERATORS, key=len, reverse=True) @classmethod def split(cls, expression): "Split expression on rightmost symbol." tail = cls.__split(expression) if tail: symbol, right = tail return expression[:-sum(map(len, tail))], symbol, right @classmethod def __split(cls, expression): "Private class method: help with split." for symbol in cls.__operators: if symbol in expression: right = expression.rsplit(symbol, 1)[1] tail = cls.__split(right) if tail is None: return symbol, right return tail ################################################################################ class Print(Expression): "Class for printing all math results." def __init__(self, expression): "Initialize the Print object." self.__expression = expression def evaluate(self, bindings): "Calculate the value of this object." value = self.__expression.evaluate(bindings) print(value) return value ################################################################################ def test(): "Run a simple demo that shows evaluator's capability." from sys import exc_info, stderr from traceback import format_exception_only local = {} while True: try: evaluate(input('>>> '), local) except EOFError: break except: stderr.write(format_exception_only(*exc_info()[:2])[-1]) if __name__ == '__main__': test()