计算机只能理解机器码。归根结底,编程语言只是一串文字,目的是为了让人类更容易编写他们想让计算机做的事情。真正的魔法是由编译器和解释器完成,它们弥合了两者之间的差距。解释器逐行读取代码并将其转换为机器码。
在本文中,我们将设计一个可以执行算术运算的解释器。
我们不会重新造轮子。文章将使用由 David M. Beazley 开发的词法解析器 —— PLY(Python Lex-Yacc(https://github.com/dabeaz/ply))。
PLY 可以通过以下方式下载:
$ pip install ply
我们将粗略地浏览一下创建解释器所需的基础知识。欲了解更多,请参阅这个 GitHub 仓库(https://github.com/dabeaz/ply)。
标记(Token)
标记是为解释器提供有意义信息的最小字符单位。标记包含一对名称和属性值。
让我们从创建标记名称列表开始。这是一个必要的步骤。
tokens = (
# 数据类型
"NUM",
"FLOAT",
# 算术运算
"PLUS",
"MINUS",
"MUL",
"DIV",
# 括号
"LPAREN",
"RPAREN",
)
词法分析器(Lexer)
将语句转换为标记的过程称为标记化或词法分析。执行词法分析的程序是词法分析器。
# 标记的正则表达
t_PLUS = r"\+"
t_MINUS = r"\-"
t_MUL = r"\*"
t_DIV = r"/"
t_LPAREN = r"\("
t_RPAREN = r"\)"
t_POW = r"\^"
# 忽略空格和制表符
t_ignore = " \t"
# 为每个规则添加动作
def t_FLOAT(t):
r"""\d+\.\d+"""
t.value = float(t.value)
return t
def t_NUM(t):
r"""\d+"""
t.value = int(t.value)
return t
# 未定义规则字符的错误处理
def t_error(t):
# 此处的 t.value 包含未标记的其余输入
print(f"keyword not found: {t.value[0]}\nline {t.lineno}")
t.lexer.skip(1)
# 如果遇到 \n 则将其设为新的一行
def t_newline(t):
r"""\n+"""
t.lexer.lineno += t.value.count("\n")
为导入词法分析器,我们将使用:
import ply.lex as lex