Lua是一种解释型语言,区别解释型语言的主要特征并不在于是否能编译他们,而是在于编译器

是否是语言运行时库的一部分,即是否有能力执行动态生成的代码。

 

8.1编译

在Lua中,dofile函数是一种内置操作,用于运行Lua代码块;但实际上dofile只是一个辅助函数,

loadfile才做了真正核心的工作。loadfile的两个功能:一是从一个文件中加载Lua代码块,但它不

会运行代码,只是编译代码,然后将编译结果作为一个函数返回;二是不会引发错误,

它只是返回错误值并不处理错误。

dofile的定义:

function dofile(filename)
    local f =assert(loadfile(filename))
    return f()
end

如果loadfile失败,那么其中assert就会引发一个错误。

对于简单任务而言,dofile非常便捷,它在一次调用中做完了整件事情。然而loadfile更灵活,

在发生错误的情况下,loadfile会返回nil及错误信息。

如果需要多次运行一个文件,那么只需在调用一次loadfile后,多次调用它的返回结果即可。

相对于多次调用dofile而言,由于只编译一次,开销就小得多。

函数loadstring 与 loadfile类似,它从一个字符串中读取代码。可以直接这么使用:

loadstring(s)()

如果代码中有语法错误,loadstring就会返回nil。为了清除地显示错误信息可以使用assert:

assert(loadstring(s))()

loadstring在编译时不涉及词法域,它总是在全局环境中编译它的字符串;示例:

i = 32
local i = 0
f = loadstring("i = i + 1;print(i)")
g = function() i = i + 1; print(i) end
f() ——> 33
g()  ——> 1

loadstring最典型的用处是执行外部代码,也就是那些位于程序之外的代码。

注意:

loadstring的期望输入是一个程序块,也就是一系列的语句。如果要对一个表达式求值,

则必须在之前添加return,这样才能构成一条语句,返回表达式的值;

loadstring返回的函数也是一个正规的函数,可以多次调用。

Lua将所有独立的程序块视为一个匿名函数的函数体,并且该匿名函数还具有可变长实参。

并且程序块中还可以声明局部变量;

f = loadstring("local a = 10; print(a +20)") 等价于
fucntion(...) 
    local a = 10
    print(a + 20)
end

 

8.2 C代码

Lua提供的所有关于动态链接的功能都聚集在一个函数中,即package.loadlib.

该函数有两个字符串参数:动态库的完整路径和一个函数名称。典型的调用代码如:

local path ="/usr/local/lib/lua/5.1/socket.so"
local f = package.loadlib(path,"luaopen_socket")

loadlib函数加载指定的库,并将其链接入Lua。不过,它并没有调用库中的任何函数。相反,

它将一个C函数作为Lua函数返回。如果在加载库或者查找初始化函数时发生任何错误,

loadlib返回nil及一条错误信息。

 

8.3错误(error)

因为Lua通常嵌入在应用程序中,所以只要发生错误Lua就应该结束当前程序块并返回应

用程序。对于Lua的错误处理通常有两种方式,一是显示地引发一个错误,通过调用error

函数并传入一个错误消息的参数。示例:

print "enter a number:"
n = io.read("*number")
if not n then error("invalid input") end

二是通过内建函数assert来完成此类工作。示例:

print "enter a number"
n = assert(io.read("*number"),"invalidinput")

assert函数检查其第一个参数是否为true,若为ture则简单地返回该参数;否则就

引发一个错误。它的第二个参数是一个可选的信息字符串。

 

一个函数遭遇异常它可以采取两种基本的行为:返回错误代码 或 引发一个错误。

在这两种选择之间并没有固定的法则。一个打开文件io.open的示例:

local file, msg
repeat
print "enter afile name:"
    local name =io.read()
    if not namethen return end
    file, msg =io.open(name, "r")
    if not filethen print(msg) end
until file

如果不想处理打开文件失败的情况,还想安全地运行程序,可以使用assert来检测即可:

file = assert(io.open(name, "r"))

 

8.4错误处理与异常

对于大多数应用程序而言,无须在Lua代码中作任何错误处理,应用程序本身会负责这类

问题。如果需要在Lua中处理错误,则必须使用函数pcall来包装那些需要执行的代码。

第一步将要执行的Lua代码封装到一个函数中,如foo;

第二部使用pcall来调用foo. —— pcall(foo)

当然pcall也可以传入一个匿名函数。pcall函数会以一种"保护模式"来调用它的第一个参数,

因此pcall可以捕获函数执行中的任何错误。如果没有发生错误,pcall会返回true及函数调用的返回值;

否则,返回fasle及错误消息。

 

8.5错误消息与追溯

错误消息可以是任何类型,但通常是一个描述出错内容的字符串。

local status, err = pcall(function() a ="a"+1 end)
print(err) --> stdin:1: attempt to performarithmetic on a string value
有时错误消息就是传递给error函数的值。
local status, err = pcall(function() error("myerror") end)
print(err) --> stdin:1: my error

 

只要错误消息是一个字符串,Lua就会加一些关于错误发生位置的信息。

error函数还可以有第二个附件参数level(层),用于指出应由调用层级中的哪个函数来

为此错误负责。

如果希望得到一个有意义的调用栈,那么就必须在pcall返回前获取该信息。为此Lua

提供了函数xpcall。该函数除了接受一个需要被调用的函数之外,还接受第二个参数——

一个错误处理函数。当错误发生时,Lua会在调用栈展开前调用错误处理函数。

在这个错误处理函数中使用debug库来获取关于错误的额外信息。


debug库提供了两个通用的错误处理函数: 一个是debug.debug,它会提供一个Lua提示符,

让用户来检查错误的原因;另一个是debug.traceback,它会根据调用栈来构建一个扩展的错误信息。