LUX - AN EMBEDDABLE AND EXTENSIBLE SCRIPTING SYSTEM BASED ON LUA
This document describes:
- Lux - standalone builds
- TcLux - an extension for Tcl
- PyLux - an extension for Python
- PerLux - an extension for Perl
- RubyLux - an extension for Ruby
LUX STANDALONE
The standalone version has been built in a number of configurations:
- lux - dynamic link, complete system
- luxd - dynamic link, complete system, with -DLUA_DEBUG
- luxm - dynamic link, minimal build (no support libs)
- luxr - dynamic link, no lexer / parser / code generator
- luxs - static link, complete system
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:
- initialize Lux, this also executes a compiled-in "preload" script
- set the global "argv0" to C's "argv[0]", i.e. the program name
- set up an "argv" vector with all command-line arguments
- execute the embedded script (generated from "run.lua", etc)
The "run.lua" script is very small:
- look for a zip archive at the end of the executable
- try to fetch/evaluate the code stored as an entry called "boot.lux"
- else, look for a first arg and move it off argv into an "argv1" global
- if the arg is a zip archive, fetch its "boot.lux" script and run that
- lastly, open the arg as script file (source or compiled), and run it
- 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:
- i - pass next arg as integer to Lux
- d - pass next arg as double to Lux
- b - pass next arg as bytearray to Lux
- s - pass next arg as string to Lux
- g - next arg is name of global var, pass its current value
- r - next arg is a reference, pass the object it refers to
- c - next arg is a Tcl callback, pass it through as Lua function
- p - pass the raw Tcl_Obj* as userdata to Lux
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:
- v - pass next arg as integer/double/string to Lux
- g - next arg is name of global var, pass its current value
- r - next arg is a reference, pass the object it refers to
- c - next arg is a Python callback, pass it through as Lua function
- p - pass the raw PyObject* as userdata to Lux
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
-- Coen Siegerink <[email protected]>
Mission Impossible 5oftware Team
https://www.equi4.com/lux/
Mon Feb 19 19:25:27 PST 2001