bulk of control communication between Lua and C, there are other
forms of control exposed through the
API: iterators, error handling, and coroutines. Iterators in Lua allow constructions such as the following one,
which iterates over all lines of a file:
for line in io.lines(file) do
print(line)
end
fluence from the API. All error handling in Lua is based on the longjump
mechanism of C. It is an example of a
feature exported from the API to the
language.
The API supports two mechanisms
for calling a Lua function: unprotected
and protected. An unprotected call
does not handle errors: any error during the call long jumps through this
code to land in a protected call farther
down the call stack. A protected call
sets a recovery point using setjmp,
so that any error during the call is
captured; the call always returns with
a proper error code. Such protected
calls are very important in an embedded scenario where a host program
cannot afford to abort because of occasional errors in a script. The bare-bones application just presented
uses lua _ pcall (protected call) to
call each compiled line in protected
mode.
The standard Lua library simply exports the protected-call API function
to Lua under the name of pcall. With
pcall, the equivalent of a try-catch in
Lua looks like this:
local ok, errorobject = pcall(function()
--here goes the protected code
...
end)
if not ok then
--here goes the error handling code
--(errorobject has more information about
the error)
...
end
Although iterators present a new
syntax, they are built on top of first-class functions. In our example, the
call io.lines(file) returns an
iteration function, which returns a new line
from the file each time it is called. So,
the API does not need anything special to handle iterators. It is easy both
for Lua code to use iterators written
in C (as is the case of io.lines) and
for C code to iterate using an iterator
written in Lua. For this case there is
no syntactic support; the C code must
do explicitly all that the for construct
does implicitly in Lua.
Error handling is another area
where Lua has suffered a strong in-
Figure 1. Passing an array through an API with eval.
void copy (int ar[], int n) {
int i;
eval(“ar = {}”); /* create an empty array */
for (i =0; i <n; i++){
}
}
Figure 2. The bare-bones Lua application.
#include <stdio.h>
#include “lauxlib.h”
#include “lualib.h”
int main (void) {
char line[256];
lua_State *L = luaL_newstate(); /* create a new state */
luaL_openlibs(L); /* open the standard libraries */
/* reads lines and executes them */
while (fgets(line, sizeof(line), stdin) != NULL) {
luaL_loadstring(L, line); /* compile line to a function */
lua_pcall(L, 0, 0, 0); /* call the function */
}
lua_close(L);
return 0;
This is certainly more cumbersome
than a try-catch primitive mechanism
built into the language, but it has a
perfect fit with the C API and a very
light implementation.
The design of coroutines in Lua is
another area where the API had a great
impact. Coroutines come in two flavors: symmetric and asymmetric. 1 Symmetric coroutines offer a single control-transfer primitive, typically called
transfer, that acts like a goto: it can
transfer control from any coroutine
to any other. Asymmetric coroutines
offer two control-transfer primitives,
typically called resume and yield, that
act like a pair call–return: a resume
can transfer control to any other coroutine; a yield stops the current coroutine and goes back to the one that
resumed the one yielding.
It is easy to think of a coroutine as a
call stack (a continuation) that encodes
which computations a program must
do to finish that coroutine. The
transfer primitive of symmetric coroutines
corresponds to replacing the entire
call stack of the running coroutine by
the call stack of the transfer target. On
the other hand, the resume primitive
adds the target stack on top of the current one.
A symmetric coroutine is simpler
than an asymmetric one but poses a big
problem for an embeddable language
such as Lua. Any active C function in a
script must have a corresponding activation register in the C stack. At any
point during the execution of a script,
the call stack may have a mix of C functions and Lua functions. (In particular,
the bottom of the call stack always has
a C function, which is the host program
that initiated the script.) A program
cannot remove these C entries from
the call stack, however, because C does
not offer any mechanism for manipulating its call stack. Therefore, the program cannot make any transfer.