This document discusses the S-Lang language syntax.  For instructions on
embedding S-Lang in your C program, see the document slang.how.


				Introduction

S-Lang (pronounced ``sssslang'') is a simple stack based language
interpreter which may be easily embedded into another application making it
extensible.

Although the language is stack based, it has many features found in higher
level languages.  This includes both global variables and local variables,
branching, looping constructs, as well as functions.  Unlike some
interpreted languages, S-Lang even includes error handling capabilities.
See the discussion on error handling below.


The syntax of the language is quite simple.  To keep the parsing stage
simple and fast, Reverse Polish Notation (RPN) is used throughout.
In many ways it resembles postscript which is also stack based.  However,
the use of local variables make S-Lang much easier to work with.

It is perhaps easiest to explain the syntax of the language by analogy with
other popular languages.  I will choose C because C has influenced the look
of S-Lang.

------------------------------------------------------------
C-Like language            |     S-Lang        
------------------------------------------------------------
Global_Variable_List	   |     [global variables]
			   |
function(parameter_list)   |	 (
{			   |        [parameter list]
  local_variable_list	   |        [local variables]
			   |
  statement1		   |        statement1
     .			   |            .
     .			   |            .
  statementn		   |        statementn
}			   |      ) function
                           |                            
------------------------------------------------------------
Here is an actual example.  This example computes the sum of the first n
integers and returns it.

int compute_sum(int n)            |     ( [n] =n
{ 				  |    
   int sum, i;			  |       [sum i] 
   				  |    
   sum = 0;			  |       0 =sum
   for (i = 1; i <= n; i = i + 1) |       1 n 1 
     {				  |         {  =i
        sum = sum + i;		  |            i sum + =sum
     }				  |         } for
     				  |    
   return (sum);		  |       sum
}				  |     ) compute_sum


The similarities of the program should be obvious.  Experienced S-Lang
programmers would never write this program as I have done above.  I have
chosen to write it this way for pedagogical purposes.  A much better way
would be to do:

			( [n sum] =n 0 =sum
			  1 n 1 { sum + =sum } for
			  sum
			) compute_sum

The important thing to realize is that all variables must be declared before
they can be used.  This is done by enclosing them in brackets.  Also note
that [n sum] is equivalent to [n] [sum].  

Variables declared within a function are considered local variables and hide
the definition of global variables of the same name.

In C, one would call the function compute_sum with parameter 100 and assign
it to a variable y as:

			    y = compute_sum(100);
  
In should be apparant that to perform this same task in S-Lang, one writes:

			     100 compute_sum =y
			     
Please note that there can be no space between the equals sign `=' and the
variable upon which it performs the assignment.


			    Arithmetic operators.

The arithmetic operators `+', `-', `*', `/' operate on integers. They are
defined:

	       x y +	-->	 x + y
	       x y -	-->	 x - y
	       x y *	-->	 x * y
	       x y /	-->	 x / y

These operators remove the top two values from the stack and put the result
of the operation on the stack.

			     Binary Operations.

The binary Boolean operators act on two integers and return an integer.  An
integer is considered TRUE if it is non-zero otherwise it is FALSE.  These
operators are

	       x y ==  	  ;; TRUE if x equals y
	       x y !=	  ;; TRUE if x is not equal to y
	       x y >	  ;; TRUE if x is greater than y 
	       x y >=	  ;; TRUE if x is greater than or equal to y 
	       x y <	  ;; TRUE if x is less than y 
	       x y <=	  ;; TRUE if x is less than or equal to y 
	       x y or	  ;; TRUE if either x or y are TRUE
	       x y and	  ;; TRUE if both x and y are TRUE

Here the operator ``pops'' the two variable off the stack and replaces it by
the value of the result, TRUE or FALSE (non-zero or zero).  

In addition to the above LOGICAL operators, S-Lang also incorporates
operators which act on the bits themselves.  These are: 

               x y &      ;; bitwise and
               x y |      ;; bitwise or
               x y xor    ;; bitwise exclusive or
	       x y shl    ;; shift bits in x left y places
	       x y shr    ;; shift bits in x right y places


			       Unary operators
			       
The UNARY operators operate only on one integer.  They are: 

	x not   ;; if x is non zero return zero else return non zero
	x chs   ;; change the  sign of x
	x ~     ;; bitwise not
	
	
			       Stack Operators

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

	       pop  	  ;; removes the top object fro 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.

			       Block Operators

