Skip to main content

If you don't have an IBM ID and password, register here.

By clicking Submit, you agree to the developerWorks terms of use.

The first time you sign into developerWorks, a profile is created for you. This profile includes the first name, last name, and display name you identified when you registered with developerWorks. Select information in your developerWorks profile is displayed to the public, but you may edit the information at any time. Your first name, last name (unless you choose to hide them), and display name will accompany the content that you post.

All information submitted is secure.

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerworks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

By clicking Submit, you agree to the developerWorks terms of use.

All information submitted is secure.

Embeddable scripting with Lua

Lua offers high-level abstraction without losing touch with the hardware

Martin Streicher ([email protected]), Editor-in-Chief, Linux Magazine
Martin Streicher is the Editor-in-Chief of Linux Magazine. He earned a master's degree in computer science from Purdue University and has been programming UNIX-like systems since 1982 in the Pascal, C, Perl, Java, and (most recently) Ruby programming languages.

Summary:  Compiled programming languages and scripting languages each have unique advantages, but what if you could use both to create rich applications? Lua is an embeddable scripting language that is small, fast, and very powerful. Before you create yet another configuration file or resource format (and yet another parser to accompany it), try Lua.

Date:  28 Apr 2006
Level:  Introductory

Comments:  

While interpreted programming languages such as Perl, Python, PHP, and Ruby are increasingly favored for Web applications -- and have long been preferred for automating system administration tasks -- compiled programming languages such as C and C++ are still necessary. The performance of compiled programming languages remains unmatched (exceeded only by the performance of hand-tuned assembly), and certain software -- including operating systems and device drivers -- can only be implemented efficiently using compiled code. Indeed, whenever software and hardware need to mesh seamlessly, programmers instinctively reach for a C compiler: C is primitive enough to get "close to the bare metal" -- that is, to capture the idiosyncrasies of a piece of hardware -- yet expressive enough to offer some high-level programming constructs, such as structures, loops, named variables, and scope.

However, scripting languages have distinct advantages, too. For example, after a language's interpreter is successfully ported to a platform, the vast majority of scripts written in that language run on the new platform unchanged -- free of dependencies such as system-specific function libraries. (Think of the many DLL files of the Microsoft® Windows® operating system or the many libcs of UNIX® and Linux®.) Additionally, scripting languages typically offer higher-level programming constructs and convenience operations, which programmers claim boost productivity and agility. Moreover, programmers working in an interpreted language can work faster, because the compilation and link steps are unnecessary. The "code, build, link, run" cycle of C and its ilk is reduced to a hastened "script, run."

Lua novelties

Like every scripting language, Lua has its own peculiarities:

  • Lua types. In Lua, values have types, but variables are dynamically typed. The nil, boolean, number, and string types work as you might expect.
    • Nil is the type of the special value nil and is used to represent the absence of a value.
    • Boolean is the type of the constants true and false. (Nil also represents false, and any non-nil value represents true.)
    • All numbers in Lua are doubles (but you can easily build code to realize other numeric types).
    • A string is an immutable array of characters. (Hence, to append to a string, you must make a copy of it.)
  • The table, function, and thread types are all references. Each can be assigned to a variable, passed as an argument, or returned from a function. For instance, here's an example of storing a function:

    -- example of an anonymous function
    -- returned as a value
    -- see http://www.tecgraf.puc-rio.br/~lhf/ftp/doc/hopl.pdf
    function add(x)
      return function (y) return (x + y) end
    end
    f = add(2)
    print(type(f), f(10))
    function  12
    

  • Lua threads. A thread is a co-routine created by calling the built-in function coroutine.create(f), where f is a Lua function. Threads do not start when created; instead, a co-routine is started after the fact, using coroutine.resume(t), where t is a thread. Every co-routine must occasionally yield the processor to other co-routines using coroutine.yield().
  • Assignment statements. Lua allows multiple assignments, and expressions are evaluated first and are then assigned. For example, the statements:

    i = 3
    a = {1, 3, 5, 7, 9}
    i, a[i], a[i+1], b = i+1, a[i+1], a[i]
    print (i, a[3], a[4], b, I)
    

    produce 4 7 5 nil nil. If the list of variables is larger than the list of values, excess variables are assigned nil; hence, b is nil. If there are more values than variables, extra values are simply discarded. In Lua, variable names are case-sensitive, explaining why I is nil.
  • Chunks. A chunk is any sequence of Lua statements. A chunk can be stored in a file or in a string in a Lua program. Each chunk is executed as the body of an anonymous function. Therefore, a chunk can define local variables and return values.
  • More cool stuff. Lua has a mark-and-sweep garbage collector. As of Lua 5.1, the garbage collector works incrementally. Lua has full lexical closures (like Scheme and unlike Python). And Lua has reliable tail call semantics (again, like Scheme and unlike Python).

