<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<HTML>
<HEAD>
<TITLE> S-Lang Library C Programmer's Guide, V1.4.0: Interpreter Interface</TITLE>
<LINK HREF="cslang-4.html" REL=next>
<LINK HREF="cslang-2.html" REL=previous>
<LINK HREF="cslang.html#toc3" REL=contents>
</HEAD>
<BODY>
<A HREF="cslang-4.html">Next</A>
<A HREF="cslang-2.html">Previous</A>
<A HREF="cslang.html#toc3">Contents</A>
<HR>
<H2><A NAME="s3">3. Interpreter Interface</A></H2>

<P> 
<P>The <B>S-Lang</B> library provides an interpreter that when embedded into
an application, makes the application extensible.  Examples of
programs that embed the interpreter include the <B>jed</B> editor and the
<B>slrn</B> newsreader.
<P>Embedding the interpreter is easy.  The hard part is to decide what
application specific built-in or intrinsic functions should be
provided by the application.  The <B>S-Lang</B> library provides some
pre-defined intrinsic functions, such as string processing
functions, and simple file input-output routines.  However, the
basic philosophy behind the interpreter is that it is not a
standalone program and it derives much of its power from the
application that embeds it.
<P>
<H2><A NAME="ss3.1">3.1 Embedding the Interpreter</A>
</H2>

<P> 
<P>Only one function needs to be called to embed the <B>S-Lang</B> interpreter
into an application: <CODE>SLang_init_slang</CODE>.  This function
initializes the interpreter's data structures and adds some intrinsic
functions:
<BLOCKQUOTE><CODE>
<PRE>
      if (-1 == SLang_init_slang ())
        exit (EXIT_FAILURE);
</PRE>
</CODE></BLOCKQUOTE>

This function does not provide file input output intrinsic nor does
it provide mathematical functions.  To make these as well as some
posix system calls available use
<BLOCKQUOTE><CODE>
<PRE>
     if ((-1 == SLang_init_slang ())    /* basic interpreter functions */
         || (-1 == SLang_init_slmath ()) /* sin, cos, etc... */
         || (-1 == SLang_init_stdio ()) /* stdio file I/O */
         || (-1 == SLang_init_posix_dir ()) /* mkdir, stat, etc. */
         || (-1 == SLang_init_posix_process ()) /* getpid, umask, etc. */
        )
       exit (EXIT_FAILURE);
</PRE>
</CODE></BLOCKQUOTE>

If you intend to enable all intrinsic functions, then it is simpler
to initialize the interpreter via
<BLOCKQUOTE><CODE>
<PRE>
     if (-1 == SLang_init_all ())
       exit (EXIT_FAILURE);
</PRE>
</CODE></BLOCKQUOTE>

See the \slang-run-time-library for more information about the
intrinsic functions.
<P>
<P>
<P>
<H2><A NAME="ss3.2">3.2 Calling the Interpreter</A>
</H2>

<P> 
<P>There are several ways of calling the interpreter.  The most common
method used by both <B>jed</B> and <B>slrn</B> is to use the
<CODE>SLang_load_file</CODE> function to interprete a file.  For example,
<B>jed</B> starts by loading a file called <CODE>site.sl</CODE>:
<BLOCKQUOTE><CODE>
<PRE>
     if (-1 == SLang_load_file ("site.sl"))
       {
         SLang_restart (1);
         SLang_Error = 0;
       }
</PRE>
</CODE></BLOCKQUOTE>

The <CODE>SLang_load_file</CODE> function returns zero upon if successful, or <CODE>-1</CODE>
upon failure.  The <CODE>SLang_restart</CODE> function resets the
interpreter back to its default state; however, it does not reset
<CODE>SLang_Error</CODE> to zero.  It is up to the application to
re-initialize the <CODE>SLang_Error</CODE> variable.
<P>There are several other mechanisms for interacting with the
interpreter.  For example, the <CODE>SLang_load_string</CODE> function
loads a string into the interpreter and interprets it:
<BLOCKQUOTE><CODE>
<PRE>
    if (-1 == SLang_load_string ("message (\"hello\");"))
      {
        SLang_restart (1);
        SLang_Error = 0;
      }
