-
-
Save ascv/5022712 to your computer and use it in GitHub Desktop.
""" | |
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() |
Fixed.
Should it not be
"exp ::= term | term + term | term - term"
instead of
"exp ::= term | exp + term | exp - term" in the docstring?
...
while True:
tokens = re.findall(r'[\d.]+|[+-/*()]', input('> ').replace(' ', ''))
print Calculator(tokens).exp()
It cannot handle with things like 12 x 21 / 13
This code failed with expressions like (2 / (2 + 3.33) * 4) - -6
. Had to merge with https://effbot.org/zone/simple-top-down-parsing.htm to make it working.
It cannot handle with things like 12 x 21 / 13
Not in the grammar.
This code failed with expressions like
(2 / (2 + 3.33) * 4) - -6
. Had to merge with https://effbot.org/zone/simple-top-down-parsing.htm to make it working.
Not in the grammar.
To clarify, this is not a general purpose calculator, it's just a simple demo to show how to do recursive descent parsing. The grammar is intentionally limited. If I recall correctly, at the time I wrote it, I was trying to keep the number of lines to <= 60. Modify to support your needs :)
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.