如何为 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 关键字一样的功能,最后编译并测试。

相关推荐
倔强青铜三19 小时前
苦练Python第58天:filecmp模块——文件和目录“找不同”的利器
人工智能·python·面试
倔强青铜三19 小时前
苦练Python第59天:tempfile模块,临时文件自动删!再也不用手动清理到怀疑人生
人工智能·python·面试
IT教程资源19 小时前
(免费分享)基于python的飞机大战游戏
python·游戏·pygame
hello 早上好19 小时前
深入理解 SPI:从定义到 Spring Boot 实践
java·spring boot·python
蒋星熠20 小时前
脑机接口(BCI):从信号到交互的工程实践
人工智能·python·神经网络·算法·机器学习·ai·交互
gc_229920 小时前
学习Python中Selenium模块的基本用法(17:使用ActionChains操作键盘)
python·selenium
大模型铲屎官20 小时前
【数据结构与算法-Day 37】超越二分查找:探索插值、斐波那契与分块查找的奥秘
人工智能·python·大模型·二分查找·数据结构与算法·斐波那契·分块查找
blank@l20 小时前
Python类和对象----实例属性,类属性(这是我理解类和对象最透彻的一次!!)
开发语言·python·python接口自动化基础·python类和对象·python实例属性·python类属性·类属性和实例属性的区别
超奇电子20 小时前
高斯包络调制正弦波的Python代码
开发语言·python
合作小小程序员小小店20 小时前
桌面预测类开发,桌面%雷达,信号预测%系统开发,基于python,tk,scikit-learn机器学习算法实现,桌面预支持向量机分类算法,CSV无数据库
python·算法·机器学习·支持向量机·scikit-learn