上文中实现了可以支持任意个整数相加减的计算器,本文将把它拓展到支持任意个整数加减乘除
大家都知道,当一个算式中同时含有加减 和 乘除时,乘除优先运算
如果实现优先级差异呢?解决办法是采用逐层分解的语法规则,如下所示
expr: term((PLUS|MINUS)term)* term: factor((MUL|DIV)factor)* factor: NUMBER
一旦定义好了语法规则,转换成代码其实很容易:
- 冒号: 转换成函数调用
- 竖线| 转换成 if … else …
- 星号(...)* 转换成 while 循环
- 如果语法中有递归,同样可以转化成函数递归(下一篇文章会讲)
下图是关于表达式 7 + 5 * 2 的处理逻辑,5 * 2 直接视为一个 term
"""
expr: term((PLUS|MINUS)term)*
term: factor((MUL|DIV)factor)*
factor: NUMBER
"""
NUMBER, PLUS, MINUS, MUL, DIV, EOF = 'NUMBER', 'PLUS', 'MINUS', 'MUL', 'DIV', 'EOF'
class Token:
def __init__(self, token_type, value):
self.type = token_type
self.value = value
def __repr__(self,):
return f'Token({self.type}, {self.value})'
class Lexer:
def __init__(self, text):
self.text = text
self.pos = 0
self.current_char = self.text[self.pos]
def error(self,):
raise Exception('Lexer Error!')
def get_next_token(self, ):
if self.pos >= len(self.text):
return Token(EOF, None)
self.current_char = self.text[self.pos]
if self.current_char == ' ':
while self.current_char == ' ':
self.pos += 1
if self.pos >= len(self.text):
return Token(EOF, None)
self.current_char = self.text[self.pos]
if self.current_char.isdigit():
number = ''
while self.current_char.isdigit():
number += self.current_char
self.pos += 1
if self.pos >= len(self.text):
break
self.current_char = self.text[self.pos]
return Token(NUMBER, int(number))
if self.current_char == '+':
self.pos += 1
return Token(PLUS, '+')
if self.current_char == '-':
self.pos += 1
return Token(MINUS, '-')
if self.current_char == '*':
self.pos += 1
return Token(MUL, '*')
if self.current_char == '/':
self.pos += 1
return Token(DIV, '/')
self.error()
class Interpreter:
def __init__(self, lexer):
self.lexer = lexer
self.current_token = self.lexer.get_next_token()
def error(self, msg = ''):
raise Exception(f'Parse Error! {msg}')
def eat(self, token_type):
if type(token_type) == list and self.current_token.type not in token_type:
self.error(f'current token not in list, expect {token_type}, got {self.current_token}.')
elif type(token_type) == str and self.current_token.type != token_type:
self.error(f'current token not match, expect {token_type}, got {self.current_token}.')
else:
self.current_token = self.lexer.get_next_token()
def factor(self,):
f = self.current_token
self.eat(NUMBER)
return f
def term(self,):
"""
term: factor(MUL|DIV)factor)*
"""
result = self.factor().value
while(self.current_token.type not in [EOF, PLUS, MINUS]):
op = self.current_token
self.eat([MUL, DIV])
f = self.factor()
if op.type == MUL:
result *= f.value
else:
result /= f.value
return Token(NUMBER, result)
def expr(self,):
result = self.term().value
while(self.current_token.type != EOF):
op = self.current_token
self.eat([PLUS, MINUS])
t = self.term()
if op.type == PLUS:
result += t.value
else:
result -= t.value
return result
def main():
while True:
try:
text = input('cal> ')
lexer = Lexer(text)
interpreter = Interpreter(lexer)
print(interpreter.expr())
except Exception as e:
print(e)
break
main()
截止目前,一个支持任意个整数加减乘除的计算器就完成了
下回进一步实现带括号的表达式!



