chapter 8 Compilation, Execution, and Errors

本文深入探讨了Lua语言中文件加载机制的细节,包括dofile、loadfile、load等函数的使用及区别,并介绍了预编译代码、C代码集成、错误处理等方面的知识。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Although we refer to Lua as an interpreted language, Lua always precompiles

source code to an intermediate form before running it.


Previously, we introduced dofile as a kind of primitive operation to run chunks
of Lua code, but dofile is actually an auxiliary function: loadfile does the
hard work. Like dofile, loadfile loads a Lua chunk from a file, but it does not
run the chunk
. Instead, it only compiles the chunk and returns the compiled
chunk as a function
.


We could define  dofile as follows:
function dofile (filename)
local f = assert(loadfile(filename))
return f()
end


Note the use of assert to raise an error if loadfile fails.


For simple tasks, dofile is handy, because it does the complete job in one  call. However, loadfile is more flexible. In case of an error, loadfile returns  nil plus the error message,   Moreover, if we need to run a file several times, we can call loadfile once  and call its result several times. This approach is much cheaper than several
calls to dofile, because it compiles the file only once.


> f=loadfile('lession3.lua');
> f();

....excute result...

>f();--excute again.

-- write a lua with error.

> l4,errormsg=loadfile('lession4.lua'); -- notice you need anther variable to accept the error msg.
> print(l4,errormsg);
nil     lession4.lua:1: syntax error near 'a'
>


The load function is similar to loadfile, except that it reads its chunk from  a string, not from a file.

1In Lua 5.1, function loadstring did this role of load.

1 For instance, consider the next line:
f = load("i = i + 1")
After this code, f will be a function that executes i=i+1 when invoked:
i = 0
f(); print(i) --> 1
f(); print(i) --> 2
The load function is powerful; we should use it with care. It is also an expensive  function


load(s)() --when load finish (create a new chunk as function),directly call the function.

However, if there is any syntax error, load will return nil and the final error
message will be something like “attempt to call a nil value”. For clearer error
messages, use assert:
assert(load(s))()


Usually, it does not make sense to use load on a literal string.


f = load("i = i + 1")
f = function () i = i + 1 end
However, the second line is much faster, because Lua compiles the function
together with its enclosing chunk. In the first line, the call to load involves
a separate compilation
.


summarize: load,loadString,loadfile, just create a new chunk return as a function. but dofile,will create the new chunk then excute the function as well.



--Lession4.lua

i = 32
local i = 0
f = load("i = i + 1; print(i)")
g = function () i = i + 1; print(i) end
f() --> 33
g() --> 1
Function g manipulates the local i, as expected, but f manipulates a global i,
because load always compiles its chunks in the global environment.Because load does not compile with lexical scoping,


The most typical use of load is to run external code, that is, pieces of code
that come from outside your program. For instance, you may want to plot a
function defined by the user; the user enters the function code and then you use
load to evaluate it.
Note that load expects a chunk, that is, statements. If you
want to evaluate an expression, you can prefix the expression with return,
so
that you get a statement that returns the value of the given expression. See the
example:
print "enter your expression:"
local l = io.read()
local func = assert(load("return " .. l))
print("the value of your expression is " .. func()) --没读一次,create a new chunk一次,

--C++ 里面加; 号expression will become a statement,but Lua, ; 是可有可无的,need to add return to become a statement.



--see my example:

customerCode=[==[
i=5;
i=i+1;
print(i);
function myTestFunction()
   local i=10;
   print(i);
end
myTestFunction();
]==];
--print(customerCode);

local myFunc=load(customerCode); --一次性执行,,,
myFunc();



--这下需要搞清楚statement and expression

in C++ primer, I got the definition: P148 ,and P191

An expression is composed of one or more operands that are combined by operators. the simplest form consists of a single literal constant or a vaariable, Note every expression yieds a result. check below case:

if(ival) //即使是single variable also will yied result, not zero is true, fails otherwise.  but in Lua. only false / nil is evaluate as fasle,others eventhoug is 0, also is true.


Statement : are analogous to sentences in a natural language. In C++ there are simple statements that excutes a single task and compund statement that excute as a unit like for, if,while,do while satement.

in C++,most statements  end with a semicolon, 这也解析了why in the class declare statement, we need to end with

semicolon. class MyStudent {}; // remember this semicolon.


as expression.such as ival+5, become as expression statement by following it with a semicolon;

; //just a semicolon is a null statement,

ival=v1+v2;; //ok.


Declaration statments: define or declare an object or class.

Compound statment :blocl ,like while,if...



--------------------------------

lession5.lua

a=5;
b=11;
c=a+b;
print(c);
local function Mytesfunc(a,b)

local c=a+b;
print("call function my:" ..c);

end
Mytesfunc(1,2);

------------------------------------

lession6.lua

