Skip to content

Instantly share code, notes, and snippets.

@haje01
Last active December 22, 2015 17:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save haje01/6506093 to your computer and use it in GitHub Desktop.
Save haje01/6506093 to your computer and use it in GitHub Desktop.
#!/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)
#!/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