TheRiver | blog

You have reached the world's edge, none but devils play past here

0%

lua-编译-运行-调试

引用

programming in lua

编译、运行、错误处理

britzl/traceback.lua

The Implementation of Lua5.0.pdf

前言

Lua会预先把代码预编译成中间码然后再执行.在解释型语言中存在编译阶段听起来不太合适.然而,解释型语言的特征不在于是否被编译,而是编译时是否是运行时的一部分.执行编译产生的中间码速度会更快.
函数dofile的存在就是说明可以将lua作为解释型语言被调用。

difile其实是个辅助的函数,真正完成功能的是loadfile函数.

loadfile ([filename [, mode [, env]]])

Similar to load, but gets the chunk from file filename or from the standard input, if no file name is given.

与dofile不同的是,loadfile编译代码成中间码并且返回编译后的chunk作为一个函数,而不执行代码.另外,loadfile不会抛出异常,而是返回错误代码.

int luaL_loadstring (lua_State *L, const char *s) [-0, +1, –]

Loads a string as a Lua chunk. This function uses lua_load to load the chunk in the zero-terminated string s.

loadstring与loadfile类似,但它不是读取文件,而是读取串,but,现在这个函数不能用了.老的教程都没有提到这一点.

参考:

From Lua 5.2 reference manual:

Function loadstring is deprecated. Use load instead; it now accepts string arguments and are exactly equivalent to loadstring.
//不推荐使用函数loadstring。使用load代替;它现在接受字符串参数,并且完全等同于loadstring。

> i=3
> loadstring("i=i+1")
stdin:1: attempt to call a nil value (global 'loadstring')
stack traceback:
        stdin:1: in main chunk
        [C]: in ?
>
> f = load("i = i + 1")
> f()
> print(i)
4
> f()
> print(i)
5

lua把每一个chunk都当成一个匿名函数处理。例如, chunk “a = 1” 等价于:

function
    a = 1
end

注意点

1 load和loadfile都不会抛出错误,如果发生错误他们将返回nil加上错误信息

print(load("1 + "))
nil     [string "1 + "]:1: unexpected symbol near '1'

2 load和loadfile都不会有边界效应,他们仅仅编译chunk成为他们内部实现的一个匿名函数.通常对他们的误解是他们定义了函数.lua中的函数定义是发生在运行时的赋值而不是发生在编译时.例如:

--file foo.lua    
function foo(x)
    print(x)
end

当我们执行命令f = loadfile(“foo.lua”)时,foo被编译了但还没有被定义.如果要定义,必须运行:

f()        --defines 'foo'

3 load编译的时候不关心词法范围:

//仔细看下面的代码,注意哪个是全局变量i,哪个是局部变量i
> function test()
>> print(i)
>> local i = 0
>>
>> f = load("i = i + 1")
>> f()
>> print(i)
>>
>> function g()
>> i = i + 1
>> end
>>
>> g()
>> print(i)
>> end
>
> test()
8
0
1
> test()
9
0
1

4 load通常用于运行程序外部的代码,load期望一个chunk,即语句.如果想要加载表达式,需要在表达式前加return,那样将返回表达式的值.

表达式,是由数字、算符、数字分组符号(括号)、自由变量和约束变量等以能求得数值的有意义排列方法所得的组合。约束变量在表达式中已被指定数值,而自由变量则可以在表达式之外另行指定数值。

//书上是local sql, 但前面提到的loadstring是用的全局变量,这里sql我就用了全局变量,不然会和实际有出入的

Lua 5.1.4  Copyright (C) 1994-2008 Lua.org, PUC-Rio

> print"enter you r expression:"
enter your expression:
> sql = io.read()
6
> func = assert(loadstring("return " .. sql))
> print("the value of your expression is " .. func())
the value of your expression is 6



Lua 5.3.5  Copyright (C) 1994-2018 Lua.org, PUC-Rio

