例子1:
function func1(c, e)
local a1, a2;
c = 2;
end
指令:
CODE
1 ADJUST 2
3 ADJUST 4
4 PUSH2
5 STORELOCAL0
7 ADJUST 2
9 RETCODE 2
CODE
0 RETCODE0
function -> function name body:
这个产生式的分析分为两个阶段,第一个阶段是在分析body之前,这个将为这个函数初始化好
需要的数据:目标指令的生成空间,在lua2.1中,函数和函数之间所生成的指令并非放置在一起的。
每个函数都有一个独立的指令保存空间。
第二个阶段是分析body的阶段,这是主要阶段
body -> ( parlist ) block end:
在parlist和block分析结束之后,本产生式对应的分析过程会将生成的指令结尾添加指令RETCODE0
或RETCODE指令,这个指令的计算结果是函数执行的返回值中的第一个返回值相对栈底的偏移值。最后
将生成好的指令拷贝到一个新分配出来的空间返回给上个层次的分析过程。
parlist -> null | parlist1:
子产生式分析完成之后,本分析过程会在生成的指令的结尾添加指令:ADJUST0或ADJUEST指令,
这个执行是为了调整栈顶,将栈顶移到函数的最后一个形参的后面。如例子1中的第一个ADJUST指令。
parlist1 -> name | parlist1,name
对于name:寻找name对应的lua对象在全局对象表中索引并在“局部变量数组”中记录起来。第一个子
产生式也是如此。
注意:到目前为止,只有形式参数才会被放置到局部变量数组中。
block -> statlist ret:
在分析statlist和ret之前,首先记录起局部变量的数量并返回给上层分析逻辑。
在分析好statlist之后和ret之后,会再次判断局部变量和分析之前的数量,如果不等,再次恢复会分析
之前的数量并添加指令ADJUST0(ADJUEST)。那么在社么情况先局部变量的数量还会变化呢,那就是函数定义
的内部有用关键字local声明了一个变量之后会增加。如例子1中,函数中用了关键字来修饰两个参数,对应的产生
的指令是ADJUST 4,4的来源是“2个参数+2个函数内部用local修饰过的变量”。
但这里需要注意的是,生成的指令中RETCODE 2并不会因此变成RETCODE 4,也就是说第一个返回值总是,
是在最后一个参数L的后面,任何多出来的对象(从L到栈顶)都有可能成为返回值,这就是脚本语言的强大之
处,返回值的数量随调用情况不断地改变(多则阶段少则补nil)。
ret -> return exprlist sc:
添加指令RETCODE0(RETCODE),如果我们不使用return,那么返回值的位置将一定是最从后参数
的后面开始,使用了return之后,将一定是从最后一个局部变量的后面第一个元素开始。
method -> function name : name body:
这个产生式实际描述了为一个对象赋予函数的过程,具体实现是在函数参数前面添加一个名字为self的 局部变量,
这也就是说如果我们要使用这个对象的数据的时候,需要用self.xxx的方式。另外一点就是用函数名和分析好的结果做
为key-value的方式保存到对象中,这样就要求对象一定要是表结构才行。
错误的例子:
a = 1;
function a:func1()
return 1;
end
正确的例子:
ta = {a1 = 1};
function ta:func1()
return self.a1;
end