</PRE>
</CODE></BLOCKQUOTE>
<P>Typically, an interactive application will load a file via
<CODE>SLang_load_file</CODE> and then go into a loop that consists of
reading lines of input and sending them to the interpreter, e.g.,
<BLOCKQUOTE><CODE>
<PRE>
      while (EOF != fgets (buf, sizeof (buf), stdin))
        {
           if (-1 == SLang_load_string (buf))
             SLang_restart (1);
           SLang_Error = 0;
        }
</PRE>
</CODE></BLOCKQUOTE>
<P>Both <B>jed</B> and <B>slrn</B> use another method of interacting with the
interpreter.  They read key sequences from the keyboard and map
those key sequences to interpreter functions via the <B>S-Lang</B> keymap
interface.
<P>
<P>
<H2><A NAME="ss3.3">3.3 Intrinsic Functions</A>
</H2>

<P> 
<P>An intrinsic function is simply a function that is written in C and
is made available to the interpreter as a built-in function.  For
this reason, the words `intrinsic' and `built-in' are often used
interchangeably.
<P>Applications are expected to add application specific functions to
the interpreter.  For example, <B>jed</B> adds nearly 300 editor-specific
intrinsic functions.  The application designer should think
carefully about what intrinsic functions to add to the interpreter.
<P>
<H3>Restrictions on Intrinsic Functions</H3>

<P> 
<P>Intrinsic functions are required to follow a few rules to cooperate
with the interpreter.  
<P>Intrinsic function must take only pointer arguments.  This is
because when the interpreter calls an intrinsic function, it passes
value to the function by reference and <EM>not</EM> by value.
For example, intrinsic with the declarations:
<BLOCKQUOTE><CODE>
<PRE>
     int intrinsic_0 (void);
     int intrinsic_1 (char *s);
     void intrinsic_2 (char *s, int *i);
     void intrinsic_3 (int *i, double *d, double *e);
</PRE>
</CODE></BLOCKQUOTE>

are all valid.  However, 
<BLOCKQUOTE><CODE>
<PRE>
     int invalid_1 (char *s, int len);
</PRE>
</CODE></BLOCKQUOTE>

is not valid since the <CODE>len</CODE> parameter is not a pointer.
<P>Intrinsic functions can only return <CODE>void</CODE>, <CODE>int</CODE>,
<CODE>double</CODE>, or <CODE>char *</CODE>.  A function such as 
<BLOCKQUOTE><CODE>
<PRE>
    int *invalid (void);
</PRE>
</CODE></BLOCKQUOTE>

is not permitted since it does not return one of these types.  The
current implementation limits the number of arguments to <CODE>7</CODE>.
<P>Another restriction is that the intrinsic should regard all its
parameters as pointers to constant objects and make no attempt to
modify the value to which they point.  For example,
<BLOCKQUOTE><CODE>
<PRE>
      void truncate (char *s)
      {
         s[0] = 0;
      }
</PRE>
</CODE></BLOCKQUOTE>

is illegal since the function modifies the string <CODE>s</CODE>.
<P>
<P>
<P>
<H3>Adding a New Intrinsic</H3>

<P> 
<P>There are two mechanisms for adding an intrinsic function to the
interpreter: <CODE>SLadd_intrinsic_function</CODE> and
<CODE>SLadd_intrin_fun_table</CODE>.
<P>As an specific example, consider a function that will cause the
program to exit via the <CODE>exit</CODE> C library function.  It is not
possible to make this function an intrinsic because it does not meet
the specifications for an intrinsic function that were described
earlier.  However, one can call <CODE>exit</CODE> from a function that is
suitable, e.g.,
<BLOCKQUOTE><CODE>
<PRE>
     void intrin_exit (int *code)
     {
        exit (*code);
     }
</PRE>
</CODE></BLOCKQUOTE>

