Last active
August 29, 2015 14:05
-
-
Save berdario/c7ff95e090c4efa299ab to your computer and use it in GitHub Desktop.
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
#! /usr/bin/env python | |
from enum import Enum | |
from numbers import Number | |
from operator import add, sub, mul, truediv, mod | |
class Operation(Enum): | |
PLUS = (add, 6) | |
MINUS = (sub, 6) | |
MULT = (mul, 7) | |
QUOT = (truediv, 7) | |
MOD = (mod, 7) | |
def __init__(self, fn, precedence): | |
self.fn = fn | |
self.precedence = precedence | |
ops_table = {'+': 'PLUS', '-': 'MINUS', '*': 'MULT', '/': 'QUOT', '%': 'MOD'} | |
class AST: | |
def __init__(self, v1, op=None, v2=None): | |
if op is None and v2 is None: | |
self.root = v1 | |
self.l = self.r = None | |
self.precedence = 10 | |
else: | |
self.root = op | |
self.precedence = op.precedence | |
self.l = v1 | |
self.r = v2 | |
def append(self, op, v): | |
if op.precedence > self.precedence: | |
self.r = AST(self.r, op, AST(v)) | |
return self | |
else: | |
return AST(self, op, AST(v)) | |
def evaluate(self): | |
if isinstance(self.root, Number): | |
return self.root | |
elif isinstance(self.root, Operation): | |
return self.root.fn(self.l.evaluate(), self.r.evaluate()) | |
else: | |
raise Exception("sorry, this shouldn't happen, root is: %s", self.root) | |
def __str__(self): | |
if isinstance(self.root, Number): | |
return str(self.root) | |
elif isinstance(self.root, Operation): | |
return '({} {} {})'.format(self.root, self.l, self.r) | |
def parse(s): | |
n, *rest = s.split() | |
ast = AST(int(n)) | |
while rest: | |
op, v, *rest = rest | |
assert op in ops_table, "%s is not a valid operation" % op | |
ast = ast.append(Operation[ops_table[op]], int(v)) | |
return ast | |
if __name__ == '__main__': | |
while True: | |
expr = parse(input('> ')) | |
print(expr.evaluate()) | |
import pytest | |
# run the tests with py.test calc.py | |
def test_single_value(): | |
assert parse('0').evaluate() == 0 | |
assert parse('5').evaluate() == 5 | |
assert parse('100000000000000000000').evaluate() == 100000000000000000000 | |
def test_5ops(): | |
assert parse('1 + 1').evaluate() == 2 | |
assert parse('1 - 1').evaluate() == 0 | |
assert parse('1 * 3').evaluate() == 3 | |
assert parse('12 / 3').evaluate() == 4 | |
assert parse('14 % 5').evaluate() == 4 | |
def test_combined_exprs(): | |
assert parse('1 + 1 + 1').evaluate() == 3 | |
assert parse('1 - 1 - 1').evaluate() == -1 | |
assert parse('2 * 3 - 1').evaluate() == 5 | |
assert parse('2 - 6 / 2').evaluate() == -1 | |
assert parse('1 + 1 - 4 * 4').evaluate() == -14 |
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
py==1.4.23 | |
pytest==2.6.1 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment