杰瑞科技汇

Python如何实现自己的Python解释器?

我们将分步实现一个简化版的Python解释器,这个解释器将支持:

Python如何实现自己的Python解释器?-图1
(图片来源网络,侵删)
  1. 基本的算术运算(, , , )
  2. 变量赋值(如 x = 10
  3. if 条件语句
  4. while 循环
  5. print 函数

为了简化,我们不会实现函数定义、类、异常处理等复杂特性。

核心思想

我们的解释器将遵循一个基本的工作流程,这被称为读取-求值-打印循环

  1. 读取:获取用户输入的一行或多行代码(字符串)。
  2. 解析:将代码字符串转换成一种更结构化的内部表示,通常是抽象语法树,对于我们的简化版,我们可以直接使用Python的ast(Abstract Syntax Trees)模块来完成这一步。
  3. 求值:遍历这个AST,并根据其节点类型执行相应的操作,这是解释器的核心。
  4. 打印:如果表达式有返回值,则将其打印出来。

第1步:环境 - 存储变量

我们需要一个地方来存储变量名和它们的值,一个简单的字典就非常完美。

class Environment:
    def __init__(self, parent=None):
        # parent用于实现作用域,全局环境的parent为None
        self.parent = parent
        self.vars = {}
    def get(self, name):
        # 首先在当前环境查找
        if name in self.vars:
            return self.vars[name]
        # 如果找不到,则去父环境查找
        if self.parent:
            return self.parent.get(name)
        # 如果都找不到,抛出异常
        raise NameError(f"name '{name}' is not defined")
    def set(self, name, value):
        # 在当前环境设置变量
        self.vars[name] = value
    def define(self, name, value):
        # 用于在当前环境定义新变量(函数参数或全局变量)
        self.vars[name] = value

第2步:解释器 - 求值器

这是我们的核心类,它将接收AST节点并递归地求值。

Python如何实现自己的Python解释器?-图2
(图片来源网络,侵删)
import ast
class Interpreter:
    def __init__(self):
        # 创建一个全局环境
        self.global_env = Environment()
        # 初始化一些内置函数
        self.global_env.define("print", self.builtin_print)
    def builtin_print(self, *args):
        # 实现print函数
        print(" ".join([str(arg) for arg in args]))
    def eval(self, node, env=None):
        if env is None:
            env = self.global_env
        # 根据节点类型分发到不同的处理方法
        if isinstance(node, ast.Expression):
            return self.eval(node.body, env)
        elif isinstance(node, ast.Num): # 数字
            return node.n
        elif isinstance(node, ast.Str): # 字符串
            return node.s
        elif isinstance(node, ast.Name): # 变量名
            return env.get(node.id)
        elif isinstance(node, ast.BinOp): # 二元操作,如 a + b
            left = self.eval(node.left, env)
            right = self.eval(node.right, env)
            op = node.op
            if isinstance(op, ast.Add):
                return left + right
            elif isinstance(op, ast.Sub):
                return left - right
            elif isinstance(op, ast.Mult):
                return left * right
            elif isinstance(op, ast.Div):
                return left / right
            # ... 可以添加更多操作符
        elif isinstance(node, ast.Assign): # 赋值,如 x = 10
            value = self.eval(node.value, env)
            for target in node.targets:
                if isinstance(target, ast.Name):
                    env.set(target.id, value)
            # 赋值语句本身没有返回值
            return None
        elif isinstance(node, ast.If): # if语句
            test_val = self.eval(node.test, env)
            if test_val:
                for stmt in node.body:
                    self.eval(stmt, env)
            else:
                for stmt in node.orelse:
                    self.eval(stmt, env)
            return None
        elif isinstance(node, ast.While): # while循环
            while self.eval(node.test, env):
                for stmt in node.body:
                    self.eval(stmt, env)
            return None
        elif isinstance(node, ast.Call): # 函数调用,如 print(x)
            func = self.eval(node.func, env)
            args = [self.eval(arg, env) for arg in node.args]
            return func(*args)
        # ... 可以添加更多AST节点类型
        else:
            raise NotImplementedError(f"AST node type {type(node).__name__} not implemented")
    def run(self, code):
        try:
            # 使用ast.parse将代码字符串解析成AST
            # mode='single'允许执行单个语句或表达式
            node = ast.parse(code, mode='single')
            # 求值AST
            result = self.eval(node)
            # 如果结果不是None,则打印它
            if result is not None:
                print(result)
        except SyntaxError as e:
            print(f"Syntax Error: {e}")
        except NameError as e:
            print(f"Name Error: {e}")
        except Exception as e:
            print(f"Error: {e}")

第3步:主循环

我们创建一个REPL来让用户可以与我们的解释器交互。

def repl(interpreter):
    print("Welcome to the Simple Python Interpreter!")
    print("Type 'exit' or 'quit' to leave.")
    while True:
        try:
            code = input(">>> ")
            if code.lower() in ('exit', 'quit'):
                break
            if not code.strip():
                continue
            interpreter.run(code)
        except KeyboardInterrupt:
            print("\nExiting...")
            break
        except EOFError:
            print("\nExiting...")
            break
if __name__ == "__main__":
    interpreter = Interpreter()
    repl(interpreter)

完整代码

将以上所有部分组合在一起,就是我们的完整解释器。

import ast
# ==============================================================================
# 1. 环境
# ==============================================================================
class Environment:
    def __init__(self, parent=None):
        self.parent = parent
        self.vars = {}
    def get(self, name):
        if name in self.vars:
            return self.vars[name]
        if self.parent:
            return self.parent.get(name)
        raise NameError(f"name '{name}' is not defined")
    def set(self, name, value):
        self.vars[name] = value
    def define(self, name, value):
        self.vars[name] = value
# ==============================================================================
# 2. 解释器
# ==============================================================================
class Interpreter:
    def __init__(self):
        self.global_env = Environment()
        self.global_env.define("print", self.builtin_print)
    def builtin_print(self, *args):
        print(" ".join([str(arg) for arg in args]))
    def eval(self, node, env=None):
        if env is None:
            env = self.global_env
        if isinstance(node, ast.Expression):
            return self.eval(node.body, env)
        elif isinstance(node, ast.Num):
            return node.n
        elif isinstance(node, ast.Str):
            return node.s
        elif isinstance(node, ast.Name):
            return env.get(node.id)
        elif isinstance(node, ast.BinOp):
            left = self.eval(node.left, env)
            right = self.eval(node.right, env)
            op = node.op
            if isinstance(op, ast.Add):
                return left + right
            elif isinstance(op, ast.Sub):
                return left - right
            elif isinstance(op, ast.Mult):
                return left * right
            elif isinstance(op, ast.Div):
                return left / right
            elif isinstance(op, ast.Pow):
                return left ** right
        elif isinstance(node, ast.Assign):
            value = self.eval(node.value, env)
            for target in node.targets:
                if isinstance(target, ast.Name):
                    env.set(target.id, value)
            return None
        elif isinstance(node, ast.If):
            test_val = self.eval(node.test, env)
            if test_val:
                for stmt in node.body:
                    self.eval(stmt, env)
            else:
                for stmt in node.orelse:
                    self.eval(stmt, env)
            return None
        elif isinstance(node, ast.While):
            while self.eval(node.test, env):
                for stmt in node.body:
                    self.eval(stmt, env)
            return None
        elif isinstance(node, ast.Call):
            func = self.eval(node.func, env)
            args = [self.eval(arg, env) for arg in node.args]
            return func(*args)
        elif isinstance(node, ast.Compare): # 处理比较操作,如 a < b
            left = self.eval(node.left, env)
            for op, right_node in zip(node.ops, node.comparators):
                right = self.eval(right_node, env)
                if isinstance(op, ast.Lt):
                    if not left < right:
                        return False
                elif isinstance(op, ast.LtE):
                    if not left <= right:
                        return False
                elif isinstance(op, ast.Gt):
                    if not left > right:
                        return False
                elif isinstance(op, ast.GtE):
                    if not left >= right:
                        return False
                elif isinstance(op, ast.Eq):
                    if not left == right:
                        return False
                # ... 其他比较操作
                left = right # 为下一次比较准备
            return True # 如果所有比较都成功
        else:
            raise NotImplementedError(f"AST node type {type(node).__name__} not implemented")
    def run(self, code):
        try:
            node = ast.parse(code, mode='single')
            result = self.eval(node)
            if result is not None:
                print(result)
        except (SyntaxError, NameError, ZeroDivisionError) as e:
            print(f"Error: {e}")
        except Exception as e:
            print(f"An unexpected error occurred: {e}")
# ==============================================================================
# 3. 主循环
# ==============================================================================
def repl(interpreter):
    print("Welcome to the Simple Python Interpreter!")
    print("Try typing: x = 10; y = 20; print(x + y)")
    print("Type 'exit' or 'quit' to leave.")
    while True:
        try:
            code = input(">>> ")
            if code.lower() in ('exit', 'quit'):
                break
            if not code.strip():
                continue
            interpreter.run(code)
        except KeyboardInterrupt:
            print("\nExiting...")
            break
        except EOFError:
            print("\nExiting...")
            break
if __name__ == "__main__":
    interpreter = Interpreter()
    repl(interpreter)

如何运行和测试

  1. 将上面的代码保存为一个文件,simple_interpreter.py
  2. 在终端中运行它:python simple_interpreter.py
  3. 你会看到一个 >>> 提示符,现在你可以输入你的简化Python代码了。

测试示例:

>>> x = 10
>>> y = 20
>>> x + y
30
>>> print(x * y)
200
>>> if x < y:
...     print("x is less than y")
... 
x is less than y
>>> z = 0
>>> while z < 5:
...     print(z)
...     z = z + 1
... 
0
1
2
3
4
>>> exit

进一步的探索方向

这个简化版解释器只是一个起点,如果你想让它变得更强大,可以考虑以下方向:

  1. 支持更多数据类型bool (True, False)、listdict,这需要为它们实现相应的AST节点处理逻辑。
  2. 函数定义和调用:这是最大的挑战之一,你需要处理 ast.FunctionDefast.Return 节点,函数需要有自己的作用域(Environment),并且需要实现一个“闭包”机制,让函数能够访问其定义时的外部环境。
  3. 作用域和命名空间:我们的Environment类已经有了parent指针,这为实现作用域打下了基础,你需要确保在函数内部、类内部等不同块中,变量查找是正确的。
  4. 错误处理:增加更细致的错误处理,比如TypeError(类型错误)、ZeroDivisionError(除零错误)等。
  5. 实现自己的AST解析器:目前我们依赖Python内置的ast模块,一个真正的项目需要自己实现一个词法分析器(将字符流变成“Token”流)和一个语法分析器(将Token流组合成AST),这会是一个巨大的工程,但也是理解编译原理的最好方式。
  6. 实现for循环:处理ast.For节点。

通过构建这样一个解释器,你会对Python代码是如何被执行的有一个前所未有的深刻理解。

分享:
扫描分享到社交APP
上一篇
下一篇