上 构建Lua解释器Part7:构建完整的语法分析器( 十 )


结合图13 , 我们通过几个例子来看一下这个流程 。
首先我们来看一下 , 编译foo函数时的情况 。如果当前token为< , c >的情况 , 此时执行expr的逻辑 , 它首先会查找foo函数的local列表 , 它在第0个位置找到了变量c , 因此生成结构 , e->k= , e->=0
然后我们来看一下 , 当前token为< , b >的情况 , 此时执行expr逻辑 , 因为在foo的local列表中找不到变量b , 所以到外层函数中查找 , 并且在外层函数的local列表中 , 找到变量b , 此时foo函数将其添加到自己的列表中 , 并且生成结构 , 此时e->k= , e->=1
最后我们来看一下 , 当前token为< , print >时 , expr的编译执行情况 , 首先到foo函数体的local列表中查找print , 因为没有找到 , 所以到自己的中查找 , 发现也没找到 。此时到外层函数的local列表中查找 , 因为没有找到 , 所以去外层函数的列表里查找 , 也没找到 , 最后只能去_ENV里查找 。编译器会先将print这个字符串 , 加入到foo函数对应的Proto结构的常量表k中 , 然后将e->k= , e->u.ind.vt= , e->u.ind.t=0 , e->u.ind.idx=258(假设print在常量表第3个位置) 。这个的含义是 , 因为e->k为 , 所以这是一个从一个table取值的处理 , 而且e->u.ind.vt为 , 所以这个被操作的表 , 要去表去查找 , 又因为e->u.ind.t为0 , 所以操作目标是[0] , 最后idx为258 , 则是去常量表里找到访问table的key值 。
我们在获得了结构以后 , 最终是要将这些信息转化为指令的 , 那么这个转化的过程是怎样的呢?我们现在来看一下:
等下我会用一些例子来展示这个转化的流程 , 这个转换的功能一般是使用函数来实现 。
2、单目运算操作
回顾一下代码片段5 , 我们的expr函数 , 最终是调用了函数 , 其最后一个参数是指定当前运算符的优先级的 , 在函数内部 , 处理完一个exp以后 , 如果紧随其后的操作符优先级高于exp前面的 , 那么后面的操作要优先处理 。接下来 , 我们来讨论一下单目运算的情况 。什么是单目运算呢?就是exp前面有-、#、~和not操作的时候 。我们现在分情况来讨论 。
第一种情况就是expr前面有’-‘的情况 。我们接下来看一下几个例子:
-100-0.5-value
’-‘操作符之后 , 应该跟数值类型 , 否则这就是一个无效的exp , 应当抛出 Error 。不过这里需要注意的是 , token ’-‘后面 , 如果跟类型的token , 那么在编译阶段 , 是不会检查该token是否是数值类型 , 它只会到虚拟机运行阶段的时候去检查 。我们先来看一下 , token ‘-‘后面紧跟的是一个数值token的情况 , 我们可以结合图14的情况来看 , 以下逻辑流程均发生在expr函数内:
图14
现在假设有如图14的表达式要经过expr函数处理 , 首先expr函数 , 会先检查当前的token , 是否是’-‘ , 如果是 , 将操作符’-‘存到一个变量中 , 并且进入到单目运算处理流程 , 然后调用函数 , 获取下一个token , 此时得到图15的情况: