12 Apr 2000 22:12
1 Apr 2000 13:15
constant tables code
Ashley Fryer <lua <at> bmarket.com>
2000-04-01 11:15:37 GMT
2000-04-01 11:15:37 GMT
I've put together a few helper macros for creating tables of constant values
in Lua. They are useful in situations where you would like scripts to have
access to a set of values without allowing the scripts to modify the values
or the set.
For example, I have used this code to generate a table of the VK_xxx and
DIK_xxx key constants for Windows programming. It could also be useful for
generating a table of error strings, mathematical constants, etc.
Here's an example...
First, define the table in C. I find it convenient to group the CONST_TABLE
definitions together with my lua_register() calls.
int num_var = 42;
char str_var[] = "this is str_var";
CONST_TABLE_BEGIN( "MY_TABLE" )
CONST_NUM_VAR( num_var )
CONST_STR_VAR( str_var )
CONST_NUM( "foo", 7 )
CONST_STR( "bar", "this is bar")
CONST_TABLE_END
Then, once the table is initialized, you can do this in Lua:
print( MY_TABLE.num_var )
42
print( MY_TABLE.str_var )
this is str_var
print( MY_TABLE.foo )
7
print( MY_TABLE.bar )
this is bar
The table is "constant" because all of these things generate an error:
MY_TABLE = 9
MY_TABLE.str_var = 5
MY_TABLE.new_var = "new variable"
The error message is:
Cannot modify constant table: MY_TABLE
One unfortunate limitation of the macros is that you can't directly use
#defined values. This is because the C pre-processor isn't reentrant. So,
for example, this fails:
#define PI 3.14
CONST_TABLE_BEGIN( "MATH_TABLE" )
CONST_NUM( "PI", PI )
CONST_TABLE_END
My workaround is:
#define PI 3.14
CONST_TABLE_BEGIN( "MATH_TABLE" )
double pi = PI;
CONST_NUM( "PI", pi )
CONST_TABLE_END
Perhaps someone can think of a better solution.
The code follows. Anyone is free to use it any way, and I hope that someone
DOES find it useful.
ashley
--- cut here ---
#define CONST_TABLE_BEGIN( TABLE_NAME ) \
{\
lua_Object table = lua_createtable();\
lua_pushobject( table );\
lua_setglobal( TABLE_NAME );\
lua_dostring( "function tmp() error(\
\"Cannot modify constant table: " TABLE_NAME "\" ) end" );
#define CONST_NUM( NAME, VALUE) \
lua_pushobject( table );\
lua_pushstring( ( char * ) NAME );\
lua_pushnumber( VALUE );\
lua_settable();
#define CONST_NUM_VAR( NAME ) \
lua_pushobject( table );\
lua_pushstring( ( char * ) #NAME );\
lua_pushnumber( NAME );\
lua_settable();
#define CONST_STR( NAME, VALUE ) \
lua_pushobject( table );\
lua_pushstring( ( char * ) NAME );\
lua_pushstring( VALUE );\
lua_settable();
#define CONST_STR_VAR( NAME ) \
lua_pushobject( table );\
lua_pushstring( ( char * ) #NAME );\
lua_pushstring( ( char * ) NAME );\
lua_settable();
#define CONST_TABLE_END \
int tag = lua_newtag();\
lua_pushobject( table );\
lua_settag( tag );\
lua_pushobject( table );\
lua_pushobject( lua_getglobal( "tmp" ) );\
lua_settagmethod( tag, "settable" );\
lua_pushobject( table );\
lua_pushobject( lua_getglobal( "tmp" ) );\
lua_settagmethod( tag, "setglobal" );\
}
--- cut here ---
1 Apr 2000 13:19
Sv: constant tables code
Erik Hougaard <erik <at> hougaard.com>
2000-04-01 11:19:32 GMT
2000-04-01 11:19:32 GMT
----- Original Message ----- From: Ashley Fryer <lua <at> bmarket.com> > I've put together a few helper macros for creating tables of constant values > in Lua. They are useful in situations where you would like scripts to have > access to a set of values without allowing the scripts to modify the values > or the set. Very nice, but ths is one of the things that I'm playing with... To make a table (not lua table) so all these constants would get replaced with their actual values on parse time, so there was no need for lua to do the lookup. In a application with many constants that should give a performance gain ! /Erik
1 Apr 2000 15:01
RE: constant tables code
Ashley Fryer <lua <at> bmarket.com>
2000-04-01 13:01:34 GMT
2000-04-01 13:01:34 GMT
Thanks to some excellent feedback from lhf, we're already up to version 1.1
of the constant table code.
This version is compatible with plain C, doesn't have the temporary
variable, and uses a lua_beginblock/lua_endblock pair.
Also, lhf points out that the following code DOES work:
#define PI 3.14
CONST_TABLE_BEGIN("MY_TABLE")
CONST_NUM( "PI", PI )
CONST_TABLE_END
So there's reason to rejoice.
Thanks, lhf!
ashley
--- cut here ---
#define CONST_TABLE_BEGIN( TABLE_NAME ) \
lua_beginblock(); {\
lua_Object err_func;\
int tag;\
lua_Object table = lua_createtable();\
lua_pushobject( table );\
lua_setglobal( TABLE_NAME );\
lua_dostring( "return function () error(\
\"Cannot modify constant table: " TABLE_NAME "\" ) end" );\
err_func = lua_lua2C( 1 );
#define CONST_NUM( NAME, VALUE) \
lua_pushobject( table );\
lua_pushstring( ( char * ) NAME );\
lua_pushnumber( VALUE );\
lua_settable();
#define CONST_NUM_VAR( NAME ) \
lua_pushobject( table );\
lua_pushstring( ( char * ) #NAME );\
lua_pushnumber( NAME );\
lua_settable();
#define CONST_STR( NAME, VALUE ) \
lua_pushobject( table );\
lua_pushstring( ( char * ) NAME );\
lua_pushstring( VALUE );\
lua_settable();
#define CONST_STR_VAR( NAME ) \
lua_pushobject( table );\
lua_pushstring( ( char * ) #NAME );\
lua_pushstring( ( char * ) NAME );\
lua_settable();
#define CONST_TABLE_END \
tag = lua_newtag();\
lua_pushobject( table );\
lua_settag( tag );\
lua_pushobject( table );\
lua_pushobject( err_func );\
lua_settagmethod( tag, "settable" );\
lua_pushobject( table );\
lua_pushobject( err_func );\
lua_settagmethod( tag, "setglobal" );\
} lua_endblock();
--- cut here ---
2 Apr 2000 19:29
2 bugs and patches
Edgar Toernig <froese <at> gmx.de>
2000-04-02 17:29:25 GMT
2000-04-02 17:29:25 GMT
Hi,
I found two bugs in lua 3.2:
1. tostring() without an argument generates a SEGV.
--- lbuiltin.c Sun Apr 2 19:15:41 2000
+++ lbuiltin.c Sun Apr 2 19:16:22 2000
<at> <at> -328,7 +328,7 <at> <at>
static void luaB_tostring (void) {
- lua_Object obj = lua_getparam(1);
+ lua_Object obj = luaL_nonnullarg(1);
TObject *o = luaA_Address(obj);
char buff[64];
switch (ttype(o)) {
2. Something like "repeat until 1 %foo()" gives a syntax error.
Looks, like '%' is missing in expfollow[]:
--- lparser.c Sun Apr 2 19:14:15 2000
+++ lparser.c Sun Apr 2 19:14:32 2000
<at> <at> -598,7 +598,7 <at> <at>
static int expfollow [] = {ELSE, ELSEIF, THEN, IF, WHILE, REPEAT, DO, NAME,
- LOCAL, FUNCTION, END, UNTIL, RETURN, ')', ']', '}', ';', EOS, ',', 0};
+ LOCAL, FUNCTION, END, UNTIL, RETURN, ')', ']', '}', ';', '%', EOS, ',', 0};
static int is_in (int tok, int *toks) {
Ciao, ET.
2 Apr 2000 23:10
Some enhancement requests
Edgar Toernig <froese <at> gmx.de>
2000-04-02 21:10:33 GMT
2000-04-02 21:10:33 GMT
Hi,
while working with lua I made numerous modifications to the source.
IMHO, some of them should be in the standard version.
1. Allow the $if/$ifnot statement to test if debugging is turned on
so that one can add additional debugging code in lua programs.
Example:
$if $debug
print("x.n=", getn(x))
foreach(x, function(i,v) assert(type(i)=="number", "bad x") end)
$end
Here's a patch that implements this:
--- llex.c Thu Jun 17 19:04:03 1999
+++ llex.c Thu Mar 23 19:32:50 2000
<at> <at> -117,8 +117,9 <at> <at>
static int checkcond (LexState *LS, char *buff) {
- static char *opts[] = {"nil", "1", NULL};
+ static char *opts[] = {"nil", "1", "$debug", NULL};
int i = luaL_findstring(buff, opts);
+ if (i == 2) return L->debug;
if (i >= 0) return i;
else if (isalpha((unsigned char)buff[0]) || buff[0] == '_')
return luaS_globaldefined(buff);
<at> <at> -132,7 +133,7 <at> <at>
static void readname (LexState *LS, char *buff) {
int i = 0;
skipspace(LS);
- while (isalnum(LS->current) || LS->current == '_') {
+ while (isalnum(LS->current) || LS->current == '_' || LS->current == '$') {
if (i >= PRAGMASIZE) {
buff[PRAGMASIZE] = 0;
luaX_syntaxerror(LS, "pragma too long", buff);
2. The second argument to the next() function should be optional so that
next(foo, nil) is the same as next(foo). I use next() regularly to test
if a table is empty (if next(tab) then print"not empty" end) and the
additional nil is disturbing and inconsistent.
--- lbuiltin.c Sun Apr 2 19:15:41 2000
+++ lbuiltin.c Sun Apr 2 19:51:54 2000
<at> <at> -320,15 +320,15 <at> <at>
static void luaB_next (void) {
Hash *a = gethash(1);
- TObject *k = luaA_Address(luaL_nonnullarg(2));
- int i = (ttype(k) == LUA_T_NIL) ? 0 : luaH_pos(a, k)+1;
+ TObject *k = luaA_Address(lua_getparam(2));
+ int i = (k == 0 || ttype(k) == LUA_T_NIL) ? 0 : luaH_pos(a, k)+1;
if (luaA_next(a, i) == 0)
lua_pushnil();
}
[Side note]
In my opinion there's an unnecessary difference between handling
of arguments to C functions and lua functions. A lua function
f(a,b) cannot say if it was called via f(a,nil) or f(a). A C
function gets either a nil or LUA_NOOBJECT for b. Why this
difference? Isn't nil supposed to stand for "no object"?
I think it would have been better to return nil for not passed
arguments. The functions that check for optional arguments
check against nil instead of LUA_NOOBJECT (then giving exactly the
same behavior as lua functions). And an additional function (i.e
lua_narg()) gives the real number of passed arguments to simulate
vararg lua functions.
But I guess it's too late now...
3. The last thing is a little bit more difficult. A garbage collected
system is a nice thing to have. You don't have to care about freeing
objects. But often you come to the situation that you want to create
a new one and assume that it is already present somewhere. For example,
you have a function to load a graphics image. It gets a file name,
load's it and returns an image object. The object itself is garbage
collected. Now, you want to cache all images, so that you don't have
to load the same image multiple times. But unfortunately, the cache
itself holds a reference to every image and it is no longer garbage
collected
What you want: weak references that won't hinder the gc of collecting
the referenced object.
My first idea was: add a function to mark a table as holding weak
reference which means, all values (not indexes) hold in that table
will not be marked during the mark phase. Later in the collection
phase replace all table values not marked with nil. But the imple-
mentation is not that easy and I'm still not sure if this really
works. (I would love to hear comments about this idea, especially
if this would really work.)
So I went an easier way. The C-API already has a method to hold weak
references (lua_ref(0)). I just created some bindings to access them
from lua. Because the references itself have to be garbage collected
I mapped them to userdata with a private tag. The result: there are
two new functions, x = ref(obj) and obj = getref(x). getref returns
nil if the object referenced by x is already garbage collected.
I think, these two function are elementary enough to be part of the
standard list of builtin functions (the short names may be a problem).
Here is a code fragment of my implementation:
lua_api
xlua_ref(void)
{
lua_pushobject(luaL_nonnullarg(1));
lua_pushusertag((void*)lua_ref(0), tag_ref);
}
lua_api
xlua_getref(void)
{
lua_Object o = lua_getparam(1);
luaL_arg_check(lua_tag(o) == tag_ref, 1, "reference expected");
o = lua_getref((int)lua_getuserdata(o));
if (o != LUA_NOOBJECT) /* object not already gc'ed */
lua_pushobject(o);
}
static void
gc_ref(void)
{
lua_unref((int)lua_getuserdata(lua_getparam(1)));
}
Ok, that's all for the moment. Discussion opened *g*
Ciao, ET.
2 Apr 2000 23:31
Re: Lua Future (Was: Re: The ~)
Edgar Toernig <froese <at> gmx.de>
2000-04-02 21:31:16 GMT
2000-04-02 21:31:16 GMT
Erik Hougaard wrote: > I know some of the things I would like to see? > ... > 1. Language constants - That would compile to values. I though about that myself, something like: x = $(f(3)+4*5) so that part inside the $(...) is executed by the lexer and the result, a lua object, is passed up to the parser as a special kind of constant. But I never started to implement it. It was just an idea... > 1. BCD support for embedded platforms without floating point. I made a complete lua with integer arithmetic. No floating point anywhere. The standard lua has some problem when LUA_NUM_TYPE is long - it still uses floating point in a lot of places. I once posted a patch for this to the mailing list. In the meantime I've added some more stuff (more operators, and, or, xor, not, shift left/right, ...). Ciao, ET.
3 Apr 2000 14:42
Re: 2 bugs and patches
Luiz Henrique de Figueiredo <lhf <at> tecgraf.puc-rio.br>
2000-04-03 12:42:11 GMT
2000-04-03 12:42:11 GMT
>From: Edgar Toernig <froese <at> gmx.de> >1. tostring() without an argument generates a SEGV. Oops, sorry about that. This will be fixed in the next version. (On the other, hand, who would want to call tostring() without an argument>2. Something like "repeat until 1 %foo()" gives a syntax error. This has been fixed in the next version. Thanks for spoting these bugs. --lhf
3 Apr 2000 14:55
Re: Some enhancement requests
Luiz Henrique de Figueiredo <lhf <at> tecgraf.puc-rio.br>
2000-04-03 12:55:27 GMT
2000-04-03 12:55:27 GMT
>From: Edgar Toernig <froese <at> gmx.de>
>1. Allow the $if/$ifnot statement to test if debugging is turned on
> so that one can add additional debugging code in lua programs.
The purpose of $debug is to enable better error messages, not to support
user-level debugging.
I think you could do what you want using a global variable, say DEBUG,
and then write:
$if DEBUG
print("x.n=", getn(x))
foreach(x, function(i,v) assert(type(i)=="number", "bad x") end)
$end
To enable this code using the standard standalone interpreter, you can simply
do lua DEBUG=1 myfile.lua
>2. The second argument to the next() function should be optional so that
> next(foo, nil) is the same as next(foo). I use next() regularly to test
> if a table is empty (if next(tab) then print"not empty" end) and the
> additional nil is disturbing and inconsistent.
Using "next" to test whether a table is empty is a clever idea, but it got me
thinking *why* you would want to do this.
Anyway, the request sounds reasonable to me. We'll see...
--lhf
3 Apr 2000 15:03
Re: Lua Future
Luiz Henrique de Figueiredo <lhf <at> tecgraf.puc-rio.br>
2000-04-03 13:03:57 GMT
2000-04-03 13:03:57 GMT
>From: Edgar Toernig <froese <at> gmx.de>
>I made a complete lua with integer arithmetic. No floating point
>anywhere. The standard lua has some problem when LUA_NUM_TYPE is
>long - it still uses floating point in a lot of places.
Not "a lot of places", but only in the lexer and in tonumber and tostring:
% grep LUA_NUMBER *.[ch]
lobject.c:double luaO_str2d (char *s) { /* LUA_NUMBER */
lobject.h:** GREP LUA_NUMBER to change that
lundump.h:#define NUMBER_FMT "%.16g" /* LUA_NUMBER */
lvm.c:int luaV_tonumber (TObject *obj) { /* LUA_NUMBER */
lvm.c:int luaV_tostring (TObject *obj) { /* LUA_NUMBER */
(NUMBER_FMT in only used in luac.)
Of course, to completely avoid floating point, you have to change the API.
I've promised to write a LTN about this. Will do.
--lhf
RSS Feed