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.



3.21.0.0. Introduction


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.

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:

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:

    /* --- HEADER SECTION --- 
     * This section loads the appropriate header files
     * (WIN32 details are specific to Visual C++ 4.x.)
     */
    #include "tlopen.h"
    /* --- 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:



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.



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.



3.21.0.1.2.0.0. Example


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:



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 )

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.




3.21.0.2. Lush Internals


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:



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. Lush Objects




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 .

p->at_union.at_number
p->at_union.at_cons.at_car
p->at_union.at_cons.at_cdr

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.

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

and one important exception



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:

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:

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.

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.



3.21.0.2.3.0. Lists


Lists are a collection of linked pairs composed of

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 .



3.21.0.2.3.0.1. Access


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.3.0.2. Creation


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''.



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 .



3.21.0.2.3.1. Numbers


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.



3.21.0.2.3.1.1. Access


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.3.1.2. Creation


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''.



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:

Therefore field p- Class> of the structure points to a structure class. There is a single structure class for each kind of external object.

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>.



3.21.0.2.3.2.1. Access


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.3.2.2. Creation


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''.



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:



3.21.0.2.4.0. Predicates


Two macros have been defined to test if an at* pointer references a list.



3.21.0.2.4.1. Access


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:



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 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.



3.21.0.2.4.2. Creation


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''.



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.



3.21.0.2.4.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.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 .

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 .

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 .



3.21.0.2.5. Strings


Strings are implemented as external objects of class string_class . Field p- Object> always points to a C structure of type struct string .



3.21.0.2.5.0. Predicates


Two macros have been defined to test if an at* pointer references a list.



3.21.0.2.5.1. Access


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);



3.21.0.2.5.2. Creation


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''.



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.



3.21.0.2.6. Symbols


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.



3.21.0.2.6.0. Predicates


Two macros have been defined to test if an at* pointer references a list.



3.21.0.2.6.1. Access


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 .



3.21.0.2.6.2. Creation


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''.



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.



3.21.0.2.7. Functions


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:

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.

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.
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.

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 .

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 .

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 .

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 .

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 .

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 .

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.

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.

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 .

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 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:



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:

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:

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:



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:

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.

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.



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.



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.

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 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:

Lush provides two ways to reach that objective. These solutions differ by the extend left to the user for reading your Lush files.



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.

   [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.



3.21.0.5. Conclusion


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.