Programming in Lua Chinese version-8. Compilation, execution, and errors

Source: Internet
Author: User
Tags define local
Chinese version of programming in Lua -- 8. Compilation, execution, and errors
Transferred from http://www.lifangjin.com/(liangfeng collection Li fangjin's personal blog)

Although we use Lua as an interpreted language Code Pre-editing is translated into an intermediate code and then executed (many interpreted languages do this ). it sounds inappropriate to have a compilation phase in interpreted language. However, the characteristics of interpreted language are not that they are compiled, but that the compiler is part of the language runtime, the intermediate code generated by executing compilation is faster. we can say that the existence of the function dofile means that Lua can be called as an interpreted language.
We have introduced dofile as an original operation of the chunk of Lua code. dofile is actually a helper function. The function that completes the function is LoadFile. Unlike dofile, LoadFile compiles code into an intermediate code and returns the compiled chunk as a function, in addition, LoadFile does not throw an error message but returns the error code. we can define dofile as follows:
Function dofile (filename)
Local F = assert (LoadFile (filename ))
Return F ()
End
If LoadFile fails, assert will throw an error.
It is more convenient to complete the simple function dofile, which reads the file and compiles and executes it. however, LoadFile is more flexible. in case of an error, LoadFile returns nil and error message, so that we can customize error handling. in addition, if we run a file multiple times, LoadFile only needs to be compiled once, but can be run multiple times. dofile is compiled every time.
Loadstring is similar to LoadFile, except that it does not read chunk from the file, but is read from a string. For example:
F = loadstring ("I = I + 1 ″)
F will be a function, which is executed in the call: I = I + 1:
I = 0
F (); print (I)-> 1
F (); print (I)-> 2
The loadstring function is powerful, but you need to be careful when using it. Check that there are no other simple solutions to the problem before using it.
Lua treats each chunk as an anonymous function. For example, Chunk "A = 1", loadstring returns its equivalent function () A = 1 end
Like other functions, Chunks can define local variables or return values:
F = loadstring ("local A = 10; return a + 20 ″)
Print (f ()-> 30
Neither LoadFile nor loadstring will throw an error. If an error occurs, they will return nil with the error message:
Print (loadstring ("I "))
-> Nil [String "I"]: 1: '= 'expected near' I'

In addition, neither LoadFile nor loadstring has a boundary effect. They compile chunk to become an internal anonymous function. the common misunderstanding is that they define functions. the function definition in Lua is a value assignment that occurs at runtime rather than during compilation. suppose we have a file Foo. lua:
-File 'foo. lua'
Function Foo (X)
Print (X)
End
After we run the command F = LoadFile ("foo. Lua"), foo is compiled but not defined yet. to define it, you must run Chunk:
F ()-Defines 'foo'
Foo ("OK")-> OK
If you want to quickly call dostring (such as loading and running), you can call the loadstring returned result loadstring (s) (). However, if the loaded content has a syntax error, loadstring returns nil and the error message (attempt to call a nil value). To return a clearer error message, use assert:
Assert (loadstring (s ))()
Loading a string using loadstring is meaningless. For example:
F = loadstring ("I = I + 1 ″)
This is probably equivalent to f = function () I = I + 1 end, but the second part of the code is faster because it only needs to be compiled once. The first part of the code will be re-compiled every time loadstring is called, another important difference is that loadstring compilation does not care about the lexical scope:
Local I = 0
F = loadstring ("I = I + 1 ″)
G = function () I = I + 1 end
In this example, G uses the local variable I as imagined, but F uses the global variable I; loadstring always compiles his string in the global environment.
Loadstring is usually used for running Program External code, such as running user-defined code. note: loadstring expects a chunk, that is, a statement. if you want to load the expression, you need to add return before the expression, then the value of the expression will be returned. example:
Print "Enter your expression :"
Local L = Io. Read ()
Local func = assert (loadstring ("return". l ))
Print ("the value of your expression is". func ())
Functions returned by loadstring are the same as normal functions and can be called multiple times.
Print "Enter function to be plotted (with variable 'X '):"
Local L = Io. Read ()
Local F = assert (loadstring ("return". l ))
For I = 1, 20 do
X = I-Global 'X' (to be visible from the chunk)
Print (string. Rep ("*", F ()))
End
8.1 require Function
Lua provides advanced require functions to load the Runtime Library. roughly speaking, require and dofile perform the same function, but there are two differences: 1. require searches for directories to load files. 2. require checks whether the file has been loaded to avoid repeated loading of the same file. because of the above features, require is a better function for loading databases in Lua.
The path used by require is different from the path we see in general. The path we usually see is a directory list. the require path is a list of modes. Each mode indicates a method to convert a Virtual File Name (the require parameter) to a real file name. more specifically, each pattern is a file name containing an optional question mark. when matching, Lua will first Replace the question mark with the virtual file name, and then check whether such a file exists. if there is no match, use the same method to use the second pattern. for example, the path is as follows:
?;?. Lua; C: \ WINDOWS \?; /Usr/local/Lua /? /?. Lua
When you call require "Lili", you will try to open these files:
Lili
Lili. Lua
C: \ windows \ Lili
/Usr/local/Lua/Lili. Lua
Require only focuses on semicolons (separators between modes) and question marks. Other information (directory separator, file extension) is defined in the path.
To determine the path, Lua first checks whether the global variable lua_path is a string. If so, it is regarded as the path; otherwise, require checks the value of the environment variable lua_path, if both of them fail, use a fixed path (typical "?;?. Lua ")
Another feature of require is to avoid repeated loading of the same file twice. lua retains a list Of all loaded files (saved using table ). if a file to be loaded is saved in the require table, a simple return is returned. The virtual name of the file to be loaded is retained in the table, instead of the actual file name. therefore, if you use different Virtual File Names (require) for the same file twice, the file will be loaded twice. for example, require "Foo" and require "Foo. lua ", path is "?;?. Lua will load Foo. lua twice. we can also use the global variable _ loaded to access the file name list, so that we can determine whether the file has been loaded. We can also use a small trick to let require load a file twice. for example, after require "Foo", _ loaded ["Foo"] will not be nil. We can assign it to nil and require "Foo. lua will load the file again.
The pattern in a path can also contain no question mark but a fixed path, for example:
?;?. Lua;/usr/local/Default. Lua
In this case, this fixed file will be used when require does not match (of course, this fixed path must be placed at the end of the mode list to make sense ). before require runs a chunk, it defines a global variable _ requiredname to save the file name of the virtual file to be required. we can use this technique to extend the require function. for an extreme example, we can set the path to "/usr/local/Lua/newrequire. lua ", so that newrequire will be run every time you call require. lua. In this case, you can use the value of _ requiredname to actually load the required file.
8.2 C-pack
Lua and C are easy to combine. C is used to write packages for Lua. unlike the write package in Lua, the C package must be loaded and connected before use. in most systems, the easiest way to implement is through the dynamic Connection Library mechanism. However, the dynamic connection library is not part of ansi c, which means it is very difficult to implement dynamic connection in Standard C.
Generally, Lua does not include any mechanism that cannot be implemented using standard C. Dynamic Connection Library is a special case. we can regard the dynamic Connection Library mechanism as the mother of other mechanisms: once we have a dynamic connection mechanism, we can dynamically load a mechanism that does not exist in Lua. therefore, in this special case, Lua breaks the principles of platform compatibility and implements a dynamic connection mechanism for some platforms through Conditional compilation. standard Lua is implemented on Windows, Linux, FreeBSD, Solaris, and other UNIX platforms. It is not difficult to extend the support of other platforms. run print (loadlib () at the Lua prompt to check the returned results. If Bad arguments is displayed, your release version supports the dynamic connection mechanism. Otherwise, the dynamic connection mechanism is not supported or not installed.
Lua provides all the dynamic connection functions in a function called loadlib. This function has two parameters: the absolute path of the library and the initialization function. A typical Call example is as follows:
Local Path = "/usr/local/Lua/lib/libluasocket. So"
Local F = loadlib (path, "luaopen_socket ")
The loadlib function loads the specified library and connects to Lua. However, it does not open the Library (that is, it does not call the initialization function), and vice versa, it returns the initialization function as a function of Lua, in this way, we can call it directly in Lua. if an error occurs when loading the dynamic library or searching for the initialization function, loadlib returns nil and error information. we can modify the previous code to check for errors and then call the initialization function:
Local Path = "/usr/local/Lua/lib/libluasocket. So"
-Or Path = "C: \ Windows \ luasocket. dll"
Local F = assert (loadlib (path, "luaopen_socket "))
F ()-actually open the library
Generally, we expect the binary publishing library to contain a stub file similar to the previous code segment. When installing the binary library, you can place it in a directory, you only need to modify the actual path of the binary library corresponding to the stub file. add the directory of the stub file to lua_path, so that you can use the require function to load the C library.
Error 8.3
Errare humanum est (Latin proverb: Making mistakes is human nature ). therefore, we should try our best to prevent errors. Lua is often embedded in other applications as an extension language, so we cannot simply crash or exit when errors occur. on the contrary, when an error occurs, Lua ends the current Chunk and returns it to the application.
When Lua encounters unexpected situations, it will throw an error, for example, adding two non-numbers and calling a non-function variable; values that do not exist in the Access Table (you can modify this behavior by using tables, which will be described later ). you can also call the error function to display the throw error. The error parameter is the error message to be thrown.
Print "enter a number :"
N = Io. Read ("* Number ")
If not n then error ("invalid input") End
Lua provides a dedicated built-in function assert to complete the above functions:
Print "enter a number :"
N = assert (Io. Read ("* Number"), "invalid input ")
Assert first checks whether the first parameter returns an error. If no error is returned, assert simply returns an error. Otherwise, assert throws an error message with the second parameter. the second parameter is optional. note that assert is a common function. It calculates two parameters and then calls the function, so the following code:
N = Io. Read ()
Assert (tonumber (N ),
"Invalid input:"... n... "is not a number ")
The connection operation will always be performed. Use the displayed test to avoid this situation.
When a function encounters an exception, there are two basic actions: Return Error code or throw an error. which of the two methods does not have a fixed rule, but there is a general principle: exceptions that are easy to avoid should throw an error or return the error code.
For example, we consider the sin function. If we use a table as a parameter and assume that we return the error code, we need to check the occurrence of the error. The code may be as follows:
Local res = math. Sin (X)
If not res then-Error
...
However, before calling a function, we can easily determine whether an exception exists:
If not tonumber (x) then-error: X is not a number
...
However, in general, we neither check the parameters nor check the returned results, because the parameter error may mean that our program has a problem somewhere. In this case, the simplest and most practical way to handle exceptions is to throw an error and terminate code execution.
Let's take a look at the IO. OPEN function to open a file. What if the file does not exist? In many systems, you can try to open a file to determine whether the file exists. so if I/O. open cannot open the file (because the file does not exist or has no permission), the function returns nil and error messages. in this way, we can reasonably solve the problem by interacting with users (for example, whether to open another file:
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 ")
If not file then print (MSG) End
Until File
If you want to be lazy, do not want to deal with these situations, and want to run the code securely, you can simply use assert:
File = assert (Io. Open (name, "R "))
There is a habit in Lua: If Io. Open fails, assert will throw an error.
File = assert (Io. Open ("no-file", "R "))
-> Stdin: 1: No-file: no such file or directory
Note: The second result (error message) returned by Io. open is the second parameter of assert.
8.4 handle exceptions and errors
In many applications, you do not need to handle errors in Lua, which is generally completed by applications. generally, the application requires Lua to run a chunk. If an exception occurs, the application processes the chunk according to the error code returned by Lua. in Console mode, if the Lua interpreter encounters an exception, print an error and continue to display the prompt to wait for the next command.
To handle errors in Lua, use the pcall function to encapsulate your code.
Assume that you want to run a Lua code to capture all exceptions and errors during code execution. Step 1: encapsulate the code in a function,
Function Foo ()
...
If unexpected_condition then error () End
...
Print (A [I])-potential error: 'A' may not be a table
...
End
Step 2: Use pcall to call this function
If pcall (FOO) then
-No errors while running 'foo'
...
Else
-'Foo' raised an error: take appropriate actions
...
End
Of course, you can also use an anonymous function to call pcall:
If pcall (function ()... End) then...
Else...
Pcall calls and runs the first parameter in protection mode, so all exceptions and errors can be captured. if no exception or error occurs, pcall returns true and any value returned by the call; otherwise, Nil is returned with an error message.
The error message is not necessarily a string (The following example is a table). Any information passed to the error will be returned by pcall:
Local Status, err = pcall (function () error ({code = 121}) end)
Print (ERR. Code)-& gt; 121
This mechanism provides all the content we need to handle exceptions and errors in Lua. We throw an exception through error and then capture it through pcall.
8.5 error message and trace (tracebacks)
Although you can use any type of value as the error message, we usually use a string to describe the error message. if an internal error occurs (for example, access to a non-Table value using the index table), Lua will generate its own error message. Otherwise, Lua uses the parameter passed to the error function as the error message. in whatever circumstances, Lua should be as clear as possible to describe the errors.
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: in my error
the error message in the example shows the file name (stdin) and the row number.
the function error can also have a second parameter, indicating the error running level. with this parameter, you will not be able to deny the error because of others. For example, you have written a function to check whether the error is called correctly:
function Foo (STR)
If type (STR )~ = "String" then
error ("string expected")
end
...
end
someone may call this function like this:
Foo ({x = 1})
Lua indicates that the error is foo rather than error, the actual error is generated when an error is called. To correct this problem, modify the previous Code so that the error is reported to the second level (your function is the first level) as follows:
function Foo (STR)
If type (STR )~ = "String" then
error ("string expected", 2)
end
...
end
when an error occurs, we often need more information about the error, not just the location where the error occurs. at least a complete trace of the Call Stack is expected to display the cause of the error. When pcall returns an error message, it has released the stack information that saves the error. therefore, if we want to get tracebacks, we must get it before pcall returns. lua provides xpcall to implement this function. xpcall accepts two parameters: Call function and error handling function. when an error occurs, Lua calls the error processing function before the stack is released. Therefore, you can use the debug library to collect error information. there are two common debug processing functions: Debug. debug and debug. traceback: the former provides a Lua prompt. You can check the error by yourself. The latter creates more error information through traceback, the latter is a function used by the console interpreter to build error messages. you can call debug at any time. traceback:
Print (debug. traceback ()

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.