Last active
December 22, 2015 17:28
-
-
Save haje01/6506093 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 | |
# -*- coding: utf-8 -*- | |
""" | |
Caculator for Python Korea GangNam Study | |
""" | |
import re | |
from collections import deque | |
context = {} | |
OPERATORS = ('+', '-', '*', '/', '^') | |
def do_expr(text): | |
text = reduce_bracket(text) | |
text = normalize_space(text) | |
tokens = deque(text.split()) | |
if len(tokens) == 0: | |
return 0 | |
left = get_int(tokens) | |
if len(tokens) == 0: | |
return left | |
return calc_with_right_side(left, tokens) | |
def calc_with_right_side(left, tokens): | |
while len(tokens) > 0: | |
op = get_op(tokens) | |
if len(tokens) == 0: | |
raise SyntaxError("Not Enough Input") | |
right = get_int(tokens, op) | |
left = do_op(left, op, right) | |
return left | |
def find_top_bracket(text): | |
idx = 0 | |
start = end = -1 | |
brno = 0 | |
for t in text: | |
if t == '(': | |
if brno == 0: | |
start = idx | |
brno += 1 | |
if t == ')': | |
brno -= 1 | |
if brno == 0: | |
end = idx | |
break | |
idx += 1 | |
return start, end | |
def reduce_bracket(text): | |
start, end = find_top_bracket(text) | |
if (start == -1 and end != -1) or (start != -1 and end == -1): | |
raise SyntaxError("Bracket syntax error") | |
if start >= 0 and end >= 0: | |
left = reduce_bracket(text[0:start]) | |
reduced = str(do_expr(text[start + 1:end])) | |
right = reduce_bracket(text[end + 1:]) | |
text = left + reduced + right | |
return text | |
def get_op_order(op): | |
if op in ('+', '-'): | |
return 1 | |
elif op in ('*', '/'): | |
return 2 | |
elif op in ('^'): | |
return 3 | |
def get_int(tokens, prev_op=None): | |
global context | |
try: | |
val = tokens.popleft() | |
if val in context: | |
val = context[val] | |
left = int(val) | |
if prev_op is not None and len(tokens) > 0: | |
next_op = get_op(tokens, False) | |
prev_op_order = get_op_order(prev_op) | |
next_op_order = get_op_order(next_op) | |
if prev_op_order < next_op_order: | |
left = calc_with_right_side(left, tokens) | |
return left | |
except ValueError, e: | |
raise SyntaxError(str(e)) | |
def get_op(tokens, pop=True): | |
if pop: | |
op = tokens.popleft() | |
else: | |
op = tokens[0] | |
if op not in OPERATORS: | |
raise SyntaxError("Illegal Operator: " + op) | |
return op | |
def do_op(left, op, right): | |
if op == '+': | |
return left + right | |
elif op == '-': | |
return left - right | |
elif op == '*': | |
return left * right | |
elif op == '/': | |
return left / right | |
elif op == '^': | |
return pow(left, right) | |
def normalize_space(text): | |
return re.sub(r'([+-]?\w+)\s*([^\w\s])\s*', r'\1 \2 ', text) | |
def do_statements(text): | |
global context | |
results = [] | |
stmts = text.split(';') | |
for stmt in stmts: | |
if '=' in stmt: | |
var, expr = stmt.split('=') | |
context[var.strip()] = do_expr(expr) | |
else: | |
ret = do_expr(stmt) | |
results.append(ret) | |
return results | |
if __name__ == "__main__": | |
while True: | |
text = raw_input(">") | |
try: | |
for r in do_statements(text): | |
print r | |
except Exception, e: | |
print str(e) |
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 | |
# -*- coding: utf-8 -*- | |
import unittest | |
import calc | |
class Test(unittest.TestCase): | |
def setUp(self): | |
pass | |
def tearDown(self): | |
pass | |
def test_simple(self): | |
assert calc.do_expr('') == 0 | |
assert calc.do_expr('1') == 1 | |
assert calc.do_expr('-1') == -1 | |
with self.assertRaises(SyntaxError): | |
calc.do_expr('&') | |
with self.assertRaises(SyntaxError): | |
calc.do_expr('1 +') | |
with self.assertRaises(SyntaxError): | |
calc.do_expr('& 1') | |
assert calc.do_expr('1 + 1') == 2 | |
assert calc.do_expr('1 + 2') == 3 | |
assert calc.do_expr('2 - 2') == 0 | |
assert calc.do_expr('3 * 2') == 6 | |
assert calc.do_expr('3 / 2') == 1 | |
assert calc.do_expr('2 ^ 3') == 8 | |
with self.assertRaises(SyntaxError): | |
calc.do_expr('3 & 2') | |
def test_sign(self): | |
assert calc.do_expr('-4 + 5') == 1 | |
assert calc.do_expr('5 + -4') == 1 | |
def test_multi(self): | |
assert calc.do_expr('1 + 2 - 3') == 0 | |
with self.assertRaises(SyntaxError): | |
calc.do_expr('1 + 2 & 3') | |
assert calc.do_expr('1 + 2 - 3 + 2') == 2 | |
with self.assertRaises(SyntaxError): | |
calc.do_expr('1 + 2 - 3 & 2') | |
def test_bracket(self): | |
assert calc.do_expr('()') == 0 | |
assert calc.do_expr('(1)') == 1 | |
assert calc.do_expr('(-1)') == -1 | |
assert calc.do_expr('(1 + 1)') == 2 | |
assert calc.do_expr('((1 + 1))') == 2 | |
assert calc.do_expr('(1 + 1) + 2') == 4 | |
assert calc.do_expr('2 + (1 + 1)') == 4 | |
assert calc.do_expr('(1 + 1) + (1 + 1)') == 4 | |
assert calc.do_expr('(2 + 1) + (1 + 1) + (1 + 2)') == 8 | |
assert calc.do_expr('(4 + 3) * 5') == 35 | |
assert calc.do_expr('5 * (4 + 3)') == 35 | |
def test_op_precedence(self): | |
assert calc.do_expr('4 * 2 + 3') == 11 | |
assert calc.do_expr('3 + 4 * 2') == 11 | |
assert calc.do_expr('3 + 4 * 2 - 3') == 8 | |
assert calc.do_expr('3 + 4 * 2 - 3 * 2') == 5 | |
assert calc.do_expr('3 + 4 * 2 - 2 ^ 3') == 3 | |
def test_normalize_space(self): | |
assert calc.normalize_space('1+1') == '1 + 1' | |
assert calc.normalize_space('-1+1') == '-1 + 1' | |
assert calc.normalize_space('1+-1') == '1 + -1' | |
assert calc.normalize_space('1++1') == '1 + +1' | |
assert calc.normalize_space('+1++1') == '+1 + +1' | |
assert calc.normalize_space('+1*+1') == '+1 * +1' | |
assert calc.normalize_space('+1/+1') == '+1 / +1' | |
assert calc.normalize_space('-2^+3') == '-2 ^ +3' | |
assert calc.normalize_space('a+b') == 'a + b' | |
def test_statements(self): | |
assert calc.do_statements('1 + 1;2 + 2') == [2, 4] | |
calc.do_statements('a = 1 + 1;b = 2 + 2') | |
assert calc.context == {'a': 2, 'b': 4} | |
assert calc.do_statements('a = 1 + 1;b = 2 + 2; a ^ b') == [16] | |
if __name__ == '__main__': | |
unittest.main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment