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


图42
由于我们的sBx是18个bit位 , 而指令是iAsBx模式 , jump指令可以往前跳转 , 因此sBx是允许负数存在的 , 又因为sBx在低位 , 无法通过最高位表示负数 。sBx的最大值是 , 它向右移一位是 , 因此这个值则表示0值 , 小于它的都是负数 , 比如表示-1 , 这里的表示pc+4 , 又因为pc寄存器本身会自增 , 于是就相当于跳转到箭头所指的位置了 。这里还需要注意的是 , 官方lua是会在这个阶段对指令进行优化的 , 将其优化为指令 , 由于只注重实现正确逻辑 , 性能还不是最优先考虑的因素 , 为了方便讲解和实现 , 并未对该指令进行任何优化 。
我们现在拿上面的例子 , 来模拟执行一下 , 感受一下这一系列指令的运作流程 , 我将从三个例子切入 , 他们分别如下所示:
a and b and c例1: a = false, b = true, c = true例2: a = true, b = false, c = true例3: a = true, b = true, c = false
我们先看例1的情况 , 它的执行流程是怎样的呢?首先第一步 , 需要将变量a通过指令 , 将变量a的值读入栈中 , 如图43所示:
图43
在将a值入栈之后 , 我们现在则需要对它进行测试 , 测试它的指令就是指令 , 此时 , 会将a的值和指令C域进行比较 , 比较代码是c语言 , 因此0代表false , 非0代表true , 这里因为a的值为false , 因此重新将false的值设置到寄存器R(0)上 , 因为是a的值是false , 所以这里会直接执行下一条指令 ,  , 最后跳过所有的TEST , 如图44所示:
图44
图中的曲线 , 就是测试通过后 , 直接跳过所有的测试指令 , 因为and逻辑中 , 只要有一个false , 立马终止测试 , 跳过所有的测试指令 。我们接下来看一下例2 , 它的运作流程如图45所示:
图45
图45一口气把例2的逻辑执行了一遍 , 其流程总结如下所示:
接下来 , 我们来看一下例3的情况 , 得到图46的结果:
图46
图46展示了例3的运作流程 , 与前面的没有很大的差别 , 这里需要注意的是 , 如果a和b的值都为true , 那么决定这个and逻辑运算结果的是最后一个表达式的值 。
我已经展示了and逻辑运算的流程 , 至于or的 , 只需要把指令的C域改成1 , 就是or的逻辑运算了 , 读者可以自己去推导 。接下来 , 我们来看一下逻辑运算中 , 包含常量的情况 。这里要分情况来讨论 , 当我们在纯粹的and逻辑处理流程过程中时 , 如果and操作符前面的exp , 经过expr函数输出的结构是VK、VFLT、VINT或VTRUE类型时 , 它是被直接忽略掉的 , 也就是说不参与到指令生成 。如图47所示的结果:
图47
我们可以看到 , 除了最后一个exp , 其他exp均在and操作符前面 , 他们在and逻辑运算中 , 如果处于and操作符之前 , 就可以直接被忽略 , 因为他们的值一定是true , 所以直接跳到下一个TEST指令之中 , 这属于编译器的一种优化 。如果and逻辑执行到最后一个exp , 说明前面所有的exp都测试通过 , 因此最后一个exp的值决定了最后的结果 , 所以不能被忽略 。