A Block is a sequence of valid executable S-Lang code.  As such, a block may
contain other blocks.  However, a block cannot include function
declarations; function declarations must take place at top level.  

The statements that comprise a block must be enclosed in curly brackets
`{}'.  Blocks are used for looping and branching.  Hence, block operators
are `while',  `for',  `if', etc.... 

   n {block} if			;; executes block if n is TRUE
   n {block} !if		;; executes block if n is FALSE
   n {block1} {block2} else	;; block1 if n is TRUE else block2
   n {bock} loop		;; executes block n times if n is positive
	 
The other block operations are more complex and require explanation.  Starting
with the simplest...

 {cond}{block} while	;; repeatedly executes block while cond is true.

 first last step	;; pops top three values from the stack as step, last
   {block} for 		;; and first, in that order.  If first is less than or
   			;; equal to last, it pushes first onto the stack and
			;; executes the block.  It then increments first by 
			;; step and repeats the process.

 {block1} ... {blockn}  ;; The orelse operator operates on a series of 1 or
   orelse		;; more blocks.  It executes the first block then tests
   			;; the top of the stack.  If it is FALSE, it executes
			;; the second block.  It continues to do so until one
       			;; of the blocks returns TRUE.  It is important to note
			;; that it does not pop the top element from the stack
			;; to perfor the test.  The stack gets propagated 
			;; through the blocks so that one block can use the 
			;; results of the previous one.
			
 {block1} ... {blockn}  ;; The andelse is just like the orelse except that it
  andelse 		;; continues to execute the blocks until one returns
  			;; FALSE.

 x {block1}		;; Switch statement.  Here blocks are ``tested'' for 
      .			;; a match against object x.  If a block ``matches''
      .			;; it is executed.  A typical block must conatin the
   {blockn}		;; colon operator `:' which in somes sense serves as
 switch			;; a label.  When execution reaches this operator, an
 			;; integer is poped off the stack.  If TRUE, a match 
			;; exists and the rest of the block is executed.  
			;; Otherwise, control is passed to the next block.
			
Since the switch structure is complicated, consider the following example: 

( [ch] =ch  ;; function is passed an integer

   ch { 32  == : page_down }   ;; ascii space character
      { 127 == : page_up }     ;; delete char is page_up
      { "q" int == :  quit }  ;; quit!
      { pop "Unknown command"  error } ;; default
      switch
) cmd_dispatch

Here, an integer is passed to the function cmd_dispatch.  The switch
statement pops the first element from the stack and pushes it back before
control is passed to a block.  For example, suppose the function is called
as in:  

			      127 cmd_dispatch

Then 127 is pushed onto the stack and the block { 32 == : page_down } is
executed.  127 32 == leaves 0 on the stack.  When the colon operator is
encountered, the integer is poped from the stack and if TRUE, the rest of
the block is executed.  In this case, a 0 is returned so the block is
aborted and control returns to the second block.  For the second block, 127
127 == returns 1 and a match is said to be made.  Then page_up is called and
control passes to the statements following the switch keyword.  C
programmers would write the above example as:

void cmd_dispatch(char ch)
{
   switch(ch)
   {
      case 32: page_down;  break;
      case 127: page_up; break;
      case 'q': quit; break;
      default: error("Unknown Command");
   }
}


  
   

			      String Operators
			      
The previous operators act upon integers and blocks.  The operators in this
section act on character strings.  

      s strlen		;; pops string and returns its length
      s1 s2 strcat	;; concatenates strings to s1s2 popping s1 and s2
      s1 s2 strcmp	;; returns 0 if s1 and s2 are the same, a negative 
      			;; number if s1 is lexically greater, otherwise it
			;; returns a positive non-zero integer (s1 ``>''s2)
      s i n substr      ;; returns n character substring from position i in
                        ;; string s.
      s i j strsub      ;; replace character at position i of string s with
                        ;; charcter whose ascii value is j
			

			      Array Operations
			      
s n1 n2 .. nd d create_array
                        ;; creates d dimensional array[n1, n2, ... nd] of 
			;; type s.  Here s (string) is one of 
			;;   "i" integer
			;;   "s" string
			;;   "f" float
			;; It returns pointer to array on stack.
			
a free_array            ;; frees previously created array pointed to by a

n1 n2 ... nd a aget     ;; returns array element a[n1, n2,...nd] to stack
x n1 n2 ... nd a aput   ;; assigns x to array element a[n1, n2,...nd]

