BTW: Here is an excerpt from the document 'Programming in Lua' i've adapted to
purebasic and i've posted already in the german forum. This is a nice
example how useful Lua can be for your application:
Extending your Application:
A main use of Lua is as a configuration language. In this section, we will illustrate how Lua is used as a configuration language, starting with a simple example and evolving it to more complex tasks. As our first task, let us imagine a very simple configuration scenario: Your program (let us call it 'config_simple') has a window, and you want the user to be able to give the initial window size. Clearly, for such a simple task, there are many options simpler than using Lua, such as environment variables or files with name-value pairs. But even for a simple text file, you have to parse it somehow. So, you decide to use a Lua configuration file (that is, a plain text file that happens to be a Lua program). In its simplest form, such file can contain something like the next lines:
Code: Select all
-- configuration file for program 'config_simple'
-- define window size
width = 200
height = 300
Now, you must use the Lua API to direct Lua to parse this file, and then to get the values of the global variables width and height. The following function does the job:
Code: Select all
Global width, height ; global variables
Procedure LoadConfig(FileName$)
L = lua_open()
lua_baselibopen(L)
lua_iolibopen(L)
lua_strlibopen(L)
lua_mathlibopen(L)
If (lua_dofile(L, FileName$) <> 0)
MessageRequester("Error","Cannot run configuration file!")
End
Else
lua_getglobal(L, "width")
lua_getglobal(L, "height")
If (lua_isnumber(L, -2) = 0) Or (lua_isnumber(L, -1) = 0)
MessageRequester("Error","Invalid configuration file!")
End
Else
width = lua_tonumber(L, -2)
height = lua_tonumber(L, -1)
EndIf
lua_close(L)
EndIf
EndProcedure
First, it opens the Lua package, and loads the standard libraries (they are optional, but usually it is a good idea to have them around). Then, it uses lua_dofile to run the chunk in file filename. If there is any error (e.g., a syntax error), we will give an error message, and lua_dofile will return an error code; otherwise lua_dofile returns 0. After running the chunk, the program needs to get the values of the global variables.
For that, it first calls the lua_getglobal function, whose single argument (besides the omnipresent lua_State L) is the global name. Each call pushes the corresponding global value into the API stack. We could index from the top, using -2 for the first value and -1 for the second.
To check the type of a stack element, the API provides the functions lua_isnil, lua_isnumber, lua_isstring, lua_isuserdata, and lua_isfunction; they receive the index of the element to check. Our example uses lua_isnumber to check whether both values are numeric. Finally, it uses lua_tonumber to convert such values to float, and PureBasic does the coercion to longs.
Is it worth the use of Lua? As we said before, for such a simple task, a simple file with only two numbers in it would be much easier to use than Lua. But even in this scenario, the use of Lua has some advantages. First, your configuration file can have comments. Second, the user already can do more complex configurations with it. For instance, the script may prompt the user for some information, or it can query an environment variable to choose a proper size:
Code: Select all
-- configuration file for program 'config_simple'
-- define window size
if os.getenv("USERNAME") == "FloHimself" then
width = 400; height = 200
else
width = 100; height = 200
end
Even in such a trivial configuration scenario, it is hard to anticipate what users will want; but as long as the script defines the two variables, your PureBasic application works without changes.
A final reason to use Lua is that now it is easy to add new configuration facilities to your program, and this easiness creates an attitude that results in more flexible programs.
download the 1. example:
http://www.florian-s.com/PBLua/example/ ... simple.zip
Let us adopt this attitude: Now, you want to configure a background color for the window, too. We will assume that the final color specification is composed by three numbers, where each number is a color component in RGB. A naive approach is to ask the user to give each component in a different global variable:
Code: Select all
-- configuration file for program 'config_complex'
width = 200
height = 300
background_red = 30
background_green = 100
background_blue = 0
This approach has several drawbacks; it is too verbose (most real programs need dozens of different colors, for window background, window foreground, menu background, etc.); and there is no way to pre-define some colors, so that, later, you can simply write something like background = WHITE. A better option is to use a table to represent a color:
The use of tables gives more structure to the script; now it is easy for the user or the application to pre-define colors for later use in her configuration file:
Code: Select all
BLUE = {r=0, g=0, b=255}
...
background = BLUE
To get these values from your PureBasic program, you can do as follows:
Code: Select all
lua_getglobal(L, "background")
If (lua_istable(L, -1) = 0)
MessageRequester("Error","'background' is not a valid color table")
End
Else
red = lua_getfield(L, "r")
green = lua_getfield(L, "g")
blue = lua_getfield(L, "b")
EndIf
As usual, you first get the value of the global variable background, and then you make sure that it is a table. Next, we use getfield to get each color component. This function is not part of the API; we must define it, as follows:
Code: Select all
Procedure lua_getfield(L, key$)
lua_pushstring(L, key$)
lua_gettable(L, -2); get background[key]
If (lua_isnumber(L, -1) = 0)
MessageRequester("Error", "Invalid component in background color!");
End
EndIf
result = lua_tonumber(L, -1)
lua_settop(L, -2) ; = lua_pop(L,n) = lua_settop(L, -(n)-1)
ProcedureReturn result
EndProcedure
Again, the problem is polymorphism: There are potentially many versions of getfield functions, varying the key type, value type, error handling, etc. lua_gettable receives the index of the table, pops the key from the stack, and pushes the corresponding value. Our private lua_getfield assumes that the table is at top of the stack, so after pushing the key (lua_pushstring) the table will be at index -2. Before returning, lua_getfield pops the retrieved value from the stack, to leave the stack in the same state it was before the call. (The argument 1 in the call to lua_pop is the number of elements to pop.) Now the user still can use color tables, but he can also use names for the more common colors, such as background = WHITE.
Code: Select all
-- configuration file for program 'config_complex'
-- define window size
BLUE = {r=0, g=0, b=255}
GREEN = {r=0, g=255, b=0}
RED = {r=255, g=0, b=0}
if os.getenv("USERNAME") == "FloHimself" then
width = 400; height = 400
background = RED
else
width = 100; height = 100
background = {r=30, g=100, b=0}
end
download the 2. example:
http://www.florian-s.com/PBLua/example/ ... omplex.zip