原作时间:2019~2021年
此次共享,腾讯内网 / 外网同步发布。
内部代码地址:https://git.code.oa.com/fatboyli/QBDF
外部代码地址:GitHub - ventureli/QBDF
两年前的一个混淆包(无源码):GitHub - ventureli/VLOCInterpreter
作者:腾讯fatboyli(李文强)
终于说到了解释器里最核心的部分了,语法制导翻译与递归子程序设计。另外本章节和下一章节(中间代码指令设计与实现)的内容交叉很多,建议两个章节同步阅读。语义解析其实有很多的方法。但是我觉得递归子程序这个是最简单的最符合程序员逻辑的一种方式,虽然这种方法效率有点低,但是在真正的使用过程中,把QBDF-OC 编译成为中间码的过程可以离线的,并不一定要在客户端做,所以这一步最重要的并不是效率。语法制导翻译与递归子程序的我们可以这样通俗理解:
参考BNF表达式,把文法翻译成一个一个的子程序。每个子程序处理一个表达式。首先我们粗略的对比下 BNF表达式和表达式的翻译的方法列表就能看出来。
这里我节选了一段 普通表达式的BNF.大家仔细的看下,就能发现子程序设计的妙处。
QBDF的BNF示意图
这里大家注意红色的部分。就是我说的表达式的左部分(‘:’左边的部分),每一个左边的定义,都会对应一个方法。现在把QBDF的解释器的方法列表截图给大家看下。
QBDF解释器的方法列表
可以看到,每一个BNF表达式的左边都对应一个方法。比如 EXP / Add /Condition 等。现在我们那一个例子来解析。
那么每一个子程序是如果工作的呢?我们拿一个简单的方法做一个解析。
我们看下。这个表达式
exclusive_or_exp: and_exp (‘^' and_exp)*
我们从这个里面可以看到,这个 exclusive_or_exp 被定义为一个 and_exp 或者 and_exp^and_exp 或者 and_exp^and_exp ^and_exp 或者 and_exp^and_exp ^and_exp^.... 反正就是后面这个东西可以有很多个就是了。
那我们写代码的时候怎么处理这个事情?如果我们放弃编译原理,BNF表达式这一大堆唬人的概念,其实这个表达式非常的简单,随便一个程序员都能写出来。我们知道 and_exp 也是一个子程序,那么我们可以不用管and_exp 里怎么操作。我们只需要再需要他的地方调用就行了,剩下的其实就是匹配下'^' 这个字符和循环就可以了。
好了,现在我们看下代码。
-(NSDictionary *)_QBDF_EXP_EXCLUSIVE_OR{
NSDictionary*leftNode = [self_QBDF_EXP_AND];
if(_currentToken->tokenType == '^' ) {
int type = _currentToken->tokenType;
[self match:'^'];
NSDictionary*rightNode = [self _QBDF_EXP_AND];
NSMutableArray *operands = [NSMutableArray new];
[operands addObject:leftNode];
[operands addObject:rightNode];
while (_currentToken->tokenType == '^') {
[selfmatch:'^'];
NSDictionary*newnode = [self_QBDF_EXP_AND];
[operands addObject:newnode];
}
NSMutableDictionary*newRootNode = [self_createEXPOperatorNode:type left:operands right:nil thridNode:nil];
return newRootNode;
}else {
returnleftNode;
}
}
这个代码极其的简单。我们首先让我们的程序递归到左边,去找and_exp 的返回值,and_exp 会把单词;列表里他自己需要的那部分给吃掉。然后他递归出来后,我们就判断当前的单词是否是‘^’ 如果是的话,那么我们根据BNF表达式知道了,我们当前的至少是一个 and_exp ^ and_exp 至于后面还有没有 ,先不管,先调用[self _QBDF_EXP_AND]; 拿到第二个操作数,拿到后,开始循环判断即可。
至于这段代码
NSMutableDictionary*newRootNode = [self_createEXPOperatorNode:type left:operands right:nil thridNode:nil];
return newRootNode;
还有这段代码
NSDictionary*rightNode = [self _QBDF_EXP_AND];
NSMutableArray *operands = [NSMutableArray new];
[operands addObject:leftNode];
[operands addObject:rightNode];
的含义,我们放到下一章节和指令设计这块去处理。因为本章节和下一章是解释器最核心的部分,本章节先介绍下,QBDF解释器的程序设计结构。
所以就像我前面章节说的。BNF真的很重要,写代码其实就是翻译它的过程。