This function may be made available to the interpreter as as an
intrinsic via the <CODE>SLadd_intrinsic_function</CODE> routine:
<BLOCKQUOTE><CODE>
<PRE>
     if (-1 == SLadd_intrinsic_function ("exit", (FVOID_STAR) intrin_exit,
                                         SLANG_VOID_TYPE, 1,
                                         SLANG_INT_TYPE))
       exit (EXIT_FAILURE);
</PRE>
</CODE></BLOCKQUOTE>

This statement basically tells the interpreter that
<CODE>intrin_exit</CODE> is a function that returns nothing and takes a
single argument: a pointer to an integer (<CODE>SLANG_INT_TYPE</CODE>).
A user can call this function from within the interpreter
via
<BLOCKQUOTE><CODE>
<PRE>
     message ("Calling the exit function");
     exit (0);
</PRE>
</CODE></BLOCKQUOTE>

After printing a message, this will cause the <CODE>intrin_exit</CODE>
function to execute, which in turn calls <CODE>exit</CODE>.
<P>The most convenient mechanism for adding new intrinsic functions is
to create a table of <CODE>SLang_Intrin_Fun_Type</CODE> objects and add the
table via the <CODE>SLadd_intrin_fun_table</CODE> function.  The table will
look like:
<BLOCKQUOTE><CODE>
<PRE>
    SLang_Intrin_Fun_Type My_Intrinsics [] = 
    {
     /* table entries */
      MAKE_INTRINSIC_N(...),
      MAKE_INTRINSIC_N(...),
            .
            .
      MAKE_INTRINSIC_N(...),
      SLANG_END_TABLE
    };
</PRE>
</CODE></BLOCKQUOTE>

Construction of the table entries may be facilitated using a set of
<CODE>MAKE_INTRINSIC</CODE> macros defined in <CODE>slang.h</CODE>.  The main
macro is called <CODE>MAKE_INTRINSIC_N</CODE> and takes ?? arguments:
<BLOCKQUOTE><CODE>
<PRE>
    MAKE_INTRINSIC_N(name, funct-ptr, return-type, num-args,
                     arg-1-type, arg-2-type, ... arg-7-type)
</PRE>
</CODE></BLOCKQUOTE>

Here <CODE>name</CODE> is the name of the intrinsic function that the
interpreter is to give to the function. <CODE>func-ptr</CODE> is a pointer
to the intrinsic function taking <CODE>num-args</CODE> and returning
<CODE>ret-type</CODE>.  The final <CODE>7</CODE> arguments specifiy the argument
types.  For example, the <CODE>intrin_exit</CODE> intrinsic described above
may be added to the table using
<BLOCKQUOTE><CODE>
<PRE>
    MAKE_INTRINSIC_N("exit", intrin_exit, SLANG_VOID_TYPE, 1,
                     SLANG_INT_TYPE, 0,0,0,0,0,0)
</PRE>
</CODE></BLOCKQUOTE>
<P>While <CODE>MAKE_INTRINSIC_N</CODE> is the main macro for constructing
table entries, <CODE>slang.h</CODE> defines other macros that may prove
useful.  In particular, an entry for the <CODE>intrin_exit</CODE> function
may also be created using any of the following forms:
<BLOCKQUOTE><CODE>
<PRE>
    MAKE_INTRINSIC_1("exit", intrin_exit, SLANG_VOID_TYPE, SLANG_INT_TYPE)
    MAKE_INTRINSIC_I("exit", intrin_exit, SLANG_VOID_TYPE)
</PRE>
</CODE></BLOCKQUOTE>

See <CODE>slang.h</CODE> for related macros.  You are also encouraged to
look at, e.g., <CODE>slang/src/slstd.c</CODE> for a more extensive examples.
<P>The table may be added via the <CODE>SLadd_intrin_fun_table</CODE>
function, e.g.,
<BLOCKQUOTE><CODE>
<PRE>
    if (-1 == SLadd_intrin_fun_table (My_Intrinsics, NULL))
      {
         /* an error occurred */
      }
</PRE>
</CODE></BLOCKQUOTE>

