解释器是编程语言的核心组件之一,它负责将源代码转化为可执行的结果。本文以最简单的算术表达式语言为例,带你走完词法分析、语法分析和求值三个阶段,理解解释器的基本骨架。代码示例使用 Python,因其简洁易读,但思路适用于任何语言。
目标语言定义
我们支持以下语法:
- 整数(如
42) - 加法
+、减法-、乘法*、除法/ - 括号
()用于改变优先级
例如:(1 + 2) * 3 应求值为 9。
第一阶段:词法分析(Lexer)
词法分析将字符流转换为有意义的记号(Token)。每个 Token 包含类型和值。
# 定义 Token 类型
INTEGER = 'INTEGER'
PLUS = 'PLUS'
MINUS = 'MINUS'
MUL = 'MUL'
DIV = 'DIV'
LPAREN = 'LPAREN'
RPAREN = 'RPAREN'
EOF = 'EOF'
class Token:
def __init__(self, type, value):
self.type = 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] if self.text else None
def advance(self):
self.pos += 1
if self.pos >= len(self.text):
self.current_char = None
else:
self.current_char = self.text[self.pos]
def skip_whitespace(self):
while self.current_char is not None and self.current_char.isspace():
self.advance()
def integer(self):
result = ''
while self.current_char is not None and self.current_char.isdigit():
result += self.current_char
self.advance()
return int(result)
def get_next_token(self):
while self.current_char is not None:
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, ')')
raise Exception(f'非法字符: {self.current_char}')
return Token(EOF, None)
第二阶段:语法分析(Parser)
语法分析将 Token 序列构建为抽象语法树(AST)。我们采用递归下降法,为运算符定义优先级:+ 和 - 优先级低于 * 和 /。
# AST 节点
class BinOp:
def __init__(self, left, op, right):
self.left = left
self.op = op
self.right = right
class Num:
def __init__(self, value):
self.value = value
class Parser:
def __init__(self, lexer):
self.lexer = lexer
self.current_token = self.lexer.get_next_token()
def eat(self, token_type):
if self.current_token.type == token_type:
self.current_token = self.lexer.get_next_token()
else:
raise Exception(f'期望 {token_type},实际 {self.current_token.type}')
def factor(self):
"""factor : INTEGER | LPAREN expr RPAREN"""
token = self.current_token
if token.type == INTEGER:
self.eat(INTEGER)
return Num(token.value)
elif token.type == LPAREN:
self.eat(LPAREN)
node = self.expr()
self.eat(RPAREN)
return node
def term(self):
"""term : factor ((MUL | DIV) factor)*"""
node = self.factor()
while self.current_token.type in (MUL, DIV):
token = self.current_token
if token.type == MUL:
self.eat(MUL)
elif token.type == DIV:
self.eat(DIV)
node = BinOp(left=node, op=token, right=self.factor())
return node
def expr(self):
"""expr : term ((PLUS | MINUS) term)*"""
node = self.term()
while self.current_token.type in (PLUS, MINUS):
token = self.current_token
if token.type == PLUS:
self.eat(PLUS)
elif token.type == MINUS:
self.eat(MINUS)
node = BinOp(left=node, op=token, right=self.term())
return node
第三阶段:求值(Evaluator)
求值器遍历 AST,计算最终结果。
class NodeVisitor:
def visit(self, node):
method_name = 'visit_' + type(node).__name__
visitor = getattr(self, method_name, self.generic_visit)
return visitor(node)
def generic_visit(self, node):
raise Exception(f'未处理的节点类型: {type(node).__name__}')
class Interpreter(NodeVisitor):
def __init__(self, parser):
self.parser = parser
def visit_BinOp(self, node):
left = self.visit(node.left)
right = self.visit(node.right)
if node.op.type == PLUS:
return left + right
elif node.op.type == MINUS:
return left - right
elif node.op.type == MUL:
return left * right
elif node.op.type == DIV:
return left // right # 整数除法
def visit_Num(self, node):
return node.value
def interpret(self):
tree = self.parser.expr()
return self.visit(tree)
运行示例
def main():
while True:
try:
text = input('calc> ')
except EOFError:
break
if not text:
continue
lexer = Lexer(text)
parser = Parser(lexer)
interpreter = Interpreter(parser)
result = interpreter.interpret()
print(result)
if __name__ == '__main__':
main()
运行后输入 (1 + 2) * 3,输出 9。
总结与扩展
本文实现了一个支持四则运算和括号的简易解释器,核心分为三步:词法分析生成 Token,语法分析构建 AST,求值遍历 AST 计算结果。你可以在此基础上扩展:
- 支持浮点数、变量和赋值
- 增加一元负号、比较运算符
- 引入符号表实现变量存储
- 添加错误恢复和更友好的报错信息
理解这个三段式结构,是学习更复杂解释器(如 Python、JavaScript 引擎)的坚实基础。