






   S-Lang

   Programmer's

   Guide



   Version 0.1



   John E. Davis
   Tue Nov 21 11:57:21 1995








1 Introduction
===============

   S-Lang (pronounced ``sssslang'') is a powerful stack based interpreter
   that supports a C-like syntax.  It has been designed from the beginning
   to be easily embedded into a program to make it extensible. S-Lang also
   provides a way to quickly develop and debug the application embedding it
   in a safe and efficient manner.  Since S-Lang resembles C, it is easy to
   recode S-Lang procedures in C if the need arises.

   The S-Lang language features both global variables and local variables,
   branching and looping constructs, as well as user defined functions.
   Unlike many interpreted languages, S-Lang allows functions to be
   dynamically loaded (function autoloading).  It also provides constructs
   specifically designed for error handling and recovery as well as
   debugging aids (e.g., tracebacks).

   The core language currently implements signed integer, string, and
   floating point data types.  Applications may also create new types
   specific to the application (e.g., complex numbers).  In addition, S-Lang
   supports multidimensional arrays those types as well as any application
   defined types.

   The syntax of the language is quite simple and is very similar to C.
   Unlike C, S-Lang variables are untyped and inherit a type upon
   assignment. The actual type checking is performed at run time.  In
   addition, there is limited support for pointers.

  

2 Variables
============

   S-Lang is an untyped language and only requires that an variable be
   declared before it is used.  Variables are declared using the `variable'
   keyword followed by a comma separated list of variable names, e.g.,

         variable larry, curly, moe;

   As in C, all statements must end with a semi-colon.  Variables can be
   declared to be either global or local. Variables defined
   inside functions are of the local variety and have no meaning outside the
   function.

   It is legal to execute statements in a variable declaration list.  That
   is,

         variable x = 1, y = sin (x);

   are legal variable declarations.  This also provides a convenient way
   of initializing a variable.  
   
   The variable's type is determined when the variable is assigned a value.
   For example, in the above example, `x' is an integer and `y' is a float
   since `1' is an integer and the `sin' function returns a floating point
   type.
   


3 Functions
============

   Like variables, functions must be declared before they may be used. The
   `define' keyword is used for this purpose.  For example,   

         define factorial ();

   is sufficient to declare a function named `factorial'.  Unlike
   `variable' keyword, the `define' keyword does not accept a list of names.
   Usually, the above form is used only for recursive functions.  The
   function name is almost always followed by a parameter list and the
   body of the function, e.g.,

         define my_function (x, y, z)
         {
           <body of function>
         }

   Here `x', `y', and `z' are also implicitly declared as local variables.
   In addition, the function body must be enclosed in braces.
   
   Functions may return zero, one or more values.  For example,

        define sum_and_diff (x, y)
         {
            variable sum, diff;

            sum = x + y;  diff = x - y;
            return sum, diff;
         }

   is a function returning two values.
   
   Please note when calling a function that returns a value, the value
   returned cannot be ignored.   See the section below on assignment
   statements for more information about this important point.



4 Statements and Expressions
=============================

   A statement may occur globally outside of functions or locally within
   functions.   If the expression occurs inside a function, it is executed
   only when the function is called.  However, statements which occur
   outside a function context are evaluated immediately.
   
   All statements must end in a semi-colon.
   
   

4.1 Assignment Statements
--------------------------


   An assignment statement follows the syntax:
   
      <variable name> = <expression>;

   Whitespace is required on both sides of the equal sign.  For example,

        x = sin (y);

   is correct but

        x =sin(y);  x= sin(y); x=sin(y);

   will generate syntax errors.
   
   Often, functions return more than one value.  For example, 

        define sum_and_diff (x, y)
         {
            return x + y, x - y;
         }

   returns two values.  The most general assignment statement syntax is 

     ( <var_1>, <var_2>, ..., <var_n> ) = <expression>;

   e.g.,

        (s, d) = sum_and_diff (10, 2);

   To ignore one of the return values, simply omit the variable name from
   the list.  For example, 
   

        (s, ) = sum_and_diff (10, 2);

   may be used if one is only interested in the first return value.
   
   Some functions return a variable number of values.  Usually, the first
   value will indicate the actual number of return values.

   For example, the `fgets' function returns either one or two values.  If
   the first value is zero, there is no other return value.  In this case,
   one must use another form of assignment since the previously discussed
   forms are inadequate.  For example,    

      n = fgets (fd);
      if (n != 0)
        {
           s = ();
           .
           .
        }

   In this example, the first value returned is assigned to `n' and tested.
   If it is non-zero, the second return value is assigned to `s'.   The
   empty set of parenthesis is required.

   Please note that RETURN VALUES CANNOT BE IGNORED.  There are several ways
   of dealing with a return value when one does not care about it.  For
   example, the function `fflush' returns a value.  However, most C
   programs that call this function almost always ignore the return value.
   In S-Lang, one can use any of the following forms:

       variable dummy;   
       dummy = fflush (fd);
     
       () = fflush (fd);
     
       fflush (fd); pop ();

  The second form is perhaps the most clear way of indicating that the return
  value is being ignored.