Please note that there is no need to load a given table more than
once, and it is considered to be an error on the part of the
application it adds the same table multiple times.  For performance
reasons, no checking is performed by the library to see if a table
has already been added.
<P>
<P>
<H3>More Complicated Intrinsics</H3>

<P> 
The intrinsic functions described in the previous example were
functions that took a fixed number of arguments.  In this section we
explore more complex intrinsics such as those that take a variable
number of arguments.
<P>Consider a function that takes two double precision numbers and
returns the lesser: 
<BLOCKQUOTE><CODE>
<PRE>
     double intrin_min (double *a, double *b)
     {
        if (*a &lt; *b) return *a;
        return *b;
     }
</PRE>
</CODE></BLOCKQUOTE>

This function may be added to a table of intrinsics using
<BLOCKQUOTE><CODE>
<PRE>
    MAKE_INTRINSIC_2("min", intrin_min, SLANG_DOUBLE_TYPE,
                     SLANG_DOUBLE_TYPE, SLANG_DOUBLE_TYPE)
</PRE>
</CODE></BLOCKQUOTE>

It is useful to extend this function to take an arbitray number of
arguments and return the lesser.  Consider the following variant:
<BLOCKQUOTE><CODE>
<PRE>
    double intrin_min_n (int *num_ptr)
    {
       double min_value, x;
       unsigned int num = (unsigned int) *num_ptr;
       
       if (-1 == SLang_pop_double (&amp;min_value, NULL, NULL))
         return 0.0;
       num--;
       
       while (num &gt; 0)
         {
            num--;
            if (-1 == SLang_pop_double (&amp;x, NULL, NULL))
              return 0.0;
            if (x &lt; min_value) min_value = x;
         }
       return min_value;
    }
</PRE>
</CODE></BLOCKQUOTE>

Here the number to compare is passed to the function and the actual
numbers are removed from the stack via the <CODE>SLang_pop_double</CODE>
function.  A suitable table entry for it is
<BLOCKQUOTE><CODE>
<PRE>
    MAKE_INTRINSIC_I("min", intrin_min_n, SLANG_DOUBLE_TYPE)
</PRE>
</CODE></BLOCKQUOTE>

This function would be used in an interpreter script via a statement
such as
<BLOCKQUOTE><CODE>
<PRE>
      variable xmin = min (x0, x1, x2, x3, x4, 5);
</PRE>
</CODE></BLOCKQUOTE>

which computes the smallest of <CODE>5</CODE> values.
<P>The problem with this intrinsic function is that the user must
explicitly specify how many numbers to compare.  It would be more
convenient to simply use
<BLOCKQUOTE><CODE>
<PRE>
      variable xmin = min (x0, x1, x2, x3, x4);
</PRE>
</CODE></BLOCKQUOTE>

An intrinsic function can query the value of the variable
<CODE>SLang_Num_Function_Args</CODE> to obtain the necessary information:
<BLOCKQUOTE><CODE>
<PRE>
    double intrin_min (void)
    {
       double min_value, x;
       
       unsigned int num = SLang_Num_Function_Args;
       
       if (-1 == SLang_pop_double (&amp;min_value, NULL, NULL))
         return 0.0;
       num--;
       
       while (num &gt; 0)
         {
            num--;
            if (-1 == SLang_pop_double (&amp;x, NULL, NULL))
              return 0.0;
            if (x &lt; min_value) min_value = x;
         }
       return min_value;
    }
</PRE>
</CODE></BLOCKQUOTE>

This may be declared as an intrinsic using:
<BLOCKQUOTE><CODE>
<PRE>
    MAKE_INTRINSIC_0("min", intrin_min, SLANG_DOUBLE_TYPE)    
</PRE>
</CODE></BLOCKQUOTE>
<P>
<P>
<P>
<P>
<H2><A NAME="ss3.4">3.4 Intrinsic Variables</A>
</H2>

