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


10.1 – Data Description

The Lua site keeps a database containing a sample of projects around the world that use Lua. We represent each entry in the database by a constructor in an auto-documented way, as the following example shows:

    entry{
      title = "Tecgraf",
      org = "Computer Graphics Technology Group, PUC-Rio",
      url = "http://www.tecgraf.puc-rio.br/",
      contact = "Waldemar Celes",
      description = [[
        TeCGraf is the result of a partnership between PUC-Rio,
        the Pontifical Catholic University of Rio de Janeiro,
        and <A HREF="http://www.petrobras.com.br/">PETROBRAS</A>,
        the Brazilian Oil Company.
        TeCGraf is Lua's birthplace,
        and the language has been used there since 1993.
        Currently, more than thirty programmers in TeCGraf use
        Lua regularly; they have written more than two hundred
        thousand lines of code, distributed among dozens of
        final products.]]
      }
The interesting thing about this representation is that a file with a sequence of such entries is a Lua program, which does a sequence of calls to a function entry, using the tables as the call arguments.

Our goal is to write a program that shows that data in HTML, so that it becomes the web page http://www.lua.org/uses.html. Because there are many projects, the final page first shows a list of all project titles, and then shows the details of each project. The result of the program is something like this:

    <HTML>
    <HEAD><TITLE>Projects using Lua</TITLE></HEAD>
    <BODY BGCOLOR="#FFFFFF">
    Here are brief descriptions of some projects around the
    world that use <A HREF="home.html">Lua</A>.
    <BR>
    <UL>
    <LI><A HREF="#1">TeCGraf</A>
    <LI> ...
    </UL>
    
    <H3>
    <A NAME="1" HREF="http://www.tecgraf.puc-rio.br/">TeCGraf</A>
    <BR>
    <SMALL><EM>Computer Graphics Technology Group,
               PUC-Rio</EM></SMALL>
    </H3>
    
        TeCGraf is the result of a partnership between
        ...
        distributed among dozens of final products.<P>
    Contact: Waldemar Celes
    
    <A NAME="2"></A><HR>
    ...
    
    </BODY></HTML>

To read the data, all the program has to do is to give a proper definition for entry, and then run the data file as a program (with dofile). Note that we have to traverse all the entries twice, first for the title list, and again for the project descriptions. A first approach would be to collect all entries in an array. However, because Lua compiles so fast, there is a second attractive solution: run the data file twice, each time with a different definition for entry. We follow this approach in the next program.

First, we define an auxiliary function for writing formatted text (we already saw this function in Section 5.2):

    function fwrite (fmt, ...)
      return io.write(string.format(fmt, unpack(arg)))
    end

The BEGIN function simply writes the page header, which is always the same:

    function BEGIN()
      io.write([[
        <HTML>
        <HEAD><TITLE>Projects using Lua</TITLE></HEAD>
        <BODY BGCOLOR="#FFFFFF">
        Here are brief descriptions of some projects around the
        world that use <A HREF="home.html">Lua</A>.
        <BR>
      ]])
    end

The first definition for entry writes each title project as a list item. The argument o will be the table describing the project:

    function entry0 (o)
      N=N + 1
      local title = o.title or '(no title)'
      fwrite('<LI><A HREF="#%d">%s</A>\n', N, title)
    end
If o.title is nil (that is, the field was not provided), the function uses a fixed string "(no title)".

The second definition writes all useful data about a project. It is a little more complex, because all items are optional.

    function entry1 (o)
      N=N + 1
      local title = o.title or o.org or 'org'
      fwrite('<HR>\n<H3>\n')
      local href = ''
    
      if o.url then
        href = string.format(' HREF="%s"', o.url)
      end
      fwrite('<A NAME="%d"%s>%s</A>\n', N, href, title)
    
      if o.title and o.org then
        fwrite('<BR>\n<SMALL><EM>%s</EM></SMALL>', o.org)
      end
      fwrite('\n</H3>\n')
    
      if o.description then
        fwrite('%s', string.gsub(o.description,
                                 '\n\n\n*', '<P>\n'))
        fwrite('<P>\n')
      end
    
      if o.email then
        fwrite('Contact: <A HREF="mailto:%s">%s</A>\n',
               o.email, o.contact or o.email)
      elseif o.contact then
        fwrite('Contact: %s\n', o.contact)
      end
    end
(To avoid conflict with HTML, which uses double quotes, we used only single quotes in this program.) The last function closes the page:
    function END()
      fwrite('</BODY></HTML>\n')
    end
Finally, the main program starts the page, runs the data file with the first definition for entry (entry0) to create the list of titles, then runs the data file again with the second definition for entry, and closes the page:
    BEGIN()
    
    N = 0
    entry = entry0
    fwrite('<UL>\n')
    dofile('db.lua')
    fwrite('</UL>\n')
    
    N = 0
    entry = entry1
    dofile('db.lua')
    
    END()