4.2 Binary Operators
---------------------


   S-Lang supports a variety of binary operators.  These include the usual
   arithmetic operators (`+', `-', `*', `/', and `mod'), the comparison
   operators (`>', `>=', `<', `<=', `!=', and `==') as well boolean
   operators (`or' and `and') and bitwise operators (`|', `&', `xor', `shl'
   and `shr').  Like the assignment operator, these operators must also be
   surrounded by whitespace.  That is, 
   

        x = y + z;

   is a legal statement but `x = y+z;' is not legal.

   To use these operators effectively, in addition to understanding the
   meaning of the operation, one must also understand the precedence level
   of the operator.

   In S-Lang, there are only three levels of precedence.  The highest level
   consists of the `*', `/', and `mod' operators.  The second level consists
   of the `+' and `-' operators.  All other binary operators fall into the
   last level of precedence.  Within a precedence level, operators are
   evaluated left to right.  Parenthesis may be used to change the order of
   evaluation.  For example, the expression:   

        a == b or c == d

   IS NOT the same as:

        (a == b) or (c == d)

   since `==' and `or' share the same level of precedence.  In fact, the
   expression without parenthesis is evaluated left to right and is
   equivalent to `((a == b) or a) == c'.

   Finally, S-Lang supports the increment and decrement operators `++'
   and `--', and the arithmetic assignment operators `+=' and
   `-='.  Presently, these operators only work with integer types and a
   type mismatch error will result from the use of these operators with
   other types.

   These following table shows the meaning of these operators.

        
      Expression         Meaning
      ----------        ---------
       ++x;             x = x + 1;
       x++;             x = x + 1;
       --x;             x = x - 1;
       x--;             x = x - 1;
       x += n;          x = x + n;
       x -= n;          x = x - n;


   Note that S-Lang does not distinguish between `x--' and `--x'
   since neither of these forms return a value as they do in C.  With this
   in mind, do not use constructs such as:

      while (i--) ....     % test then decrement 
      while (--i) ....     % decrement first then test 

   Instead, use something like

      while (i, i--) ....  % test then decrement
      while (i--, i) ....  % decrement first then test

   These operators work only on simple scalar variables. In particular,
   `++(x)' is NOT the same as `++x' and will generate an error.

   Whenever possible, these latter four operations should be used since they
   execute 2 to 3 times faster than the longer forms.



4.2.1 Short Circuit Boolean Evaluation
.......................................


   The boolean operators `or' and `and' ARE NOT SHORT CIRCUITED as they are
   in some languages.  S-Lang uses the `orelse' and `andelse' operators for
   short circuit boolean evaluation.  However, these are not binary
   operators. Expressions of the form:

          <expr_1> and <expr_2> and <expr_3> ... and <expr_n>

   can be replaced by the short circuited version using `andelse':

        andelse {<expr_1>} {<expr_2>} {<expr_3>} ... {<expr_n>}

   A similar syntax holds for the `orelse' operator.  For example, consider
   the statement: 

      if ((x != 0) and (1 / x < 10)) do_something ();

   Here, if `x' were to have a value of zero, a division by zero error
   would occur because even though `x != 0' evaluates to zero, the
   `and' operator is not short circuited and the `1 / x' expression
   would be evaluated. For this case, the `andelse' operator could be
   used to avoid this problem:

        if (andelse
            {x != 0}
            {1 / x < 10})  do_something ();



4.3 Unary Operators
--------------------


   The UNARY operators operate only upon a single integer.  They are defined
   by the following table below.  In this table, the variable `i' is an
   integer type and `x' represents either a floating point or integer
   variable. 

     Unary Expression      Meaning
     ----------------    -------------------------------------------------
        not (i)          if i is non-zero return zero else return non-zero
        ~(i)             bitwise not
        sqr(x)           the square of x
        mul2(x)          multiplies x by 2
        chs (x)          change the sign of x
        -x               same as chs (x)
        sign (x)         +1 if x > 0, -1 if x < 0, and 0 if x equals 0
        abs (x)          absolute value of x

   Note the following points:

   

  *  All unary operators except `not' and `' operator on both integer
      and floating point types.


  *  The `!' operator used in C is not used in S-Lang,  `not' must be
      used instead.


  *  The bitwise not operator `' must enclose its argument in
      parenthesis.  `i' will be flagged as a syntax error.


  *  Some applications which embed S-Lang may overload these operators
      to work with application defined data types.

     


4.4 Data Types
---------------


   Currently, S-Lang only supports integer, floating point (double
   precision), and character string data types.  It is possible for an
   application that embeds S-Lang to define other, application specific,
   data types (e.g., complex numbers).   In addition, the language supports
   arrays of any of these types (including application specific types).
   

4.4.1 Integers
...............

   
   Unsigned integers are not supported.  An integer can be specified in one
   of several ways:



  *  As a decimal integer consisting of the characters `0' through
      `9', e.g., `127'.  The number cannot begin with a leading
      `0'.  That is, `0127' is not the same as `127'.


  *  Using hexidecimal (base 16) notation consisting of the characters
      `0' to `9' and `A' through `F'.  The hexidecimal
      number must be preceded by the characters `0x'.  For example,
      `0x7F' is the same thing as decimal `127'.


  *  In Octal notation using characters `0' through `7'.  The Octal
      number must begin with a leading `0'.  For example, `0177' is
      the same thing as `127' decimal.


  *  Using character notation containing a character enclosed in single
      quotes as `'a''.   The value of the integer specified this way will
      lie in the range 0 to 256 and will be determined by the ascii
      value of the character in quotes.  For example, 

              i = '0';

      results in a value of 48 for `i' since the character `0' has an
      ascii value of 48. 

      Strictly speaking, S-Lang has no character type.



    Any integer may be preceded by a minus sign to indicate that it is a
    negative integer.
   
   

4.4.2 Floating Point Numbers
.............................


    Floating point numbers must contain either a decimal point or an
    exponent (or both). Here are examples of specifying the same floating
    point number:

         12.,    12.0,    12e0,   1.2e1,   120e-1,   .12e2

    Note that `12' is NOT a floating point number since it contains neither
    a decimal point nor an exponent.  If fact, `12' is an integer.
    
      

4.4.3 Strings
..............

    
    A literal string must be enclosed in double quotes as in:

      "This is a string".

    Although there is no imposed limit on the length of a string, literal
    strings must be less than 256 characters.  It is possible to go beyond
    this limit by string concatenation.  Any character except a newline
    (ascii 10) or the null character (ascii 0) may appear in the 
    definition of the string.

    The backslash is a special character and is used to include special
    characters (such as a newline character) in the string. The special
    characters recognized are:

       \"    --  double quote
       \'    --  single quote
       \\    --  backslash
       \a    --  bell character
       \t    --  tab character
       \n    --  newline character
       \e    --  escape  (S-Lang extension)
       \xhhh --  character expressed in HEXIDECIMAL notation
       \ooo  --  character expressed in OCTAL notation
       \dnnn --  character expressed in DECIMAL (S-Lang extension)

    For example, to include the double quote character as part of the
    string, it is to be preceded by a backslash character, e.g.,

                            "This is a \"quote\""



4.5 Mixing integer and floating point arithmetic.
--------------------------------------------------


   If a binary operation (+, -, * , /) is performed on two integers, the
   result is an integer.  If at least one of the operands is a float, the
   other is converted to float and the result is float.  For example:


    11 / 2           --> 5   (integer)
    11 / 2.0         --> 5.5 (float)
    11.0 / 2         --> 5.5 (float)
    11.0 / 2.0       --> 5.5 (float)

   Finally note that only integers may be used as array indices, for loop
   control variables, shl, shr, etc bit operations.  Again, if there is any
   doubt, use the conversion functions `int' and `float' where appropriate:


    int (1.5)         --> 1 (integer)
    float(1.5)        --> 1.5 (float)
    float (1)         --> 1.0 (float)



4.6 Conditional and Branching Statements
-----------------------------------------


   S-Lang supports a wide variety of looping (`while', `do while',
   `loop', `for', `forever', and `_for') and branching
   (`if', `!if', `else', `andelse', `orelse', and
   `switch') statements.

   These constructs operate on code statements grouped together in 
   blocks.  A block is a sequence of S-Lang statements enclosed in braces
   and may contain other blocks.  However, a block cannot include function
   declarations; function declarations must take place at the top level.  In
   the following, `statement' refers to either a single S-Lang statement
   or to a block of statements and `{ block }' refers to a block of
   statements.


4.6.1 if, if-else
..................

   
              if (expression) statement;

        Evaluates `statement' if the result of `expression' is
        non-zero.  The `if' statement can also be followed by an `else':

              if (expression) statement; else statement;

    

4.6.2 !if
..........

   
              !if (expression) statement;

        Evaluates `statement' if `expression' is evaluates to zero.  Note
        that there is no `!if-else' statement.
        

4.6.3 orelse, andelse
......................

   
        These constructs were discussed earlier.  The syntax for the
        `orelse' statement is:
        
            orelse { block } { block } ... { block }.

        This causes each of the blocks to be executed in turn until one of
        them returns a non-zero integer value.  The result of this statement
        is the integer value returned by the last block executed.  For
        example,

            orelse { 0; } { 6; } { 2; } {3; }

        returns `6' since the second block returns the non-zero result `6'
        and the last two block will not get executed.

        The syntax for the `andelse' statement is:

            andelse { block } { block } ... { block }.

        Each of the blocks will be executed in turn until one of
        them returns a zero value.  The result of this statement is the
        integer value returned by the last block executed.  For example,
        
            andelse { 6; } { 2; } { 0; } {4; }

        returns `0' since the third block will be the last to execute.

     

4.6.4 while
............

   
               while (expression) statement;

       Repeat `statement' while `expression' returns non-zero.  For example, 

          j = 20; i = 10; while (i) { j = j + i; i = i - 1; }

       will cause the block to execute 10 times.
        

4.6.5 do-while
...............

   
               do statement; while (expression);

        Execute `statement' then test `expression'.  Repeat while
	`expression' is returns non-zero.  This guarantees that
	`statement' will be executed at least once.


4.6.6 for
..........

   
               for (expr1; expr2; expr3) statement;

        Evaluate `expr1' first.  Then loop executing `statement' while
        `expr2' returns non-zero.  After every evaluation of `statement'
        evaluate `expr3'.  For example,

             variable i, sum;
             sum = 0;
             for (i = 1; i <= 10; i++) sum += i;

        computes the sum of the first 10 integers.
          

4.6.7 loop
...........


               loop (n) statement;

        Evaluate `statement' `n' times.  If `n' is less than
	zero, `statement' is not executed.
       

4.6.8 forever
..............

    
        forever statement;

       Loop evaluating statement forever.  Forever means until either a
       `break' or `return' statement is executed.
       

4.6.9 switch
.............

    
       The switch statement deviates the most from its C counterpart.  The
       syntax is:

          switch (x)
            { ...  :  ...}
              .
              .
            { ...  :  ...}

        Here the object `x' is pushed onto the stack and the sequence of
        blocks is executed.  The `:' operator is a S-Lang special symbol
        which means to test the top item on the stack, if it is non-zero,
        the rest of the block is executed and control then passes out of the
        switch statement.  If the test is false, execution of the block is
        terminated and the process repeats for the next block.
        
        The special keyword `case'  may be used to compare the value of
        objects.   It  returns non-zero if the objects correspond to the
        same object and zero otherwise.
        
        For example:

            variable x = 3;
            switch (x)
              { case 1: print("Number is one.")}
              { case 2: print("Number is two.")}
              { case 3: print("Number is three.")}
              { case 4: print("Number is four.")}
              { case 5: print("Number is five.")}
              { pop(); print ("Number is greater than five.")}

        Here `x' is assigned a value of 3 and the `switch'
	statement pushes the 3 onto the stack.  Control then passes to the
	first block.  The first block uses the `case' construct to
	compare the top top stack item (3) with 1. This test will result
	with zero at the top of the stack. The `:' operator will then
	pop the top stack item and if it is zero, control will be passed to
	the next block where the process will be repeated.  In this case,
	control will pass to the second block and on to the third block.
	When the `:' operator is executed for the third block, a
	non-zero value will be left on the top of the stack and the
	`print' function will be called. Control then passes onto the
	statement following the last block of the `switch' statement.

        Note that, in this example, the last block does not test the value
	of `x' against anything. Instead, if this block is executed, the
	top stack item (the value of `x' in this case) will be removed
	from the stack by the `pop' function and the rest of the block
	executed.

        Unlike most other languages with some form of switch statement, `x'
        does not have to be a simple integer.  For example, the following is
        perfectly acceptable:

            variable x;
            x = "three";
            switch (x)
              { case "one": print("Number is 1.")}
              { case "two": print("Number is 2.")}
              { case "three": print("Number is 3.")}
              { case "four": print("Number is 4.")}
              { case "five": print("Number is 5.")}
              { pop(); print ("Number is greater than 5.")}

        Again, the `case' function is used to test the top stack item and
        the last block serves as a ``catch-all''.
        


4.6.10 break, return, continue
...............................


   S-Lang also includes the non-local transfer functions `return', `break',
   and `continue'.  The `return' statement causes control to return to the
   calling function while the `break' and `continue' statements are used in
   the context of loop structures.  Here is an example:


       define fun ()
       {
          forever 
            {
               s1;
               s2;
               ..
               if (condition_1) break;
               if (condition_2) return;
               if (condition_3) continue;
               ..
               s3;
            }
          s4;
          ..
       }

   Here, a function `fun' has been defined that includes a `forever'
   loop which consists of statements `s1', `s2',...,`s3' and
   3 boolean conditions.  As long as `condition_1',
   `condition_2', and `condition_3' return 0, statements
   `s1', `s2',...,`s3' will be repeatedly executed.  However,
   if `condition_1' returns a non-zero value, the `break' statement
   will get executed, and control will pass out of the `forever' loop to
   the statement immediately following the loop which in this case is
   `s4'. Similarly, if `condition_2' returns a non-zero number,
   `return' will cause control to pass back to the caller of `fun'.
   Finally, the `continue' statement will cause control to pass back to the
   start of the loop, skipping the statement `s3' altogether.


4.7 Arrays
-----------



   Arrays are created using the function call `create_array'.  The type of
   the array and the size of the array are specified by parameters to this
   function.  The calling syntax is:
   

       x = create_array (<type>, i_1, i_2 ... i_dim, dim);

   Here a `dim' dimensional array of type specified by `<type>' is
   created.  The size of the array in the nth dimension is specified by
   the parameters `i_1'...`i_n' parameter.  The `<type>'
   parameter may be any one of the values given in the following table:

      Parameter         Type of array
      ---------         -------------
        's'             array of strings
        'f'             array of floats
        'i'             array of integers
        'c'             array of characters

   Other integer values for the type may be given for applications which
   defined application specific types to create arrays of those types.

   In the current implementation, `dim' cannot be larger than 3.  Also
   note that space is dynamically allocated for the array and that, upon
   assignment, copies of the array are NEVER used.  Rather, references to
   the array are used by the assignment statements.

   For example:
   

      variable a = create_array ('f', 10, 20, 1);
      variable b = a;

   This creates a 2 dimensional 10 x20 array of 200 floats and
   assigns it to `a'.  The second statement makes the variable `b' also
   refer to the array specified by variable `a'.

   Accessing a specific element of the array may be accomplished by placing
   the ``coordinates'' of the element in square brackets.  For example, to
   access the (3, 4) element of the above array use `a[3, 4]'.  Note
   that this differs from the way the C language specifies array access and
   that, like the C language, array subscripts start from 0.

   Finally, array notation may also be used for extracting characters from a
   string.  For example, if one has:


         variable ch,  s = "Hello World";


   then `ch = s[0]' could be used to extract the first character from
   the string `s'.  However, this syntax cannot be used to replace
   characters in the string, i.e., `s[0] = ch' is illegal and will
   generate an error.  For the latter case, one must either use the
   `strsub' function or use a character array.

   Examples:

   Here is a function that computes the trace (sum of the diagonal elements)
   of a square 2 dimensional n xn array:


      define array_trace (a, n)
      {
         variable sum = 0, i;
         for (i = 0; i < n; i++) sum = sum + a[i, i];
         return sum;
      }

   This fragment creates a 10 x10 integer array, sets its diagonal
   elements to 5, and then computes the trace of the array:

      variable a, j, the_trace;
      a = create_array('i', 10, 10, 2);
      for (j = 0; j < 10; j++) a[j, j] = 5;
      the_trace = array_trace(a, 10);



4.8 Stack Operators
--------------------


   The use of local variables greatly simplifies the task of maintaining the
   stack.  Nevertheless, S-Lang is really a stack based language and there
   are times when they are useful.

               pop        % removes the top object from the stack
               dup        % duplicates the top object on the stack
               exch       % exchanges top 2 objects on the stack

   These operators work on all data types -- they are not limited to
   integers.



5 Advanced Topics
==================

   This section should be thoroughly understood by anyone serious about
   using the S-Lang language.


5.1 Loading Files: evalfile and autoload
-----------------------------------------


   Most S-Lang based applications will load a startup file which consists
   of S-Lang function definitions.  The file may load other files of
   S-Lang code via the `evalfile' intrinsic function.  This function
   takes one string argument (the filename) and returns a non-zero value if
   the file was successfully loaded, otherwise it returns zero.  For
   example,

      !if (evalfile("my_functs.sl")) error("Error loading File!");

   instructs the interpreter to load the file `my_functs.sl' and returns an
   error message upon failure.

   A nice feature found in S-Lang and not found in many interpreters in
   the ability to automatically load functions when they are used.  For
   example, consider the JED editor which embeds S-Lang as its
   extension language. JED includes a set of S-Lang routines defined in
   a file `info.sl' which read GNU info files. In particular, JED's
   online documentation is in info format. It is extremely unlikely that one
   would read the online documentation every time one edits a file. Thus, it
   is not normally necessary to load this file of S-Lang code.  Since the
   main entry point into the info reader is the function
   `info_run_info', JED includes the line

                    autoload("info_run_info", "info.sl");

   in its main startup file (`site.sl').  This line lets the S-Lang
   interpreter know that when the `info_run_info' function is called, the
   file `info.sl' is to be loaded first.


5.2 Error Handling
-------------------


   Many intrinsic functions signal errors in the event of failure.  This is
   done internally in the underlying C code by setting `SLang_Error' to
   a non-zero value.  Once this happens, S-Lang will start to return to
   top level by ``unwinding'' the stack. However, there are times when some
   cleanup needs to be done. This is facilitated in S-Lang through the
   concept of error blocks. An error block is a block of code that
   gets executed in the event of an error.  An error block is declared by
   the `ERROR_BLOCK' directive.  As an example, consider the following:

      define example ()
      {
         ERROR_BLOCK {print("Error Block executed!"); }
         while (1);  % executes forever
      }

   Here a function called `example' has been defined.  It assumes an
   intrinsic function `print' has been defined and that there is some
   way for the user to signal a quit condition which the underlying C code
   will trap and raise `SLang_Error' to a non-zero value.  The
   `while' loop will execute forever until an error condition is
   signaled.  When that happens, the error block will get executed.

   Consider another example from the JED editor.  When the user starts up
   JED with no filename, a message is displayed in the `*scratch*'
   buffer until the user hits a key:

      define startup_hook()
      {
        !if (strcmp("*scratch*", whatbuf())) return;
        insert("This is the JED editor.\n\nFor help, hit Control-H Control-H");
        bob(); update(1);
        () = input_pending(300);    % wait up to 300 seconds for input
        erase_buffer();
      }

   This is a function that JED automatically calls upon startup.  If the
   buffer is the `*scratch*' buffer, a short help message is displayed;
   otherwise the function returns.  Then JED will wait 300 seconds or
   until the user hits a key at which point it will erase the buffer.  This
   will work fine unless the user does something to generate an error.  For
   example, errors will be generated of the user presses the Ctrl-G
   key to signal a quit condition, tries to delete past the beginning of the
   buffer, etc... If an error is generated, S-Lang will abort before
   erasing the buffer. This will leave the help message on the screen and in
   the buffer which is not what is desired.  To prevent this, an error block
   is used:

     define startup_hook()
     {
        !if (strcmp("*scratch*", whatbuf())) return;
        ERROR_BLOCK {erase_buffer();}
        insert("This is the JED editor.\n\nFor help, hit Control-H Control-H");
        bob(); update(1);
        () = input_pending(300);    % wait up to 300 seconds for input
        EXECUTE_ERROR_BLOCK;
     }

   Here an error block has been declared and consists of the single
   statement `erase_buffer()'. If an error occurs the buffer will be
   erased. The statement `EXECUTE_ERROR_BLOCK' is a S-Lang directive
   that says to go ahead and execute the error block even if no error has
   occurred.

   Error blocks will not get executed for all errors.  For example, if there
   is a memory allocation error or an error associated with the stack, it
   would make no sense to call the error block.  

   Once an error has been caught by an error block, the error can be cleared
   by the `_clear_error' function.  After the error has been cleared,
   execution resumes at the next statement at the level of the error block
   following the statement that generated the error.  For example, consider:

       define make_error ()
       {
           error ("Error condition created.");
           print ("This statement is not executed.");
       }
       
       define test ()
       {
           ERROR_BLOCK 
             {
                _clear_error ();
             }
           make_error ();
           print ("error cleared.");
       }

   Note that although the error was triggered in the `make_error' function,
   the error was cleared in the `test' function.  As a result, execution
   resumes after the statement that makes the call to `make_error' since
   this statement is at the same level as the error block that cleared the
   error.
   
   Here is another example that illustrates how multiple error blocks work.

       define test ()
       {
          variable n = 0, s = "";
          
          ERROR_BLOCK { 
            print (Sprintf ("s = %s, n = %d", s, n, 2));
            _clear_error (); 
          }
          
          forever
          {
             ERROR_BLOCK {
               s = strcat (s, "0");
               _clear_error ();
             }
             if (n == 0) error ("");
             
             ERROR_BLOCK {
               s = strcat (s, "1");
             }
             if (n == 1) error ("");
             
             n++;
          }
       }     

   Here, three error blocks have been declared.  One has been declared
   outside the `forever' loop and the other two have been declared
   inside the `forever' loop.  Each time through the loop, the variable
   `n' is incremented and a different error block is triggered.  The
   error block that gets triggered is the last one encountered.  On the
   first time through the loop, `n' will be zero and the first error
   block in the loop will be executed.  This error block clears the error
   and execution will resume following the `if' statement that triggered
   the error.  The variable `n' will be incremented to 1 and, on the
   second cycle through the loop, and the second `if' statement will
   trigger an error and the second error block will execute. This time, the
   error is not cleared and the `forever' loop will abort causing the
   error block outside the `forever' loop to fire.  This block prints
   out the values of the variables `s' and `n'.  It will clear the
   error and execution resumes on the statement following the
   `forever' loop.  In the end, `s = 01, n = 1' will be printed.

   To summarize, `ERROR_BLOCK' is a directive that declares an error block.
   The `EXECUTE_ERROR_BLOCK' directive indicates that the error block is to
   be executed at this point--- no error is necessary.  In addition, error
   blocks may be defined at multiple levels.  As the stack unwinds in
   response to an error condition, all error blocks in scope will get
   executed.  The function `_clear_error' can be used to clear the error
   condition.

   Emacs elisp programmers should note the similarity to the Emacs elisp
   function `unwind-protect'.


5.3 Exit and User Blocks
-------------------------


   Error blocks get executed as the result of an error or when the directive
   `EXECUTE_ERROR_BLOCK' is encountered.  There are two other types of
   blocks that are useful: exit blocks and user blocks.
   

5.3.1 Exit Blocks
..................

   
   An exit block is declared using the directive `EXIT_BLOCK'.  It is executed
   when the function returns.  For example, consider:

      define test ()
      {
         variable n = 0;
         
         EXIT_BLOCK { print ("Exit block called."); }
         
         forever
          {
            if (n == 10) return;
            n++;
          }
      }

   Here, the function contains an exit block and a `forever' loop.  The
   loop will terminate via `return' when `n' is 10.  When it
   returns, the exit block will be called.

   Exit blocks are very useful for cleaning up when a function returns via
   an explicit call to `return' deep within the function.  
   
   

5.3.2 User Blocks
..................


   A user block is similar to a function within a function.  Up to 5 user
   blocks may be declared per function.  Unlike functions, user blocks do
   not take arguments but do have access the function's local variables.
   User blocks are denoted by the directives `USER_BLOCK0',
   `USER_BLOCK1', `USER_BLOCK2', `USER_BLOCK3', and
   `USER_BLOCK4'.  The directives `X_USER_BLOCK0',
   `X_USER_BLOCK1', `X_USER_BLOCK2', `X_USER_BLOCK3', and
   `X_USER_BLOCK4' may be used to call the user blocks.

   Here is an example:

      define silly ()
      {
         variable s1, s2, ch = 'a';
         
         USER_BLOCK0 
          {
            s1 = ();
            Sprintf ("%s & %c", s1, ch, 2);
            ch++;
          }
          
         s2 = "";
         s2 = X_USER_BLOCK0 (s2);
         s2 = X_USER_BLOCK0 (s2);
         s2 = X_USER_BLOCK0 (s2);
         s2 = X_USER_BLOCK0 (s2);
         print (s2);
      }

   Here a user block has been declared.  Although user blocks are not
   functions, as the example demonstrates, they can be used as functions.
   This is possible because S-Lang is a stack based language and parameters
   are passed via the stack.
   
   

5.4 Aliases
------------


   S-Lang supports a limited concept of a pointer known as an `alias'.
   Variables can be declared to be aliases for global functions or global
   variables. Consider the following three functions:

     define first () { print("First"); }
     define second () { print("Second"); }
     define first_second()
     {
        variable f;
        f = &first;  f();      % Line 1 (see text)
        f = &second;  f();     % Line 2 (see text)
     }

   Here three functions have been defined.  The functions `first' and
   `second' should be quite clear.  However, the function
   `first_second' looks somewhat strange because of the appearance of
   the `&' character. When the interpreter encounters a function or
   variable name immediately preceded by the `&' character, it pushes
   the address of the OBJECT referenced by the name onto the stack rather
   than pushing its value (variable) or executing it (function).  The object
   referred to must be a global object, either a global variable or a
   global function. Thus `&first' pushes the address of the function
   `first' on the stack rather than calling `first'. The local
   variable `f' is then assigned this address.  Note that after the
   assignment, `f' is neither a string type nor an integer type. Rather,
   it becomes a function type and and is synonymous with the function
   `first', i.e., it is an alias for `first'. Hence the line labeled
   ``Line 1'' above simply calls the function `first'. A similar
   statement holds for ``Line 2'' which results in the function `second'
   getting called.

   Finally note that aliases may be passed as arguments.  Consider:

      define execute_function (f)
      {
         f();
      }

   Then

       execute_function(&first);  execute_function(&second);

   will work as in the above example.

   This concept should be clear to most C programmers or to LISP programmers
   who will interpret the `&' prefixing the object name as simply
   quoting the object.



5.5 Functions returning multiple values
----------------------------------------


   There are some functions which return multiple values.  For example,
   S-Lang's implementation of the `fgets' function takes a single
   argument, a handle an to open file, and usually returns two values: the
   number of characters read followed by the character string itself.  The
   question immediately arises about how to handle such a function.  The
   answer to this question is to understand the stack.  Consider the
   following function which ``types'' out a file to the standard output
   device.

     define display_file(file)
     {
        variable n, fp, buf;
        
        fp = fopen(file);
        if (fp == -1) error("fopen failed.");
        
        while (n = fgets(fp), n > 0)
          {
             buf = ();                  % <----- stack
             () = fputs(buf, stdout);   % ignore return value.
          }
        if (fclose(fp) <= 0) error("fclose failed.");
     }

   The `fgets' function returns 2 items to the stack only when it
   reads one or more characters from the file.  If it encounters the end of
   the file, it returns 0 and nothing else.  If an error occurs when
   reading, it returns -1.  The line containing the comment above
   illustrates how to assign the top stack item to a variable.  Note also,
   that the two lines:

      buf = ();
      () = fputs(buf, 1);

   can be replaced by the single line:  

      () = fputs((), 1);

   It is also permissible to replace `buf = ()' by simply `=buf'.
   Note that there is no space between the `=' and `buf' in the
   latter form.

   Someone might simply suggest to do it like it is done in C, i.e.,

      n = fgets(buf, fp);

   However, this is impossible in S-Lang, and it is not even desirable.
   S-Lang is an interpreted language and one of the reasons for using it
   over the C language is to free oneself from problems which can arise in
   the above `C' expression.

   Finally, note that, when used as an argument, `()' is simply a
   disguise for the the top stack item.  Consider the following code
   fragment:

    variable div_1, div_2;

    12; div_1 = () / 4;              % push 12 on stack then do division
    4;  div_2 = 12 / ();             % push 4 on stack then do division

   The value of `div_1' will be the expected result of 12 / 4 = 3;
   however, `div_2' will have the value of 4 / 12!  The reason is
   that after parsing, `()' will be replaced by the top
   stack item and in the second case, 12 will be the top stack item, not
   4.   If this point is unclear, it is better to simply avoid the use of
   `()' in this manner.


----------------------------------------------------------------------------
             Internal functions of SLang
----------------------------------------------------------------------------

Sprintf 
    Prototype:  String Sprintf(String format, ..., Integer n);
     Sprintf formats a string from 'n' objects according to 'format'.  
     Unlike its C counterpart, Sprintf requires the number of items to
     format.  For example.
     
          Sprintf("%f is greater than %f but %s is better than %s\n",
                   PI, E, "Cake" "Pie", 4);
                   
     The final argument to Sprintf is the number of items to format; in
     this case, there are 4 items.
    
--------------------
_clear_error 
     May be used in error blocks to clear the error that triggered the
     error block.  Execution resumes following the statement
     triggering the block. 
--------------------
_stkdepth  
     returns number of items on stack 
--------------------
acos 
--------------------
aget 
     Syntax: i j ... k  ARRAY aget
     returns ARRAY[i][j]...[k] 
--------------------
aput 
     Syntax: x i j ... k  ARRAY put
     sets ARRAY[i][j]...[k] = x 
--------------------
array_sort 
     Requires an array on the stack as well as a function name to call 
    for the comparison.  The array to be placed on the stack is the
    array to be sorted.  The routine returns an integer index array which 
    indicates the result of the sort.  The first array is unchanged. 
--------------------
asin 
--------------------
atan 
--------------------
autoload 
    Prototype: Void autoload(String fun, String file);
    This function simply declares function 'fun' to the interpreter.  When
    'fun' is actually called, its actual function definition will be loaded
    from 'file'.
    Example:  autoload ("bessel_j0", "/usr/lib/slang/bessel.sl");
    See Also: evalfile 
--------------------
byte_compile_file 
    Prototype Void byte_compile_file (String file, Integer method);
    byte compiles 'file' producing a new file with the same name except 
    a 'c' is added to the output file name.  For example, 
          byte_compile_file("site.sl");
    produces a new file named 'site.slc'.  If 'method' is non-zero, the 
    file is preprocessed only.  Note that the resulting bytecompiled file
    must only be used by the executable that produced it.  Set 'method' to 
    a non-zero value to use the byte compiled file with more than one 
    executable.
    
--------------------
case 
    Prototype: Integer case(a, b);
    This function is designed to make the switch statement look more like
    the C one.  Basically, it does a generic compare operation.  
    Both parameters 'a' and 'b' must be of the same type.  It returns zero
    if their types differ or have different values.
    In a switch statment, it may be used as:
      switch (token)
      { case "return": return_function ();}
      { case "break": break_function ();}
    
    Unlike the C version, it one cannot have:
    
      switch (i)
      {case 10: case 20: do_ten_or_twenty (i);}
    
    One must do:
    
      switch (i)
      {case 10 or case (i, 20) : do_ten_or_twenty (i);}
    
--------------------
char 
     Prototype: String char (Integer c);
    This function takes and integer and returns a string of length 1 whose 
    first character has ascii value 'c'.
    
--------------------
chmod 
    Prototype Integer chmod(String file, Integer mode);
    'chmod' changes the permissions of 'file' to those specified by 'mode'.
    It returns 0 upon success, -1 if the process lacks sufficient privilege
    for the operation, or -2 if the file does not exist.
    See also: chown, stat_file
    
--------------------
chown 
    Prototype Integer chown(String file, Integer uid, Integer gid);
    Change ownership of 'file' to that of user id 'uid' and group id 'gid'.
    This function returns 0 upon success and a negative number up failure.
    It returns -1 if the process does not have sufficent privileges and -2
    if the file does not exist. 
    See also: chmod, stat_file 
--------------------
copy_array 
    Prototype: Void copy_array(Array b, Array a);
    Copies the contents of array 'a' to array 'b'.  Both arrays must be of
    the same type and dimension.
    
--------------------
cos 
--------------------
create_array 
     Prototype: create_array (Integer type, Integer i_1, i_2 ... i_dim, dim);
    Creates an array of type 'type' with dimension 'dim'.
    i_n is an integer which specifies the maximum size of array in 
    direction n.   'type' is a control integer which specifies the type 
    of the array.
     Types are:  's' : array of strings
                 'f' : array of floats
                 'i' : array of integers
                 'c' : array of characters
    At this point, dim cannot be larger than 3.
    Also note that space is dynamically allocated for the array and that
    copies of the array are NEVER put on the stack.  Rather, references to
    the array are put on the stack.  When the array is no longer needed, it
    must be freed with 'free_array'
    Example:
       variable a = create_array ('f', 10, 1);
    This creates a 1 dimensional array of 10 floats and assigns it to 'a'
    See also: free_array
    
--------------------
define_case 
     Two parameters are integers in the range 0 to 255.  The first
     integer is the ascii value of the upprcase character and the 2nd
     integer is the value of its lowercase counterpart.  For example, to
     define X as the uppercase of Y, do:
       X Y define_case 
--------------------
dup 
     duplicate top object on the stack. 
--------------------
eval 
     evaluate STRING as an S-Lang expression. 
--------------------
evalfile 
     Prototype: Integer evalfile (String file);
    Load 'file' as S-Lang code.  If loading is successful, a non-zero result 
    will be returned.  If the file is not found, zero will be returned.
    See also: eval, autoload 
--------------------
exp 
--------------------
extract_element 
     Prototype: String extract_element (String list, Integer nth, Integer delim);
    Returns 'nth' element in 'list' where 'delim' separates elements of the 
    list. 'delim' is an Ascii value.  Elements are numbered from 0.
    
    For example:
      extract_element ("element 0, element 1, element 2", 1, ',');
    returns the string " element 1", whereas 
      extract_element ("element 0, element 1, element 2", 2, ' ');
    returns "0,".
    See also: is_list_element.
    
--------------------
float 
     Convert from integer or string representation to floating point.  
     For example, "12.34" float returns 12.34 to stack.
     as another example, consider:
     1 2 /   ==>  0  since 1 and 2 are integers
     1 2 float / ==> 0.5 since float converts 2 to 2.0 and floating point 
     division is used.
     
--------------------
free_array 
     Prototype: Void free_array (Array a);
    Frees up the space which array occupies.  All reference to this space
    will now be meaningless and will generate an error.
    
--------------------
getenv 
     Prototype: String getenv(String var);
    Returns value of an environment variable 'var' as a string.  The empty
    "" is returned if the 'var' is not defined. 
    See also: putenv 
--------------------
init_char_array 
    Prototype: Void init_char_array(Array_Type a, String s);
    a is an array of type 'c' (character array) and s is a string.
    
--------------------
int 
     returns ascii value of the first character of a string. 
--------------------
integer 
     Convert from a string representation to integer.  For example,
     "1234" integer returns 1234 to stack. 
--------------------
is_defined 
     Prototype: Integer is_defined (String obj);
    This function is used to determine whether or not 'obj' has been defined.
    If 'obj' is not defined, it returns 0.  Otherwise, it returns a non-zero
    value that defpends on the type of object 'obj' represents.  Specifically:
    
             +1 if arg is an intrinsic function 
             +2 if user defined function
             -1 if intrinsic variable
             -2 if user defined variable 
--------------------
is_list_element 
     Prototype: Integer is_list_element (String list, String elem, Integer delim);
    If 'elem' is an element of 'list' where 'list' is a 'delim' seperated 
    list of strings, this function returns 1 plus the matching element 
    number.  If 'elem' is not a member of the list, zero is returned.
    Example:
      is_list_element ("element 0, element 1, element 2", "0,", ' ');
    returns 2 since "0," is element number one of the list (numbered from
    zero).
    See also: extract_element.
    
--------------------
is_substr  
     Syntax: "a" "b" is_substr
     returns the position of "b" in "a".  If "b" does not occur in "a"
     it returns 0--- the first position is 1 
--------------------
isdigit 
     returns TRUE if CHAR (string of length 1) is a digit. 
--------------------
log 
--------------------
log10 
--------------------
lstat_file 
    Prototype: Integer lstat_file(String file);
    This function is like 'stat_file' but it returns information about 
    the link itself. See 'stat_file' for usage.
    See also: stat_file 
--------------------
make_printable_string 
    Prototype: String make_printable_string(String str);
    Takes input string 'str' and creates a new string that may be used by the
    interpreter as an argument to the 'eval' function.  The resulting string is
    identical to 'str' except that it is enclosed in double quotes and the
    backslash, newline, and double quote characters are expanded. 
    See also: eval
    
--------------------
polynom 
     Usage:
      a b .. c n x polynom  =y
     This computes:
      ax^n + bx^(n - 1) + ... c = y  
--------------------
pop 
     Prototype: Void pop ();
    'pop' is used to remove the top object from the S-Lang stack.  It is 
    typically used to ignore values from function that return a value. 
--------------------
pow 
--------------------
print_stack 
     dumps tha S-Lang stack 
--------------------
putenv 
    Prototype: Void putenv(String s);
    This functions adds string 's' to the environment.  Typically, 's' should
    be a String of the form "name=value".  It signals an error upon failure.
    
--------------------
set_float_format 
    Prototype: Void set_float_format (String fmt);
    This function is used to set the floating point format to be used
    when floating point numbers are printed.  The routines that use this
    are the traceback routines and the 'string' function. The default
    value is "%f".
    
--------------------
sin 
--------------------
slang_trace_function 
     only argument is a string that specifies a function name that is 
     to be traced. See also the variable _slangtrace. 
--------------------
slapropos 
--------------------
sqrt 
--------------------
stat_file  
    Prototype: Integer stat_file(String file);
    This function returns information about 'file' through the use of the 
    system 'stat' call.  If the stat call fails, the function returns a 
    negative integer.  If it is successful, it returns zero.  Upon failure it 
    returns a negative number.
    
    To retrieve information obtained by this call, use the 'stat_struct'
    function.
    See also: lstat_file, stat_struct 
--------------------
stat_struct 
    Prototype Integer stat_struct(String field);
    This functions returns information previously obtained by a call to the
    'stat_file' or 'lstat_file' functions.  The 'field' argument specifies
    what piece of information to return.   Valid values for 'field' are:
    
        "dev"
        "ino"
        "mode"
        "nlink"
        "uid"
        "gid"
        "rdev"
        "size"
        "atime"
        "mtime"
        "ctime"
        
    See the man page for 'stat' for a discussion of these fields.
    The following example returns the size of the file "jed.rc":
    
       variable size;
       if (stat_file("jed.rc") < 0) error ("Unable to stat file!");
       size = stat_struct("size");
       
--------------------
str_quote_string 
    Prototype: String str_quote_string(String str, String qlis, Integer quote);
    Return a string identical to 'str' except that all characters in the 
    string 'qlis' are escaped with the 'quote' character including the quote
    character itself.
    
--------------------
str_uncomment_string 
    Prototype: String str_uncomment_string(String s, String beg, String end);
    'beg' and 'end' are strings whose characters define a set of comment 
    delimeters.  This function removes comments defined by the delimeter set
    from the input string 's' and returns it.  For example,
    
       str_uncommen_string ("Hello (testing) 'example' World", "'(", "')");
    
    returns the string: "Hello  World"; 
    
    This routine does not handle multicharacter comment delimeters and it
    assumes that comments are not nested.
    
--------------------
strcat 
     Prototype: String strcat(String a, String b);
    Conconcatenates 'a' and 'b' and returns the result.
    See also: Sprintf 
--------------------
strcmp 
     Prototype: Integer strcmp (String a, String b);
    'strcmp' performs a case sensitive comparison between two strings.  It
    returns 0 if the strings are identical, a negative number if 'a' is less 
    than 'b' and a positive result if 'a' is greater than 'b' (in a
    lexicographic sense).
    See also: strup, strlow 
--------------------
string 
     Prototype: String string (obj);
    Here 'obj' can be of any type.  The function 'string' will return a string
    representation of 'obj'.
    Example: string (12.34) returns "12.34"
    See also: Sprintf
    
--------------------
string_match_nth 
    Prototype: Integer Integer string_match_nth(Integer nth);
    This function returns 2 integers describing the result of the last
    call to 'string_match'.  It returns both the offset into the string 
    and the length of characters matches by the 'nth' submatch.  
    By convention, 'nth' equal to zero means the entire match.  Otherwise,
    'nth' must be an integer, 1 to 9, and refers to the set of characters
    matched by the 'nth' regular expression given by \(...\).
    For example, consider:
    
       variable matched, pos, len;
       matched = string_match("hello world", "\\([a-z]+\\) \\([a-z]+\\)", 1);
       if (matched) {
           (pos, len) = string_match_nth(2);
       }
    
    This will set 'matched' to 1 since a match will be found at the first
    position, 'pos' to 7 since 'w' is the 7th character of the string, and
    len to 5 since "world" is 5 characters long. 
--------------------
string_match 
    Prototype Integer string_match(String str, String pat, Integer pos);
    Returns 0 if 'str' does not match regular expression specified by
    'pat'. This function performs the match starting at position 'pos' in
    'str'.  The first character of 'str' corresponds to 'pos' equal to one.
    This function returns the position of the start of the match.  To find
    the exact substring actually matched, use 'string_match_nth'. 
    See also: string_match_nth, strcmp, strncmp
    
--------------------
strlen 
     Prototype: Integer strlen (String a);
    Returns the length of 'a'.
    
--------------------
strlow 
     Takes a string off the stack a replaces it with all characters
     in lowercase. 
--------------------
strncmp 
     like strcmp but takes an extra argument--- number of characters to
    compare.  Example, "apple"  "appliance"  3 strcmp --> 0 
--------------------
strsub 
     Syntax:  "string"  n ascii_value strsub
    This forces string to have a char who asciii value is ascii_val at
    the nth position.  The first character in the string is at position
    1. 
--------------------
strtrim 
     Trims leading and trailing whitespace from a string.  WHitespace
     is defined to be spaces, tabs, and newline chars. 
--------------------
strup 
     Takes a string off the stack a replaces it with all characters
     in uppercase. 
--------------------
substr 
     Syntax: "string" n len substr
      returns a substring with length len of string beginning at position n.
    
--------------------
system 
--------------------
tan 
--------------------
unix_ctime 
    Prototype: String unix_ctime(Integer secs);
    Returns a string representation of the time as given by 'secs' seconds
    since 1970. 
--------------------
unix_kill  
    Prototype: Integer unix_kill(Integer pid, Integer sig);
    
--------------------
E 
--------------------
PI 
--------------------
_slang_version 
--------------------
_slangtrace 
    Prototype: Integer _slangtrace;
    If non-zero, begin tracing when function declared by 
    lang_trace_function is entered.  This does not trace intrinsic functions.
    
--------------------
_traceback 
     If non-zero, dump S-Lang tracback on error. 
--------------------