for l in io.lines("lession5.lua") do
print(l); --逐行print out
end
 
for l in io.lines("lession5.lua","*L") do
print(l); --不知道为什么加*L 虽然也是逐行print out但会多一些空行
end


for l in io.lines("lession5.lua",1024) do
print(l); -1024  ,一次性print out  出来了
end


print("test end....");
fk=load(io.lines("lession5.lua","*L"));
fk();

fk=load(io.lines("lession5.lua","1024")); -- more faster.because it will return once when call io.lines with 1024 space.
fk();

-- but with io.lines("lession5.lua","*L") will return one line by line with some blank space  , 因为load 也call 返回io.lines

返回的iterator function. when this iterator function return nill, then will end the reading. then will starting to create a new chunck.


Lua treats any independent chunk as the body of an anonymous function  with a variable number of arguments.

For instance, load("a = 1") returns the  equivalent of the following expression:

function (...) a = 1 end
Like any other function, chunks can declare local variables:

f = load("local a = 10; print(a + 20)")
f() --> 30


The load functions never raise errors. In case of any kind of error, they return  nil plus an error message:
print(load("i i"))
--> nil [string "i i"]:1: '=' expected near 'i' --也就是程序的执行不会中断。。。



-- file 'foo.lua'
function foo (x)
    print(x)
end
We then run the command
f = loadfile("foo.lua")
After this command, foo is compiled, but it is not defined yet. To define it, you   must run the chunk:
print(foo) --> nil  -- foo 此时还是不存在的
f() -- defines 'foo'  ,当执行了这个loaded chunk 后 foo 才存在
foo("ok") --> ok




8.2 Precompiled Code


The simplest way to produce a precompiled file—also called a binary chunk  in Lua jargon—is with the luac program

$ luac -o prog.lc prog.lua   --compile
$ lua prog.lc   -- excute



p=loadfile("lession5.lua");  --loadfile will compile lession5.lua, we use string.dump,  把compile 的结果dump 出来
f=io.open("lession5.lc","wb");
f:write(string.dump(p)); --然后io write to a new binary file.
f:close();


The key function here is string.dump: it receives a Lua function and returns its
precompiled code as a string, properly formatted to be loaded back by Lua.


C:\Trevor\lua\bin>luac -l -o lession6.lc lession6.lua

main <lession6.lua:0,0> (66 instructions at 004B2230)
0+ params, 7 slots, 1 upvalue, 15 locals, 19 constants, 0 functions
        1       [1]     GETTABUP        0 0 -1  ; _ENV "io"
        2       [1]     GETTABLE        0 0 -2  ; "lines"
        3       [1]     LOADK           1 -3    ; "lession5.lua"
        4       [1]     CALL            0 2 4
        5       [1]     JMP             0 3     ; to 9
        6       [2]     GETTABUP        5 0 -4  ; _ENV "print"
        7       [2]     MOVE            6 3
        8       [2]     CALL            5 2 1


option  -l lists the opcodes that the compiler generates for a given chunk.


Code in precompiled form is not always smaller than the original, but it loads  faster.


Besides its required first argument, load has three more arguments, all of
them optional. The second is a name for the chunk, which is used only in error
messages.
The fourth argument is an environment, which we will discuss in
Chapter 14. The third argument is the one we are interested here; it controls
what kinds of chunks can be loaded.
If present, this argument must be a
string: the string “t” allows only textual (normal) chunks; “b” allows only binary
(precompiled) chunks; “bt”, the default, allows both formats.


8.3 C Code

Normally, Lua does not include facilities that cannot be implemented in
ANSI C. However, dynamic linking is different.   in this particular case, Lua breaks its portability  rules and implements a dynamic linking facility for several platforms.


Lua provides all the functionality of dynamic linking through a single function,  called package.loadlib. It has two string arguments: the complete path of  a library and the name of a function in that library. So, a typical call to it looks
like the next fragment


local path = "/usr/local/lib/lua/5.1/socket.so"
local f = package.loadlib(path, "luaopen_socket")
The loadlib function loads the given library and links Lua to it. However, it  does not call the given function. Instead, itreturns the C function as a Lua  function. If there is any error loading the library or finding the initialization
function, loadlib returns nil plus an error message.


More often than not, we load C  libraries using require. This function searches for the library and uses loadlib
to load an initialization function for the library. When called, this initialization
function builds and returns a table with the functions from that library, much
as a typical Lua library does. We will discuss require in Section 15.1, and more
details about C libraries in Section 27.3.


8.4 Errors

Errare humanum est  

[e'reəri hju:'meinəmest]  人无完人

You can also  explicitly raise an error calling the error function with an error message as an
argument. Usually, this function is the appropriate way to signal errors in your
code:

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


This construction of calling error subject to some condition is so common that
Lua has a built-in function just for this job, called assert:
print "enter a number:"
n = assert(io.read("*n"), "invalid input")

