== Global ideas about the Lua API == - Object model - When the C++ host pushes an object on the Lua stack, it has two choices: 1. It can push it statically, meaning the object is still owned by the C++ host, and its presence in the Lua VM is only a borrowing from the host. It's usually done when the host wants to push a static object that would stay forever, and can still be used internally by the host. 2. Or it can push it dynamically, loosing the object to the Lua VM. The later case will introduce garbage collector hooks, as well as a "destroy" method. The object shouldn't be referenced anymore by the C++ host, and it's up to the Lua VM to destroy it, either explicitely by calling the "destroy" method, or implicitely, by letting the Lua garbage collector the duty to collect the object when its table isn't referenced anymore. In all cases, an object being pushed is a table. It's up to the host to decide what method to put in it, but the Lua VM can pretty much mess up with the object as it wants. Most of the time, the tables will hold metatables though, either for the garbage collect hook, or for the index / newindex metamethods. For the sake of object identification and reference, there will always be a field called __obj, containing some userdata. Identification of a table as an object is as easy as checking the presence of this field. Additionnally, the object may most likely contain another field called __objname, which is a string describing the object's type. Checking if the object is owned by Lua or the C++ host is as easy as checking the presence of the "destroy" function. - Thread model - The system is able to handle multiple Lua VMs bound to the same environment. Concurrent access to the variables will be properly handled via the host provided locking mechanism. Each thread can be safely be spawned within Lua, and will be properly be threaded using the host's provided threading mechanism. Garbage collection on a thread is fine if it has been spanwed by Lua itself. If the thread has been spawned by the host, it has a reference in the registry in order to avoid collection. == Generic API == - Basic VM set up - The Lua VM contains a few binary functions by default. These are andB, orB, xorB, notB, shl, and shr. They all take 32-bits integer numbers. Number's sex is always considered unsigned, which is important for the shr function. The function "hex" is exported, but is considered dangerous. It takes one mandatory numeric argument, and one optionnal string argument, which equals "%02x". Basically, this is the hex formatting of a single 8-bits number, which is done using sprintf; thus the reason this function isn't safe. The function "getglobal" is exported. It takes a single string argument, and runs basically the following Lua code: "return ". The function "dumpvars" is exported. It takes a minimum of two arguments, and a maximum of three. The first argument is always a Handle (see below). The second argument is always the variable to output, which has to be a table. If the second argument is a string, then the variable is considered global, and its name will be used as the main table assignment in the output. If it's a table instead, it'll be accessed directly, and its assignment name for the output has to be the third string argument. The function "print" is exported, and is overriding the base print function. Its purpose is to provide an abstract printer mechanism, in order to redirect the output based on the Lua thread. - Modifications of the built-in Lua API - The "base" library, if opened by the host, will contain two additional functions: mkdir and time. The "dir" library has been added to the system, and is openable by the host. As a result, you'll have a "dir" function which works as an iterator usable by for loops. You can then write code such as: for entry in dir "*.txt" do ... end The "entry" variable will be a table containing the following fields: . name, as a string, containing the filename of the current entry . type, as a string, equals to either FLAG_DIR or FLAG_FILE. The "string" library, if opened by the host, will contain an additional function: iconv. It'll take 3 string arguments. The first one is the string to convert, the second one is the input encoding, and the last one is the output encoding. Note that lua-interface only opens the following libraries: base, math, string, table, dir, and debug. The io library is considered dangerous, thus the addition of the mkdir and time functions within the base library. - XML parser - If opened by the C++ host, the Lua VM may have the xml table, containing two functions, called LoadString and LoadHandle. They'll both try to parse an XML document, and return its result. Here's a full example: Input XML file: Test Normal test
Bold test. Output Lua table: { [1] = { [1] = { [1] = { [1] = "Test", ["name"] = "title", ["n"] = 1, }, ["name"] = "head", ["n"] = 1, }, [2] = { [1] = "Normal test", [2] = { ["name"] = "br", }, [3] = { [1] = "Bold test.", ["name"] = "b", ["n"] = 1, }, ["attr"] = { ["bgcolor"] = "#ffeedd", }, ["name"] = "body", ["n"] = 3, }, ["name"] = "html", ["n"] = 2, }, ["n"] = 1, } == Objects exported to the Lua VM == The C++ host may or may not export the classes to the Lua VM. The list here is the list of the objects present in the library, but their availability isn't guaranteed. - Handle - The Handle object is probably the most versatile and useful object into the system. It's a channel where you can write and/or read data. It can virtually represent anything. In the C++ host, this object is a pure virtual one, which means it has to be derived in order to be used. The C++ host provides internal mechanisms in order to export easily new handles sub-types over the Lua VM, which means there's basically no way to know exactly what the data is, nor where it goes and/or comes from. In all cases, any Handle subtype will have the following base property: . The string "__handletype" will explicitely tell the handle's subtype. See below for a list of the base subtypes. Again, the C++ host may have loaded code in order to introduce more subtypes. All Handle subtypes will share the following set of methods: . read(size) Without arguments, works the same as readstring. Otherwise, it'll take one numerical argument, the number of bytes to read, and will return two results: a table containing the bytes, and the amount of bytes read. The table starts with a zero index. . write(size, table) With only one string argument, works the same as writestring. Otherwise, it'll take one numerical argument, indicating the number of bytes to write, and a table which starts with a zero index. . readstring() Reads a string from the file, up to the end of the line (or the file) and will eventually remove the end of line marker (LF or CRLF, autodetected). . writestring(string) Writes the string, and returns nothing. . readU8() / readU16() / readU32() / readFloat() / readDouble() Reads a binary number in Little Endian, and returns it. . writeU8(v) / writeU16(v) / writeU32(v) / writeFloat(v) / writeDouble(v) Writes a binary number in Little Endian. . copyfrom(source[, size]) Reads up to 'size' bytes from the given source, and put them inside of the calling handle. If not present, size will be "-1", which means "up to the end". The 'source' handle will then be closed (since it hits the end of file) . copyto(dest[, size]) Does the same thing as copyfrom, just the other way around. . isclosed() / isnonblock() / canread() / canwrite() / canseek() / canwatch() Returns booleans in order to describe the status of the calling handle. . setnonblock() Sets the handle as non blocking. . tell() Returns the actual file position pointer, or if the handle is non seekable, will just return a counter of the number of bytes read so far. . getname() Returns a string containing the name of the handle. The filename, if possible, or a generic text, depending on the capabilities of the input Handle, and its virtuality. . getsize() Returns the handle size in bytes. If it is a Fifo, it will return the number of remaining bytes. (see below) . getmodif() Returns ... ? . close() Closes the file, and flushes the remaining data in caches. . flush() Flushes the remaining data in caches. . seek(pos[, wheel]) Seeks the file pointer at position "pos". The "wheel" argument can be of SEEK_SET, SEEK_CUR or SEEK_END, to specify the pointer movement. . setz([level]) Enables the zlib against the handle, at the specified level, from 0 (no compression) to 9 (max compression). The argument 'level' is 9 by default. This is basically meant to produce .gz files. The last but not the least, the Handle base object will provide the following global functions: . handlecopy(from, to[, size]) Basically the same as the method 'copyfrom'. . exists(fname) Returns a boolean specifying the existence of the filename in the filesystem (virtual or not). . zlib_inflate(from, to) Calls the zlib's inflate fonction onto the data in handle 'from', and puts the result in the handle 'to'. . zlib_deflate(from, to) Calls the zlib's deflate fonction onto the data in handle 'from', and puts the result in the handle 'to'. - Input - This object is a derivation of the Handle class, and doesn't contain any extra method. The constructor takes one string argument, which is the filename that gets opened. Note the actual file being opened may come from one of the opened archives. See below for details. The Handle is read-only. - Output - This object is a derivation of the Handle class, and doesn't contain any extra method. The constructor takes one string argument, which is the filename that gets opened. The Handle is write-only. - Buffer - This object creates a buffer in memory, and is a derivation of the Handle class. Its constructor takes one optionnal boolean argument, to set the seekability of the created buffer. A non-seekable buffer is a fifo, and will free memory as data is being read out of it. A seekable buffer will never free its memory. Seekable buffers will get these additional methods: wtell, wseek and reset. The normal tell and seek methods will work on the read pointer, whereas the newly introduced methods wseek and wtell methods will work on the write pointer. The reset method will restore the Buffer into an initial, empty state. - Regex - You can create regular expressions objects using the following constructor: Regex(regexp[, cflags[, eflags]]) The regexp is a normal string describing the regular expression you want to match. The cflags and eflags fields are the compilation and execution flags. Look at regex(3) for more details about them. The constants are exported so that you can use them straight in Lua. The defaults are REG_EXTENDED for cflags, and no flags set for eflags. The resulting object will have the method Match, which takes one string as argument, and returns a boolean telling if the string has matched, as well as a table with the list of the submatches. - ConfigFile - This object will read a Handle and transform it into a read-only associative array. So its constructor only takes one handle. The structure of the ini files it'll read is the standard windows-like files, such as: [section1] name1=value1 name2=value2 [section2] name1=value1 And any ConfigFile object will have two tables named section1 and section2 containing the indexes mentionned in the exemple.