<P> 
<P>It is possible to access an application's global variables from
within the interpreter.  The current implementation supports the
access of variables of type <CODE>int</CODE>, <CODE>char *</CODE>, and
<CODE>double</CODE>.
<P>There are two methods of making an intrinsic variable available to
the interpreter.   The most straight forward method is to use the
function <CODE>SLadd_intrinsic_variable</CODE>:
<BLOCKQUOTE><CODE>
<PRE>
     int SLadd_intrinsic_variable (char *name, VOID_STAR addr, 
                                   unsigned char data_type, 
                                   int read_only);
</PRE>
</CODE></BLOCKQUOTE>

For example, suppose that <CODE>I</CODE> is an integer variable, e.g.,
<BLOCKQUOTE><CODE>
<PRE>
     int I;
</PRE>
</CODE></BLOCKQUOTE>

One can make it known to the interpreter as <CODE>I_Variable</CODE> via a
statement such as 
<BLOCKQUOTE><CODE>
<PRE>
     if (-1 == SLadd_intrinsic_variable ("I_Variable", &amp;I, 
                                          SLANG_INT_TYPE, 0))
       exit (EXIT_FAILURE);
</PRE>
</CODE></BLOCKQUOTE>

Similarly, if <CODE>S</CODE> is declared as
<BLOCKQUOTE><CODE>
<PRE>
    char *S;
</PRE>
</CODE></BLOCKQUOTE>

then 
<BLOCKQUOTE><CODE>
<PRE>
     if (-1 == SLadd_intrinsic_variable ("S_Variable", &amp;S,
                                          SLANG_STRING_TYPE, 1))
       exit (EXIT_FAILURE);
</PRE>
</CODE></BLOCKQUOTE>

makes <CODE>S</CODE> available as a <EM>read-only</EM> variable with the name
<CODE>S_Variable</CODE>.  Note that if a pointer variable is made available
to the interpreter, its value is managed by the interpreter and
not the application.  For this reason, it is recommended that such
variables be declared as <EM>read-only</EM>.
<P>It is important to note that if <CODE>S</CODE> were declared as an array of
characters, e.g.,
<BLOCKQUOTE><CODE>
<PRE>
     char S[256];
</PRE>
</CODE></BLOCKQUOTE>

then it would not be possible to make it directly available to the
interpreter.  However, one could create a pointer to it, i.e.,
<BLOCKQUOTE><CODE>
<PRE>
     char *S_Ptr = S;
</PRE>
</CODE></BLOCKQUOTE>

and make <CODE>S_Ptr</CODE> available as a read-only variable.
<P>One should not make the mistake of trying to use the same address
for different variables as the following example illustrates:
<BLOCKQUOTE><CODE>
<PRE>
     int do_not_try_this (void)
     {
        static char *names[3] = {"larry", "curly", "moe"};
        unsigned int i;

        for (i = 0; i &lt; 3; i++)
          {
             int value;
             if (-1 == SLadd_intrinsic_variable (names[i], (VOID_STAR) &amp;value,
                                                 SLANG_INT_TYPE, 1))
               return -1;
          }
        return 0;
     }
</PRE>
</CODE></BLOCKQUOTE>

Not only does this piece of code create intrinsic variables that use
the same address, it also uses the address of a local variable that
will go out of scope.
<P>The most convenient method for adding many intrinsic variables to
the interpreter is to create an array of <CODE>SLang_Intrin_Var_Type</CODE>
objects and then add the array via <CODE>SLadd_intrin_var_table</CODE>.
For example, the array
<BLOCKQUOTE><CODE>
<PRE>
    static SLang_Intrin_Var_Type Intrin_Vars [] =
    {
       MAKE_VARIABLE("I_Variable", &amp;I, SLANG_INT_TYPE, 0),
       MAKE_VARIABLE("S_Variable", &amp;S_Ptr, SLANG_STRING_TYPE, 1),
       SLANG_END_TABLE
    };
</PRE>
</CODE></BLOCKQUOTE>

may be added via
<BLOCKQUOTE><CODE>
<PRE>
    if (-1 == SLadd_intrin_var_table (Intrin_Vars, NULL))
      exit (EXIT_FAILURE);