The assert function checks whether its first argument is not false and simply  returns this argument; if the argument is false, assert raises an error. Its  second argument, the message, is optional. Beware, however, that assert is  a regular function. As such, Lua always evaluates its arguments before calling
the function. Therefore, if you have something like
n = io.read()
assert(tonumber(n), "invalid input: " .. n .. " is not a number")


Lua will always do the concatenation, even when n is a number. It may be wiser
to use an explicit test in such cases.

When a function finds an unexpected situation (an exception), it can assume  two basic behaviors: it can return an error code (typically nil) or it can raise an  error, calling function error. There are no fixed rules for choosing between these  two options, but we can provide a general guideline:
 an exception that is easily  avoided should raise an error; otherwise, it should return an error code.

local res = math.sin(x)
if not res then -- error?  --其实没办法执行到这一步,因为已经抛到main ,由applicaion 自己处理了
<error-handling code>
However, we could as easily check this exception before calling the function:
if not tonumber(x) then -- x is not a number? --this is the best way
<error-handling code>


local file, msg
repeat
print "enter a file name:"
local name = io.read()
if not name then return end -- no input
file, msg = io.open(name, "r") --自己会返回一个error msg.
if not file then print(msg) end
until file


file = assert(io.open(name, "r"))
This is a typical Lua idiom: if io.open fails, assert will raise an error.
file = assert(io.open("no-file", "r")) --其实就是io.open 返回了两个结果pass to assert function.if the first is nil,then raise an error with the second error msg result.
--> stdin:1: no-file: No such file or directory



8.5 Error Handling and Exceptions
For many applications, you do not need to do any error handling in Lua; 就像math.sin, 如果传入字符串,直接抛到applicaion main 这一层,我们都没法捕获 (办法在下面 :pcall) the   application program does this handling

However, if you need to handle errors in Lua, you must use the pcall (protected
call) function to encapsulate your code.

local ok, msg = pcall(function ()
<some code>
if unexpected_condition then error() end
<some code>
print(a[i]) -- potential error: 'a' may not be a table
<some code>
end)


----
local ok,msg=pcall(
function () --注意一定要用function 包裹要call的funciton.
return math.sin('abc')  --notic ,you need to return, orelse ,if no error happen, can  not get the  sin result.
end
);
print(ok, msg); --
i=0;
i=i+1;
print(i); --可以看到用了pcall后,哪怕math.sin 抛出了error,都可以被捕获到,,是可以执行到这里的不会被中断,,

If there are no errors, pcall
returns true, plus any values returned by the call. Otherwise, it returns false,
plus the error message.

local status, err = pcall(function () error({code=121}) end)
print(err.code) --> 121
These mechanisms provide all we need to do exception handling in Lua. We
throw an exception with error and catch it with pcall. The error message
identifies the kind of error.


local k,m=pcall(
function ()
error({code=120,message='theris an error msg'}); --其实这个table 可以自己定义,,code or errrcode,自己喜欢了
end

);

print(m.code,m.message);



8.6 Error Messages and Tracebacks

When there is an internal  error (such as an attempt to index a non-table value), Lua generates the error
message; otherwise, the error message is the value passed to the error function.

Whenever the message is a string, Lua tries to add some information about the  location where the error happened:


local status, err = pcall(function () a = "a"+1 end)
print(err)
--> stdin:1: attempt to perform arithmetic on a string value

local status, err = pcall(function () error("my error") end)
print(err)
--> stdin:1: my error


The location information gives the file name (stdin, in the example) plus the  line number (1, in the example).

The error function has an additional second parameter, which gives the level  where it should report the error; you use this parameter to blame someone else  for the error. For instance, suppose you write a function whose first task is to  check whether it was called correctly:

function foo(str)
if type(str) ~='string' then
error("hey,please iput a string",1);--with leve 1.
end
print('you input is :'..str);
end
foo(1234);


---
lession6.lua:47: hey,please iput a string   --47 行就是 call error function 的那行
stack traceback:
        [C]: in function 'error'
        lession6.lua:47: in function 'foo'
        lession6.lua:51: in main chunk
        [C]: in function 'dofile'
        stdin:1: in main chunk
        [C]: in ?

---------
lession6.lua:51: hey,please iput a string --51 行是 call foo(1234) 的那一行,,
stack traceback:
        [C]: in function 'error'
        lession6.lua:47: in function 'foo'
        lession6.lua:51: in main chunk
        [C]: in function 'dofile'
        stdin:1: in main chunk
        [C]: in ?