Since the array may be hard to understand, here is an example routine 
to compute trace of an nxn matrix of integers

( [n a] =a  ;; assumes array a on stack as well as size

  [sum i] 0 =sum
  
  1 n 1                 ;; array elements always start at 1
    {
      =i
      i a aget sum +
    } for
  sum
) trace

;;; this routine creates a 10x10 array and sets it diagonal elements to 5

[a]  "i" 10 10 2 create_array =a
     1 10 1 { =i
              5 i i a aput
	    }
	  for



      
			      Transfer Functions
			      
These operators convert data from one type to another.

      n string		;; converts integer n to its string equivalent.
      
      n char		;; converts integer to a string of length 1 whose first
			;; character has ascii value n

      s int		;; returns the ascii value of the first character in
      			;; the string
			
      s integer         ;; converts string s to integer, ie.g., "123" -> 123
      s float           ;; converts string s to a float, ie.g., "1.2" -> 1.2
      		        ;; This is only available if compiled with floating
			;; point option.

			   Miscellaneous Functions
			   
      s isdigit		;; returns TRUE if the first character of string s is
      			;; a digit (0-9)
			
      s eval		;; evaluates string s as S-Lang code.
      
      s defined?	;; returns TRUE if string s is the name of a variable
      			;; or function

The operations described above are the only ones intrinsic to the S-Lang
language.  However, S-Lang is meant to be embedded into an application which
would then add new intrinsic operations to the language (e.g., the JED
editor).  It is impossible to say more about these functions without
explicit reference to the application.

Error Handling.

  Many intrinsic functions may signal errors.  This is done internally in
the underlying C code by setting Lang_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 that gets executed in the event of an error.  As an example,
consider the following:

  (
     { "Error Block executed!" print} ERROR_BLOCK
     { 
       ;; null statement
     } forever
  ) example
  
Here a function called example has been defined.  It assumes an intrinsic
function `print' has been defined and that there is someway for the user to
signal a quit condition which the underlying C code will trap and raise
Lang_Error to a non-zero value.  Thus, the null block will execute forever
until an error condition has been signaled.  At this point, the error block
will be executed.

Consider another example for 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:

   ( "*scratch*" whatbuf strcmp {return} if
     "This is the JED editor.\n\nFor help, hit Control-H Control-H" insert
     bob update
     300 input_pending pop     ;; wait up to 300 seconds for input
     
     erase_buffer
   ) startup_hook
   
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 then erase the buffer.  This will work fine unless the user does
something to generate an error.  For example, the ^G key will generate a
quit condition, the backspace key will try to delete past he 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 want is desired.  To prevent this, use an
ERROR_BLOCK as below:

   ( "*scratch*" whatbuf strcmp {return} if
   
     { erase_buffer } ERROR_BLOCK

     "This is the JED editor.\n\nFor help, hit Control-H Control-H" insert
     bob update
     300 input_pending pop     ;; wait up to 300 seconds for input
     
     EXECUTE_ERROR_BLOCK
     
   ) startup_hook

Here {erase_buffer} has been declare as an error block.  If an error gets
generated, the buffer will be erased.  The statement `EXECUTE_ERROR_BLOCK'
is a S-Lang directive which says to go ahead and execute the error block
even if no error has occurred.

The upshot is that the ERROR_BLOCK directive declares an error block.  The
EXECUTE_ERROR_BLOCK directive means that the error block is to be executed
at this point--- no error is necessary.  Also note that error blocks may be
defined at multiple levels.  As the stack unwinds, they get executed.

Emacs lisp programmers should not the similarity to the emacs lisp function
`unwind-protect'.

			       Advanced Topics
			       
Pointers.

Consider the following code:

  ( "First" print ) first
  ( "Second" print )  second

  (
    [f]
    &first =f   f   ; Line 1  (see text)
    &second =f  f   ; Line 2  (see text)
  ) first_second

Here three functions have been defined.  'print' is a function which
displays its argument.  The functions 'first' and 'second' should be quite
clear.  However, the function 'first_second' looks somewhat strange because
of the appearance of the '&' character.  A function or variable name
immediately preceeded by the '&' character means to push the address of the
OBJECT referenced referenced by the name onto the stack rather than pushing
its value (variable) or executing it (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. In this case it
becomes a function type and and is synonymous with the function `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. 

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