Retired Lua Faq |
|
You don't: Lua does not have or need variable declarations.
But why do you need to declare variables?
Some people want to declare variables to protect themselves against typing mistakes. For example, the code
color="red" print(colour)
will print nil
because color
has been mistyped in line 2 and colour
has not been defined.
To safeguard against these mistakes, and also to spot undefined variables, you can use the getglobal
tag method:
function safe_getglobal(x) local v=rawgetglobal(x) if v then return v else error("undefined global variable "..x) end end settagmethod(tag(nil),"getglobal",safe_getglobal)
With this code in place, any attempt to use an undefined variable (which would have the value nil
) will produce an error message.
Lua is a dynamically typed language. This means that variables do not have type, only values do. Since Lua provides access to the type of values (via the type
function), you can write your own functions for dynamic typing. Here is one possible scheme.
To check simple types, just use type
. The interesting case is checking tables, because we must check that all fields are present and correctly filled, according to pre-defined "templates". So, we need a way to describe the "type" of tables. As mentioned above, a simple type is described by its name, as reported by the type
function. Tables are described by "type templates", which are tables that map each field to its required type. Here are some examples useful for user interface toolkits:
TNumber="number" TPoint={x=TNumber, y=TNumber} TColor={red=TNumber, blue=TNumber, green=TNumber} TRectangle={topleft=TPoint, botright=TPoint} TWindow={title="string", bounds=TRectangle, color=TColor}
Given such descriptions, the following function checks whether a value has a given type:
function checkType(d, t) if type(t) == "string" then -- t is the name of a type return (type(d) == t) else -- t is a table, so d must also be a table if type(d) ~= "table" then return nil else -- d is also a table; check its fields local i,v = next(t,nil) while i do if not checkType(d[i],v) then return nil end i,v = next(t,i) end end end return 1 end
In large projects, and also in libraries, it is sometimes important to protect some variables so that they cannot be redefined. This is specially useful when several libraries are loaded and you want to make sure they don't redefine each other's functions by accident.
You can protect functions in Lua by using the setglobal
tag method:
function protect(x) error("cannot redefine "..x) end settagmethod(tag(protect),"setglobal",protect)
This code should be run after all libraries have been loaded.
With this code in place, any attempt to redefine functions will produce an error message. You can still redefine functions, but you have to do it explicitly:
rawsetglobal("print",nil) function print (x) ... end
To extend this scheme to other kinds of values, you need to wrap values with tables, and use both setglobal
and getglobal
tag methods:
RO=newtag() -- tag for read-only values function ROvalue(value) -- make a read-only value local t={value=value} settag(t,RO) return t end function ROsetglobal(x) -- protects assignment error(x.." is read-only") end function ROgetglobal(x,value) -- get the actual value return value.value end settagmethod(RO,"getglobal",ROgetglobal) settagmethod(RO,"setglobal",ROsetglobal) tolerance=ROvalue(0.12) -- make some read-only variables color=ROvalue("red") myprint=ROvalue(print)
This scheme only affects variables created with ROvalue
, because only those have the RO
tag.
Lua 5.0 now has a block comment syntax:
--Double-dashes begin a single-line comment --[[Double-dashes immediately followed by double left square bracket Begin a multi-line comment That continues until the __matching__ double right square bracket, so you can do [[this]] too. ]]
Lua doesn't have a special syntax for block comments. One solution is to enclose the comment in a string:
comment = [[
this is my
multi-line comment
]]
A variant of this solution, which is convenient for temporarily disabling a block of code, is the following:
comment = [[
disabled code
--]]
You can enable the code simply by adding --
to the assigment:
--comment = [[ enabled code --]]
If the block is big, you may want to avoid letting it float in memory until the garbage collector removes it. You can then write:
local comment = [[ disabled code --]] ; comment = nil
Luiz Henrique de Figueiredo suggested also:
do local C=[[ --]] end
"C goes immediately out of scope. The parser could exploit this (but doesn't). There's also:"
local C= 1 or [[ <syntaxly correct code to comment out> --]]
"The code generator could exploit this (but doesn't)."
Another solution is to use an editor macro to prefix selected lines with "-- "; or with "--~ " for example, so the commented block stand out of surrounding regular comments.
For commenting out a block of code, it's possible to use if nil then ... end
. (See the next question.)
In Lua, tables are manipulated by reference. So, if the value of x
is a table, then the assignment y=x
does not duplicate the table: x
and y
contain the same table. (In other words, the values of x
and y
are references to the same table.)
If you really want to copy a table, you can use the built-in function next
, as in the code below:
function clone(t) -- return a copy of the table t local new = {} -- create a new table local i, v = next(t, nil) -- i is an index of t, v = t[i] while i do new[i] = v i, v = next(t, i) -- get next index end return new end
If you want a deep copy , add if type(v)=="table" then v=clone(v) end
before new[i] = v
.
The best way to implement a set in Lua is to store its elements as keys in a table. An element is present in a set when the corresponding value in the table is not nil
.
Here are some code snippets for sets:
s={} -- create an empty set s[x]=1 -- insert element x into set s s[x]=nil -- remove element x from set s x_is_present=s[x] -- does s contain x?
Bags are similar to sets, except that elements may appear more than once. Bags can be implemented similarly to sets, but using the value associated to an element as its counter. Here is a code snippet for bags:
-- insert element x into bag s if s[x] then s[x] = s[x]+1 else s[x] = 1 end -- remove element x from set s if s[x] then s[x] = s[x]-1 if s[x] == 0 then s[x] = nil end end
As of Lua 5.0, use string.char(n)
or even string.format("%c", n)
. Prior to Lua 5.0, use strchar(n)
or format("%c",n)
.
[needs updating!]
The pattern matching capabilities of read
are powerful, but writing patterns is not for everyone. Here are some useful patterns:
Word (sequence of letters and digits) {{"%w%w*"}} Word (sequence of non-white space characters) {{"%S%S*"}} Integer {{"[+-]?%d%d*"}} Real {{"[+-]?%d%d*[.]%d%d*"}} Double quoted string {{'"[^"]*"'}} Single quoted string {{"'[^']*'"}}
If you want to skip whitespace before reading an item, just prepend "{%s*}"
to your pattern. For instance, "{%s*}%S%S*"
skips whitespace and then reads the next word.
Version 3.2 made this even simpler: you can use "*w"
to read a word and "*n"
to read a number. See the reference manual for a complete list of predefined patterns.
Writing a self-reproducing program in any language is fun but not always easy because you have to be careful with quotes. In Lua it is easy because there are alternative quotes:
y = [[ print("y = [[" .. y .. "]]\ndostring(y)") ]]
dostring(y)
VersionNotice: above is Lua 4.0.
For self-reproducing programs in other languages, see the Quine page [1]. Read also a good explanation of how self-reproducing programs work [2].
[needs updating!]
Try this code:
function save() local g={} foreachvar(function (n,v) %g[n]=v end) return g end function restore(g) foreach(g,setglobal) end
Since version 3.1, you can do these same, and much more, in C, using lua_open
, lua_close
, and lua_setstate
.
[needs updating!]
You must follow a simple protocol: Get any parameters from Lua with lua_getparam
, make sure they are of the correct type with the appropriate lua_is...
function, convert them with the appropriate lua_get...
function, do something with the converted values, and push the results back to Lua with the appropriate lua_push...
function.
Let's see a practical example and export the getenv
function to Lua. For more examples, see the code that implements the standard library.
The getenv
function in C takes a string and returns another string. Its prototype is
char* getenv(char*);
So, the appropriate functions to call are lua_isstring
, lua_getstring
and lua_pushstring
:
void wrap_getenv(void) { lua_Object o=lua_getparam(1); if (lua_isstring(o)) lua_pushstring(getenv(lua_getstring(o))); else lua_error("string expected in argument #1 to getenv"); }
Since version 3.0, Lua includes auxiliary functions that simplify writing wrappers. With these functions, we can write wrap_getenv
as:
void wrap_getenv(void) { lua_pushstring(getenv(luaL_check_string(1))); }
After writing the wrapper, you must make it visible to Lua by calling lua_register("getenv",wrap_getenv)
. In fact, this is almost exactly what the standard library does.
Just write a wrapper for each function in your library, as explained above.
Several solutions are provided for automating this process. See "Code wrappers" on LuaAddons.
VersionNotice: this is Pre-Lua5.
Except for trivial cases, such as an infinite recursion, you get this message when you call Lua from C in a loop because arguments and return values accumulate in Lua's stack. For example, this code will produce "stack size overflow":
for (i=0;;i++) { lua_pushnumber(i); lua_call("print"); }
The solution is to enclose the code that talks to Lua with calls to lua_beginblock
and lua_endblock
:
for (i=0;;i++) { lua_beginblock(); lua_pushnumber(i); lua_call("print"); lua_endblock(); }
To quote from the reference manual: The use of explicit nested blocks is strongly encouraged.