在“lua2.1 语法解析过程(2)(编辑中)”一文中我们分析了表达式expr,接下来在本文中将分析另外一个重要的非终结符:stat --语句。
stat -> stat1 忽略
stat1 -> if expr1 then PrepJump block PrepJump elsepart end:
一个小小的if语句看似简单,但是在语法构造上却如此复杂。if语句的核心在于“跳转”,上面的文法中有两个跳转点,假设有语句
"if (e) then A else B end",那么第一个跳转点就是为了越过block A,第二个跳转点是为了跳过block B。
elsepart -> else block | else cond then PrepJump block elsepart:
和if语句的分析大同小异,将else部分独立出来是为了解决编译原理中的经典问题:“文法的二义性”。
stat1 -> while expr1 do PrepJump block PrepJump end:
while语句的关键同样是跳转,不过和if语句不同的是它有向后跳转的情况以实现循环。如语句"while e do A end",第一个跳转点
是在执行e之后,如果结果为false,那么将直接跳出while语句。第二个跳转点紧跟在A的后面,这个跳转是一个无条件跳转,它将跳到e
指向之前。
stat1 -> repeat block until cond PrepJump:
repeat 语句的关键同样是跳转,它只有一个跳转点,而且是一个条件跳转,cond执行的结果为false的时候会跳回到block之前。
varlist1 = exprlist1:
脚本语言中的一个比较有趣的点:一条语句分别对多个变量赋值,这个产生式就是为了实现这个功能的描述。
例子1,如下面语句:
a, b = 1, 2;
a, b = 1;
a, b = 1, 2, 3
产生的指令为:
CODE
0 PUSH1
1 PUSH2
2 STOREGLOBAL 51
5 STOREGLOBAL 50
8 PUSH1
10 ADJUST 2
11 STOREGLOBAL 51
14 STOREGLOBAL 50
17 PUSH1
18 PUSH2
20 PUSHBYTE 3
22 ADJUST 2
23 STOREGLOBAL 51
26 STOREGLOBAL 50
29 RETCODE0
第二种情况(补充nil)下会多出一个ADJUST 2指令,是为了调整等式的左右两边的数数量上的一致,这个指令的一个
副作用是会将nil对象赋予b。
第三种情况(截断多余)下也会多出一个ADJUST 2指令,和上一个不同的是它将最后一个数字3给阶段。
例子2,如下语句:
function func(a)
return a;
end
a, b, c = 1, func(1);
a, b, c = func(1), 1;
a, b, c = func(1);
产生的指令有:
CODE
0 PUSH1
1 PUSHGLOBAL 51
4 PUSH1
5 CALLFUNC 1 2
8 STOREGLOBAL 53
11 STOREGLOBAL 52
14 STOREGLOBAL 50
17 PUSHGLOBAL 51
20 PUSH1
21 CALLFUNC 1 1
24 PUSH1
26 ADJUST 3
27 STOREGLOBAL 53
30 STOREGLOBAL 52
33 STOREGLOBAL 50
36 PUSHGLOBAL 51
39 PUSH1
40 CALLFUNC 1 3
43 STOREGLOBAL 53
46 STOREGLOBAL 52
49 STOREGLOBAL 50
52 RETCODE0
第一种情况比较理想,func(1)会主动地返回两个值。
第二种情况func(1)只会返回一个值,也就是函数的返回值数量和其位置有比较大的关系。
第三种情况func会返回3个值。
stat1 -> functioncall:
之前也说过functioncal会在表达式和语句中出现,而在语句中出现的话他的返回值数量一定是0.
stat1 -> local localdeclist decinit:
和varlist = exprlist1类似只 不过varlist中的变量会多出一个属性:它们都是局部变量。