A simple python calculator to demo recursive descent parsing. Execute the script to use the calculator. It accepts only well formed input. Use parentheses to specify operator precedence.
""" | |
exp ::= term | exp + term | exp - term | |
term ::= factor | factor * term | factor / term | |
factor ::= number | ( exp ) | |
""" | |
class Calculator(): | |
def __init__(self, tokens): | |
self._tokens = tokens | |
self._current = tokens[0] | |
def exp(self): | |
result = self.term() | |
while self._current in ('+', '-'): | |
if self._current == '+': | |
self.next() | |
result += self.term() | |
if self._current == '-': | |
self.next() | |
result -= self.term() | |
return result | |
def factor(self): | |
result = None | |
if self._current[0].isdigit() or self._current[-1].isdigit(): | |
result = float(self._current) | |
self.next() | |
elif self._current is '(': | |
self.next() | |
result = self.exp() | |
self.next() | |
return result | |
def next(self): | |
self._tokens = self._tokens[1:] | |
self._current = self._tokens[0] if len(self._tokens) > 0 else None | |
def term(self): | |
result = self.factor() | |
while self._current in ('*', '/'): | |
if self._current == '*': | |
self.next() | |
result *= self.term() | |
if self._current == '/': | |
self.next() | |
result /= self.term() | |
return result | |
if __name__ == '__main__': | |
while True: | |
lst = list(raw_input('> ').replace(' ', '')) | |
tokens = [] | |
for i in range(len(lst)): | |
if lst[i].isdigit() and i > 0 and (tokens[-1].isdigit() or tokens[-1][-1] is '.'): | |
tokens[-1] += lst[i] | |
elif lst[i] is '.' and i > 0 and tokens[-1].isdigit(): | |
tokens[-1] += lst[i] | |
else: | |
tokens.append(lst[i]) | |
print Calculator(tokens).exp() |
This comment has been minimized.
This comment has been minimized.
Fixed. |
This comment has been minimized.
This comment has been minimized.
cdpm
commented
May 30, 2014
Should it not be |
This comment has been minimized.
This comment has been minimized.
felix-d
commented
Jun 22, 2016
•
...
while True:
tokens = re.findall(r'[\d.]+|[+-/*()]', input('> ').replace(' ', ''))
print Calculator(tokens).exp() |
This comment has been minimized.
This comment has been minimized.
dannytranit
commented
Mar 25, 2018
It cannot handle with things like 12 x 21 / 13 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This comment has been minimized.
6502 commentedJul 14, 2013
I didn't try it, but looking at the code I'd say that it cannot handle things like "1+2-3+4-5" because expects a sequences of zero or more "+" followed by a sequence of zero or more "-". Ditto for "*" and "/".
Instead of two consecutive while loops should do one while loop accepting all operators of the same precedence.