Find more examples of Lua code in Programming in Lua and in the Lua-users wiki (for links, see the Resources section below).

As in all engineering pursuits, choosing between a compiled language and an interpreted language means measuring the pros and cons of each in context, weighing the trade-offs, and accepting compromises.


Mixing the best of both worlds

But what if you could enjoy the best of both worlds: close to bare metal performance and high-level, powerful abstractions? Better yet, what if you could optimize algorithms and functions that are processor intensive and system dependent as well as separate logic that's system independent and highly susceptible to changes in requirements?

Balancing the need for high-performance code and high-level programming is the purview of Lua, an embeddable scripting language. Applications that include Lua are a combination of compiled code and Lua scripts. Compiled code can drop to the metal when necessary, yet can call Lua scripts to manipulate complex data. And because the Lua scripts are separate from the compiled code, you readily and separately revise the scripts. With Lua, the development cycle is more akin to "Code, Build, Run, Script, Script, Script...".

For example, the Lua Web site "Uses" page (see Resources) lists several mass-market computer games, including World of Warcraft and (the home console version of arcade classic) Defender, that integrate Lua to run everything from the user interface to the artificial intelligence of foes. Other applications of Lua include an extension mechanism for the popular Linux software update tool apt-rpm and the control of the "Crazy Ivan" Robocup 2000 champion. Many of the testimonials on that page laud Lua's small size and excellent performance.


Getting started with Lua

Lua version 5.0.2 was the current version at the time of this writing, although version 5.1 was released recently. You can download Lua as source code from lua.org, and you can find a variety of pre-built binaries at the Lua-users wiki (see Resources for links). The entire Lua 5.0.2 core, including the standard libraries and the Lua compiler, is less than 200KB in size.

If you use Debian Linux, you can install Lua 5.0 quickly and easily by running this command:

# apt-get install lua50

as the superuser. All the examples shown here were run on Debian Linux "Sarge" using Lua 5.0.2 and the 2.4.27-2-686 Linux kernel.

After you've installed Lua on your system, give the stand-alone Lua interpreter a try. (All Lua applications must be embedded in a host application. The interpreter is simply a special kind of host, useful for development and debugging.) Create a file called factorial.lua, and enter the lines:

-- defines a factorial function
function fact (n)
  if n == 0 then
    return 1
  else
    return n * fact(n-1)
  end
end

print("enter a number:")
a = io.read("*number")
print(fact(a))

The code in factorial.lua -- more specifically, any sequence of Lua statements -- is called a chunk, as described in the Lua novelties section above. To execute the chunk you just created, run the command lua factorial.lua:

$ lua factorial.lua
enter a number:
10
3628800