</PRE>
</CODE></BLOCKQUOTE>

It should be rather obvious that the arguments to the
<CODE>MAKE_VARIABLE</CODE> macro correspond to the parameters of the
<CODE>SLadd_intrinsic_variable</CODE> function.  
<P>
<P>
<P>
<H2><A NAME="ss3.5">3.5 Aggregate Data Objects</A>
</H2>

<P> 
An aggregate data object is an object that can contain more than one
data value.  The <B>S-Lang</B> interpreter supports several such objects:
arrays, structure, and associative arrays.  In the following
sections, information about interacting with these objects is given.
<P>
<H3>Arrays</H3>

<P> 
An intrinsic function may interact with an array in several different
ways.  For example, an intrinsic may create an array and return it.
The basic functions for manipulating arrays include:
<BLOCKQUOTE><CODE>
<PRE>
   SLang_create_array
   SLang_pop_array_of_type
   SLang_push_array
   SLang_free_array
   SLang_get_array_element
   SLang_set_array_element
</PRE>
</CODE></BLOCKQUOTE>

The use of these functions will be illustrated via a few simple
examples.
<P>The first example shows how to create an return an array of strings
to the interpreter.  In particular, the names of the four seasons of
the year will be returned:
<BLOCKQUOTE><CODE>
<PRE>
    void months_of_the_year (void)
    {
       static char *seasons[4] =
         {
            "Spring", "Summer", "Autumn", "Winter"
         };
       SLang_Array_Type *at;
       int i, four;
       
       four = 4;
       at = SLang_create_array (SLANG_STRING_TYPE, 0, NULL, &amp;four, 1);
       if (at == NULL)
         return;
       
       /* Now set the elements of the array */
       for (i = 0; i &lt; 4; i++)
         {
           if (-1 == SLang_set_array_element (at, &amp;i, &amp;seasons[i]))
             {
                SLang_free_array (at);
                return;
             }
         }
      
      (void) SLang_push_array (at, 0);
      SLang_free_array (at);
    }
</PRE>
</CODE></BLOCKQUOTE>

This example illustrates several points.  First of all, the
<CODE>SLang_create_array</CODE> function was used to create a 1 dimensional
array of 4 strings.  Since this function could fail, its return value
was checked.  Then the <CODE>SLang_set_array_element</CODE> function was
used to set the elements of the newly created array.  Note that the
address containing the value of the array element was passed and not
the value of the array element itself.  That is,
<BLOCKQUOTE><CODE>
<PRE>
    SLang_set_array_element (at, &amp;i, seasons[i])
</PRE>
</CODE></BLOCKQUOTE>

was not used.  The return value from this function was also checked
because it too could also fail.  Finally, the array was pushed onto
the interpreter's stack and then it was freed.  It is important to
understand why it was freed.  This is because arrays are
reference-counted.  When the array was created, it was returned with
a reference count of <CODE>1</CODE>.  When it was pushed, the reference
count was bumped up to <CODE>2</CODE>.  Then since it was nolonger needed by
the function, <CODE>SLang_free_array</CODE> was called to decrement the
reference count back to <CODE>1</CODE>.  For convenience, the second
argument to <CODE>SLang_push_array</CODE> determines whether or not it is to
also free the array.  So, instead of the two function calls:
<BLOCKQUOTE><CODE>
<PRE>
   (void) SLang_push_array (at, 0);
   SLang_free_array (at);
</PRE>
</CODE></BLOCKQUOTE>

it is preferable to combine them as
<BLOCKQUOTE><CODE>
<PRE>
   (void) SLang_push_array (at, 1);
