如何为 Python 新增语法

Python 是一套语法规范,规定了开发者如何编写 Python 的代码。如何解析、执行 Python 的源码,最后输出则是 Python 解释器的职责。我们平时使用的 Python 一般指的是 CPython,其解释器是由 C 语言编写。除此之外,还有比如 Jython, 使用 Java 编写的。Pypy 则是用 Python 写的。

这篇文章描述了如何获取 Python 的源码,源码的目录结构以及如何重新编译 Python。这篇文章使用的 Python 版本是 3.9,如果你想重现文章中的实验,最好是采用相同的版本。

获取源码

我们可以通过 git 来获取 CPython 的源码:

bash 复制代码
git clone --branch 3.9 <https://github.com/python/cpython>

项目使用的是 IDE 是 JetBrain CLion,开发环境采用的是 macOS。你可以使用任何你熟悉的编辑环境。

源码的目录结构

获取源码之后,我们首先熟悉一下源码的目录结构:

  • Doc:文档目录
  • Grammer:用于定义 Python 语法规则,计算机可以直接读取和处理的文件
  • Include:C 的头文件(Header files)
  • Lib:使用 Python 写的标准库模块
  • Mac:macOS 系统支持的文件
  • Misc:杂七杂八的文件,没办法分类的文件
  • Modules:使用 C 语言编写的标准库模块
  • Objects:核心类型和对象模型
  • Parser:Python 解析器的源码,语法分析,生成语法树或AST
  • PC:用于旧版本的 Windows 系统的构建的支持文件
  • PCbuild:用于新版本的 Windows 系统的构建的支持文件
  • Programs:Python 可执行文件和其他二进制文件的源码
  • Python:CPython 解释器的源码,解释执行 Python 的源码
  • Tools:用于构建和扩展 CPython 的一些独立的工具
  • m4: 用于配置 Makefile 的一些自定义脚本

编译 CPython

首先安装编译的依赖:

复制代码
 brew install openssl xz zlib gdbm sqlite

我以 macOS 为例,首先执行 ./configure 检查环境并生成 Makefile :

ini 复制代码
$ cpython % CPPFLAGS="-I$(brew --prefix zlib)/include" \
		LDFLAGS="-L$(brew --prefix zlib)/lib" \
		./configure --with-openssl=$(brew --prefix openssl) --with-pydebug

我来解释一下:

  • CPPFLAGS 是告诉 C 预处理器,zlib 库的 Header files 的存放的目录;
  • LDFLAGS 是在链接阶段,去 $(brew --prefix openssl) 所在目录查找库文件(一般以 *.so 或者 .a 作为扩展名;
  • --with-pydebug 可以让你在开发或者测试过程中启用调试。

不出意外,你可以看到如下这般输出:

lua 复制代码
config.status: creating pyconfig.h
creating Modules/Setup.local
creating Makefile

这表明你当前的系统符合编译要求,并且已经生成了编译所需的 Makefile 文件。可以进行编译了。

go 复制代码
make -j14 -s

这条命令中,-j14 表示通过 14 个任务并行编译(一般等于你的 CPU 核心数就行),-s全称是 --silent 表示不打印执行的命令本身,让输出更加简洁。

不出意外,你可以看到如下的输出,表明已经编译成功了:

复制代码
Python build finished successfully!

你会在源码的根目录下看一个 python.exe 的可执行文件。虽然扩展名为 .exe ,但这并不是 Windows 下的可执行文件,而是 Python 的开发者特意加上这个扩展名和源码目录下的 Python 目录做区分,因为在 macOS 系统中并不区分大小写,所以加上 .exe 避免歧义。

你可以执行执行这个可执行文件,输出编译的 Python 的版本:

shell 复制代码
$ ./python.exe --version
Python 3.9.22+

修改 Python 源码

接下来,我们尝试修改 Python 的源码。为 Python 增加一个关键字。我们知道,Python 中,如果你想暂时不实现一个方法,可以通过 pass 关键字,例如:

python 复制代码
def do_somethings():
	pass

我希望当我输入 pass 或者 ps 都能实现同样的效果。这就需要修改 Python 的语法定义。在 Grammar 目录下,找到 python.gram 文件,并且搜索 small_stmt 关键字,你会看到如下语句:

scss 复制代码
small_stmt[stmt_ty] (memo):
    | assignment
    | e=star_expressions { _Py_Expr(e, EXTRA) }
    | &'return' return_stmt
    | &('import' | 'from') import_stmt
    | &'raise' raise_stmt
    | 'pass' { _Py_Pass(EXTRA) }

更改如下,将 'pass' 更改为 ('pass' | 'ps') , 表明 pass 或者 ps 都可以:

scss 复制代码
- | 'pass' { _Py_Pass(EXTRA) }
+ | ('pass' | 'ps') { _Py_Pass(EXTRA) }

修改完成之后,执行如下命令重新生成语法解析器的表:

bash 复制代码
$ make regen-pegen
PYTHONPATH=./Tools/peg_generator python3 -m pegen -q c \
                ./Grammar/python.gram \
                ./Grammar/Tokens \
                -o ./Parser/pegen/parse.new.c
python3 ./Tools/scripts/update_file.py ./Parser/pegen/parse.c ./Parser/pegen/parse.new.c

然后我们重新编译 Python:

go 复制代码
make clean && make -j14 -s

最后测试一下:

总结

这篇文章描述了如何在 macOS 上编译 CPython 的源码,最终你会得到一个 python.exe 的可执行文件。

在了解了如何编译 Python 解释器之后,我们修改了 Python 的语法规则,增加了一个 ps 的关键字,来实现和 pass 关键字一样的功能,最后编译并测试。

相关推荐
HHBon20 分钟前
判断用户输入昵称是否存在(Python)
linux·开发语言·python
敢敢变成了憨憨2 小时前
java操作服务器文件(把解析过的文件迁移到历史文件夹地下)
java·服务器·python
敲键盘的小夜猫2 小时前
Milvus向量Search查询综合案例实战(下)
数据库·python·milvus
简简单单做算法3 小时前
基于mediapipe深度学习的虚拟画板系统python源码
人工智能·python·深度学习·mediapipe·虚拟画板
愿望会实现吧4 小时前
|从零开始的Pyside2界面编程|绘图、布局及页面切换
python
zstar-_5 小时前
【Ragflow】24.Ragflow-plus开发日志:增加分词逻辑,修复关键词检索失效问题
人工智能·python·llm
love530love5 小时前
【笔记】2025 年 Windows 系统下 abu 量化交易库部署与适配指南
大数据·运维·人工智能·windows·笔记·python·conda
love530love5 小时前
【笔记】为 Python 项目安装图像处理与科学计算依赖(MINGW64 环境)
开发语言·图像处理·人工智能·windows·笔记·python·numpy
奉系坤阀5 小时前
Ubuntu终端性能监视工具
linux·运维·服务器·python·ubuntu
YYXZZ。。5 小时前
PyTorch-Transforms的使用(二)
人工智能·pytorch·python