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

相关推荐
MediaTea11 分钟前
Python 库手册:xmlrpc.client 与 xmlrpc.server 模块
开发语言·python
悦悦子a啊13 分钟前
Python之--字典
开发语言·python·学习
水军总督15 分钟前
OpenCV+Python
python·opencv·计算机视觉
qyhua1 小时前
Windows 平台源码部署 Dify教程(不依赖 Docker)
人工智能·windows·python
一车小面包1 小时前
Python高级入门Day6
开发语言·python
攻城狮凌霄2 小时前
PHP与ChatGPT结合的技术王炸,开发高效创作小红书内容系统
python
秃然想通2 小时前
Python编程:初入Python魔法世界
python
胡耀超2 小时前
我们如何写好提示词、发挥LLM能力、写作指南:从认知分析到动态构建的思维方法
人工智能·python·学习·大模型·llm·提示词·八要素思维
倔强青铜三2 小时前
Python的Lambda,是神来之笔?还是语法毒瘤?
人工智能·后端·python
倔强青铜三3 小时前
Python之父差点砍掉import!认为模块系统是最大败笔?!
人工智能·python·面试