This first edition was written for Lua 5.0. While still largely relevant for later versions, there are some differences.
The fourth edition targets Lua 5.3 and is available at Amazon and other bookstores.
By buying the book, you also help to support the Lua project.


25.1 – Table Manipulation

Let us adopt that attitude: Now, we want to configure a background color for the window, too. We will assume that the final color specification is composed of three numbers, where each number is a color component in RGB. Usually, in C, those numbers are integers in some range like [0,255]. In Lua, because all numbers are real, we can use the more natural range [0,1].

A naive approach here is to ask the user to set each component in a different global variable:

    -- configuration file for program `pp'
    width = 200
    height = 300
    background_red = 0.30
    background_green = 0.10
    background_blue = 0
This approach has two drawbacks: It is too verbose (real programs may need dozens of different colors, for window background, window foreground, menu background, etc.); and there is no way to predefine common colors, so that, later, the user can simply write something like background = WHITE. To avoid these drawbacks, we will use a table to represent a color:
    background = {r=0.30, g=0.10, b=0}
The use of tables gives more structure to the script; now it is easy for the user (or for the application) to predefine colors for later use in the configuration file:
    BLUE = {r=0, g=0, b=1}
    ...
    background = BLUE
To get these values in C, we can do as follows:
    lua_getglobal(L, "background");
    if (!lua_istable(L, -1))
      error(L, "`background' is not a valid color table");
    
    red = getfield("r");
    green = getfield("g");
    blue = getfield("b");
As usual, we first get the value of the global variable background and ensure 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:
    #define MAX_COLOR       255
    
    /* assume that table is on the stack top */
    int getfield (const char *key) {
      int result;
      lua_pushstring(L, key);
      lua_gettable(L, -2);  /* get background[key] */
      if (!lua_isnumber(L, -1))
        error(L, "invalid component in background color");
      result = (int)lua_tonumber(L, -1) * MAX_COLOR;
      lua_pop(L, 1);  /* remove number */
      return result;
    }
Again, we face the problem of polymorphism: There are potentially many versions of getfield functions, varying the key type, value type, error handling, etc. The Lua API offers a single function, lua_gettable. It receives the position of the table in the stack, pops the key from the stack, and pushes the corresponding value. Our private getfield assumes that the table is on the top of the stack; so, after pushing the key (lua_pushstring), the table will be at index -2. Before returning, getfield pops the retrieved value from the stack, to leave the stack at the same level that it was before the call.

We will extend our example a little further and introduce color names for the user. The user can still use color tables, but she can also use predefined names for the more common colors. To implement this feature, we need a color table in our C application:

    struct ColorTable {
      char *name;
      unsigned char red, green, blue;
    } colortable[] = {
      {"WHITE",   MAX_COLOR, MAX_COLOR, MAX_COLOR},
      {"RED",     MAX_COLOR,   0,   0},
      {"GREEN",     0, MAX_COLOR,   0},
      {"BLUE",      0,   0, MAX_COLOR},
      {"BLACK",     0, 0, 0},
      ...
      {NULL,        0, 0, 0}  /* sentinel */
    };

Our implementation will create global variables with the color names and initialize these variables using color tables. The result is the same as if the user had the following lines in her script:

    WHITE = {r=1, g=1, b=1}
    RED   = {r=1, g=0, b=0}
    ...
The only difference from these user-defined colors is that the application defines these colors in C, before running the user script.

To set the table fields, we define an auxiliary function, setfield; it pushes the index and the field value on the stack, and then calls lua_settable:

    /* assume that table is at the top */
    void setfield (const char *index, int value) {
      lua_pushstring(L, index);
      lua_pushnumber(L, (double)value/MAX_COLOR);
      lua_settable(L, -3);
    }
Like other API functions, lua_settable works for many different types, so it gets all its operands from the stack. It receives the table index as an argument and pops the key and the value. The setfield function assumes that before the call the table is at the top of the stack (index -1); after pushing the index and the value, the table will be at index -3.

The setcolor function defines a single color. It must create a table, set the appropriate fields, and assign this table to the corresponding global variable:

    void setcolor (struct ColorTable *ct) {
      lua_newtable(L);               /* creates a table */
      setfield("r", ct->red);        /* table.r = ct->r */
      setfield("g", ct->green);      /* table.g = ct->g */
      setfield("b", ct->blue);       /* table.b = ct->b */
      lua_setglobal(L, ct->name);    /* `name' = table */
    }
The lua_newtable function creates an empty table and pushes it on the stack; the setfield calls set the table fields; finally, lua_setglobal pops the table and sets it as the value of the global with the given name.

With those previous functions, the following loop will register all colors in the application's global environment:

    int i = 0;
    while (colortable[i].name != NULL)
      setcolor(&colortable[i++]);
Remember that the application must execute this loop before running the user script.

There is another option for implementing named colors. Instead of global variables, the user can denote color names with strings, writing her settings as background = "BLUE". Therefore, background can be either a table or a string. With this implementation, the application does not need to do anything before running the user's script. Instead, it needs more work to get a color. When it gets the value of the variable background, it has to test whether the value has type string, and then look up the string in the color table:

    lua_getglobal(L, "background");
    if (lua_isstring(L, -1)) {
      const char *name = lua_tostring(L, -1);
      int i = 0;
      while (colortable[i].name != NULL &&
             strcmp(colorname, colortable[i].name) != 0)
        i++;
      if (colortable[i].name == NULL)  /* string not found? */
        error(L, "invalid color name (%s)", colorname);
      else {  /* use colortable[i] */
        red = colortable[i].red;
        green = colortable[i].green;
        blue = colortable[i].blue;
      }
    } else if (lua_istable(L, -1)) {
      red = getfield("r");
      green = getfield("g");
      blue = getfield("b");
    } else
        error(L, "invalid value for `background'");

What is the best option? In C programs, the use of strings to denote options is not a good practice, because the compiler cannot detect misspellings. In Lua, however, global variables do not need declarations, so Lua does not signal any error when a user misspells a color name. If the user writes WITE instead of WHITE, the background variable receives nil (the value of WITE, a variable not initialized), and that is all that the application knows: that background is nil. There is no other information about what is wrong. With strings, on the other hand, the value of background would be the misspelled string; so, the application can add that information to the error message. The application can also compare strings regardless of case, so that a user can write "white", "WHITE", or even "White". Moreover, if the user script is small and there are many colors, it may be odd to register hundreds of colors (and to create hundreds of tables and global variables) only for the user to choose a few. With strings, you avoid this overhead.