-----------
When pcall returns  its error message, it destroys part of the stack (the part that goes from it to the  error point). Consequently, if we want a traceback, we must build it before pcall  returns. To do this, Lua provides the xpcall function. Besides the function to  be called, it receives a second argument, a message handler function. In case
of an error, Lua calls this message handler before the stack unwinds, so that it can use the debug library to gather any extra information it wants about the  error. Two common message handlers are debug.debug, which gives you a Lua   prompt so that you can inspect by yourself what was going on when the error   happened; and debug.traceback, which builds an extended error message with
a traceback.3 The latter is the function that the stand-alone interpreter uses to
build its error messages.

local ok,msg=xpcall(function ()
foo(1234)
end
,debug.debug);
--will continue this part at chapter 24




























































































<think>嗯,用户问的是“function created with compilation errors”,也就是在编译过程中出现错误的函数。首先,我需要理解这个问题的常见原因。可能用户是在编程的时候遇到了编译错误,特别是在定义函数的时候。那么,编译错误通常有哪些原因呢? 首先想到的是语法错误,比如缺少括号、分号或者拼写错误。比如在C语言里,函数声明如果漏了花括号或者分号,编译器就会报错。然后是类型不匹配,比如函数声明的返回类型和实际返回的值类型不一致,或者参数类型不匹配。例如,声明了一个返回int的函数,但实际返回了字符串。 还有作用域的问题,比如在函数内部使用了未声明的变量,或者变量名拼写错误。此外,函数重名也可能导致冲突,尤其是在大型项目中,如果函数名重复,编译器无法确定该用哪个,就会报错。 另外,有些语言需要函数在使用前声明,如果函数调用在定义之前,而编译器没有看到原型声明,也会出错。比如C语言中,如果函数定义在调用之后,而没有前置声明,就会报编译错误。 还有链接错误,比如函数声明了但没有实现,或者实现的时候参数列表不一致,导致链接器找不到对应的函数定义,这可能在编译阶段或链接阶段报错,具体看语言和编译器。 用户可能需要具体的例子来理解这些错误。比如举一个缺少分号的例子,或者参数类型不匹配的情况。同时,解决方法是检查语法,仔细核对函数声明和定义的每个部分,使用编译器的报错信息定位问题所在的行和具体原因。 另外,可能用户对编译错误的信息不太熟悉,不知道如何根据错误信息排查问题。这时候需要解释如何阅读错误信息,比如错误类型、行号、提示信息等,这些都是解决问题的关键线索。 总结下来,回答的结构应该先解释编译错误的原因,然后分点列出常见错误类型,给出例子,最后提供解决方法和建议。确保用户能够一步步排查问题,理解错误来源,并学会如何避免和修复。</think>在编程中,“function created with compilation errors”表示在编译阶段因代码错误导致函数无法正常通过编译。以下是逐步分析及常见原因: --- ### 一、什么是编译错误? 编译错误(Compilation Errors)是编译器在解析代码时发现的语法或语义问题。当函数存在此类错误时,编译器会拒绝生成可执行文件。 --- ### 二、常见错误类型及示例 #### 1. **语法错误** - **缺失符号**:如缺少分号 `;`、括号 `()` 或花括号 `{}`。 ```c int add(int a, int b) { return a + b // 错误:缺少分号 } ``` - **拼写错误**:如关键字拼写错误(`funciton` 代替 `function`)。 #### 2. **类型不匹配** - **返回值类型不符**: ```c int divide(int a, int b) { return a / b; // 正确 } float divide(int a, int b) { // 错误:与已存在函数冲突 return (float)a / b; } ``` - **参数类型错误**: ```java void print(String message) { ... } print(123); // 错误:参数应为字符串 ``` #### 3. **作用域问题** - **未声明变量**: ```python def calculate(): result = x + 5 # 错误:x 未定义 ``` #### 4. **重复定义** - **同名函数冲突**: ```c++ void log() { ... } void log() { ... } // 错误:函数重复定义 ``` #### 5. **未实现声明** - **声明未定义**: ```c void myFunction(); // 函数声明 int main() { myFunction(); // 错误:未实现具体定义 } ``` --- ### 三、解决方法 1. **阅读错误信息** 编译器会提示错误类型和位置(如行号)。例如: ``` error: expected ';' before '}' token ``` 2. **逐行检查代码** 检查符号闭合、拼写和语法结构。 3. **隔离测试** 注释掉部分代码,逐步定位问题。 4. **工具辅助** 使用 IDE 的语法高亮和实时检查功能(如 VS Code、IntelliJ)。 --- ### 四、示例修复 **错误代码**: ```c #include <stdio.h> int main() { printf("Hello World") return 0; } ``` **错误分析**:`printf` 后缺少分号 `;`。 **修复后**: ```c #include <stdio.h> int main() { printf("Hello World"); // 添加分号 return 0; } ``` --- ### 五、总结 编译错误通常由语法、类型或作用域问题引起。通过仔细阅读错误信息、检查代码逻辑,并结合工具辅助,可高效定位并解决问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值