LUX - AN EMBEDDABLE AND EXTENSIBLE SCRIPTING SYSTEM BASED ON LUA

This document describes:


LUX STANDALONE

The standalone version has been built in a number of configurations:

Only "lux", "luxr", and possibly "luxs" are intended for general use. The "luxm" executable will not run, it acts as baseline for size comparisons. The "luxd" build is intended to help debugging the lux system itself.

The main program is defined in "mlux.c", which gets expanded to "lux.c" during the build. This is an equivalent version, but with all Lux-related includes merged into a single source file (the "onesrc.tcl" script does this). Similarly, the Lux core is defined in "mluxsys.c", which gets expanded to "luxsys.c" during the build. This represents the main code, and incorporates all of Lua 4.0 with a few minor modifications.

The logic which defines the startup process for standalone use is in "run.lua", which is compiled with "luac" and then converted into a C array datastructure by 'examples/bin2c.lua'. A similar approach is used for "preload.lua", but this script gets incorporated into all Lux configurations, not just the standalone one. In its current form, this script defines functions to decode zip archives.

The code in "mlux.c" does the following:

  1. initialize Lux, this also executes a compiled-in "preload" script
  2. set the global "argv0" to C's "argv[0]", i.e. the program name
  3. set up an "argv" vector with all command-line arguments
  4. execute the embedded script (generated from "run.lua", etc)
The "run.lua" script is very small:
  1. look for a zip archive at the end of the executable
  2. try to fetch/evaluate the code stored as an entry called "boot.lux"
  3. else, look for a first arg and move it off argv into an "argv1" global
  4. if the arg is a zip archive, fetch its "boot.lux" script and run that
  5. lastly, open the arg as script file (source or compiled), and run it
  6. if no args are specified, display the version string on stdout

The result of all this, is that a zip archive can be tagged onto the end of a standalone executable (any one of them), and if it is properly constructed, it will get control very early on. This also works for shared libraries, but in that case an explicit attempt to run the boot script must be made, e.g. in Tcl:
    % load ./tclux.so
    % tclux gs dostring {lux.ZipBoot('./tclux.so')}

Alternately, the script to run can be specified as command-line argument, with as special case that if a zip archive is specified, the "boot.lux" script in it will get control (it has to be present).

Finally, a "scripted document" approach can be used, by creating a file which starts off as shell script - followed by a zip archive with the bootstrap. On Unix, the following will create such a runnable scripted document:

    $ echo '#!/usr/bin/env lux' >myapp
    $ cat myapp.zip >>myapp
    $ chmod +x myapp
On Win32, you'll need to also create a "myapp.bat" file, containing just:
    @lux myapp
This will make the scripted document "runnable" / "double-clickable".

Some points to keep in mind: if you use the "luxr" runtime (which is a few Kb smaller), make sure the scripts you put into the archive are pre-compiled by "luac". And if you don't, be careful to store scripts with only LF's as line endings, since the extracted files do not get converted according to the different ways Windows and Macintosh treat line endings.


LUX IN TCL

TcLux lets you run Lux/Lua scripts from Tcl. The wrapper source is "tclux.c", it will eventually be merged into "luxsys.c".

TcLux defines a single tclux command, which takes a format string plus a variable number of arguments. Each character in the format string describes one argument, the size of the format string must correspond to the number of remaining arguments. The types currently defined are:

Some examples to illustrate how this can be used:
tclux gs print {Hello Lux!}
calls global "print" function with a single string argument
tclux gsc setglobal tcl eval
set global "tcl" to contain a very useful callback into Tcl
tclux gs tcl {puts "Hello Tcl!"}
calls "tcl" with a string argument (with the definition above, this will call back into Tcl)
tclux gs dostring {tcl('puts "Hello Tcl!"')}
another way to do the same
tclux gs dostring {tcl('set','result','Hello Tcl!')}
returns a value from Lux back to Tcl
The "r" (reference) mechanism is useful during callbacks, because TcLux sets up references for tables and userdata, and then passes those references. In the callback, you can then use those references to call back (yes, it gets confusing!) into Lux, e.g. to extract individual values as needed. Note that references do not remain valid after the callback returns.
LUX IN PYTHON

PyLux lets you run Lux/Lua scripts from Python. The wrapper source is "pylux.c", it will eventually be merged into "luxsys.c".

PyLux defines a pylux module with a single eval command, which takes a format string plus a variable number of arguments. Each character in the format string describes one argument, the size of the format string must correspond to the number of remaining arguments. The types currently defined are:

Some examples to illustrate how this can be used:
pylux.eval('gs','print','Hello Lux!')
calls global "print" function with a single string argument
(More examples will be added, see 'examples/pydemo.py' for now...)

The "r" (reference) mechanism is useful during callbacks, because PyLux sets up references for tables and userdata, and then passes those references. In the callback, you can then use those references to call back (yes, it gets confusing!) into Lux, e.g. to extract individual values as needed. Note that references do not remain valid after the callback returns.


LUX IN PERL

PerLux lets you run Lux/Lua scripts from Perl. The wrapper source is "perlux.c", it will eventually be merged into "luxsys.c".

This interface is still limited to string-only evaluation, e.g.:

    perlux::eval("print('Hello Lux!')");
A better interface, similar to TcLux and PyLux, will be implemented.
LUX IN RUBY

RubyLux lets you run Lux/Lua scripts from Ruby. The wrapper source is "rubylux.c", it will eventually be merged into "luxsys.c".

This interface is still limited to string-only evaluation, e.g.:

    Rubylux.eval("print('Hello Lux!')");
A better interface, similar to TcLux and PyLux, will be implemented.

This Lux project is work-in-progress by

-- Jean-Claude Wippler <jcw@equi4.com>

   Mission Impossible 5oftware Team
   http://www.equi4.com/lux/


Mon Feb 19 19:25:27 PST 2001