> print"enter you r expression:"
enter you r expression:
> sql = io.read()
5
> func = assert(load("return " .. sql))
> print("the value of your expression is " .. func())
the value of your expression is 5

reuire函数

lua提供更高级的require函数来加载运行库。require和dofile有两点不同:
1 require会搜索目录加载文件
2 require会判断文件是否已经加载避免重复加载同一文件。

require使用的路径和普通我们看到的路径还有些区别,我们一般看到的路径都是一个目录列表.require的路径是一个模式列表,每一个模式指明一种由虚文件名(require的参数)转成实文件名的方法.匹配的时候lua会首先将问号用虚文件名替换,然后看是否有这样的文件存在.如果不存在继续用同样的方法用第二个模式匹配.

例如,路径如下:

?;?.lua;C:\windows?;/usr/local/lua/?/?.lua

调用 require("lili")时会尝试打开这些文件:

lili
lili.lua
c:\windows\lili
/usr/local/lili/lili.lua

require只关心分号和问号,其他的信息在路径中定义.

路径:
Lua将require搜索的模式字符串放在变量package.path中。当Lua启动后,便以环境变量LUA_PATH的值来初始化这个变量。如果没有找到该环境变量,则使用一个编译时定义的默认路径来初始化。如果require无法找到与模块名相符的Lua文件,就会找C程序库。C程序库的搜索模式存放在变量package.cpath中。而这个变量则是通过环境变量LUA_CPATH来初始化的。

> print("LUA MODULES:\n",(package.path:gsub("%;","\n\t")),"\n\n C MODULES:\n",(package.cpath:gsub("%;","\n\t")))
LUA MODULES:
    ./?.lua
    /usr/share/lua/5.1/?.lua
    /usr/share/lua/5.1/?/init.lua
    /usr/lib64/lua/5.1/?.lua
    /usr/lib64/lua/5.1/?/init.lua    

 C MODULES:
    ./?.so
    /usr/lib64/lua/5.1/?.so
    /usr/lib64/lua/5.1/loadall.so

一个路径中的模式也可以不包含问号而只是一个固定的路径,比如:

?;?.lua;/usr/local/default.lua

这种情况下,require没有匹配的时候就会用这个固定的文件(这个缺省的文件放在模式列表的最后才有意义)

异常和错误处理

如果在lua中需要处理错误,需要调用pcall函数封装你的代码,从而捕捉异常和错误.
pcall在保护模式下调用它的第一个参数并运行,因此可以捕获所有的异常和错误.
如果没有异常和错误,pcall返回true和调用返回的任何值:

> first,second = pcall( function() print("code blocks...") return "success!" end)
code blocks...
> print(first, second)
true    success!

否则,返回nil加错误信息:

> first,second = pcall( function() error({err1 = "step one"}) end)
> print(first, second)
false    table: 0xbd6690
> print(second.err1)
step one

错误信息不一定非要是字符串,传递给error的任何信息都会被pcall返回.
这种机制提供了我么在lua中处理异常和错误的全部内容。我们通过error抛出异常,然后通过pcall来捕获它.

错误信息和回跟踪

error函数

debug.traceback()

当pcall返回错误的时候它已经释放了保存错误发生情况的栈的信息,而我们想获取相关的堆栈信息就必须在pcall返回前获取,lua提供了xpcall来实现这个功能,xpcall接受2个参数,调用函数和错误处理函数.
可以手动调用debug或者debug.traceback来查看错误信息.

> function a()
>> print("a" + 1)
>> end
> 
> function b()
>> a()
>> end
> 
> function c()
>> b()
>> end
> 
> function err()
>> print("deal err func:")
>> print(debug.traceback())
>> end
> 
> print(xpcall( c, err))
deal err func:
stack traceback:
    stdin:3: in function <stdin:1>
    stdin:2: in function 'a'
    stdin:2: in function 'b'
    stdin:2: in function <stdin:1>
    [C]: in function 'xpcall'
    stdin:1: in main chunk
    [C]: ?
false    nil

ending

----------- ending -----------