Or, as in other interpreted languages, you can add a "shebang" (#!) line at the top of the script, make the script executable, and then run the file as a stand-alone command:

$ (echo '#! /usr/bin/lua'; cat factorial.lua) > factorial 
$ chmod u+x factorial
$ ./factorial
enter a number:
4
24


The Lua language

Lua has many of the conveniences found in a modern scripting language: scope, control structures, iterators, and a host of standard libraries for processing strings, emitting and collecting data, and performing mathematical operations. A complete description of the Lua language is in the Lua 5.0 Reference Manual (see Resources).

In Lua, only values have a type, but variables are dynamically typed. There are eight fundamental types (of values) in Lua: nil, boolean, number, string, function, thread, table, and userdata. The first six types are largely self-descriptive (see the Lua novelties section above for the exceptions); the last two need some explanation.

Lua tables

Tables are the catch-all data structure in Lua. Indeed, tables are the only data structure in Lua. You can use a table as an array, a dictionary (also called a hash or an associative array), a tree, a record, and so on.

Unlike other programming languages, the contents of a Lua table need not be homogeneous: The table can include any combination of types and can contain a mix of array-like elements and dictionary-like elements. Moreover, any Lua value -- including a function or another table -- can serve as a dictionary element key.

To explore tables, start the Lua interpreter and type the lines shown in bold in Listing 1.


Listing 1. Experimenting with Lua tables

$ lua
> -- create an empty table and add some elements
> t1 = {}
> t1[1] = "moustache"
> t1[2] = 3
> t1["brothers"] = true

> -- more commonly, create the table and define elements
> all at once
> t2 = {[1] = "groucho", [3] = "chico", [5] = "harpo"}
> t3 = {[t1[1]] = t2[1], accent = t2[3], horn = t2[5]}
> t4 = {}
> t4[t3] = "the marx brothers"
> t5 = {characters = t2, marks = t3}
> t6 = {["a night at the opera"] = "classic"}

> -- make a reference and a string
> i = t3
> s = "a night at the opera"

> -- indices can be any Lua value
> print(t1[1], t4[t3], t6[s])
moustache   the marx brothers classic

> -- the phrase table.string is the same as table["string"]
> print(t3.horn, t3["horn"])
harpo   harpo

> -- indices can also be "multi-dimensional"
> print (t5["marks"]["horn"], t5.marks.horn)
harpo   harpo

> -- i points to the same table as t3
> = t4[i]
the marx brothers

> -- non-existent indices return nil values
> print(t1[2], t2[2], t5.films)
nil     nil     nil

>  -- even a function can be a key 
> t = {}
> function t.add(i,j)
>> return(i+j)
>> end
> print(t.add(1,2))
3
> print(t['add'](1,2))
3
>  -- and another variation of a function as a key 
> t = {}
> function v(x)
>> print(x)
>> end
> t[v] = "The Big Store"
> for key,value in t do key(value) end
The Big Store

As you might expect, Lua also provides a fair number of iterator functions to process tables. The global variable table provides the functions (yes, Lua packages are just tables, too). Some functions, like table.foreachi(), expect a contiguous range of integer keys starting at 1 (the numeral one):

> table.foreachi(t1, print)
1 moustache
2 3

Others, such as table.foreach(), iterate over an entire table:

> table.foreach(t2,print)
1       groucho
3       chico
5       harpo
> table.foreach(t1,print)
1       moustache
2       3
brothers        true

While some iterators are optimized for integer indices, all simply process (key, value) pairs.

For fun, create a table, t, with elements {2, 4, 6, language="Lua", version="5", 8, 10, 12, web="www.lua.org"}, and run table.foreach(t, print) and table.foreachi(t, print).

Userdata

Because Lua is intended to be embedded in a host application written in a language such as C or C++ and is intended to cooperate with the host application, data must be shared between the C environment and Lua. As the Lua 5.0 Reference Manual says, the userdata type allows "arbitrary C data to be stored in Lua variables." You can think of userdata as an array of bytes -- bytes that might represent a pointer, a structure, or a file in the host application.

The contents of userdata originates with C, so it cannot be modified in Lua. Of course, because the userdata originates in C, there are no pre-defined operations for userdata in Lua. However, you can create operations that operate on userdata using another Lua mechanism, called metatables.

Metatables

Because tables and userdata are so flexible, Lua permits you to overload operations for objects of either type. (You cannot overload the six other types.) A metatable is a (normal) Lua table that maps standard operations to custom functions that you provide. The keys of the metatable are called events; the values (in other words, the functions) are called metamethods.

The functions setmetatable() and getmetatable() modify and query an object's metatable, respectively. Each table and userdata object can have its own metatable.

For example, one event is __add, for addition. Can you deduce what this chunk does?

-- Overload the add operation
-- to do string concatenation
--
mt = {}

function String(string)
  return setmetatable({value = string or ''}, mt)
end

-- The first operand is a String table
-- The second operand is a string
-- .. is the Lua concatenate operator
--
function mt.__add(a, b)
  return String(a.value..b)
end

s = String('Hello')
print((s + ' There ' + ' World!').value )

This chunk emits the following text:

Hello There World!

The function String() takes a string, string, wraps it in a table ({value = s or ''}), and assigns the metatable mt to the table. Function mt.__add() is a metamethod that appends the string b to the string found in a.value b times. The line print((s + ' There ' + ' World!').value ) invokes the metamethod twice.

__index is another event. The metamethod for __index is called whenever a key doesn't exist in a table. Here's an example that "memoizes," or remembers, the value of a function:

-- code courtesy of Rici Lake, [email protected]
function Memoize(func, t)
  return setmetatable(
     t or {},
    {__index =
      function(t, k)
        local v = func(k);
        t[k] = v;
        return v;
        end
    }
  )
end

COLORS = {"red", "blue", "green", "yellow", "black"}
color = Memoize(
  function(node)
    return COLORS[math.random(1, table.getn(COLORS))]
    end
)

Put the code into the Lua interpreter, and then type print(color[1], color[2], color[1]). You should see something like blue black blue.

This code, given a key, node, looks up the color for the node. If it doesn't exist, the code permanently assigns node a new, randomly chosen color. Otherwise, the node's assigned color is returned. In the former case, the __index metamethod is executed once to assign a color. The latter case is a simple and fast hash lookup.

The Lua language offers many other powerful features, and all of them are well documented. But in case you ever run into trouble or want to talk to a wizard, head over to the Lua Users Chat Room IRC Channel (see Resources) for some very enthusiastic support.


Embed and extend

Beyond Lua's simple syntax and powerful table structure, Lua's real power is evident when mixing it with a host language. As has been intimated, Lua scripts can extend the host language's own capabilities. But the reciprocal is true as well: The host language can simultaneously extend Lua. C functions can call Lua functions and vice versa, for example.

At the heart of the symbiotic relationship between Lua and its host language is a virtual stack. The virtual stack -- like a real stack -- is a last in-first out (LIFO) data structure that temporarily stores function arguments and function results. To make a call from Lua to its host language and vice versa, the caller pushes values onto the stack and calls the target function; the callee pops the arguments (verifying the type and value of each argument, of course), processes the data, and pushes the result on the stack. When control returns to the caller, the caller extracts the return values from the stack.

Virtually all the C application program interface (API) for Lua operations operate through the stack. The stack can hold any Lua value; however, the type of the value must be known to the caller and callee, and specific functions push and pop each type from the stack (such as lua_pushnil() and lua_pushnumber().

Listing 2 shows a simple C program (taken from Chapter 24 of the Programming in Lua book cited in Resources) that implements a minimal but functional Lua interpreter.


Listing 2. A simple Lua interpreter

 1 #include <stdio.h>
 2 #include <lua.h>
 3 #include <lauxlib.h>
 4 #include <lualib.h>
 5
 6 int main (void) {
 7   char buff[256];
 8   int error;
 9   lua_State *L = lua_open();   /* opens Lua */
10   luaopen_base(L);             /* opens the basic library */
11   luaopen_table(L);            /* opens the table library */
12   luaopen_io(L);               /* opens the I/O library */
13   luaopen_string(L);           /* opens the string lib. */
14   luaopen_math(L);             /* opens the math lib. */
15
16   while (fgets(buff, sizeof(buff), stdin) != NULL) {
17     error = luaL_loadbuffer(L, buff, strlen(buff), "line") ||
18             lua_pcall(L, 0, 0, 0);
19     if (error) {
20       fprintf(stderr, "%s", lua_tostring(L, -1));
21       lua_pop(L, 1);  /* pop error message from the stack */
22     }
23   }
24
25   lua_close(L);
26   return 0;
27 }

Lines 2 through 4 include the Lua standard functions, several convenience functions that are used in all Lua libraries, and functions to open libraries, respectively. Line 9 creates a Lua state. All states are initially empty; you add libraries of functions to the state using luaopen_...(), as shown in Lines 10 through 14.

Line 17 and luaL_loadbuffer() take the input from stdin as a chunk and compile it, placing the chunk on the virtual stack. Line 18 pops the chunk from the stack and executes it. If an error occurs during execution, a Lua string is pushed on the stack. Line 20 accesses the top of the stack (the top of the stack has an index of -1) as a Lua string, prints the message, and then removes the value from the stack.

Using the C API, your application can also "reach" into the Lua state to extract information. This snippet grabs two global variables from the Lua state:

..
if (luaL_loadfile(L, filename) || lua_pcall(L, 0, 0, 0))
  error(L, "cannot run configuration file: %s", lua_tostring(L, -1));

lua_getglobal(L, "width");
lua_getglobal(L, "height");
..
width = (int) lua_tonumber(L, -2);
height = (int) lua_tonumber(L, -1);
..

Again, notice that the stack enables the transfer. Calling any Lua function from C is similar to this code: Grab the function using lua_getglobal(), push arguments, make a lua_pcall(), and then process the results. If a Lua function returns n values, the first value is at location -n in the stack, and the last value is at position -1.

The inverse -- calling a C function from Lua -- is similar. If your operating system supports dynamic loading, Lua can load and call functions on demand. (In operating systems where static loading is a necessity, extending the Lua engine to call a C function requires you to rebuild Lua.)


Lua is out of this world

Lua is an incredibly easy language to pick up, but its simple syntax disguises its power: The language supports objects (which are similar to Perl's), metatables make its table type quite malleable, and the C API allows great integration and extension between scripts and the host language. Lua has been hosted in the C, C++, C#, Java™, and Python languages.

Before you create yet another configuration file or resource format (and yet another parser to accompany it), try Lua. The Lua language -- like its community -- is robust, inventive, and ready to help.


Resources

Learn

Get products and technologies

  • Get the Lua 5.0.2 or Lua 5.1 source code to build Lua from scratch.

  • At the Lua-users wiki, browse a variety of pre-built, installable Lua binaries.

  • At LuaForge, discover a vast library of code, including many language bindings and specialized computation libraries.

  • Order the SEK for Linux, a two-DVD set containing the latest IBM trial software for Linux from DB2®, Lotus®, Rational®, Tivoli®, and WebSphere®.

  • With IBM trial software, available for download directly from developerWorks, build your next development project on Linux.

Discuss

About the author

Martin Streicher is the Editor-in-Chief of Linux Magazine. He earned a master's degree in computer science from Purdue University and has been programming UNIX-like systems since 1982 in the Pascal, C, Perl, Java, and (most recently) Ruby programming languages.

Report abuse help

Report abuse

Thank you. This entry has been flagged for moderator attention.


Report abuse help

Report abuse

Report abuse submission failed. Please try again later.


developerWorks: Sign in

If you don't have an IBM ID and password, register here.


Forgot your IBM ID?


Forgot your password?
Change your password


By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. This profile includes the first name, last name, and display name you identified when you registered with developerWorks. Select information in your developerWorks profile is displayed to the public, but you may edit the information at any time. Your first name, last name (unless you choose to hide them), and display name will accompany the content that you post.

Choose your display name

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

(Must be between 3 – 31 characters.)


By clicking Submit, you agree to the developerWorks terms of use.

 


Rate this article

Comments

Help: Update or add to My dW interests

What's this?

This little timesaver lets you update your My developerWorks profile with just one click! The general subject of this content (AIX and UNIX, Information Management, Lotus, Rational, Tivoli, WebSphere, Java, Linux, Open source, SOA and Web services, Web development, or XML) will be added to the interests section of your profile, if it's not there already. You only need to be logged in to My developerWorks.

And what's the point of adding your interests to your profile? That's how you find other users with the same interests as yours, and see what they're reading and contributing to the community. Your interests also help us recommend relevant developerWorks content to you.

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

Removing this interest does not alter your profile, but rather removes this piece of content from a list of all content for which you've indicated interest. In a future enhancement to My developerWorks, you'll be able to see a record of that content.

View your My developerWorks profile

Return from help

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Linux, Open source
ArticleID=110233
ArticleTitle=Embeddable scripting with Lua
publish-date=04282006
author1-email-cc=

Tags

Help
Use the search field to find all types of content in My developerWorks with that tag.

Use the slider bar to see more or fewer tags.

For articles in technology zones (such as Java technology, Linux, Open source, XML), Popular tags shows the top tags for all technology zones. For articles in product zones (such as Info Mgmt, Rational, WebSphere), Popular tags shows the top tags for just that product zone.

For articles in technology zones (such as Java technology, Linux, Open source, XML), My tags shows your tags for all technology zones. For articles in product zones (such as Info Mgmt, Rational, WebSphere), My tags shows your tags for just that product zone.

Use the search field to find all types of content in My developerWorks with that tag. Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).