</PRE>
</CODE></BLOCKQUOTE>
<P>The second example returns a diagonal array of a specified size to
the stack.  A diagonal array is a 2-d array with all elements zero
except for those along the diagonal, which have a value of one:
<BLOCKQUOTE><CODE>
<PRE>
   void make_diagonal_array (int n)
   {
      SLang_Array_Type *at;
      int dims[2];
      int i, one;

      dims[0] = dims[1] = n;
      at = SLang_create_array (SLANG_INT_TYPE, 0, NULL, dims, 2);
      if (at == NULL)
        return;
      
      one = 1;
      for (i = 0; i &lt; n; i++)
        {
           dims[0] = dims[1] = i;
           if (-1 == SLang_set_array_element (at, dims, &amp;one))
             {
                SLang_free_array (at);
                return;
             }
        }
      
      (void) SLang_push_array (at, 1);
   }
</PRE>
</CODE></BLOCKQUOTE>

In this example, only the diagonal elements of the array were set.
This is bacause when the array was created, all its elements were
set to zero.
<P>Now consider an example that acts upon an existing array.  In
particular, consider one that computes the trace of a 2-d matrix,
i.e., the sum of the diagonal elements:
<BLOCKQUOTE><CODE>
<PRE>
   double compute_trace (void)
   {
      SLang_Array_Type *at;
      double trace;
      int dims[2];

      if (-1 == SLang_pop_array_of_type (&amp;at, SLANG_DOUBLE_TYPE))
        return 0.0;
      
      /* We want a 2-d square matrix.  If the matrix is 1-d and has only one
         element, then return that element. */
      trace = 0.0;
      if (((at-&gt;num_dims == 1) &amp;&amp; (at-&gt;dims[0] == 1))
          || ((at-&gt;num_dims == 2) &amp;&amp; (at-&gt;dims[0] == at-&gt;dims[1])))
        {
           double dtrace;
           int n = at-&gt;dims[0];

           for (i = 0; i &lt; n; i++)
             {
                dims[0] = dims[1] = i;
                (void) SLang_get_array_element (at, &amp;dims, &amp;dtrace);
                trace += dtrace;
             }
        }
     else SLang_verror (SL_TYPE_MISMATCH, "Expecting a square matrix");
     
     SLang_free_array (at);
     return trace;
   } 
</PRE>
</CODE></BLOCKQUOTE>

In this example, <CODE>SLang_pop_array_of_type</CODE> was used to pop an
array of doubles from the stack.  This function will make implicit
typecasts in order to return an array of the requested type.
<P>
<P>
<H3>Structures</H3>

<P> 
<P>For the purposes of this section, we shall differentiate structures
according to whether or not they correspond to an application defined
C structure.  Those that do are called intrinsic structures, and
those do not are called <B>S-Lang</B> interpreter structures.
<P>
<H3>Interpreter Structures</H3>

<P>
<P>The following simple example shows how to create and return a
structure to the stack with a string an integer field:
<BLOCKQUOTE><CODE>
<PRE>
    int push_struct_example (char *string_value, int int_value)
    {
       char *field_names[2];
       unsigned char field_types[2];
       VOID_STAR field_values[2];
       
       field_names[0] = "string_field";
       field_types[0] = SLANG_STRING_TYPE;
       field_values[0] = &amp;string_value;
       
       field_names[1] = "int_field";
       field_types[1] = SLANG_INT_TYPE;
       field_values[1] = &amp;int_value;
       
       if (-1 == SLstruct_create_struct (2, field_names, 
                                            field_types, field_values))
         return -1;
       return 0;
    }
</PRE>
</CODE></BLOCKQUOTE>

Here, <CODE>SLstruct_create_struct</CODE> was used to push a
structure with the specified field names and values onto the
interpreter's stack.
<P>
<H3>Intrinsic Structures</H3>

<P>
<P>Here we show how to make intrinsic structures available to
the interpreter.  The simplest interface is to structure pointers and not
to the actual structures themselves.  The latter would require the
interpreter to be involved with the creation and destruction of the
structures.  Dealing with the pointers themselves is far simpler.
<P>As an example, consider an object such as
<BLOCKQUOTE><CODE>
<PRE>
    typedef struct _Window_Type
    {
       char *title;
       int row;
       int col;
       int width;
       int height;
    } Window_Type;
</PRE>
</CODE></BLOCKQUOTE>

