原文
CALL执行一个函数调用。寄存器A中存放函数对象,所有参数按顺序放置在A后面的寄存器中。B-1表示参数个数 。如果参数列表的最后一个表达式是变长的,则B会设置为0,表示使用A+1到当前栈顶作为参数。函数调用的返回值会按顺序存放在从寄存器A开始的C-1个寄存器中。如果C为0,表示返回值的个数由函数决定。
f();
1 [1] GETTABUP 0 0 -1 ; _ENV "f" 2 [1] CALL 0 1 1 3 [1] RETURN 0 1
第一行取得全局变量f的值,保存到寄存器0。第二行CALL调用寄存器0中的函数,参数和返回值都是0。
local t = {f(...)};
1 [1] NEWTABLE 0 0 0 2 [1] GETTABUP 1 0 -1 ; _ENV "f" 3 [1] VARARG 2 0 4 [1] CALL 1 0 0 5 [1] SETLIST 0 0 1 ; 1 6 [1] RETURN 0 1
第一行NETTABLE创建一个表放到寄存器0中。第二行获取全局变量f放到寄存器1中。第三行VARARG表示使用当前函数的变长参数列表。第四行的CALL调用寄存器1中的函数,B为0,代表参数是变长的。前面讲过,如果表的构造的最后一项是多返回值的表达式,则这个表会接受所有的返回值。这里就是这种情况,表的构造会接受函数所有的返回值,所以C也为0。
name args desc OP_TAILCALL A B C A B C return R(A)(R(A+1), ... ,R(A+B-1))
如果一个return statement只有一个函数调用表达式,这个函数调用指令CALL会被改为TAILCALL指令。TAILCALL不会为要调用的函数增加调用堆栈的深度,而是直接使用当前调用信息。ABC操作数与CALL的意思一样,不过C永远都是0。TAILCALL在执行过程中,只对lua closure进行tail call处理,对于c closure,其实与CALL没什么区别。
return f();
1 [1] GETTABUP 0 0 -1 ; _ENV "f" 2 [1] TAILCALL 0 1 0 3 [1] RETURN 0 0 4 [1] RETURN 0 1
上面如果f是一个lua closure,那么执行到第二行后,此函数就会返回了,不会执行到后面第三行的RETURN。如果f是一个c closure,那就和CALL一样调用这个函数,然后依赖第三行的RETURN返回。这就是为什么TAILCALL后面还会己跟着生成一个RETURN的原因。
name
args
desc
OP_RETURN
A B
return R(A), ... ,R(A+B-2)
RETURE将返回结果存放到寄存器A到寄存器A+B-2中。如果返回的为变长表达式,则B会被设置为0,表示将寄存器A到当前栈顶的所有值返回。
return 1;
1 [1] LOADK 0 -1 ; 1 2 [1] RETURN 0 2 3 [1] RETURN 0 1
RETURN只能从寄存器返回数据,所以第一行LOADK先将常量1装载道寄存器0,然后返回。
return ...;
1 [1] VARARG 0 0 2 [1] RETURN 0 0 3 [1] RETURN 0 1因为返回的为变长表达式,B为0。
name args desc OP_CLOSURE A Bx R(A) := closure(KPROTO[Bx])
CLOSURE为指定的函数prototype创建一个closure,并将这个closure保存到寄存器A中。Bx用来指定函数prototype的id。
local function f() end
main <test.lua:0,0> (2 instructions at 0x102a016f0) 0+ params, 2 slots, 1 upvalue, 1 local, 0 constants, 1 function 1 [2] CLOSURE 0 0 ; 0x102a019b0 2 [2] RETURN 0 1 constants (0) for 0x102a016f0: locals (1) for 0x102a016f0: 0 f 2 3 upvalues (1) for 0x102a016f0: 0 _ENV 1 0 function <test.lua:1,2> (1 instruction at 0x102a019b0) 0 params, 2 slots, 0 upvalues, 0 locals, 0 constants, 0 functions 1 [2] RETURN 0 1 constants (0) for 0x102a019b0: locals (0) for 0x102a019b0: upvalues (0) for 0x102a019b0:上面生成了一个主函数和一个子函数,CLOSURE将为这个索引为0的子函数生成一个closure,并保存到寄存器0中。
name args desc OP_VARARG A B R(A), R(A+1), ..., R(A+B-2) = vararg
VARARG直接对应'...'运算符。VARARG拷贝B-1个参数到从A开始的寄存器中,如果不足,使用nil补充。如果B为0,表示拷贝实际的参数数量。
local a = ...;
1 [1] VARARG 0 2 2 [1] RETURN 0 1上面第一行表示拷贝B-1个,也就是1个变长参数到寄存器0,也就是local a中。
f(...);
1 [1] GETTABUP 0 0 -1 ; _ENV "f" 2 [1] VARARG 1 0 3 [1] CALL 0 0 1 4 [1] RETURN 0 1由于函数调用最后一个参数可以接受不定数量的参数,所以第二行生成的VARARG的B参数为0。
name
args
desc
OP_SELF
A B C
R(A+1) := R(B); R(A) := R(B)[RK(C)]
SELF是专门为“:”运算符准备的指令。从寄存器B表示的table中,获取出C作为key的closure,存入寄存器A中,然后将table本身存入到寄存器A+1中,为接下来调用这个closure做准备。
a:b();
1 [1] GETTABUP 0 0 -1 ; _ENV "a" 2 [1] SELF 0 0 -2 ; "b" 3 [1] CALL 0 2 1 4 [1] RETURN 0 1看一下与上面语法等价的表示方法生成的指令:
a.b(a);
1 [1] GETTABUP 0 0 -1 ; _ENV "a" 2 [1] GETTABLE 0 0 -2 ; "b" 3 [1] GETTABUP 1 0 -1 ; _ENV "a" 4 [1] CALL 0 2 1 5 [1] RETURN 0 1比使用“:"操作符多使用了一个指令。所以,如果需要使用这种面向对象调用的语义时,应该尽量使用”:"。
首先下载最新的Ubuntu系统的安装包:
http://www.newlisp.org/downloads/newlisp_10.4.5-utf8_i386.deb
然后安装:
dpkg -i ./newlisp_10.4.5-utf8_i386.deb
现在看看怎么用。
运行newlisp,报错:
/usr/bin/newlisp: error while loading shared libraries: libreadline.so.6: cannot open shared object file: No such file or directory
那就安装libreadline吧。
sudo apt-get install libreadline6 libreadline6-dev
安装完后,仍然报这个错。检查一下libreadline.so.6居然在这里:
/lib/x86_64-linux-gnu/libreadline.so.6
那就添加一个链接吧:
ln -s /lib/x86_64-linux-gnu/libreadline.so.6 /usr/lib/
再运行newlisp,还是报错。
newlisp newlisp: error while loading shared libraries: libreadline.so.6: wrong ELF class: ELFCLASS64
用apt-get autoremove newlips即可。
这里下载:http://sourceforge.net/projects/newlisp/files/newlisp-10.4.5.tgz/download
解压后进入目录,运行make,报错:
newlisp.h:36:19: fatal error: ffi.h: No such file or directory
安装libffi-dev
apt-get install libffi-dev
再运行make,成功了,然后运行sudo make install
安装完成。
现在运行newlisp,不带参数进入交互模式,看到:
root@chenshu-beijing:/usr/src/newlisp-10.4.5# newlisp newLISP v.10.4.5 64-bit on Linux IPv4/6 UTF-8 libffi, execute 'newlisp -h' for more info. >
成功了。可以运行lisp语句(+ 2 5)。
退出按C-c 然后根据提示按x.
1.绿色软件是很多人的最爱,但是也有一些问题,需要我们来解决,那就是如何将常用的软件添加到鼠标右键,比如,notepad++绿色版的软件,我们想让她能够出现在鼠标右键中,这样当我们遇到一个文件想用notepad++打开时,只要用鼠标右键就可以直接打开了。
2.下面介绍如何把一个程序添加到鼠标右键。主要就是修改注册表来实现的。
Windows Registry Editor Version 5.00
[HKEY_CLASSES_ROOT\*\shell]
[HKEY_CLASSES_ROOT\*\shell\Notepad++ Portable]
@="Notepad++ Portable"
[HKEY_CLASSES_ROOT\*\shell\Notepad++ Portable\Command]
@="F:\\Program Files\\Notepad++\\notepad++.exe %1"
将上边的程序保存成a.reg双击就可以导入了。
每一行的意思大致介绍一下,第一行声明注册表版本,第二行进入到shell文件夹,第三行建立Notepad++ Portable文件夹,并取名为“Notepad++ Portable”,第五行在刚才建立的文件夹下建立Commnad命令给出其参数为Notepad++的文件路径即可。