木兰编译器技术验证 源文件解析与exec
续前文,用尽量简单的语法验证将源码转换为可用 exec 执行的 Python 语法树。
目标
解析一个源码文件,内容很简单,只有一个数,比如 3。运行效果:
$ python3 数.py
import sys
from math import *
ARGV = sys.argv[1:]
3
显然,输出的并不是运行结果。且看下一部分。
大致框架
先看大致过程(数.py)。
源码文件 = '数.mulan'
with open(源码文件, 'r') as f:
源码 = f.read()
各词 = 分词器.lex(源码)
节点 = 分析器.parse(各词)
print(python.dump(节点))
可执行码 = compile(节点, 源码文件, 'exec')
exec(可执行码, {})
源码读入后,先词法分析,再语法分析生成 Python 语法树。
python.dump(节点)
是借用了木兰逆向工程中的 python 模块,将 Python 语法树转换为 Python 可执行码,为了方便确认语法树生成正确。
最后调用 exec 执行。
解析和语法树生成
解析部分(数分析器.py)与前文类似,但砍掉了所有运算符部分,仅留着数相关部分:
分词器母机 = LexerGenerator()
分词器母机.add('数', r'\d+')
分词器 = 分词器母机.build()
### 语法分析器部分
分析器母机 = ParserGenerator(
# 所有词名
['数']
)
如果语法分析返回的语法树不是 Module,而是比如 Expr 节点,就会报错:
code = compile(节点, 源码文件, 'exec')
TypeError: expected Module node, got Expr
于是,需要最后用 Module 再套一层(下面的语法树创建方法是用中文封装了 ast,详见此):
@分析器母机.production('表达式 : 数')
def 数表达式(片段):
数值 = int(片段[0].getstr(), 0)
数 = 语法树.数(数值, 行号=0, 列号=0)
表达式 = 语法树.表达式(值 = 数, 行号=0, 列号=0)
return 语法树.模块(主体=[表达式], 忽略类型=[])
语法树构建方法是参考了 Python 的语法描述。
module Python
{
mod = Module(stmt* body)
stmt = ...
| Expr(expr value)
expr = ...
| Num(object n) -- a number as a PyObject.
并参考了木兰逆向工程的具体调用方式。行号和列号等等参数尚未深究,暂时全部置零。
到此基本完成,为了直观地测试上面的语法树生成过程的确有效,可以把这里改为负数:
数值 = -int(片段[0].getstr(), 0)
再运行可见生成代码的变化:
$ python3 数.py
import sys
from math import *
ARGV = sys.argv[1:]
-3
后记
上面初步验证了生成可执行码的过程,下面是真正将其实用化的漫漫长路。由于至今积累的大多数测试代码都依赖于print
,需要研究是否能/需要添加不依赖它的测试。
再接下去,就是逐步添加语法规则,贴近木兰的实现。