which defines a window object with a title, size (<CODE>width</CODE>,
<CODE>height</CODE>), and location (<CODE>row</CODE>, <CODE>col</CODE>).
<P>We can make variables of type <CODE>Window_Type</CODE> available to the
interpreter via a table as follows:
<BLOCKQUOTE><CODE>
<PRE>
   static SLang_IStruct_Field_Type Window_Type_Field_Table [] =
   {
     MAKE_ISTRUCT_FIELD(Window_Type, title, "title", SLANG_STRING_TYPE, 1),
     MAKE_ISTRUCT_FIELD(Window_Type, row, "row", SLANG_INT_TYPE, 0),
     MAKE_ISTRUCT_FIELD(Window_Type, col, "col", SLANG_INT_TYPE, 0),
     MAKE_ISTRUCT_FIELD(Window_Type, width, "width", SLANG_INT_TYPE, 0),
     MAKE_ISTRUCT_FIELD(Window_Type, height, "height", SLANG_INT_TYPE, 0),
     SLANG_END_TABLE
   };
</PRE>
</CODE></BLOCKQUOTE>

More precisely, this defines the layout of the <CODE>Window_Type</CODE> structure.
Here, the <CODE>title</CODE> has been declared as a read-only field.  Using
<BLOCKQUOTE><CODE>
<PRE>
     MAKE_ISTRUCT_FIELD(Window_Type, title, "title", SLANG_STRING_TYPE, 0),
</PRE>
</CODE></BLOCKQUOTE>

would allow read-write access.
<P>Now suppose that <CODE>My_Window</CODE> is a pointer to a <CODE>Window_Type</CODE>
object, i.e.,
<BLOCKQUOTE><CODE>
<PRE>
    Window_Type *My_Window;
</PRE>
</CODE></BLOCKQUOTE>

We can make this variable available to the interpreter via the
<CODE>SLadd_istruct_table</CODE> function:
<BLOCKQUOTE><CODE>
<PRE>
    if (-1 == SLadd_istruct_table (Window_Type_Field_Table,
                                   (VOID_STAR) &amp;My_Window,
                                   "My_Window"))
      exit (1);
</PRE>
</CODE></BLOCKQUOTE>

This creates a S-Lang interpreter variable called <CODE>My_Win</CODE> whose value
corresponds to to the <CODE>My_Win</CODE> structure.  This would permit one to
access the fields of <CODE>My_Window</CODE> via <B>S-Lang</B> statements such as
<BLOCKQUOTE><CODE>
<PRE>
     define set_width_and_height (w,h)
     {
         My_Win.width = w;
         My_Win.height = h;
     }
</PRE>
</CODE></BLOCKQUOTE>
<P>It is extremely important to understand that the interface described in
this section does not allow the interpreter to create new instances of
<CODE>Window_Type</CODE> objects.  The interface merely defines an association or
correspondence between an intrinsic structure pointer and a <B>S-Lang</B>
variable.  For example, if the value of <CODE>My_Window</CODE> is <CODE>NULL</CODE>, then
<CODE>My_Win</CODE> would also be <CODE>NULL</CODE>.
<P>One should be careful in allowing read/write access to character string
fields.  If read/write access is allowed, then the application should
always use the <CODE>SLang_create_slstring</CODE> and <CODE>SLang_free_slstring</CODE>
functions to set the character string field of the structure.  Finally,
note that access to character array fields is not permitted via this
interface.  That is, a structure such as
<BLOCKQUOTE><CODE>
<PRE>
     typedef struct
     {
        char name[32];
     }
     Name_Type;
</PRE>
</CODE></BLOCKQUOTE>

is not permitted since <CODE>char name[32]</CODE> is not a
<CODE>SLANG_STRING_TYPE</CODE> object.
<P>
<P>
<P>
<P>
<P>
<P>
<HR>
<A HREF="cslang-4.html">Next</A>
<A HREF="cslang-2.html">Previous</A>
<A HREF="cslang.html#toc3">Contents</A>
</BODY>
</HTML>
