我只是个项目的搬运工,来自国外一个非常有趣的博客
英文原博: Ruslan Spivak
中文翻译可参考:博乐在线
之前第一版只是实现了非常简单的加减法,原博最终完成版就是带括号的加减乘除。
其实实现这个计算器,说难也不难说简单也不简单,最重要的是理解词法分析器和解析器,从而简单地了解解释器的处理过程。在这一版的完善过程中,作者同时讲解了文法
和语法图
的概念,其实这俩东西很强大,在处理逻辑的过程中可以很简单地将文法对应到代码,当项目越复杂,文法越显作用。
"""
1. 任意数量加减乘除
2. 可以有括号
3. 去除运算符与数字间空格
"""
# token类型:integer, '+', '-', '*', '/', '(', ')', end-of-file
INTEGER, PLUS, MINUS, MUL, DIV, LPAREN, RPAREN, EOF = 'INTEGER', 'PLUS', 'MINUS', 'MUL', 'DIV', '(', ')', 'EOF'
class Token(object):
"""记号类"""
def __init__(self, type, value):
self.type = type
self.value = value
def __str__(self):
return 'Token({type}, {value})'.format(
type = self.type,
value = self.value
)
def __repr__(self):
return self.__str__()
class Lexer(object):
"""词法分析器"""
def __init__(self, text):
self.text = text # 输入的内容
self.pos = 0 # 当前内容的索引
self.current_char = self.text[self.pos] # 当前字符
def error(self):
raise Exception('Invalid character')
def advance(self):
"""后移pos设置current_char"""
self.pos += 1
if self.pos > len(self.text)-1:
self.current_char = None
else:
self.current_char = self.text[self.pos]
def skip_whitespace(self):
"""跳过空格"""
while self.current_char and self.current_char.isspace():
self.advance()
def integer(self):
"""返回多位数字"""
result = ''
while self.current_char and self.current_char.isdigit():
result += self.current_char
self.advance()
return int(result)
def get_next_token(self):
"""获取当前记号"""
while self.current_char:
if self.current_char.isspace():
self.skip_whitespace()
continue
if self.current_char.isdigit():
return Token(INTEGER, self.integer())
if self.current_char == '+':
self.advance()
return Token(PLUS, '+')
if self.current_char == '-':
self.advance()
return Token(MINUS, '-')
if self.current_char == '*':
self.advance()
return Token(MUL, '*')
if self.current_char == '/':
self.advance()
return Token(DIV, '/')
if self.current_char == '(':
self.advance()
return Token(LPAREN, '(')
if self.current_char == ')':
self.advance()
return Token(RPAREN, ')')
self.error()
return Token(EOF, None)
class Interpreter(object):
"""解释器类"""
def __init__(self, lexer):
self.lexer = lexer
self.current_token = self.lexer.get_next_token() # 当前记号
def error(self):
# 解析错误时抛出异常
raise Exception('Invalid syntax')
def eat(self, token_type):
"""消除与 token_type 类型符合的当前记号,后面的记号前移"""
if self.current_token.type == token_type:
self.current_token = self.lexer.get_next_token()
else:
self.error()
def factor(self):
"""
返回数值并设置当前token
factor: INTEGER | LPAREN expr RPAREN
"""
token = self.current_token
if token.type == INTEGER:
self.eat(INTEGER)
return token.value
elif token.type == LPAREN:
self.eat(LPAREN)
result = self.expr()
self.eat(RPAREN)
return result
def term(self):
"""
返回优先级高的运算结果
term: factor((MUL|DIV)factor)*
"""
result = self.factor()
while self.current_token.type in (MUL, DIV):
token = self.current_token
if token.type == MUL:
self.eat(MUL)
result *= self.factor()
elif token.type == DIV:
self.eat(DIV)
result /= self.factor()
return result
def expr(self):
"""
文法:
expr: term((PLUS|MINUS)term)*
term: factor((MUL|DIV)factor)*
factor: INTEGER | LPAREN expr RPAREN
"""
result = self.term()
while self.current_token.type in (PLUS, MINUS):
token = self.current_token
if token.type == PLUS:
self.eat(PLUS)
result += self.term()
elif token.type == MINUS:
self.eat(MINUS)
result -= self.term()
return result
def main():
while True:
try:
text = input('calc>')
except EOFError:
break
if not text:
continue
lexer = Lexer(text)
interpreter = Interpreter(lexer)
result = interpreter.expr()
print(result)
if __name__ == '__main__':
main()
另外,这天真冷~
网友评论