3.21.0. Lush Interpreter Internals
|
|
This manual describes the internals of TL3, which is a precursor of the
Lush lisp interpreter. Most of the information still applies with the
notable exception of the matrix data structures and the dynamic code
loader.
Lush includes numerous primitives and provides a complete development
language. However the full power of Lush lies in its ability to take
advantage of subroutines and data structures written in the C language.
Indeed, there are many reasons for extending Lush with C subroutines.
-
Speed is certainly an issue. A C subroutine runs orders of magnitude
faster than a Lush interpreted program. On the other hand, a C
subroutine is also more difficult to write and to debug.
- You might already have developed C subroutines that fit your
specific needs. These subroutines may perform tasks that are not well
suited for the Lush language.
The TL/Open protocol provides tools for interfacing C code with Lush.
These powerful tools allow you to call both your C functions and your C
data structures like ordinary Lush functions and objects.
3.21.0.0.0. Prerequisites
|
|
This documentation is by no means a C manual. You should have a
significant experience of programming in C before attempting to use the
TL/Open protocol.
The Windows version of TL/Open (TL/Open for WinLush) requires the
Microsoft Visual C++ 4.x compiler. Other compilers (e.g. Watcom,
Borland, ...) may be supported in the future.
The Unix version of Lush requires a properly installed ANSI C compiler.
We recommend using the freely available GNU GCC compiler on supported
platforms. We also support the native ANSI C compiler of each platform
when it comes with the operating system.
3.21.0.0.1. How to Read this Section of the Manual
|
|
Chapter "Interfacing a C Function with Lush" gives a step by step recipe
for adding a new primitive function to the Lush interpreter. Although
this chapter does not describe much of the internals of Lush, the
information provided here is sufficient for many users.
Chapter "Lush Internal Architecture" explains a lot of facts about the
Lush internal mechanisms. This background helps understanding the odds
of TL/Open. This information is necessary for accessing most Lush
objects and implementing advanced extensions to the Lush system.
Chapter "TL/Open Examples" comments the examples provided with TL/Open.
Three examples are provided with various levels of complexity.
Chapter "Installing TL/Open Extensions" explains where to install
TL/Open extension in the Lush directories.
3.21.0.1. Interfacing a C Function to Lush
|
|
See: (mod-load dllname )
There are two ways to interface a C function with Lush:
- The
simplest method, namely the Dynamical Link method, consists in having
Lush load your C function at runtime. This manual explains how to
prepare a dynamically linkable file, named Dynamic Link Library (DLL
file) under Windows and Shared Object (SO file) under Unix. You can then
load this file using the Lush function mod-load
and call your functions.
- The most portable method, namely the Static Link method, consists
in creating a new executable file containing the Lush interpreter and
your functions. This possibility is only offered under Unix for backward
compatibility purposes.
This manual focuses on the Dynamical Link method. Additional comments
explain how the Static Link method diverges from the Dynamical Link
method.
3.21.0.1.0. Preparations to Compile C Code
|
|
3.21.0.1.0.0. Setting up a Compilation Project Workspace with Visual C++ (Windows).
|
|
You must start up Microsoft Visual C++ 4.x or 5.x and select the item
New in menu File . The
``document type selection'' window pops up. Select
Project Workspace and click OK
. The ``new project'' window pops up. You must select the ``TLOpen
Wizard'', type the project name and the project directory in the usual
edit fields, and click Create .
Troubleshooting: if you do not find the TLOpen wizard icon in the ``new
project'' window, make sure that TL/Open and Visual C++ have been
properly installed. You may run again the WinLush installation program
and check the installation options.
The TL/Open wizard just displays a confirmation window describing the
project type and locating the various directories containing include
files and libraries. Visual C++ creates and open the project when you
click button OK .
The new project contains just one file named
"user_dll.c" . This skeleton file defines a simple function
hypot which returns the square root of the sum of the squares
of two numbers. is created and Click OK
.
Pressing key F7 builds a dynamical
library file (DLL) in the subdirectory "Debug"
located under your compilation directory. This file is easy to locate
because of the suffix ".DLL" .
You can debug your function by pressing key F5
. Visual C++ asks you the name of the debug executable. You must enter
there the path of the WinLush main executable. This executable is
located in the subdirectory "bin" of
the WinLush installation directory. If you have installed WinLush in the
default location, you must type "c:\program
files\winlush\winlush\bin\winlush.exe" .
If you mistype this filename, Visual C++ will signal an error, but will
no longer offer you to enter the debug executable path by just pressing
key F5 . You will have to change this
filename in the project settings by typing key
Alt-F7 and selecting tab Debug
.
WinLush will eventually start and Visual C++ will turn into debug mode.
Type the following command in the WinLush console:
? (mod-load "<your-dir>/Debug/<your-project-name>.dll")
This command loads the dynamical library and declares the new function
to the Lush interpreter. You can now type:
? (hypot 3 4)
= 5
Using the Visual C++ debugger, you can set breakpoints in the C function
"xhypot" and see the actual computations taking place in your
TL/Open extension.
3.21.0.1.0.1. Setting up a Compilation Directory under Unix (or Cygwin).
|
|
Unix supports dynamic linking as well as static. The dynamic link method
consists in creating a shared object that you can load at runtime into
your version of Lush. The static link method consists in creating a
private executable containing both Lush (or SN28) and your C functions.
[ THIS PART NEED REWRITING ]
STATIC LINK --> Use the source compilation tree,
Install your extensions in file 'user.c'
(equivalent to 'user_dll.c' for shared objects.
DYNAMIC LINK --> Copy template from <"<prefix>/lush/open/unix">.
Follow instructions below.
The template contains a a skeleton file named
"user_dll.c" . This file shows the generic structure of the
Lush extension files. This file defines a simple function
hypot which returns the square root of the sum of the squares
of two numbers.
You can start the compilation by:
% make
This generates a shared library named "module.so"
(or module.sl on HP/UX,
module.dll on Cygwin). You can now run
"lush" and check that function hypot
is not defined :
? hypot
= ()
This can be changed by loading the shared object in the running instance
of Lush:
? (mod-load "module")
Function hypot is now defined as a DX.
? hypot
= ::DX:hypot
? (hypot 3 4)
= 5
3.21.0.1.1. Writing a New Function
|
|
3.21.0.1.1.0. user_dll.c File
|
|
The file "user_dll.c" should be used
as a skeleton for inserting your C functions. In particular, it contains
a function init_user_dll which is
called when Lush loads the dynamical library.
File "user_dll.c" is composed of three
sections:
- The ``header section'' essentially consists of
preprocessor directives for including the file
"header.h" . This file describes all the information required
by the C compiler about the internal data structures of Lush.
/* --- HEADER SECTION ---
* This section loads the appropriate header files
* (WIN32 details are specific to Visual C++ 4.x.)
*/
#include "tlopen.h"
- The ``primitive section'' contains the primitives implemented
by this source file. In file "user_dll.c"
, this section only contains a simple interface function implementing a
new primitive xhypot which computes
the square root if the sum of the squares of two numbers.
/* --- PRIMITIVE SECTION ---
* This section contains your new Lush primitives.
* You may also choose to implement them in auxiliary files.
*/
DX(xhypot)
{
real x, y;
ARG_NUMBER(2);
ALL_ARGS_EVAL;
x = AREAL(1);
y = AREAL(2);
return NEW_NUMBER(sqrt(x*x + y*y));
}
/* --- INITIALISATION SECTION ---
* Function 'init_user_dll' is called when loading extension.
* -- perform your initializations here
* -- allocate and initialize global data structures.
* -- declare your primitives here
*/
int init_user_dll(int major, int minor)
{
/* Check version */
if (major!=TLOPEN_MAJOR || minor<TLOPEN_MINOR)
return -2;
/* Perform initializations */
/* Declare primitives */
dx_define("hypot", xhypot);
return 0;
}
The first two lines test the version of the module against the version
of the Lush interpreter. The macros TLOPEN_MAJOR
and TLOPEN_MINOR represent the version
number of the interpreter for which this module is compiled. The
arguments major and
minor represent the version number of the running
interpreter. The running interpreter must have the same major version
number and a greater or equal minor version number than the interpreter
for which the module is compiled.
A return value of -2 means that the
extension is incompatible with the current version of Lush. The
interpreter then unloads the module and displays an appropriate error
message.
A return value of -1 means that the
initialization failed. The TL/Open extension is then unloaded and an
error is reported. You should print an explanation because the error
message only states that the module initialization failed.
This function must return 0 when the
initialization succeeds.
3.21.0.1.1.1. Where to Write Primitives
|
|
There are in fact two strategies for implementing your primitives.
You may directly insert the C code of your primitives in file
"user_dll.c" . This is handy (only) for testing small
primitives. You may also create a new C file with a convenient name,
like "myfile.c" .
3.21.0.1.1.1.0. Writing Primitives Outside user_dll.c
|
|
First you have to create an alternate file and write your functions. We
suggest you to clearly identify several sections: a header section, a
primitive section and an initialization section.
/* ------ HEADER SECTION
* (copied from user_dll.c)
*/
#include "tlopen.h"
/* ------ PRIMITIVE SECTION
* (write your functions here)
*/
/* ------ INITIALISATION SECTION
* (write your initialization code here)
*/
void init_myfile()
{
}
Second you must ensure that the initialisation function
init_myfile() will be called during the initialisation of
Lush. To achieve this, insert a call to
init_myfile() in the initialisation function of file
"user_dll.c" . Since Lush calls the function
init_user_dll() during the initialisation process, it will
also run init_myfile() .
Third you must update the list of object files into the Makefile:
-
Under Windows, you must add "myfile.c"
in the project using the item ``Files into Project'' of menu ``Insert''.
You can then press key F7 to build
your customized dynamic link library.
- Under Unix, you must insert filename
"myfile.o" after each occurrence of the filename
"user_dll.o" in the file "Makefile"
. You can then invoke the program make
to build your customized shared object.
3.21.0.1.1.2. Checks and Error Handling
|
|
Since you are programming at the C level, no checks are made at run
time. If there is a run time memory integrity error in your function,
Lush crashes. Therefore, it is sound to include multiple checks in your
function. In particular, you must check thoroughly the validity of the
arguments. If you detect an inconsistency, you must call function
error() .
3.21.0.1.1.2.0. error ( char *where , char *text , at *arg )
|
|
The function error() returns the
control to Lush. It prints an error message composed of string
where , string text and
Lush object arg . The default value
for where (used when
where is equal to NIL ) is
the name of your primitive as declared with
dx_define() or dy_define()
.
See: Error Recovery.
See: A Complete TL/Open Example.
3.21.0.1.1.3. C Function Example
|
|
In the following example function, we write a function for capitalizing
the nth character of a string. You can test this example by inserting
this function after the definition of xhypot
in the file "user_dll.c" .
The first call to error() just prints
an error text. The second call to error()
passes the illegal argument as a Lush object. It uses the macro
NEW_NUMBER() which converts a number into a Lush object.
char *capitalize_nth(s,n)
char *s;
int n;
{
static char buffer[256];
char c;
int i;
/* Checks */
i = strlen(s);
if (i>255)
error(NIL,"string is too long",NIL);
if (n<0 || n>=i)
error(NIL,"no nth character", NEW_NUMBER(n));
/* Action */
strcpy( buffer, s);
c = buffer[n];
if (c>='a' && c<='z')
buffer[n] = c+'A'-'a';
/* Return result in a static area */
return buffer;
}
3.21.0.1.2. Writing an Interface Function
|
|
Once the C low-level function is written, you must define an interface
function. The interface function must (a) parse the argument list, (b)
convert the Lush objects to the right C data structures, (c) call the C
function, and (d) convert the return value to a Lush object.
There are two kinds of interface functions in Lush.
- DX
interface functions are easier to use and more powerful. Several macros
are provided to help defining DX interface functions.
- DY interface functions are a low level method frequently used for
implementing control structures, like if
, let , or cond
. They require explicit programming for parsing the argument list.
3.21.0.1.2.0. DX Interface Functions
|
|
DX interface functions are the right choice for most applications.
Several utilities have been designed to help the programmer.
Here is a simple DX interface function for our function
capitalize_nth() . This function is usually inserted just
after the C function itself in the primitive section of your file.
DX(xcapitalize_nth)
{
ARG_NUMBER(2);
ALL_ARGS_EVAL;
return new_string(
capitalize_nth(ASTRING(1),AINTEGER(2)) );
Let us give a few explanations:
- The macro
DX(xcapitalize_nth) generates the header of a DX interface
function whose name is xcapitalize_nth
. Conventionally, the name of the DX interface function is built by
prepending the character 'x' to the name of the function.
- The macro ARG_NUMBER(2) check
that two arguments have been provided. If a different number of
arguments are provided, an appropriate error message is reported to the
user.
- The macro ALL_ARGS_EVAL evaluates
all the arguments.
- The macro ASTRING(1) returns the
first argument as a zero-terminated string of characters. If this
argument is not a Lush string, an appropriate error message is
generated.
- The macro AINTEGER(2) converts
the first argument as an integer. If this argument is not a Lush number,
an appropriate error message is generated.
- The function new_string() creates
a new Lush string.
3.21.0.1.2.0.1. Prototypes
|
|
In fact, the Lush kernel passes an array of Lush arguments to the DX
interface functions. This technique is similar to the
main(argc,argv) method for passing arguments to a C program.
The macro DX() generates the following
header for the DX interface function:
at *xcapitalize_nth(int arg_number,
at **arg_array )
- Argument arg_number contains
the number of Lush arguments.
- Argument arg_array points to the
table of the Lush arguments. The Lush arguments are stored in array
arg_array . The first element of this array (i.e.
arg_array[0] ) is undefined. The effective arguments are
numbered from 1 to
arg_number .
We suggest to always use the macro DX()
for generating this header. Therefore, your programs will resist a
future change in the implementation of these macros.
3.21.0.1.2.0.2. Argument Number Checking
|
|
The macro ARG_NUMBER() is the
preferred way to check the number of arguments, because it generates
standard error messages.
It is possible however to check the number of arguments by testing
directly the value of variable arg_number
. In this case you might call ARG_NUMBER(-1)
to generate a standard error message about the number of arguments.
3.21.0.1.2.0.2.0. ARG_NUMBER( int n )
|
[#define] |
The macro function ARG_NUMBER() checks
that the current DX interface function has been called with exactly
n arguments. It generates an appropriate error message when
this is not true.
3.21.0.1.2.0.3. Argument Evaluation
|
|
When the DX interface function is called, arguments are still
unevaluated. For instance a symbol has not been replaced by its value
yet. Two macros are provided for controlling this evaluation:
3.21.0.1.2.0.3.0. ARG_EVAL( int n )
|
[#define] |
The macro function ARG_EVAL()
evaluates the n th argument of a DX
interface function. It calls the Lush evaluator and plugs the result in
array arg_array .
3.21.0.1.2.0.3.1. ALL_ARGS_EVAL
|
[#define] |
The macro instruction ALL_ARGS_EVAL
evaluates all the arguments of a DX interface function. It calls the
Lush evaluator and plugs the results in array
arg_array .
3.21.0.1.2.0.4. Argument Translation
|
|
The following macro functions translate the i
th argument of a DX as standard C objects. They generate standard error
message if the type of the Lush object is inadequate.
The complex structures are manipulated by using first
APOINTER() to get a Lush object and then by accessing the
fields of the Lush object as described in chapter ``Lush Internals''.
3.21.0.1.2.0.4.0. AREAL( int i )
|
[#define] |
This macro function translates the i
th argument of a DX and returns a real
(usually double ).
3.21.0.1.2.0.4.1. AINTEGER( int i )
|
[#define] |
This macro function translates the i
th argument of a DX and returns a int
. Floating point numbers are rounded to the nearest integer.
3.21.0.1.2.0.4.2. AFLT( int i )
|
[#define] |
This macro function translates the i
th argument of a DX and returns a flt
(usually float ).
3.21.0.1.2.0.4.3. ASTRING( int i )
|
[#define] |
This macro function translates the i
th argument of a DX and returns a zero-terminated array of
char .
3.21.0.1.2.0.4.4. APOINTER( int i )
|
[#define] |
This macro function fetches the i th
argument of a DX and returns a pointer to the Lush object without
checking anything.
3.21.0.1.2.0.4.5. ACONS( int i )
|
[#define] |
This macro function fetches the i th
argument of a DX and checks that this argument is a pointer to a non
empty list.
3.21.0.1.2.0.4.6. ALIST( int i )
|
[#define] |
This macro function translates the i
th argument of a DX and checks that this argument is a pointer to a
possibly empty list.
3.21.0.1.2.0.5. Returning from DX Interface Functions
|
|
A DX interface function must always return a Lush object.
The simplest method consists in using one of the following alternatives:
return NIL;
returns the empty list which usually means ``false''.
return TLtrue();
returns the symbol t which usually means ``true''.
return NEW_NUMBER(real n);
return NEW_NUMBER(int n);
returns a Lush object representing the number n
.
return new_string(char *s);
returns a Lush object representing a copy of the zero terminated string
s .
3.21.0.1.2.0.6. Complex Argument Syntax Parsing
|
|
In certain cases, it is useful to parse optional arguments or to test
the type of the arguments in the DX interface function.
For example, here is an alternate definition of the DX interface
function of our primitive capitalize_nth()
. This new definition checks whether one or two arguments have been
provided. If one argument only is provided, this function capitalizes
the first character of the string.
DX(xcapitalize_nth)
{
char *s;
ALL_ARGS_EVAL;
switch(arg_number) {
case 1:
s = capitalize_nth( ASTRING(1), 0 );
break;
case 2:
s = capitalize_nth( ASTRING(1), AINTEGER(2) );
break;
default:
ARG_NUMBER(-1); /* never return */
}
return new_string(s);
}
This is easily achieved by testing directly the value of
arg_number and by using the following macros.
3.21.0.1.2.0.6.0. ISNUMBER( int i )
|
[#define] |
This macro function tests if the i th
argument of the DX fucntion is a number.
3.21.0.1.2.0.6.1. ISSTRING( int i )
|
[#define] |
This macro function tests if the i th
argument of the DX fucntion is a string.
3.21.0.1.2.0.6.2. ISCONS( int i )
|
[#define] |
This macro function tests if the i th
argument of the DX fucntion is a non empty list.
3.21.0.1.2.0.6.3. ISLIST( int i )
|
[#define] |
This macro function tests if the i th
argument of the DX fucntion is a list.
3.21.0.1.2.1. DY Interface Functions
|
|
We give just a brief explanation about the DY interface functions. A DY
interface function takes a list of Lush objects as unique argument. The
function header of a DY interface function is thus:
at yname(ARG_LIST)
at *ARG_LIST;
This header should be generated using the
DY(yname) macro. Like DX interface functions, DY interface
functions must check the number of arguments, evaluate the arguments,
check the type of the arguments, call the C function and return a Lush
object.
No macro or predefined function have been defined for making this task
easier. Therefore, DY interface functions are rarely used. They are
mostly used for implementing control structures, like
if , let , or
cond .
3.21.0.1.3. New Function Declaration
|
|
Finally, you must declare your interface function to the Lush kernel.
This is achieved by inserting a call to function
dx_define or dy_define in
the initialisation function of your C file.
3.21.0.1.3.0. dx_define( char *name, at *(*xfunc)() )
|
|
This function declares a new primitive named name
to the Lush kernel. This primitive is identified by its DX interface
function xfunc . The function
dx_define must be called during the initialisation process.
This function should not be called twice with the same identifier string
*name .
3.21.0.1.3.1. dy_define( char *name, at *(*yfunc)() )
|
|
This function declares a new primitive named name
to the Lush kernel. This primitive is identified by its DY interface
function yfunc . The function
dy_define must be called during the initialisation process.
This function should not be called twice with the same identifier string
*name .
3.21.0.1.4. Compiling and Debugging
|
|
You can then compile your TL/Open extension using command
make under Unix or key F7 under Visual C++ for Windows.
Please refer to the section describing compilation directories for more
information about running and debugging your code.
See: Setting up a Compilation Project Workspace under
Windows.
See: Setting up a Compilation Directory under Unix.
This part of the documentation reviews several aspects of the
implementation of the Lush interpreter. These aspects are relevant to
the creation of more sophisticated TL/Open extensions.
Here are the main points of this chapter:
- Some elementary C
types are defined in order to improve the portability of the source
code.
- The fundamental type of Lush objects is the
at polymorph structure. Every Lush object is represented by a
pointer to an at structure. Every
at structure contains a reference count. You must be aware of
certain rules for incrementing or decrementing this reference count.
- The at structure sometimes
contains a pointer to another C structure according to the object class:
array, matrix, string, symbol, function. Each class comes with functions
implementing standard functionalities (testing the object type, creating
an object) and specific functions.
- It is possible to define new primitive classes by providing a few
functions for interfacing the Lush interpreter and garbage collecting
system.
3.21.0.2.0. Elementary C Types and Functions
|
|
A few elementary C types are defined by the header file
"header.h" in order to improve the portability of the source
code. They correspond to various C fundamental types which vary from a
machine to another.
3.21.0.2.0.0. Universal Pointer Type
|
|
3.21.0.2.0.0.0. ptr
|
[#define] |
The type ptr is a generic pointer
similar to the ANSI C type void* .
This macro has been defined to accommodate very old C compilers that did
not implement type void* . The habit
of using ptr instead of
void* has survived.
3.21.0.2.0.1. Floating Point Types
|
|
There are two floating point number types in Lush. These types are
usually defined as:
#define real double
#define flt float
Instances of these types can be manipulated by the standard C operators.
The type flt however is better to be
handled by specific macros.
3.21.0.2.0.1.0. real
|
[#define] |
The type real is a floating point type
able to represent double precision numbers. This is defined as
double on most machines available today (Crays are
different!). We recommend to handle this type as a floating point type
distinct from both double and
float . Explicit casts will not hurt.
(double)My_real;
(float)My_real;
(real)My_float;
(real)My_double;
3.21.0.2.0.1.1. flt
|
[#define] |
On modern computers, the type flt is
always defined as float and could be
handled by the usual operators.
There is however a long forgotten possibility to compile a version of
Lush based on fixed point arithmetic. Type flt
is then defined as long and the usual
arithmetic operators do not work. Conscientious programmers therefore
use the following macros for dealing with flt
numbers. It is however unlikely that you will get into trouble if you do
not use them.
3.21.0.2.0.1.2. rtoF(x)
|
[#define] |
The macro function rtoF casts a
real to a flt .
3.21.0.2.0.1.3. dtoF(x)
|
[#define] |
The macro function dtoF casts a
double to a flt .
3.21.0.2.0.1.4. Ftor(x)
|
[#define] |
The macro function Ftor casts a
flt to a real .
3.21.0.2.0.1.5. Ftod(x)
|
[#define] |
The macro function Ftod casts a
flt to a double .
3.21.0.2.0.1.6. Fadd(x,y)
|
[#define] |
The macro function Fadd adds two
flt numbers x and
y .
3.21.0.2.0.1.7. Fsub(x,y)
|
[#define] |
The macro function Fsub subtracts two
flt numbers x and
y .
3.21.0.2.0.1.8. Fmul(x,y)
|
[#define] |
The macro function Fmul multiplies two
flt numbers x and
y .
3.21.0.2.0.1.9. Fdiv(x,y)
|
[#define] |
The macro function Fdiv divides
flt numbers x and
y .
3.21.0.2.0.1.10. Fzero
|
[#define] |
The macro constant Fzero represents
value 0 for flt
numbers.
3.21.0.2.0.1.11. Fone
|
[#define] |
The macro constant Fone represents
value 1 for flt
numbers.
3.21.0.2.0.1.12. Ftwo
|
[#define] |
The macro constant Ftwo represents
value 2 for flt
numbers.
3.21.0.2.0.1.13. Fminus
|
[#define] |
The macro constant Fminus represents
value -1 for
flt numbers.
3.21.0.2.0.1.14. Fhalf
|
[#define] |
The macro constant Fhalf represents
value 0.5 for
flt numbers.
3.21.0.2.0.1.15. Fsqrt(x)
|
[#define] |
The macro function Fsqrt returns the
square root of a flt number
x .
3.21.0.2.0.1.16. Fsin(x)
|
[#define] |
The macro function Fsin returns the
sinus of a flt number
x .
3.21.0.2.0.1.17. Fcos(x)
|
[#define] |
The macro function Fcos returns the
cosinus of a flt number
x .
3.21.0.2.0.1.18. Ftan(x)
|
[#define] |
The macro function Ftan returns the
tangent of a flt number
x .
3.21.0.2.0.1.19. Fatn(x)
|
[#define] |
The macro function Fatn returns the
arc-tangent of a flt number
x .
3.21.0.2.0.1.20. Fsinh(x)
|
[#define] |
The macro function Fsinh returns the
hyperbolic sine of a flt number
x .
3.21.0.2.0.1.21. Fcosh(x)
|
[#define] |
The macro function Fcosh returns the
hyperbolic cosine of a flt number
x .
3.21.0.2.0.1.22. Ftanh(x)
|
[#define] |
The macro function Ftanh returns the
hyperbolic tangent of a flt number
x .
3.21.0.2.0.1.23. Flog(x)
|
[#define] |
The macro function Flog returns the
natural logarithm of a flt number
x .
3.21.0.2.0.1.24. Fexp(x)
|
[#define] |
The macro function Fexp returns the
exponential of a flt number
x .
3.21.0.2.0.2. Input/Output Functions
|
|
The following functions are useful to print data to the console or to a
file and to read data from the console or from a file. These functions
convert newline characters to whatever representation is usual under the
operating system (CRLF, CR or LF).
3.21.0.2.0.2.0. void print_char (char c)
|
|
This function prints character c on
the current output file defined by the Lush interpreter.
3.21.0.2.0.2.1. void print_string(char *s)
|
|
This function prints the zero terminated character string
s on the current output file defined by the Lush interpreter.
3.21.0.2.0.2.2. char read_char(void)
|
|
This function reads the next character available on the current input
file defined by the Lush interpreter. This function returns character
"\e" (ascii code 255) when the end of the file is reached.
3.21.0.2.0.2.3. char next_char(void)
|
|
This function returns the next character available on the current input
file defined by the Lush interpreter. This function returns character
"\e" (ascii code 255) when the end of the file is reached.
Unlike function read_char , this
function leaves the character on the pending character queue. The next
call to read_char or
next_char will return the same character.
3.21.0.2.1.0. at Fundamental Structure
|
|
The fundamental data type of the Lush interpreter is a structure named
at . All Lush objects are represented by pointer of type
at* . This structure provides fields describing both the type
and the data associated with a Lush object. It also stores informnation
used by the garbage collector.
Here is the C definition of the type at
.
typedef struct at; {
unsigned int count;
unsigned short flags;
unsigned short ident;
union {
struct { /* cons */
struct at *at_car;
struct at *at_cdr;
} at_cons;
real at_number; /* number */
struct { /* external */
ptr at_object;
struct class *at_class;
} at_extern;
} at_union;
} at;
In other words, the at structure
contains a reference counter count , a
couple of flags flags and a three way
union named at_union .
- The
empty list is represented by pointer NIL
whose value is 0 .
- A Lush number is represented by a pointer p
to an at structure. Its numerical
value is stored in the field:
p->at_union.at_number
- A list is also represented by a pointer
p to an at structure. The
``car'' and the ``cdr'' of the list are stored in the fields:
p->at_union.at_cons.at_car
p->at_union.at_cons.at_cdr
- All other Lush objects are represented by a pointer to an at
structure containing a pointer to the object class and a pointer to the
object instance. Matrices, windows, functions and symbols are good
examples of such external objects. The class and instance pointers are
stored in fields:
p->at_union.at_extern.at_class
p->at_union.at_extern.at_object
In order to simplify programming, a few shorthands have been defined.
For instance, you can write p- Car>
instead of p-
at_union.at_cons.at_car>.
#define Number at_union.at_number
#define Cdr at_union.at_cons.at_cdr
#define Car at_union.at_cons.at_car
#define Object at_union.at_extern.at_object
#define Class at_union.at_extern.at_class
3.21.0.2.1.1. Generic Operations on Lush Objects
|
|
The Lush kernel provides utility functions that performs the same task
than several primitive functions of the interpreter. The prototypes can
be found in the TL/Open header file "header.h"
.
We document here a few utility functions that are useful for all Lush
objects regardless of their type:
3.21.0.2.1.1.0. char *print_list(at *p)
|
|
This function prints a textual representation of object
p to the current output defined by the Lush interpreter.
Circular references are detected. This is similar to the Lush function
prin .
3.21.0.2.1.1.1. char *pname(at *p)
|
|
This function returns a pointer to a static area containing a textual
representation of object p . The size
of the textual representation is limited to a few lines. This is similar
to the Lush function pname .
3.21.0.2.1.1.2. at *read_list(void)
|
|
This function parses the textual representation of a lisp object on the
current input file and returns a new lisp object This is similar to the
Lush function read .
3.21.0.2.1.1.3. int eq_test(at *p, at *q)
|
|
This function returns a non zero result if the Lush objects
p and q are logically
equal. This is similar to the Lush function =
.
3.21.0.2.1.1.4. int comp_test(at *p, at *q)
|
|
This function compares objects p and
q and returns -1 is
p is less than q ,
0 if p is greater than
q , and 0 if
p and q are logically
equal. This function causes an error if objects p
and q cannot be compared. This is
similar to the Lush comparison functions.
3.21.0.2.2. Garbage Collection
|
|
An important feature of the Lush kernel is its ability to recycle
perempted Lush objects. This feature is called ``Garbage Collection''.
Lush relies two very simple garbage collection strategies.
- The
basic garbage collection is light and fast. It relies on a reference
counter stored in field count of each
at structure. This basic algorithm is unable to reclaim
memory used by self-referencing objects. Its operation requires that all
C functions dealing with lisp object comply with a few simple locking
conventions.
- The full garbage collection occurs whenever a Lush error is
triggered. This algorithm reclaims the memory used by all
pepreemptedbjects, recomputes the counters of all objects in use, and
checks the consistency of all Lush data structure.
3.21.0.2.2.0. Managing Counters in an at Structure
|
|
Every at structure contains a
reference count which records how many pointers to this Lush object are
held by other Lush objects, by active C functions, or by the Lush kernel
itself. This counter is manipulated by the LOCK()
and UNLOCK() macros. This locking
mechanism is the basis of the basic garbage collecting system. It relies
on a set of conventions which allow the garbage collector to work.
3.21.0.2.2.0.0. LOCK(at *p)
|
[#define] |
This macro increases the counter of the at structure
*p by one.
For your information, here is the full definition of this macro:
#define LOCK(x) {if (x) (x)->count++;}
3.21.0.2.2.0.1. UNLOCK(at *p)
|
[#define] |
This macro decrease this counter of the at structure
*p by one. If the counter reaches zero, the object is
destroyed and its memory is returned to a free memory pool.
For your information, here is the full definition of this macro:
#define UNLOCK(x) {if ((x)&&--((x)->count)==0) purge(x);}
3.21.0.2.2.1. Locking Conventions
|
|
These conventions have been designed in such a way that you should not
worry about them if you are only browsing Lush objects. You must however
strictly obey these conventions when creating, modifying or destroying
Lush objects.
There are three conventions
- [a] arguments,
- [b] adding or deleting pointers and
- [c] return values
and one important exception
- [d] function
cons() is special.
3.21.0.2.2.1.0. Locking Convention [a]: Arguments
|
|
The reference count of the arguments of a C function should not be
changed, unless the conditions of conventions [b] or [c] are met.
3.21.0.2.2.1.1. Locking Convention [b]: Adding or Deleting Pointers
|
|
Every C function calls LOCK() or
UNLOCK() on a Lush object if it directly affects the number
of pointers held by other Lush objects.
This happens when you change the value of a pointer managed by another
Lush object. For example, if you change the value of the pointer to the
``Car'' of a list, you must lock the new value and unlock the old value.
void myrplaca(p,q)
at *p, *q;
{
LOCK(q);
UNLOCK(p->Car);
p->Car = q;
}
However, if you affect this pointer via another C function, like
rplaca() , the reference count will be managed by this other
C function.
at *myrplaca(p,q)
at *p, *q;
{
return rplaca(p,q);
}
3.21.0.2.2.1.2. Locking Convention [c]: Return Values
|
|
A C function which returns a pointer to an at
structure always returns a locked Lush object.
This rule has two sides:
- If you call a function returning a
pointer to an at structure, you must
consider this pointer as a ``hot'' potatoe. You must return it or unlock
it by hand. Once you have unlocked this Lush object you must stop
accessing it, because it may have been destroyed during the unlock
operation.
- If you write a function returning a pointer to an
at structure, you must either return a pointer returned by
another function (ie. already locked) or exexplicitlyock the pointer.
Therefore, it is safe to return directly a pointer returned by another C
function:
at *abc()
{
return new_string("abc");
}
In all other cases, you must lock this pointer by hand.
at *myidentity(p)
at *p;
{
LOCK(p);
return p;
}
at *mycar(p)
at *p;
{
at *q = p->Car;
LOCK(q);
return q;
}
3.21.0.2.2.1.3. Locking Convention [d]: Function cons() Is Special
|
|
The regular function new_cons()
creates a new dotted pair and handles the reference counters according
to the usual conventions. A special function
cons() also creates a new dotted pair, but it does not lock
its arguments as required by convention [b].
This exception is designed to overcome a potential inefficiency of
convention [c] which leads to unnecessary lock/unlock sequences. For
instance, the following function creates the list
(1 2 3 4) with the regular function
new_cons() :
at *ret1234a()
{
at *c1, *c2, *c3, *c4;
at *n1, *n2, *n3, *n4;
n1 = NEW_NUMBER(1);
n2 = NEW_NUMBER(2);
n3 = NEW_NUMBER(3);
n4 = NEW_NUMBER(4);
c4 = new_cons(n4, NIL);
UNLOCK(n4); /* hot and no longer used */
c3 = new_cons(n3, c4);
UNLOCK(n3); UNLOCK(c4); /* hot and no longer used */
c2 = new_cons(n2, c3);
UNLOCK(n2); UNLOCK(c3); /* hot and no longer used */
c1 = new_cons(n1, c2);
UNLOCK(n1); UNLOCK(c2); /* hot and no longer used */
return c1; /* already locked */
}
The same task is easily achieved with the function
cons() , because the at
structures returned by the macro NEW_NUMBER()
are already locked.
at *ret1234b()
{
return cons(NEW_NUMBER(1),
cons(NEW_NUMBER(2),
cons(NEW_NUMBER(3),
cons(NEW_NUMBER(4), NIL ))));
}
3.21.0.2.2.2. Error Recovery
|
|
Each time a Lush error occurs, the stack is rewound and the counters set
by locking convention [c] are no longer exact. When you exit the ``debug
toplevel'' an alternate mechanism resets all the reference counts to
their right values.
This alternate mechanism finds the set of used objects, invokes the
destructors on unneeded objects, computes the correct reference counts,
returns the unused memory to the pool and finally calls Lush function
toplevel to reenter the interpreter interactive loop.
See: Locking Conventions.
See: error ( char *where , char *text , at *arg )
3.21.0.2.2.3. Finding Locking Bugs
|
|
Locking conventions are the major cause of bugs in Lush. Two kind of
bugs occurs when the locking conventions are violated:
- An
overlock occurs when a reference count is larger than the correct value.
The memory associated to an overlocked object will not be freed until a
Lush error occurs.
This condition has little consequence during interactive sessions
because Lush errors always occur from time to time. The error garbage
collector will then recompute the counters and clear the problem. An
overlock however may cause to a memory overflow when a Lush program runs
continuously.
- An underlock occurs when a reference count is smaller than the
correct value. The memory associated to an underlocked object will be
returned to the pool too early.
Unlike overlocks, underlocks always cause to deadly bugs involving the
corruption of the pool. Lush might crash when you invoke your new
primitive or crash later when it tries to print the underlocked object.
Sometimes it does not crashes, but performs random changes in the
existing Lush structures.
It is therefore important to check the validity of your C functions as
early as possible. As a rule of thumb, you should rather risk an
overlock than an underlock. You can then use the Lush function
used to identify the potential overlock. Here is a simple
procedure:
(A) Causing an error will invoke the error garbage collector and make
sure that all counters are correct at this point.
? (())
*** read : Cannot evaluate list
** in: (load "$stdin" "$stdout")
** from: (let ((break-hook (lambda () (beep) ...
Debug toplevel [y/n] ?n
You can then count the number of Lush objects minus the number of
symbols, run your new primitive function, and count again the number of
Lush objects minus the number of symbols.
? (- (used) (length (oblist)))
= 15843
? (my-beautiful-c-function)
= t
? (- (used) (length (oblist)))
= 15847
Changes represent objects that have been created or destroyed by your
function. You can check that this new count is correct by causing a new
error.
? (())
*** read : Cannot evaluate list
** in: (load "$stdin" "$stdout")
** from: (let ((break-hook (lambda () (beep) ...
Debug toplevel [y/n] ?n
? (- (used) (length (oblist)))
= 15847
You must get *exactly* the same number here.
- An overlock in
your function would leave unneeded objects. Since the error garbage
collector destroys these objects, the new object count would be smaller.
- An underlock in your function would prematurely destroy objects
referenced elsewhere. Lush would probably crash during the execution of
the error garbage collector.
Note: Function used returns the number
of currently used Lush objects. The error garbage collector however
destroys all unreferenced symbols whose value is
() . We subtract the number of symbols from the number of
objects in order to obtain a quantity that do not depend on this
process.
3.21.0.2.3. Lists/Numbers/External Objects
|
|
As previously introduced, elementary Lush objects are lists and numbers.
These objects are entirely contained in the at
structure. The other objects are called ``external'' because they need
more memory than an at structure can
provide. The at structure points to a
memory block located outside the at
structure.
Lists are a collection of linked pairs composed of
- the
``Car'', a pointer to the first element of the list,
- the ``Cdr'', a pointer the list of the remaining elements.
As described above, this information is directly stored in two fields of
the at structure located in the branch at_cons
of the union at_union .
3.21.0.2.3.0.0. Predicates
|
|
Two macros have been defined to test if an at*
pointer references a list.
3.21.0.2.3.0.0.0. int CONSP(at *p )
|
[#define] |
The macro CONSP() returns
1 if and only if the pointer p
references a non empty list. It indicates that you can access the fields
p- Car> and p- Cdr>.
3.21.0.2.3.0.0.1. int LISTP(at *p )
|
[#define] |
The macro NEW_CONSP() returns
1 if the pointer p
references a possibly empty list. A positive answer does not allow you
to access the fields p- Car> and
p- Cdr>, because p might
be pointer NIL .
As stated above, you must read section ``Locking Conventions'' before
attempting to alter the contents of the fields p-
Car> and p- Cdr>. You can
however safely read these fields and follow the list structure. For
instance you can write directly:
/* This code loops on the elements of a list <p> */
while (CONSP(p)) {
at *q = p->Car;
/* q now points to the current element of the list */
p = p->Cdr;
}
See: Locking Conventions.
There are two functions for creating a list element:
cons() and new_cons() .
Both functions are essentially similar to the Lush function
cons . These functions differ only as described in section
``Locking Conventions''.
- The function
new_cons() follows the usual locking conventions.
- The function cons() is often used
for efficiency reasons. As described in section ``Locking Conventions'',
this function does not affect the reference counts of
p and q .
3.21.0.2.3.0.2.0. at *new_cons(at *p, at *q )
|
|
The function new_cons() returns a new
list by prepending element p in front
of list q . The function
new_cons() follows the usual locking conventions.
3.21.0.2.3.0.2.1. at *cons(at *p, at *a )
|
|
See: Locking Conventions.
The function cons() returns a new list
by prepending element p in front of
list q . The function
cons() is often used for efficiency reasons. As described in
section ``Locking Conventions'', this function does not affect the
reference counts of p and
q .
3.21.0.2.3.0.3. Utilities
|
|
Two functions rplaca() and
rplacd() are provided for changing the pointers and adjust
the reference counts at the same time.
Both functions return a new pointer to a structure
at . According to the locking conventions described in
section ``Locking Conventions'', this pointer is ``hot'': you must
return it or unlock it with the UNLOCK()
macro.
3.21.0.2.3.0.3.0. at *rplaca(at *p, at *q )
|
|
The function rplaca replaces the
``Car'' of the list element p by the
object pointed to by q . It does not
check its arguments but adjusts the reference counters and possibly
frees the memory associated to the previous ``Car'' of list
p .
3.21.0.2.3.0.3.1. at *rplacd(at *p, at *q )
|
|
The function rplacd replaces the
``Cdr'' of the list element p by the
object pointed to by q . It does not
check its arguments but adjusts the reference counters and possibly
frees the memory associated to the previous ``Cdr'' of list
p .
This section describes the internal representation of the numbers used
by the Lush interpreter. A Lush number is represented by a pointer to an
at structure. Its value is stored in the member
at_number of the union at_union
.
3.21.0.2.3.1.0. Predicates
|
|
Two macros have been defined to test if an at*
pointer references a list.
3.21.0.2.3.1.0.0. int NUMBERP(at *p )
|
[#define] |
The macro function NUMBERP() returns 1
if and only if the pointer p
references a number. It indicates that you can access the fields
p->Number.
As stated above, you must read section ``Locking Conventions'' before
attempting to alter the contents of the fields p-
Car> and p- Cdr>. You can
however safely read these fields and follow the list structure. For
instance you can write directly:
/* This code loops on the elements of a list <p> */
while (CONSP(p)) {
at *q = p->Car;
/* q now points to the current element of the list */
p = p->Cdr;
}
See: Locking Conventions.
There are two functions for creating a list element:
cons() and new_cons() .
Both functions are essentially similar to the Lush function
cons . These functions differ only as described in section
``Locking Conventions''.
- The function
new_cons() follows the usual locking conventions.
- The function cons() is often used
for efficiency reasons. As described in section ``Locking Conventions'',
this function does not affect the reference counts of
p and q .
3.21.0.2.3.1.2.0. at *new_number(real x)
|
|
The function new_number() returns a
new Lush number representing number x
.
3.21.0.2.3.1.2.1. at *NEW_NUMBER(x)
|
[#define] |
The macro function NEW_NUMBER returns
a new Lush number representing number x
. This macro casts number x to type
real and calls new_number()
. This macro should be always prpreferrednless a function pointer is
required.
3.21.0.2.3.2. External Objects
|
|
All the other Lush objects are represented by an
at* pointer p to an
at structure whose field p-
Object> contains a void* pointer to
an arbitrary C structure. Such objects are called ``external objects''.
Therefore, any C structure can be made visible from the Lush level as an
external object. This powerful feature raises two problems:
- It
is necessary to be able to check the type of a Lush object. There is
little use in having an lisp object pointer p
when we do not know what kind of C structure is pointed to by the field
p- Object>.
- The interpreter should be able to perform several essential actions
on external objects, like evaluating an object, printing an object or
interacting with the garbage collector.
Therefore field p- Class> of the
structure points to a structure class. There is a single structure class
for each kind of external object.
- For checking the type of an
external object, it is sufficient to test that p-
Class> points to the right structure class.
- The structure class itself describes how the the external object
interacts with the Lush interpreter.
Sections ``Arrays and Matrices'', ``Strings'', ``Symbols'' and
``Functions'' describe several kinds of external objects. Moreover, the
TL/Open protocol allows you to define new Lush objects by defining a new
structure class. This is described in section ``User Defined Classes''.
3.21.0.2.3.2.0. Predicates
|
|
Two macros have been defined to test if an at*
pointer references a list.
3.21.0.2.3.2.0.0. int EXTERNP(at *p, class *someclass )
|
[#define] |
The macro EXTERNP() returns
1 if and only if the pointer p
references an external object of class someclass
. It indicates that you can access the field p-
Object>.
As stated above, you must read section ``Locking Conventions'' before
attempting to alter the contents of the fields p-
Car> and p- Cdr>. You can
however safely read these fields and follow the list structure. For
instance you can write directly:
/* This code loops on the elements of a list <p> */
while (CONSP(p)) {
at *q = p->Car;
/* q now points to the current element of the list */
p = p->Cdr;
}
See: Locking Conventions.
There are two functions for creating a list element:
cons() and new_cons() .
Both functions are essentially similar to the Lush function
cons . These functions differ only as described in section
``Locking Conventions''.
- The function
new_cons() follows the usual locking conventions.
- The function cons() is often used
for efficiency reasons. As described in section ``Locking Conventions'',
this function does not affect the reference counts of
p and q .
3.21.0.2.3.2.2.0. at *new_extern(class *cl , void *pt)
|
|
The function new_extern() returns a
new external object, using a pointer cl
to a class structure and a pointer pt
to an arbitrary C structure. Of course, the type of the C structure must
match the class structure cl .
3.21.0.2.4. Arrays/Matrices
|
|
Arrays and matrices are implemented as external objects. The
p- Object> field always points to a C structure of type
struct array described below.
There are several kind of array or matrices:
- An array contains
at* pointers to other Lush objects. It is associated to the
class structure array_class .
- A single precision matrix contain flt
numbers, usually float numbers. It is
associated to the class structure matrix_class
.
- A double precision matrix contain real
numbers usually double numbers. It is
associated to the class structure dmatrix_class
.
- An integer matrix contain int
numbers. It is associated to the class structure
imatrix_class .
- A short integer matrix contain short
numbers. It is associated to the class structure
smatrix_class .
- A byte matrix contain unsigned char
numbers. It is associated to the class structure
bmatrix_class .
- A packed matrix contains small numbers encoded on a single byte. It
is associated to the class structure
pmatrix_class .
Two macros have been defined to test if an at*
pointer references a list.
As stated above, you must read section ``Locking Conventions'' before
attempting to alter the contents of the fields p-
Car> and p- Cdr>. You can
however safely read these fields and follow the list structure. For
instance you can write directly:
/* This code loops on the elements of a list <p> */
while (CONSP(p)) {
at *q = p->Car;
/* q now points to the current element of the list */
p = p->Cdr;
}
3.21.0.2.4.1.0. "struct array" Structure
|
|
Consider an at* pointer
p which references a Lush array or a Lush matrix. The
corresponding C structure can be accessed as:
struct array *arr = (struct array*)(p->Object);
This structure contains all information relevant to the array or matrix.
Here are the main fields of this structure:
short ndim;
int dim[MAXDIMS];
int modulo[MAXDIMS];
ptr data;
The arr- ndim> field contains the
number of dimensions. The sizes of dimensions are stored as
arr- dim[0], ... , arr->dim[ndim-1]>.
The arr- data> field points to a
contiguous zone of memory. This pointer is a generic
ptr pointer and must be cast to the appropriate type.
at **x = (at **)(arr->data) /* array */
flt *x = (flt *)(arr->data) /* single matrix */
int *x = (int *)(arr->data) /* integer matrix */
int *x = (short*)(arr->data) /* short integer matrix */
real *x = (real *)(arr->data) /* double matrix */
unsigned char *x = (unsigned char*)(arr->data) /* byte matrix */
char *x = (char *)(arr->data) /* packed matrix */
The elements of the array or matrix can then be read or stored as
x[offset] . Offsets are computed using the information
located in the arr- modulo> field.
This is described in the next subsection.
Accessing the elements of an array or of a packed matrix need some
further operations:
- While accessing an array, you get Lush
objects. According to the locking conventions, you must lock the new
object and unlock the old object before modifying an element of an
array.
- The elements of a packed matrix are small numbers encoded on single
bytes. The function pack() converts a
real to a byte. The function unpack()
performs the opposite conversion.
3.21.0.2.4.1.0.0. real unpack( unsigned char c )
|
|
The function unpack converts a
real to a byte. It is used to read packed matrices.
3.21.0.2.4.1.0.1. unsigned char pack( real x )
|
|
The function pack converts a byte to a
real . It is used to write packed matrices.
3.21.0.2.4.1.1. Computing Offsets
|
|
The offsets are computed using the following rules.
- The offset
of element A(0,...,0) is 0.
- The offset of the element
A(i[0]...i[k]+1...i[ndim-1]) is obtained by adding
arr- modulo[k]> to the offset of element
A(i[0],...,i[ndim-1]) .
- The offset of the element
A(i[0]...i[k]-1...i[ndim-1]) is obtained by subtracting
arr- modulo[k]> to the offset of element
A(i[0],...,i[ndim-1]) .
The offset of a random element A(i[0],...,i[ndim-1]) is thus given by
the following multiplicative formula:
i[0] * arr->modulo[0] + ...
... + i[ndim-1] * arr->modulo[ndim-1]
This later expression is a rather inefficient access method. In most
cases, you just want to iterate over certain dimensions of the matrix.
You can perform efficient iterations on Lush matrices using additive
arithmetic. The following fragment of code, for instance, clears the
second plane of a three dimensional matrix:
int i,j,off1,off2;
flt *data = (flt*)(arr->data);
for( i=0, off1=2*arr->modulo[0]; /* first element of second plane */
i<arr->dim[1];
i++, off1+=arr->modulo[1] ) /* move OFF1 to next line */
{
for( j=0, off2=off1; /* first element of line */
j<arr->dim[2];
j++, off2+=arr->modulo[2] ) /* move OFF2 to next line */
{
data[off2] = Fzero;
}
}
The modulo (and so the offsets) of an array or a matrix may be negative.
Negative modulos are used when you want to access the same data in a
reverse order. They are needed for example if you want to rotate an
image of a quarter of a turn without duplicating it. They are
effectively used by function rotate .
Therefore you should make sure that your C functions properly handle
negative modulos. The majority of problems occurs in code looping over
the elements of one of several matrices. Certain loops depend on a
stopping criterion based on pointer or offset inequalities (e.g.
for(p=start; p<start+len; p+=modulo)>) and therefore do not
support negative modulos. Most programmers however will find that
nothing has to be changed in their code.
See: Locking Conventions.
There are two functions for creating a list element:
cons() and new_cons() .
Both functions are essentially similar to the Lush function
cons . These functions differ only as described in section
``Locking Conventions''.
- The function
new_cons() follows the usual locking conventions.
- The function cons() is often used
for efficiency reasons. As described in section ``Locking Conventions'',
this function does not affect the reference counts of
p and q .
3.21.0.2.4.2.0. at *array(int ndim, int dim[])
|
|
The function array() returns a pointer
to a Lush array with ndim dimensions of sizes
dim[0],...,dim[n-1] . This function is similar to the Lush
function array .
3.21.0.2.4.2.1. at *fmatrix(int ndim, int dim[])
|
|
The function fmatrix() returns a
pointer to a Lush single precision matrix with
ndim dimensions of sizes
dim[0],...,dim[n-1] . This function is similar to the Lush
function matrix .
3.21.0.2.4.2.2. at *imatrix(int ndim, int dim[])
|
|
The function imatrix() returns a
pointer to a Lush integer matrix with ndim
dimensions of sizes dim[0],...,dim[n-1]
. This function is similar to the Lush function
imatrix .
3.21.0.2.4.2.3. at *smatrix(int ndim, int dim[])
|
|
The function smatrix() returns a
pointer to a Lush short integer matrix with ndim
dimensions of sizes dim[0],...,dim[n-1]
. This function is similar to the Lush function
smatrix .
3.21.0.2.4.2.4. at *dmatrix(int ndim, int dim[])
|
|
The function dmatrix() returns a
pointer to a Lush double precision matrix with
ndim dimensions of sizes
dim[0],...,dim[n-1] . This function is similar to the Lush
function dmatrix .
3.21.0.2.4.2.5. at *pmatrix(int ndim, int dim[])
|
|
The function pmatrix() returns a
pointer to a Lush packed matrix with ndim
dimensions of sizes dim[0],...,dim[n-1]
. This function is similar to the Lush function
bmatrix .
3.21.0.2.4.2.6. at *submatrix(at *base, int mind[], int maxd[])
|
|
The function submatrix() creates a
submatrix or subarray of a given mother array or matrix.
The tables mind[] and
maxd[] specify, for each dimension, the limits of the part of
the array or matrix mother referred to by the sub-matrix or sub-array.
If mind[k] is stricly lower than
maxd[k] , the submatrix starts on plane
mind[k] and finishes on plane
maxd[k]-1 in the k -th
dimension. This is equivalent to specify a list
(mind[k] maxd[k]-1) as the k
-th subscript specification of the Lush function
submatrix .
If mind[k] is equal to
maxd[k] , the submatrix is restricted to plane
mind[k] in the k -th
dimension. This is equivalent to specify the number
mind[k] as the k -th
subscript specification of the Lush function
submatrix .
3.21.0.2.4.2.7. at *copy_matrix(at *origin, at *target)
|
|
The funcrtion copy_matrix() copies the
contents of a source matrix or array origin
into a destination matrix or array target
. The source and destination matrices or arrays may have different types
but must have the same geometry.
If argument target is equal to NIL, a
destination matrix of appropriate size is created. This function always
returns the destination matrix.
3.21.0.2.4.2.8. at *copy_any_matrix(at *origin, at *target)
|
|
The function copy_any_matrix is
essentially similar to copy_matrix() .
It is able however to copy data between two matrices or array of
different geometry, provided that they have the same number of elements.
Two functions rplaca() and
rplacd() are provided for changing the pointers and adjust
the reference counts at the same time.
Both functions return a new pointer to a structure
at . According to the locking conventions described in
section ``Locking Conventions'', this pointer is ``hot'': you must
return it or unlock it with the UNLOCK()
macro.
3.21.0.2.4.3.0. struct array *check_vector(at *p, int *n )
|
|
The function check_vector() checks
that the Lush object p is a
one-dimensional matrix of flt numbers. It compares then the size of
p with the integer *n .
-
If *n is equal to zero,
check_vector() stores the size of p
into *n .
- If *n is greater than zero,
check_vector() compares the size of p
with *n and signals an error if they
are different.
Finally, this function returns a pointer to the structure array of
p .
3.21.0.2.4.3.1. struct array *check_matrix(at *p, int *m, int *n)
|
|
The function check_matrix first checks
that the Lush object p is a
two-dimensional matrix of flt numbers.
It compares then the sizes of p with
the integers *m and
*n .
- If either *m
or *n is equal to zero,
check_matrix() stores the corresponding size of
p into *m or
*n .
- If either *m or
*n is greater than zero,
check_matrix() compares the corresponding size with
*m or *n and signals an
error if they are different.
Finally, this function returns a pointer to the structure array of
p .
3.21.0.2.4.3.2. Complete Example of Matrix Computation
|
|
Here is a listing of the C code for the Lush function
m+m (matrix addition). This is a good example of using
check_matrix() for checking the arguments and filtering the
dimensions:
at *
maddm(v1, v2, ans)
at *v1, *v2, *ans;
{
int n, m, i, j;
flt *f1, *f2, *fa, *ff1, *ff2, *ffa;
struct array *vv1, *vv2, *vva;
/* get the structure array for v1 and v2 */
n = m = 0;
vv1 = check_matrix(v1, &n, &m);
vv2 = check_matrix(v2, &n, &m);
/* create ans if NIL is provided */
if (ans)
LOCK(ans); /* because we return ans */
else {
int dim[2]; dim[0]=n; dim[1]=m;
ans = matrix(2,dim);
}
/* get the structure array for ans */
vva = check_matrix(ans, &n, &m);
/* loop */
for( j=0, ff1 = vv1->data,
ff2 = vv2->data,
ffa = vva->data;
j<n;
j++, ffa += vva->modulo[0],
ff1 += vv1->modulo[0],
ff2 += vv2->modulo[0] )
{
for( i=0, f1 = ff1,
f2 = ff2,
fa = ffa;
i<m;
i++, fa += vva->modulo[1],
f1 += vv1->modulo[1],
f2 += vv2->modulo[1] )
{
*fa = Fadd(*f1, *f2);
}
}
/* return */
return ans;
}
3.21.0.2.4.4. Numerical Recipes Interface
|
|
Several functions help interfacing Lush with C routines using the vector
and matrix formats suggested by the well known book "Numerical Recipes
in C" (Press et al.). A TL/Open example shows how to interface various
routines published in this book to the Lush interpreter.
The pointer returned by these functions is stored in the
struct array structure and is used by Lush. Therefore, it
should not be altered. Of course this restriction deals with the
pointers themselves, and do not restrict the usage of the contents of
the matrices they address.
In "Numerical Recipes", a vector is represented by a pointer
u to an array of float ,
double , int or
unsigned char . The first element is most often referred to
as u[1] . The functions prefixed with
get_nr1_XXXvector convert a Lush matrix into such a vector.
In "Numerical Recipes", a matrix is represented by a pointer
u to an array of pointers to an array of
float , int or
double . The first element of the first row of a matrix
u is referred to as u[1][1]
. The functions get_nr1_XXX convert a
Lush matrix into such a matrix.
The functions get_nr0_XXX are similar
functions except the subscripts of the vectors and matrices they return
range between 0 and
n-1 instead of 1 and
n .
3.21.0.2.4.4.0. float *get_nr1_vector(at *p, int *n )
|
|
This function checks that Lush object p
is a one dimensional single precision matrix (usually created with
function matrix .) It also checks that
the elements are contiguous and returns a pointer under the Numerical
Recipes format. It handles argument n
like function check_vector .
Following the standard representation of matrices and vectors in
``Numerical Recipes'', the subscripts of the returned pointer range
between 1 and n
.
3.21.0.2.4.4.1. double *get_nr1_dvector(at *p, int *n )
|
|
This function checks that Lush object p
is a one dimensional double precision matrix (usually created with
function dmatrix ). It also checks
that the elements are contiguous and returns a pointer under the
Numerical Recipes format. It handles argument n
like function check_vector .
Following the standard representation of matrices and vectors in
``Numerical Recipes'', the subscripts of the returned pointer range
between 1 and n
.
3.21.0.2.4.4.2. int *get_nr1_ivector(at *p, int *n )
|
|
This function checks that Lush object p
is a one dimensional integer matrix (usually created with function
imatrix ). It also checks that the elements are contiguous
and returns a pointer under the Numerical Recipes format. It handles
argument n like function
check_vector .
Following the standard representation of matrices and vectors in
``Numerical Recipes'', the subscripts of the returned pointer range
between 1 and n
.
3.21.0.2.4.4.3. short *get_nr1_svector(at *p, int *n )
|
|
This function checks that Lush object p
is a one dimensional short integer matrix (usually created with function
smatrix ). It also checks that the elements are contiguous
and returns a pointer under the Numerical Recipes format. It handles
argument n like function
check_vector .
Following the standard representation of matrices and vectors in
``Numerical Recipes'', the subscripts of the returned pointer range
between 1 and n
.
3.21.0.2.4.4.4. unsigned char *get_nr1_bvector(at *p, int *n )
|
|
This function checks that Lush object p
is a one dimensional byte matrix (usually created with function
bmatrix ). It also checks that the elements are contiguous
and returns a pointer under the Numerical Recipes format. It handles
argument n like function
check_vector .
Following the standard representation of matrices and vectors in
``Numerical Recipes'', the subscripts of the returned pointer range
between 1 and n
.
3.21.0.2.4.4.5. float **get_nr1_matrix(at *p, int *m, int *n)
|
|
This function checks that Lush object p
is a two dimensional single precision matrix (usually created with
function matrix .) It also checks that
the elements of the rows are contiguous and returns a pointer to the
data under the Numerical Recipes format. It handles arguments
n and m like function
check_matrix .
Following the standard representation of matrices and vectors in
``Numerical Recipes'', the subscripts of the returned pointer range
between 1 and n
.
3.21.0.2.4.4.6. double **get_nr1_dmatrix(at *p, int *m, int *n)
|
|
This function checks that Lush object p
is a two dimensional double precision matrix (usually created with
function dmatrix ). It also checks
that the elements of the rows are contiguous and returns a pointer to
the data under the Numerical Recipes format. It handles arguments
n and m like function
check_matrix .
Following the standard representation of matrices and vectors in
``Numerical Recipes'', the subscripts of the returned pointer range
between 1 and n
.
3.21.0.2.4.4.7. int **get_nr1_imatrix(at *p, int *m, int *n)
|
|
This function checks that Lush object p
is a two dimensional integer matrix (usually created with function
imatrix ). It also checks that the elements of the rows are
contiguous and returns a pointer to the data under the Numerical Recipes
format. It handles arguments n and
m like function check_matrix
.
Following the standard representation of matrices and vectors in
``Numerical Recipes'', the subscripts of the returned pointer range
between 1 and n
.
3.21.0.2.4.4.8. short **get_nr1_smatrix(at *p, int *m, int *n)
|
|
This function checks that Lush object p
is a two dimensional short integer matrix (usually created with function
smatrix ). It also checks that the elements of the rows are
contiguous and returns a pointer to the data under the Numerical Recipes
format. It handles arguments n and
m like function check_matrix
.
Following the standard representation of matrices and vectors in
``Numerical Recipes'', the subscripts of the returned pointer range
between 1 and n
.
3.21.0.2.4.4.9. unsigned char **get_nr1_bmatrix(at *p, int *m, int *n)
|
|
This function checks that Lush object p
is a two dimensional byte matrix (usually created with function
bmatrix ). It also checks that the elements of the rows are
contiguous and returns a pointer to the data under the Numerical Recipes
format. It handles arguments n and
m like function check_matrix
.
Following the standard representation of matrices and vectors in
``Numerical Recipes'', the subscripts of the returned pointer range
between 1 and n
.
3.21.0.2.4.4.10. float *get_nr0_vector(at *p, int *n )
|
|
This function checks that Lush object p
is a one dimensional single precision matrix (usually created with
function matrix .) It also checks that
the elements are contiguous and returns a pointer under the Numerical
Recipes format. It handles argument n
like function check_vector .
Unlike the standard representation of matrices and vectors in
``Numerical Recipes'', the subscripts of the returned pointer range
between 0 and
n-1 .
3.21.0.2.4.4.11. double *get_nr0_dvector(at *p, int *n )
|
|
This function checks that Lush object p
is a one dimensional double precision matrix (usually created with
function dmatrix ). It also checks
that the elements are contiguous and returns a pointer under the
Numerical Recipes format. It handles argument n
like function check_vector .
Unlike the standard representation of matrices and vectors in
``Numerical Recipes'', the subscripts of the returned pointer range
between 0 and
n-1 .
3.21.0.2.4.4.12. int *get_nr0_ivector(at *p, int *n )
|
|
This function checks that Lush object p
is a one dimensional integer matrix (usually created with function
imatrix ). It also checks that the elements are contiguous
and returns a pointer under the Numerical Recipes format. It handles
argument n like function
check_vector .
Unlike the standard representation of matrices and vectors in
``Numerical Recipes'', the subscripts of the returned pointer range
between 0 and
n-1 .
3.21.0.2.4.4.13. short *get_nr0_svector(at *p, int *n )
|
|
This function checks that Lush object p
is a one dimensional short integer matrix (usually created with function
smatrix ). It also checks that the elements are contiguous
and returns a pointer under the Numerical Recipes format. It handles
argument n like function
check_vector .
Unlike the standard representation of matrices and vectors in
``Numerical Recipes'', the subscripts of the returned pointer range
between 0 and
n-1 .
3.21.0.2.4.4.14. unsigned char *get_nr0_bvector(at *p, int *n )
|
|
This function checks that Lush object p
is a one dimensional byte matrix (usually created with function
bmatrix ). It also checks that the elements are contiguous
and returns a pointer under the Numerical Recipes format. It handles
argument n like function
check_vector .
Unlike the standard representation of matrices and vectors in
``Numerical Recipes'', the subscripts of the returned pointer range
between 0 and
n-1 .
3.21.0.2.4.4.15. float **get_nr0_matrix(at *p, int *m, int *n)
|
|
This function checks that Lush object p
is a two dimensional single precision matrix (usually created with
function matrix .) It also checks that
the elements of the rows are contiguous and returns a pointer to the
data under the Numerical Recipes format. It handles arguments
n and m like function
check_matrix .
Unlike the standard representation of matrices and vectors in
``Numerical Recipes'', the subscripts of the returned pointer range
between 0 and
n-1 .
3.21.0.2.4.4.16. double **get_nr0_dmatrix(at *p, int *m, int *n)
|
|
This function checks that Lush object p
is a two dimensional double precision matrix (usually created with
function dmatrix ). It also checks
that the elements of the rows are contiguous and returns a pointer to
the data under the Numerical Recipes format. It handles arguments
n and m like function
check_matrix .
Unlike the standard representation of matrices and vectors in
``Numerical Recipes'', the subscripts of the returned pointer range
between 0 and
n-1 .
3.21.0.2.4.4.17. int **get_nr0_imatrix(at *p, int *m, int *n)
|
|
This function checks that Lush object p
is a two dimensional integer matrix (usually created with function
imatrix ). It also checks that the elements of the rows are
contiguous and returns a pointer to the data under the Numerical Recipes
format. It handles arguments n and
m like function check_matrix
.
Unlike the standard representation of matrices and vectors in
``Numerical Recipes'', the subscripts of the returned pointer range
between 0 and
n-1 .
3.21.0.2.4.4.18. short **get_nr0_smatrix(at *p, int *m, int *n)
|
|
This function checks that Lush object p
is a two dimensional short integer matrix (usually created with function
smatrix ). It also checks that the elements of the rows are
contiguous and returns a pointer to the data under the Numerical Recipes
format. It handles arguments n and
m like function check_matrix
.
Unlike the standard representation of matrices and vectors in
``Numerical Recipes'', the subscripts of the returned pointer range
between 0 and
n-1 .
3.21.0.2.4.4.19. unsigned char **get_nr0_bmatrix(at *p, int *m, int *n)
|
|
This function checks that Lush object p
is a two dimensional byte matrix (usually created with function
bmatrix ). It also checks that the elements of the rows are
contiguous and returns a pointer to the data under the Numerical Recipes
format. It handles arguments n and
m like function check_matrix
.
Unlike the standard representation of matrices and vectors in
``Numerical Recipes'', the subscripts of the returned pointer range
between 0 and
n-1 .
Strings are implemented as external objects of class
string_class . Field p-
Object> always points to a C structure of type
struct string .
Two macros have been defined to test if an at*
pointer references a list.
As stated above, you must read section ``Locking Conventions'' before
attempting to alter the contents of the fields p-
Car> and p- Cdr>. You can
however safely read these fields and follow the list structure. For
instance you can write directly:
/* This code loops on the elements of a list <p> */
while (CONSP(p)) {
at *q = p->Car;
/* q now points to the current element of the list */
p = p->Cdr;
}
3.21.0.2.5.1.0. char *SADD(at *p)
|
[#define] |
The macro function SADD() returns a
pointer to a null terminated array of characters. Since this macro
function does not check the type of p
, you should use the string predicate described above before using
SADD() .
Therefore, you can write:
if (EXTERNP(p, &string_class))
my_char_ptr = SADD(my_at->Object);
else
error(NIL,"Not a string",p);
See: Locking Conventions.
There are two functions for creating a list element:
cons() and new_cons() .
Both functions are essentially similar to the Lush function
cons . These functions differ only as described in section
``Locking Conventions''.
- The function
new_cons() follows the usual locking conventions.
- The function cons() is often used
for efficiency reasons. As described in section ``Locking Conventions'',
this function does not affect the reference counts of
p and q .
3.21.0.2.5.2.0. at *new_string(char *s)
|
|
The function new_string() takes a C
string s and returns an
at* pointer to a Lush string. During the process, the string
s is copied into a private buffer associated to the Lush
object. Therefore, you may change the contents of
s without affecting the Lush string.
Lush symbols are implemented as external objects of class
symbol_class . Their internal structure is rather complex
because they manage the locality of the Lush variables.
Two macros have been defined to test if an at*
pointer references a list.
As stated above, you must read section ``Locking Conventions'' before
attempting to alter the contents of the fields p-
Car> and p- Cdr>. You can
however safely read these fields and follow the list structure. For
instance you can write directly:
/* This code loops on the elements of a list <p> */
while (CONSP(p)) {
at *q = p->Car;
/* q now points to the current element of the list */
p = p->Cdr;
}
3.21.0.2.6.1.0. at *var_get( at *symbol)
|
|
The function var_get() returns the
contents of the variable currently bound to symbol
symbol . Please, remember that this function returns a
pointer to a structure at . According
to the locking conventions described in section ``Locking Conventions'',
this pointer is ``hot''. You must return it or unlock it with the macro
function UNLOCK() .
See: Locking Conventions.
3.21.0.2.6.1.1. void var_set( at *symbol, at *value)
|
|
The function var_set() stores the
value value into the variable currently bound to symbol
symbol . In fact, this function is equivalent to the Lush
function setq .
See: Locking Conventions.
There are two functions for creating a list element:
cons() and new_cons() .
Both functions are essentially similar to the Lush function
cons . These functions differ only as described in section
``Locking Conventions''.
- The function
new_cons() follows the usual locking conventions.
- The function cons() is often used
for efficiency reasons. As described in section ``Locking Conventions'',
this function does not affect the reference counts of
p and q .
3.21.0.2.6.2.0. at *named( char *s)
|
|
The function named() returns a symbol
whose name is given by the string s .
3.21.0.2.6.2.1. at *var_define( char *s)
|
|
The function var_define() creates a
global symbol during the initialisation process. This function must be
called from an initialisation function, like
dx_define() .
It returns a pointer to a symbol whose name is given by the string
s . This pointer can only be used as a key for the functions
var_set() or var_get()
described above. Never lock or unlock this pointer.
Although they are the heart of the Lush kernel, the functions are
implemented as external objects. There are in fact many classes of
functions, like de_class or
dx_class . A full description would exceed the purpose of
this manual.
Two functions are especially useful for dealing with functions:
3.21.0.2.7.0. at *eval(at *p)
|
|
The function eval() takes a Lush
expression p and returns the result of
its evaluation. For instance, the Lush function
if is defined as follows:
DY(yif)
{
register at *q;
ifn(CONSP(ARG_LIST) && CONSP(ARG_LIST->Cdr))
error(NIL, "bad 'if' syntax", NIL);
if (q=eval(ARG_LIST->Car)) {
UNLOCK(q);
return eval(ARG_LIST->Cdr->Car);
} else
return progn(ARG_LIST->Cdr->Cdr);
}
3.21.0.2.7.1. at *apply( at *function, at *arglist )
|
|
The function apply() returns the
result of applying function func to
the argument list arglist. For instance, the following function calls a
Lush function func on a real number
x and returns a real number as a result.
real callfunc( func, x )
at *func;
real x;
{
at *arglist;
at *result;
/* build the argument list */
arglist = cons(new_number(x), NIL);
/* calls the function */
result = apply(func,arglist);
/* checks the result */
ifn (NUMBERP(result))
error(NIL,"Not a number",result);
/* unlock hot pointers */
x = result->Number;
UNLOCK(arglist);
UNLOCK(result);
return x;
}
3.21.0.2.8. User Defined Classes
|
|
It is possible to define a new primitive class in Lush. In other words,
your data structure can be handled by the Lush interpreter like a full
featured external Lush object. The TL/Open example
"complex" provides a full demonstration of this capability.
To define a new primitive class, you must achieve a few simple tasks:
-
You must define a class descriptor structure of type
struct class . This class descriptor contains a few function
pointers that describe how the Lush interpreter interacts with your data
structures.
- You must write a primitive Lush function for creating a new
instance of your data structures.
- You must declare the new class to the Lush kernel using function
class_define in the initialization function.
We describe now these successive steps using the example of a class
named MYDATA corresponding to a C
structure type struct mydata .
struct mydata { at *pl; at *pr; char *s; double d; int i; };
3.21.0.2.8.0. Class Structure Definition
|
|
The class descriptor is defined by the TL/Open headers. You must declare
a new instance of this structure and initialize its first components
with pointers to various C functions.
Assume that our new class is named mydata
, the class structure will be initialized by the following C language
idiom:
class mydata_class = { /* This is C language */
mydata_dispose,
mydata_action,
mydata_name,
mydata_eval,
mydata_listeval,
mydata_serialize,
mydata_compare,
mydata_hash,
};
This idiom is not a C++ class definition. The C identifier
class actually is a typedef name that refers to the type of
the class descriptor structure. The above idiom is nothing more than the
definition and initialization of a class descriptor structure. The first
few fields of the class descriptor are initialized pointers to the
functions mydata_dispose ,
mydata_action , etc.
C++ programmers will be concerned by the fact that
class is a reserved keyword in their preferred language. This
name "class" was chosen well before
the advent of C++ (or even ANSI C). To fix the consequences of this
unfortunate choice, we have introduced a new identifier
TLclass available in both C and C++.
TLclass mydata_class = { /* This is C++ language */
mydata_dispose, mydata_action,
mydata_name, mydata_eval, mydata_listeval,
mydata_serialize, mydata_compare, mydata_hash,
};
The Lush interpreter processes objects by calling the appropriate
function using the pointers specified by the class descriptor. This is a
C implementation of the virtual function concept popularized with the
C++ language.
Lush provides simple default functions for all the entries of the class
descriptor. These functions are named
generic_dispose , generic_action
, generic_name , etc. All these
functions will be described in the next subsections.
3.21.0.2.8.0.0. Garbage Collection
|
|
An important feature of the Lush kernel is its ability to recycle
perempted Lush objects. This feature is called ``Garbage Collection''.
Lush relies two very simple garbage collection strategies.
- The
basic garbage collection is light and fast. It relies on a reference
counter stored in field count of each
at structure. This basic algorithm is unable to reclaim
memory used by self-referencing objects. Its operation requires that all
C functions dealing with lisp object comply with a few simple locking
conventions.
- The full garbage collection occurs whenever a Lush error is
triggered. This algorithm reclaims the memory used by all
pepreemptedbjects, recomputes the counters of all objects in use, and
checks the consistency of all Lush data structure.
3.21.0.2.8.0.0.0. void mydata_dispose(at *p)
|
|
This function is called when the Lush object p
is destroyed.
If your data structure contains pointers to other Lush objects, this
function must unlock all Lush objects referenced by your data structure.
It must also deallocate all the memory privately allocated for your data
structure.
void mydata_dispose(at *p)
{
struct mydata *md = p->Object;
/* unlock all Lush objects pointed to by this object */
UNLOCK(md->pl);
UNLOCK(md->pr);
/* free all memory allocated for this data structure */
if (md->s) free(md->s);
free(md);
}
3.21.0.2.8.0.0.1. void mydata_action( at *p, void (* action) (q) )
|
|
This function is called by the full garbage collection algorithm. This
algorithm runs whenever an error occurs.
- If your data
structure contains pointers to other Lush objects, this function must
call the C function (*action)(q) on
all Lush objects q referenced by your
data structure.
- If your data structure contains no such pointers, you may
initialize the class structure with the function
generic_action() which does nothing.
void mydata_action(at *p, void (*action)(at *))
{
struct mydata *md = p->Object;
/* call action on all Lush objects pointed to by this object */
(*action)(md->pl);
(*action)(md->pr);
}
3.21.0.2.8.0.1. Textual Representation
|
|
The third function of the class descriptor must return the textual
representation of the object returned by function
pname and displayed by function print
.
You can always use the default function
generic_name which returns a simple name of the form
"::classname:address" .
char * mydata_name(at *p)
{
static char buffer[80];
struct mydata *md = p->Object;
sprintf(buffer,"::MYDATA:<%s>", md->s);
return buffer;
}
Defining this function only affects the output function. It does not
allow the Lush reader to interpret this string as a Lush object. You can
modify the behavior of the Lush reader however by defining
macro-characters using function dmc .
3.21.0.2.8.0.2. Evaluation
|
|
Two function pointers in the class structure lets you define how user
defined objects interact with the Lush evaluator.
3.21.0.2.8.0.2.0. at *mydata_eval(at *p)
|
|
This function returns the result of the evaluation of object
p . For instance, the evaluation of a symbol returns the
symbol value. This is implemented by the evaluation function pointed by
the symbol class descriptor.
It is generally adequate to use the default function
generic_eval() which returns the object itself. All Lush
objects (except the symbols) use this default function.
3.21.0.2.8.0.2.1. at *mydata_listeval (at *p,at *q)
|
|
This function returns the result of the evaluation of a list
q whose first member evaluates to object
p . For instance the evaluation of a list whose first member
is a function returns the result of the function call. This is
implemented by the list evaluation function pointed to by the function
class.
It is generally adequate to use the default function
generic_listeval() which displays an error message. All Lush
objects (except functions and matrices) use this default function.
3.21.0.2.8.0.3. Serialization
|
|
The serialization is the process of converting a Lush object to (or
from) a sequence of bytes. This conversion happens automatically when
you call the Lush functions bwrite or
bread . New object types are serialized by means of the
serialization function defined by the class.
The object serialization is handled by function
mydata_serialize pointed by the class descriptor structure.
Serialization however is an optional feature. Specifying the default
serialization function pointer generic_serialize
will simply cause an error if you attempt to write an object of this
class.
- Writing a Lush object to a file consists in writing all
specific information held by the object, including all Lush objects
pointed to by this object. Circular references therefore must be
detected and handled properly. This process requires several passes over
the objects being written.
- Reading a Lush object from a file sets similar problems. Circular
references must be restored properly.
Although the serialization process is quite complex, powerful utility
functions, named serialize_xxx help
writing straightforward serialization functions. All serialization
functions take an argument code that
indicates which pass of the serialization process is active. All utility
functions know how to handle elementary data types during each pass.
3.21.0.2.8.0.3.0. void mydata_serialize(at **p, int code)
|
|
This function is called to convert a Lush object to (or from) a sequence
of bytes. The responsibilities of the serialization function depends on
the value of integer code .
-
When code is SRZ_READ , the
serialization function must create an instance of the class and
initialize the at* variable pointed to
by p with a pointer to this object.
- Regardless of the value of code ,
the serialization function must call once the appropriate
serialize_xxx function on all fields that must be saved. The
sequence of calls must be independent of the value of
code .
Here is an example of serialization function. for the class
MYDATA proposed above as an example. The functions
serialize_xxx are described later in this section.
void mydata_serialize(at **p, int code)
{
struct mydata *md;
/* Create object when <code> is <SRZ_READ> */
if (code == SRZ_READ)
{
if (! (md = malloc(sizeof(struct mydata))) )
error(NIL,"Out of memory", NIL);
memset(md, 0, sizeof(struct mydata));
*p = new_extern(&mydata_class, md);
}
/* Obtain a pointer to the object */
md = (*p)->Object;
/* Call serialization function on all members */
serialize_atstar(&md->pl, code);
serialize_atstar(&md->pr, code);
serialize_string(&md->s, code, -1);
serialize_double(&md->d, code);
serialize_int(&md->i, code);
}
The memory allocated for an object depends sometimes on the values
stored in the object. The memory allocated for a vector of
at objects, for instance, depends on the size of the vector.
It is perfectly legal to read the vector size before allocating memory
for the vector data. You must make sure however that the sequence of
calls to functions serialize_xxx will
not depend on the value of code .
void vector_serialize(at **p, int code)
{
int i;
struct vector *v;
if (code == SRZ_READ)
{
/* Create vector when code is SRZ_READ */
int nelem, memsize;
serialize_int(&nelem, code);
memsize = sizeof(struct vector) + size * sizeof(at*);
if (! (v = malloc(memsize)) )
error(NIL,"Out of memory",NIL);
memset(v, 0, memsize);
v->nelem = nelem;
*p = new_extern(&vector_class, v);
}
else
{
/* Catch up with sequence of calls to <serialize_xxx> */
v = (*p)->Object;
serialize_int(&v->nelem, code);
}
/* Serialize the vector elements */
for (i=0; i<v->nelem; i++)
serialize_atstar(&v->vec[i], code);
}
3.21.0.2.8.0.3.1. void serialize_char(char *data, int code)
|
|
This function perform the serialization of a variable of type
char .
- When code
is SRZ_WRITE , the character pointed
by argument data is encoded and
written to the output file.
- When code is
SRZ_READ , the character variable pointed by argument
data is read from the input file.
- This function ignores all other values of argument
code .
3.21.0.2.8.0.3.2. void serialize_short(short int *data, int code)
|
|
This function perform the serialization of a variable of type
short .
- When code
is SRZ_WRITE , the short integer
pointed by argument data is encoded
and written to the output file.
- When code is
SRZ_READ , the short integer variable pointed by argument
data is read from the input file.
- This function ignores all other values of argument
code .
3.21.0.2.8.0.3.3. void serialize_int(int *data, int code)
|
|
This function perform the serialization of a variable of type
int .
- When code
is SRZ_WRITE , the integer pointed by
argument data is encoded and written
to the output file.
- When code is
SRZ_READ , the integer variable pointed by argument
data is read from the input file.
- This function ignores all other values of argument
code .
3.21.0.2.8.0.3.4. void serialize_float(float *data, int code)
|
|
This function perform the serialization of a variable of type
float .
- When code
is SRZ_WRITE , the single precision
floating point number pointed by argument data
is encoded and written to the output file.
- When code is
SRZ_READ , the single precision floating point variable
pointed by argument data is read from
the input file.
- This function ignores all other values of argument
code .
3.21.0.2.8.0.3.5. void serialize_double(double *data, int code)
|
|
This function perform the serialization of a variable of type
double .
- When code
is SRZ_WRITE , the double precision
floating point number pointed by argument data
is encoded and written to the output file.
- When code is
SRZ_READ , the double precision floating point variable
pointed by argument data is read from
the input file.
- This function ignores all other values of argument
code .
3.21.0.2.8.0.3.6. void serialize_string(char **data, int code, int maxlen)
|
|
This function perform the serialization of a zero terminated character
string.
- When code is
SRZ_WRITE , the zero terminated character string identified
by the char* variable pointed by
argument data is encoded and written
to the output file. The value of argument maxlen
is ignored.
- When code is
SRZ_READ , a zero terminated character string is read from
the input file.
If argument maxlen is negative, this
string is copied into a memory buffer allocated with
malloc . A pointer is stored into the
char* variable pointed by argument
data .
If argument maxlen is positive, an
error is signaled if the string length exceeds
maxlen character. The string is then copied into the memory
buffer identified by the char*
variable pointed by argument data .
- This function ignores all other values of argument
code .
3.21.0.2.8.0.3.7. int serialize_atstar(at **data, int code)
|
|
This function perform the serialization of a zero terminated character
string.
- When code is
SRZ_SETFL or SRZ_CLRFL
function serialize_atstar cooperates
with the Lush kernel to identify circular references in the set of
objects being serialized.
- When code is
SRZ_WRITE , function serialize_atstar
writes a binary representation of the lisp object identified by the
at* variable pointed by argument data
. This binary representation may be an explicit description of the
object contents or just a reference to an already saved object.
- When code is
SRZ_READ , function serialize_atstar
reads the binary representation of a lisp object on the input file.
If this binary representation is an explicit description or a
reference of an already read object, function
serialize_atstar will store a pointer to this object into the
at* variable pointed by argument data
and return 0 .
This binary representation however may be a reference to an object
that has not been read yet. Function
serialize_atstar will record the address of the
at* variable pointed to by argument
data and return 1 . The
serialization routines will update this at*
variable after reading the referenced object.
Unlike the other serialize_xxx
functions, calling function serialize_atstar
with code equal to
SRZ_READ does not ensure that the at*
variable pointed by argument data will
be a valid Lush object when the function returns.
It is therefore forbidden to perform any action that may require that
the at* pointers refer to valid Lush
objects. Special programming techniques are sometimes necessary:
A Lush hash table object, for instance, records a collection of
associations between keys and values. A hashing function computes a
numerical quantity (named hash code) using the information associated
with each key. This numerical quantity is used to decide where to store
the association information in the hash table data structure. Since the
keys are arbitrary Lush objects, the hash table serialization function
process them using serialize_atstar .
When reading a hash table object from the disk, there is no way to
compute the hash code within the serialization function. Associations
are stored into random locations. A flag however indicates that the hash
table must be reorganized. When a hash table function notices this
flags, it recomputes all hash codes and moves all associations to the
location corresponding to their hash codes.
3.21.0.2.8.0.4. Comparisons
|
|
The logical comparison of two objects are handled by function
mydata_compare pointed by the class descriptor structure.
Equality tests are sometimes expedited using the hashing function
mydata_hash pointed by the class descriptor structure.
You may defined these two function as explained below. You can also use
the default function pointers generic_compare
and generic_hash when initializing the
class descriptor structure. These default pointer will just compare the
object addresses in memory (physical equality).
3.21.0.2.8.0.4.0. int mydata_compare(at *p, at *q, int order)
|
|
The comparison function defined by the class descriptor is called
whenever the Lush user performs a logical comparison of two instances of
this class. This function returns an integer representing the result of
the comparison of p and
q .
- When argument order
is non zero, this function must return -1
if p is less than
q , +1 if
p is greater than q , and
0 if p is (logically) equal
to q .
- When argument order is zero, this
function must return 0 is
p is logically equal to q
and must return a non zero value otherwise.
It is not always possible to define an adequate order relation. There is
no universaly accepted order relation for complex numbers, for instance.
In such a case, function mydata_compare
must signal an error when argument order
is non zero.
Example:
int mydata_compare(at *p, at *q, int order)
{
struct mydata *mdp = p->Object;
struct mydata *mdq = q->Object;
/* Cannot perform ordering on these objects */
if (order)
error(NIL,"Cannot rank objects of class |MYDATA|",NIL);
/* Compare objects <p> and <q> */
if (! eq_test(mdp->pl, mdq->pl)) return 1;
if (! eq_test(mdp->pr, mdq->pr)) return 1;
if (strcmp(mdp->s, mdq->s)) return 1;
if (mdp->d != mdq->d) return 1;
if (mdp->i != mdq->i) return 1;
/* Objects <p> and <q> are logically equal */
return 0;
}
3.21.0.2.8.0.4.1. unsigned long mydata_hash(at *p)
|
|
The hashing function defined by a class descriptor is called whenever an
instance of this class is used as a hash table key. This function must
return a long integer number, named hash code, depending on the data
represented by the object p .
-
Function mydata_hash must return the
same hash code when called on two objects for which function
mydata_compare returns 0 .
- The best performance is achieved when function
mydata_hash , as often as possible, returns different hash
code for objects that are considered as different by function
mydata_compare .
Function mydata_hash can use the
utility functions hash_value and
hash_pointer described later in this section.
Example:
unsigned long mydata_hash(at *p)
{
char *s;
unsigned long x = 0;
struct mydata *md = p->Object;
s = md->s;
while (s && *s)
x = (x<<3) ^ (*s++);
x = x ^ hash_value(md->pl);
x = x ^ hash_value(md->pr);
x = (x<<3) ^ md->i ^ *(unsigned long*)&(md->d);
return x;
}
It is particulary important that functions
mydata_hash and mydata_compare
implement the same concept of logical equality.
3.21.0.2.8.0.4.2. unsigned long hash_value(at *p)
|
|
This function returns a hash code for the Lush object
p . This function will return the same hash code when called
on two logically equal objects (as tested by the Lush function
= .)
3.21.0.2.8.0.4.3. unsigned long hash_pointer(at *p)
|
|
This function returns a hash code for the Lush object
p . This function will return the same hash code when called
on two physically equal objects (as tested by the Lush function
== .)
3.21.0.2.8.1. Creation Function Definition
|
|
Then, you must define a C function which returns a new instance of the
new class of Lush object. This function must allocate and initialize
your data structure and return a Lush object created by the function
new_extern() .
at *new_mydata(at *lp, at *rp, char *s, double d, int i)
{
struct mydata *md;
/* Allocate */
if (! (md = malloc(sizeof(struct md)) ))
error(NIL,"no memory",NIL);
/* Initialize */
md->lp = lp; LOCK(lp);
md->rp = rp; LOCK(rp);
md->s = strdup(s);
md->d = d;
md->i = i;
/* Return <at*> pointer */
return new_extern( &mydata_class, md );
}
You must also write an interface function for calling this function as a
Lush primitive.
DX(xnew_mydata)
{
ALL_ARGS_EVAL;
ARG_NUMBER(5);
return new_mydata(APOINTER(1), APOINTER(2),
ASTRING(3), AREAL(4), AINTEGER(5));
}
3.21.0.2.8.2. User Defined Classes Declaration
|
|
During the initialization process, you must declare the new class by
calling function class_define() and
you must declare the creation primitive by calling function
dx_define() .
void init_myfile()
{
class_define("MYDATA",&mydata_class,NIL);
dx_define("new_mydata", xnew_mydata);
}
Your user defined class is useless unless you define other primitive
functions that process the data represented by your new object type.
You can define these functions as usual via the DX or DY conventions.
You can check that a Lush object p
belongs to your new object class using the usual predicate for external
objects:
EXTERNP(p, &mydata_class)
You can then access your data structure through pointer
p- Object>:
((struct mydata*)(p->Object))
3.21.0.3. TL/Open Examples
|
|
The directory "lushdir/open/examples"
contains three TL/Open example that illustrate various techniques.
3.21.0.3.0. String Capitalization
|
|
See: Interfacing a C Function to Lush.
The first example is the string capitalization function already
described in chapter ``Interfacing a C Function to Lush''. This example
is located in directory
"lushdir/open/examples/capnth" . This directory
contains the following files:
- File
"user_dll.c" contains the function
capitalize_nth described in chapter ``Interfacing a C
Function to Lush''. Please refer to this chapter for detailled comments.
- File "tlopen.h" is the main
TL/Open header file. This file includes the appropriate TL/Open headers
and libraries. Under Windows, you may have to edit the directory names
in this file.
- File "capnth.lsh" contains Lush
code that loads the TL/Open extension, provides documentation for the
new function and runs a few tests. This file is commented later in this
section.
- File "readme.txt" provides
information for compiling the TL/Open extension under your operating
system. The directory typically contains other files required by the
compilation environment.
3.21.0.3.0.0. File "capnth/capnth.lsh"
|
|
See: Installing a TL/Open Extension as a Package.
See: Adding Help.
We suggest that all Lush extension should come with a small Lush file.
Loading file should perform several valuable tasks:
- Searching
and loading the TL/Open extension.
- Documenting the primitive functions implemented by the TL/Open
extension. The Lush file can contain special comments that will be
integrates in the online documentation system.
- Testing that the new primitives are working properly. This feature
is specially valuable during the development of the TL/Open extension.
- Possibly provinding additional functions that are more conveniently
written using the Lush language.
File capnth.lsh illustrates this
concept. The user of the string capitalization extension therefore only
needs to load file capnth.lsh . This
operation searches and loads the extension, incorporate some
documentation into the Lush online documentation, and finally performs a
few tests.
The first lines of file capnth.lsh are
reproduced below. They search and load a TL/Open extension whose name
matches the name of the current Lush file. You can copy these lines
verbatim into other files.
(let* ((here (dirname file-being-loaded))
(base (basename file-being-loaded "sn")) )
;; Search along path
(let ((oldpath (path)))
(path (concat-fname here)
(concat-fname here "Debug")
(concat-fname here "Release")
(concat-fname lushdir "bin") )
(setq here (filepath base ".so|.sl|.dll"))
(apply path oldpath ) )
;; Go
(when ~here
(error 'mod-load "TL/Open extension not found" base) )
(when winlushp
(setq here (upcase here)) )
(when (not (member here (mod-list)))
(printf " [+%s]\n" base)
(mod-load here) ) )
3.21.0.3.1. Complex Numbers
|
|
See: User Defined Classes.
The complex number example illustrates more advanced techniques. Complex
numbers are introduced as a TL/Open user defined class, as described by
section ``User Defined Classes''. These new numbers are first class
objects in Lush. You can type a complex number just as it is printed.
You can use the usual operators (eg. +
, - , etc.) on complex numbers.
This example is located in
"lushdir/open/examples/complex" . This directory
contains the following files:
- File
"user_dll.c" defines the new complex class and provides a
couple of functions working on complex numbers.
- File "tlopen.h" is the main
TL/Open header file. This file includes the appropriate TL/Open headers
and libraries. Under Windows, you may have to edit the directory names
in this file.
- File "complex.lsh" loads the
TL/Open extension, defines a macro-character for directly typing complex
numbers, override the usual operators with complex aware functions,
define additional complex functions, provide some documentation and run
a few tests.
- File "readme.txt" provides
information for compiling the TL/Open extension under your operating
system. The directory typically contains other files required by the
compilation environment.
The following sections give a brief overview of these files. Extensive
comments can be found in the files themselves.
3.21.0.3.1.0. File "complex/user_dll.c"
|
|
See: User Defined Classes.
File user_dll.c is organized in six
sections:
- Section ``Header Section'' contains the traditional TL/Open
preprocessor directives that include the master TL/Open include file and
specify the TL/Open library.
- Section ``Declarations'' first declares the C structure
COMPLEX . Lush complex numbers are represented, either as a
regular Lush number (when the imaginary part is zero), or as an external
object pointing to an instance of this C structure (when the imaginary
part is non zero).
This section also defines and initializes a variable names
complex_alloc . This variable represents a pool of
preallocated structures managed by the Lush fast memory allocation
routines allocate and
deallocate . This system is just a fast alternative to the
usual memory allocation functions malloc
and free .
- Section ``Class Definition'' defines the class structure as
explained in section ``User Defined Classes''. Function
complex_name in particular defines the textual representation
of complex numbers "#{ real ,
imag }".
- Section ``Utilities'' defines a few functions that will be useful
in the definition of the primitives.
Function new_complex returns a Lush
object representing the complex number specified by its argument. If the
imaginary part is zero, this function returns a regular Lush number.
Otherwise, this function creates an instance of class
complex_class .
Function get_complex takes either a
Lush regular number or an instance of class
complex_class . It returns the real and the imaginary part of
this number.
Function polar_to_cartesian and
cartesian_to_polar perform the conversion between the
cartesian and the polar representation of a complex number. This is used
for implementing transcendental functions.
- Section ``Primitives'' implement various primitives for handling
complex numbers. You can find a complete documentation of these
primitives in file "complex.lsh" .
This section defines in particular a number of operators working on
complex numbers (eg. complex+ ,
complex- , etc.) The syntax of these operators is compatible
with the syntax of the usual Lush operators (eg.
+ , - , etc.). File
"complex.lsh" will redefine the usual operator as calls to
these new operators. This redefinition allows the use of regular
operators with complex numbers.
- Finally the ``Initialization Section'' contains the usual function
init_user_dll . This function declares the new class and the
new primitives to the Lush kernel.
3.21.0.3.1.1. File "complex/complex.lsh"
|
|
See: TL/Open Example: File "capnth/capnth.lsh".
See: Adding Help.
See: (dmc symb .
body )
See: (unlock-symbol s1 ...
sn )
See: (lock-symbol s1 ...
sn )
As explained with the previous example, the user of the complex number
extension only needs to load file capnth.lsh
. The bulk of the file complex.lsh
consists of the documentation of the new complex number functions. This
feature relies heavily on the Lush online help system.
Besides providing this documentation, file
complex.lsh performs the following tasks:
- The first lines of this file are similar to the first lines of
file capnth.lsh . These lines locate
and load a TL/Open extension whose name match the filename.
- A call to function dmc then
defines macro-character #{ This
macro-character allows the user to directly type a complex number as it
would be printed (eg. #{2,1} ). Note:
This technique requires that the first letters of the textual
representation are a legal macro-character.
- A number of simple functions are conveniently as Lush functions.
These functions are the complex number predicate
complexp , the conjugation cconj
and a number of transcendental functions derived from functions
clog and cexp .
- Most complex number functions (defined in C or in Lush) come with a
simple testing code. This testing code is very helpful during the
development stage. Successfully loading file
complex.lsh is a good indication that the extension is
working adequately.
- The value of the traditional operator symbols (eg.
+ , - , etc.) are saved
into new variables (eg. real+ ,
real- , etc.). These operators are then replaced by their
complex equivalent functions. Note that the traditional operator symbols
are protected against accidental redefinition. We must use functions
unlock-symbol and lock-symbol
to redefine and reprotect them.
File complex.lsh is therefore an
important part of the complex number package. This file actually plugs
the new primitives into the core functions of the Lush interpreter. The
cooperation of files user_dll.c and
complex.lsh accounts for the seemless integration of complex
numbers into the Lush language.
3.21.0.3.2. Numerical Recipes Interface
|
|
Several functions help interfacing Lush with C routines using the vector
and matrix formats suggested by the well known book "Numerical Recipes
in C" (Press et al.). A TL/Open example shows how to interface various
routines published in this book to the Lush interpreter.
The pointer returned by these functions is stored in the
struct array structure and is used by Lush. Therefore, it
should not be altered. Of course this restriction deals with the
pointers themselves, and do not restrict the usage of the contents of
the matrices they address.
In "Numerical Recipes", a vector is represented by a pointer
u to an array of float ,
double , int or
unsigned char . The first element is most often referred to
as u[1] . The functions prefixed with
get_nr1_XXXvector convert a Lush matrix into such a vector.
In "Numerical Recipes", a matrix is represented by a pointer
u to an array of pointers to an array of
float , int or
double . The first element of the first row of a matrix
u is referred to as u[1][1]
. The functions get_nr1_XXX convert a
Lush matrix into such a matrix.
The functions get_nr0_XXX are similar
functions except the subscripts of the vectors and matrices they return
range between 0 and
n-1 instead of 1 and
n .
3.21.0.3.2.0. File "nr/user_dll.c"
|
|
File "nr/user_dll.c" is composed of
five parts.
- The ``Header Section'' of file
"user_dll.c" is slightly different from the usual TL/Open
extension header section.
- The ``NR Utility Routines'' section implements various utility
routines used by the Numerical Recipes routines.
- The ``TL Utility Routines'' section contains subroutines used by
some DX interface functions for parsing arguments. The ``Primitive""
section contains about fifty DX interface functions for the Numerical
Recipes routines.
- The ``Initialisation section'' simply declares the DX interfaces to
the Lush kernel.
3.21.0.3.2.0.0. File "nr/user_dll.c": Header Section
|
|
The ``Header Section'' of file "user_dll.c"
is slightly different from the usual TL/Open extension header section.
There are indeed a few conflicts between names defined by the Numerical
Recipes library and names defined by Lush (eg.
matrix , imatrix ,
dmatrix , erf , and
rank .)
When compiling a dynamically linkable TL/Open extension, the linker will
resolve these names with the NR routine rather than the Lush routine.
Including the Lush header file would however define the prototype for
the Lush routine and not the NR routine.
The solution implemented by file "user_dll.c"
consists in redefining temporarily the conflicting names before the
inclusion of the Lush header files.
3.21.0.3.2.0.1. File "nr/user_dll.c": NR Utility Routines
|
|
The ``NR Utility Routines'' section implements various utility routines
usually provided by the Numerical Recipes file
"nrutil.c" .
The Numerical Recipes routines call function
nrerror whenever they detect an error condition. Our version
of this function calls the usual Lush function
error .
We also redefine a number of routines for allocating temporary vectors
and matrices. These routines are widely used by the Numerical Recipes
routines. The new allocation routines call the Lush function
error when an error is detected.
3.21.0.3.2.0.2. File "nr/user_dll.c": TL Utility Routines
|
|
See: AFLT( int i )
See: Locking Conventions.
The ``TL Utility Routines'' section contains subroutines that help
writing DX interfaces for the Numerical Recipes routines.
-
Function at_to_float checks that a
Lush object is a number and returns this number as a floating point
number. This function is quite similar to macro
AFLT(n) , but takes an arbitrary lisp object as argument
instead of implicitly referring to a DX argument.
- Function var_to_float gets the
value of a Lush symbol, checks that this value is a number and returns
the number as a float.
- The standard method for setting the value of a symbol is function
var_set(at *symb, at *value) . This function complies with
the usual locking conventions. Writing
var_set(symb, NEW_NUMBER(3)) is not allowed because the
object created by expression NEW_NUMBER(3)
is still locked. We have therefore defined a function
var_set_unlock that calls var_set
and unlocks the argument. This function is useful to set a variable with
a Lush object returned by a Lush function.
- Function nr1_to_lush_vector is
called when we need to create a one dimensional Lush matrix using the
data held by a Numerical Recipes vector. This function creates a Lush
matrix containing a copy of the contents of the NR vector.
- Function lush_vector_to_nr1
copies the contents of a contiguous 1D Lush matrix into a Numerical
Recipes vector. The implementation relies on function
get_nr1_vector .
3.21.0.3.2.0.3. File "nr/user_dll.c": Primitive Section
|
|
See: Numerical Recipes Interface.
The ``Primitive"" section contains about fifty DX interface functions
for the Numerical Recipes routines. We comment here some of the
techniques used by these interface functions.
- A number of Numerical Recipes routines (eg.
caldat ) return their results using function arguments
pointing to simple variables. The corresponding Lush primitives (eg.
nr-caldat ) expect symbol names as argument. The results are
stored into these symbols using function var_set
or var_set_unlock .
- Most functions illustrate the use of the functions
get_nr1_vector and get_nr1_matrix
documented in section ``Numerical Recipes Interface''. These functions
return a Numerical Recipes vector or matrix identifier that directly
address the data held by a Lush matrix.
- Some Numerical Recipes routines take function pointers as argument
(eg. brent ). Some functions call
specific functions provided by the user (eg.
sparse calls functions asub
and atsub ). The corresponding Lush
primitives take Lush functions as argument.
This result is achieved by passing small stub functions to the
Numerical Routines. These stub functions create a Lush list with the
function arguments, and use function apply
to call the Lush function identified by a global static variable.
You may notice that these stubs are declared using old style
prototypes (often referred to as K&R in reference to the authors of the
initial C language, Kernighan and Richie). This is required because the
Numerical Recipes routines (1st edition) use K&R prototypes everywhere,
and because ANSI protoyped functions and K&R prototyped functions often
use different argument passing conventions.
3.21.0.4. TL/Open Extensions Installation
|
|
This chapter addresses the installation and the distribution of TL/Open
extensions. There are two ways for installing and distributing a TL/Open
extension.
- The simplest way consists in installing the
extension as a package. A Lush user can then ``load the package'' and
use your TL/Open extension. This is the best choice for small projects.
- On the other hand, your TL/Open extensions may change the nature of
Lush to such an extend that you consider it as a separate application.
When the user runs your application, a script launches Lush, loads all
extensions and all required libraries. Your program then takes control.
3.21.0.4.0. TL/Open Extension Installation as a Package.
|
|
See: TL/Open Example: File "capnth/capnth.lsh".
See: TL/Open Example: File "complex/complex.lsh".
Installing a TL/Open extension as a package is certainly the best
solution when the extension has a general purpose and is self contained.
-
The first component of a package is of course the dynamic library
(extension ".dll" under Windows,
extension ".so" under Unix) that
contains your C code.
- The main component of a package however is a Lush file (named
"extension-name.lsh" ) that loads the dynamic library
and provides the required documentation and support functions. All
examples presented in the previous chapter come with such a file and
illustrate the appropriate techniques.
The recommended installation procedure consists in copying both the
dynamic library and the Lush file into directory
"lushdir/packages" . This simple procedure does not
require complex installation scripts.
Directory "lushdir/packages" is
automatically searched whenever you call function
load .
The user of a package named "linpack"
would just type:
? (load "linpack")
to load the package. This simple action will attach the dynamic library
and add the corresponding documentation into the help system.
3.21.0.4.1. TL/Open Extension Installation as an Application.
|
|
Your TL/Open extensions and your Lush programs may change radically the
purpose and the audience of Lush. Such a project is no longer an
extension of Lush, but rather is a separate application based on Lush.
This viewpoint change has several consequences:
- You want to
install your product in a separate directory. This includes the dynamic
libraries (DLL or SO files) as well as the required Lush files (SN
files).
- You want to deliver your product with an installation script. This
script will locate Lush and make sure that the Lush search path will
include both your directories and the Lush directories.
Lush provides two ways to reach that objective. These solutions differ
by the extend left to the user for reading your Lush files.
-
The ``open'' solution gives a maximal access to your Lush files. The
resulting product is very close to Lush itself. It appears as a Lisp
interpreter enriched with specialized primitives and libraries. The
neural network simulator SN28 is a typical example of this philosophy.
- The ``closed'' solution does not give access to the underlying Lisp
interpreter. All Lush functions are stored in a binary dump file that is
loaded during the startup procedure. The statistical forecasting tool
TL/Prevision is a typical example of this philosophy.
3.21.0.4.1.0. Installation of an "Open" Application Based on Lush
|
|
An ``open'' product based on Lush should be installed in a directory
replicating the organization of the Lush directories. In particular, you
should setup a subdirectory "lib"
containing the Lush files (SN files) and a directory
"bin" containing the dynamic libraries (DLL or SO files) and
shell scripts.
The user lauches your application using either a shell script (under
Unix) or an item of the start menu (under Windows). This operation
executes the following command:
<lushdir>/bin/winlush <yourdir>/lib/stdenv.lsh
where lushdir is the Lush root
directory and yourdir is the directory
where your application is installed.
File "yourdir/lib/stdenv.lsh"
should be directly derived from file
"lushdir/lib/stdenv.lsh" . This file must however
adjust the file search paths and start your application.
Here is a possible Lush code for this adjustment: This code stores the
directory name of your application into variable
mydir and adds your "lib"
directory in the file search path. It defines then variables
help-dir-list and help-book-list
that specify the directories searched for help files and the list of
precompiled help books.
;; Locate application directory.
(setq mydir (dirname (dirname file-being-loaded)))
;; Add application lib directory to file search path.
(addpath (concat-fname mydir "lib"))
;; Add application help directory to help file search path.
(setq help-dir-list
(list (concat-fname lushdir "help")
(concat-fname mydir "help") ) )
;; Define relevant help books.
(setq help-book-list
(list "lush.hlp" "ogre.hlp" "open.hlp"
"myapp.hlp" "mytool.lsh" ) )
3.21.0.4.1.1. Installation of a "Closed" Application Based on Lush
|
|
See: Lush Runtime.
See: (dump fname [
exec ])
A ``closed'' product based on Lush should be installed in a directory
hierarchy distinct from the Lush directories. This hierarchy should at
least contain the required dynamic library files (DLL or SO files) as
well as a dump file created with function dump
.
The user lauches your application using either a shell script (under
Unix) or an item of the start menu (under Windows). This operation
executes the following command:
<lushdir>/bin/winlush @<yourdir>/myapp.dump
where lushdir is the Lush root
directory and yourdir is the directory
where your application is installed.
This command starts Lush and loads the dump file
"myapp.dump" that you have prepared using the command
dump . During this process, the expression specified by the
second argument of command dump is
executed. This expression should load all required dynamic libraries
(DLL or SO files).
Here is a possible invocation of the dump
command. The executable expression code stores the directory name of
your application into variable mydir
and loads the dynamic library myapp.dll
.
(dump "myapp.dump"
'(progn
(setq mydir (dirname file-being-loaded))
(mod-load (concat-fname mydir "myapp.dll")) ) )
The Lush interpreter then restores the state of the session saved in the
dump file. Function startup is then
called. This function should define the file search paths according to
your needs, possibly initialize the ogre library, and finally run your
application.
3.21.0.4.1.2. Installation Script Considerations
|
|
The two proposed solutions for installing a Lush based application share
a common quality. There is no need to specify which directories contain
Lush or your application except in the command that launches the
application. Installation problems however are radically different under
Unix and Windows.
- Installation scripts under Unix are usually written using the
Bourne shell. The directories used by both Lush and your application can
be provided as arguments to the installation command. These directory
names should be used to generate the shell script that launches your
application.
- Installation scripts under Windows are subject to more stringent
requirements. Popular tools are available to design installation
programs. The tool ``InstallShield SDK (tm)'', for instance, is an
installation scripting language distributed for free with the Visual C++
CDROM.
The tool usually provides a dialog window where the user can specify
where the application should be installed. The installation program must
however find by itself where Lush has been installed. This information
is used to create start menu entries that launch your application.
This information is available in the Windows registry. The complete
installation path of WinLush is available by querying the following
registry key:
[HKEY_LOCAL_MACHINE]\Software\Neuristique\WinLush\Path
All installation scripting tools provide ways to examine the registry.
You can also manually explore the Windows registry using the program
"regedit" usually located in the
"Windows" directory.
Both Lush and TL/Open have been heavily used as a convenient development
platform for complex numerical applications. This experience makes us
confident that Lush and TL/Open make a fast and robust tool for
developping and debugging such applications.
Let us finish this TL/Open documentation with a few good programming
suggestions.
- Rather than aiming at the maximal performance,
choose the best compromise between performance and simplicity. This is
the price of reusable code.
- Take time to decide which functions should be implemented in C and
which functions are best implemented in Lush. The first one are very
fast, but are difficult to modify. The second ones are slower, but are
very easy to modify.
- Write the online help before implementing the functions. You will
discover that describing your ideas to other people is not obvious.
Writing the online help will force you to select a conceptually clear
architecture for your program.
- Test your primitives as soon as you write them. The Lush
interpreter makes this very easy: just load your module and try your
primitives. In particular, you should test your primitives for overlocks
and underlocks as explained in this document.