Metropoli BBS
VIEWER: slang.c MODE: TEXT (ASCII)
/* -*- mode: C; mode: fold; -*- */
/* slang.c  --- guts of S-Lang interpreter */
/* Copyright (c) 1992, 1995 John E. Davis
 * All rights reserved.
 *
 * You may distribute under the terms of either the GNU General Public
 * License or the Perl Artistic License.
 */

#include "config.h"
#include "sl-feat.h"

#include <stdio.h>

#if SLANG_HAS_FLOAT
# include <math.h>
#endif

#include "slang.h"
#include "_slang.h"

int SLang_Version = SLANG_VERSION;

struct _SLBlock_Type;
typedef struct
{
   char *name;
   SLang_Name_Type *next;
   char name_type;

   struct _SLBlock_Type *addr;	       /* address of function */
   unsigned char nlocals;	       /* number of local variables */
   unsigned char nargs;		       /* number of arguments */
}
_SLang_Function_Type;

typedef struct
{
   char *name;
   SLang_Name_Type *next;
   char name_type;
   
   SLang_Object_Type obj;
}
SLang_Global_Var_Type;

typedef struct
{
   char *name;
   SLang_Name_Type *next;
   char name_type;
   
#define SLANG_MAX_LOCAL_VARIABLES 254
#define AUTOLOAD_NUM_LOCALS (SLANG_MAX_LOCAL_VARIABLES + 1)
   int local_var_number;
}
SLang_Local_Var_Type;

typedef struct _SLBlock_Type
{
   unsigned char bc_main_type;
   unsigned char bc_sub_type;
   union
     {
	struct _SLBlock_Type *blk;
	int i_blk;

	SLang_Name_Type *nt_blk;
	SLang_App_Unary_Type *nt_unary_blk;
	SLang_Intrin_Var_Type *nt_ivar_blk;
	SLang_Intrin_Fun_Type *nt_ifun_blk;
	SLang_Global_Var_Type *nt_gvar_blk;
	_SLang_Function_Type *nt_fun_blk;

	VOID_STAR ptr_blk;
	char *s_blk;
#if SLANG_HAS_FLOAT
	double *f_blk;		       /*literal double is a pointer */
#endif
	long l_blk;
	struct _SLang_Struct_Type *struct_blk;
	int (*call_function)(void);
     }
   b;
}
SLBlock_Type;

/* Debugging and tracing variables */

void (*SLang_Enter_Function)(char *) = NULL;
void (*SLang_Exit_Function)(char *) = NULL;
/* If non null, these call C functions before and after a slang function. */

int _SLang_Trace = 0;
/* If _SLang_Trace = -1, do not trace intrinsics */
static Trace_Mode = 0;

static char *Trace_Function;	       /* function to be traced */
int SLang_Traceback = 0;
/* non zero means do traceback.  If less than 0, do not show local variables */

/* These variables handle _NARGS processing by the parser */
int SLang_Num_Function_Args;
static int *Num_Args_Stack;
static unsigned int Recursion_Depth;
static SLang_Object_Type *Frame_Pointer;
static int Next_Function_Num_Args;
static unsigned int Frame_Pointer_Depth;
static unsigned int *Frame_Pointer_Stack;

static int Lang_Break_Condition = 0;
/* true if any one below is true.  This keeps us from testing 3 variables.
 * I know this can be perfomed with a bitmapped variable, but...
 */
static int Lang_Break = 0;
static int Lang_Return = 0;
static int Lang_Continue = 0;

SLang_Object_Type *_SLRun_Stack;
SLang_Object_Type *_SLStack_Pointer;
static SLang_Object_Type *_SLStack_Pointer_Max;

/* Might want to increase this. */
static SLang_Object_Type Local_Variable_Stack[SLANG_MAX_LOCAL_STACK];
static SLang_Object_Type *Local_Variable_Frame = Local_Variable_Stack;



static void lang_dump(char *s)
{
   while (*s)
     {
	if (*s == '\n') fputc ('\r', stderr);
	fputc (*s, stderr);
	s++;
     }
}
void (*SLang_Dump_Routine)(char *) = lang_dump;
static void do_traceback (char *, unsigned int);

/*{{{ push/pop/etc stack manipulation functions */

/* This routine is assumed to work even in the presence of a SLang_Error. */
int SLang_pop (SLang_Object_Type *x)
{
   register SLang_Object_Type *y;

   y = _SLStack_Pointer;
   if (y == _SLRun_Stack)
     {
	if (SLang_Error == 0) SLang_Error = SL_STACK_UNDERFLOW;
	_SLStack_Pointer = _SLRun_Stack;
	x->data_type = 0;
	return -1;
     }
   y--;
   *x = *y;

   _SLStack_Pointer = y;
   return 0;
}

int _SLang_pop_i_val (unsigned char type, int *i_val)
{
   register SLang_Object_Type *y;

   y = _SLStack_Pointer;
   if (y == _SLRun_Stack)
     {
	if (SLang_Error == 0) SLang_Error = SL_STACK_UNDERFLOW;
	_SLStack_Pointer = _SLRun_Stack;
	return -1;
     }

   y--;
   _SLStack_Pointer = y;
   
   if (y->data_type != type)
     {
	_SLclass_type_mismatch_error (type, y->data_type);
	SLang_free_object (y);
	return -1;
     }
   
   *i_val = y->v.i_val;
   return 0;
}

int SLang_peek_at_stack (void)
{
   if (_SLStack_Pointer == _SLRun_Stack)
     {
	if (SLang_Error == 0)
	  SLang_Error = SL_STACK_UNDERFLOW;
	return -1;
     }

   return (_SLStack_Pointer - 1)->data_type;
}

void SLang_free_object (SLang_Object_Type *obj)
{
   unsigned char data_type;
   SLang_Class_Type *cl;

   data_type = obj->data_type;
#if _SLANG_OPTIMIZE_FOR_SPEED
   if (_SLclass_is_scalar_type [data_type])
     return;
#endif
   cl = _SLclass_get_class (data_type);
   if (cl->cl_class_type != SLANG_CLASS_TYPE_SCALAR)
     (*cl->cl_destroy) (data_type, (VOID_STAR) &obj->v);
}

int SLang_push (SLang_Object_Type *x)
{
   register SLang_Object_Type *y;
   y = _SLStack_Pointer;

   /* if there is a SLang_Error, probably not much harm will be done
    if it is ignored here */
   /* if (SLang_Error) return; */

   /* flag it now */
   if (y >= _SLStack_Pointer_Max)
     {
	if (!SLang_Error) SLang_Error = SL_STACK_OVERFLOW;
	return -1;
     }

   *y = *x;
   _SLStack_Pointer = y + 1;
   return 0;
}

int _SLang_push_void_star (unsigned char type, VOID_STAR pval)
{
   register SLang_Object_Type *y;
   y = _SLStack_Pointer;

   if (y >= _SLStack_Pointer_Max)
     {
	if (!SLang_Error) SLang_Error = SL_STACK_OVERFLOW;
	return -1;
     }

   y->data_type = type;
   y->v.p_val = pval;

   _SLStack_Pointer = y + 1;
   return 0;
}

int _SLang_push_i_val (unsigned char type, int i_val)
{
   register SLang_Object_Type *y;
   y = _SLStack_Pointer;

   if (y >= _SLStack_Pointer_Max)
     {
	if (!SLang_Error) SLang_Error = SL_STACK_OVERFLOW;
	return -1;
     }

   y->data_type = type;
   y->v.i_val = i_val;

   _SLStack_Pointer = y + 1;
   return 0;
}

int _SLang_pop_object_of_type (unsigned char type, SLang_Object_Type *obj)
{
   if (-1 == SLang_pop (obj))
     return -1;

   if (obj->data_type != type)
     {
	_SLclass_type_mismatch_error (type, obj->data_type);
	SLang_free_object (obj);
	return -1;
     }

   return 0;
}

/*  This function reverses the top n items on the stack and returns a
 *  an offset from the start of the stack to the last item.
 */
int _SLreverse_stack (int n)
{
   SLang_Object_Type *otop, *obot, tmp;

   otop = _SLStack_Pointer;
   if ((n > otop - _SLRun_Stack) || (n < 0))
     {
	SLang_Error = SL_STACK_UNDERFLOW;
	return -1;
     }
   obot = otop - n;
   otop--;
   while (otop > obot)
     {
	tmp = *obot;
	*obot = *otop;
	*otop = tmp;
	otop--;
	obot++;
     }
   return (int) ((_SLStack_Pointer - n) - _SLRun_Stack);
}

int _SLroll_stack (int np)
{
   int n, i;
   SLang_Object_Type *otop, *obot, tmp;

   if ((n = abs(np)) <= 1) return 0;    /* identity */

   obot = otop = _SLStack_Pointer;
   i = n;
   while (i != 0)
     {
	if (obot <= _SLRun_Stack)
	  {
	     SLang_Error = SL_STACK_UNDERFLOW;
	     return -1;
	  }
	obot--;
	i--;
     }
   otop--;

   if (np > 0)
     {
	/* Put top on bottom and roll rest up. */
	tmp = *otop;
	while (otop > obot)
	  {
	     *otop = *(otop - 1);
	     otop--;
	  }
	*otop = tmp;
     }
   else
     {
	/* Put bottom on top and roll rest down. */
	tmp = *obot;
	while (obot < otop)
	  {
	     *obot = *(obot + 1);
	     obot++;
	  }
	*obot = tmp;
     }
   return 0;
}

int _SLstack_depth (void)
{
   return (int) (_SLStack_Pointer - _SLRun_Stack);
}

/*}}}*/

/*{{{ inner interpreter and support functions */

static int increment_frame_pointer (void)
{
   if (Recursion_Depth >= SLANG_MAX_RECURSIVE_DEPTH)
     {
	SLang_verror (SL_STACK_OVERFLOW, "Num Args Stack Overflow");
	return -1;
     }
   Num_Args_Stack [Recursion_Depth] = SLang_Num_Function_Args;
   
   SLang_Num_Function_Args = Next_Function_Num_Args;
   Next_Function_Num_Args = 0;
   Recursion_Depth++;
   return 0;
}

static int decrement_frame_pointer (void)
{
   if (Recursion_Depth == 0)
     {
	SLang_verror (SL_STACK_UNDERFLOW, "Num Args Stack Underflow");
	return -1;
     }
   
   Recursion_Depth--;
   if (Recursion_Depth < SLANG_MAX_RECURSIVE_DEPTH)
     SLang_Num_Function_Args = Num_Args_Stack [Recursion_Depth];

   return 0;
}

static int do_name_type_error (SLang_Name_Type *nt)
{
   char buf[256];
   if (nt != NULL)
     {
	sprintf (buf, "(Error occurred processing %s)", nt->name);
	do_traceback (buf, 0);
     }
   return -1;
}

/* local and global variable assignments */


static int do_binary_ab (int op, SLang_Object_Type *obja, SLang_Object_Type *objb)
{
   SLang_Class_Type *a_cl, *b_cl, *c_cl;
   unsigned char b_data_type, a_data_type, c_data_type;
   int (*binary_fun) (int,
		      unsigned char, VOID_STAR, unsigned int,
		      unsigned char, VOID_STAR, unsigned int,
		      VOID_STAR);
   VOID_STAR pa;
   VOID_STAR pb;
   VOID_STAR pc;
   int ret;

   b_data_type = objb->data_type;
   a_data_type = obja->data_type;
   a_cl = _SLclass_get_class (a_data_type);
   b_cl = _SLclass_get_class (b_data_type);

   if (NULL == (binary_fun = _SLclass_get_binary_fun (op, a_cl, b_cl, &c_cl)))
     return -1;

   c_data_type = c_cl->cl_data_type;

#if _SLANG_OPTIMIZE_FOR_SPEED
   if (_SLclass_is_scalar_type [a_data_type])
     pa = (VOID_STAR) &obja->v;
   else
#endif
     pa = _SLclass_get_ptr_to_value (a_cl, obja);

#if _SLANG_OPTIMIZE_FOR_SPEED
   if (_SLclass_is_scalar_type [b_data_type])
     pb = (VOID_STAR) &objb->v;
   else
#endif
     pb = _SLclass_get_ptr_to_value (b_cl, objb);

   pc = c_cl->cl_transfer_buf;

   if (1 != (*binary_fun) (op,
			   a_data_type, pa, 1,
			   b_data_type, pb, 1,
			   pc))
     {
	SLang_verror (SL_NOT_IMPLEMENTED,
		      "Binary operation between %s and %s failed",
		      a_cl->cl_name, b_cl->cl_name);

	return -1;
     }

   /* apush will create a copy, so make sure we free after the push */
   ret = (*c_cl->cl_apush)(c_data_type, pc);
#if _SLANG_OPTIMIZE_FOR_SPEED
   if (0 == _SLclass_is_scalar_type [c_data_type])
#endif
     (*c_cl->cl_adestroy)(c_data_type, pc);

   return ret;
}

static void do_binary (int op)
{
   SLang_Object_Type obja, objb;

   if (SLang_pop (&objb)) return;
   if (0 == SLang_pop (&obja))
     {
	(void) do_binary_ab (op, &obja, &objb);
#if _SLANG_OPTIMIZE_FOR_SPEED
	if (0 == _SLclass_is_scalar_type [obja.data_type])
#endif
	  SLang_free_object (&obja);
     }
#if _SLANG_OPTIMIZE_FOR_SPEED
   if (0 == _SLclass_is_scalar_type [objb.data_type])
#endif
     SLang_free_object (&objb);
}


static int do_unary_op (int op, SLang_Object_Type *obj, int unary_type)
{
   int (*f) (int, unsigned char, VOID_STAR, unsigned int, VOID_STAR);
   VOID_STAR pa;
   VOID_STAR pb;
   SLang_Class_Type *a_cl, *b_cl;
   unsigned char a_type, b_type;
   int ret;

   a_type = obj->data_type;
   a_cl = _SLclass_get_class (a_type);

   if (NULL == (f = _SLclass_get_unary_fun (op, a_cl, &b_cl, unary_type)))
     return -1;
   
   b_type = b_cl->cl_data_type;

#if _SLANG_OPTIMIZE_FOR_SPEED
   if (_SLclass_is_scalar_type [a_type])
     pa = (VOID_STAR) &obj->v;
   else
#endif
     pa = _SLclass_get_ptr_to_value (a_cl, obj);

   pb = b_cl->cl_transfer_buf;

   if (1 != (*f) (op, a_type, pa, 1, pb))
     {
	SLang_verror (SL_NOT_IMPLEMENTED,
		      "Unary operation for %s failed", a_cl->cl_name);
	return -1;
     }

   ret = (*b_cl->cl_apush)(b_type, pb);
   /* cl_apush creates a copy, so make sure we call cl_adestroy */
#if _SLANG_OPTIMIZE_FOR_SPEED
   if (0 == _SLclass_is_scalar_type [b_type])
#endif
     (*b_cl->cl_adestroy)(b_type, pb);

   return ret;
}

static int do_unary (int op, int unary_type)
{
   SLang_Object_Type obj;
   int ret;

   if (-1 == SLang_pop (&obj)) return -1;
   ret = do_unary_op (op, &obj, unary_type);
#if _SLANG_OPTIMIZE_FOR_SPEED
   if (0 == _SLclass_is_scalar_type [obj.data_type])
#endif
     SLang_free_object (&obj);
   return ret;
}


static int do_assignment_binary (int op, SLang_Object_Type *obja_ptr)
{
   SLang_Object_Type objb;
   int ret;

   if (SLang_pop (&objb)) 
     return -1;

   ret = do_binary_ab (op, obja_ptr, &objb);
#if _SLANG_OPTIMIZE_FOR_SPEED
   if (0 == _SLclass_is_scalar_type [objb.data_type])
#endif
     SLang_free_object (&objb);
   return ret;
}

static int
perform_lvalue_operation (unsigned char op_type, SLang_Object_Type *obja_ptr)
{
   switch (op_type)
     {
      case _SLANG_BCST_ASSIGN:
	break;
	
      case _SLANG_BCST_PLUSEQS:
	if (-1 == do_assignment_binary (SLANG_PLUS, obja_ptr))
	  return -1;
	break;

      case _SLANG_BCST_MINUSEQS:
	if (-1 == do_assignment_binary (SLANG_MINUS, obja_ptr))
	  return -1;
	break;

      case _SLANG_BCST_PLUSPLUS:
      case _SLANG_BCST_POST_PLUSPLUS:
#if _SLANG_OPTIMIZE_FOR_SPEED
	if (obja_ptr->data_type == SLANG_INT_TYPE)
	  return _SLang_push_i_val (SLANG_INT_TYPE, obja_ptr->v.i_val + 1);
#endif
	if (-1 == do_unary_op (SLANG_PLUSPLUS, obja_ptr, _SLANG_BC_UNARY))
	  return -1;
	break;
	
      case _SLANG_BCST_MINUSMINUS:
      case _SLANG_BCST_POST_MINUSMINUS:
#if _SLANG_OPTIMIZE_FOR_SPEED
	if (obja_ptr->data_type == SLANG_INT_TYPE)
	  return _SLang_push_i_val (SLANG_INT_TYPE, obja_ptr->v.i_val - 1);
#endif
	if (-1 == do_unary_op (SLANG_MINUSMINUS, obja_ptr, _SLANG_BC_UNARY))
	  return -1;
	break;

      default:
	SLang_Error = SL_INTERNAL_ERROR;
	return -1;
     }
   return 0;
}

   
static int
set_lvalue_obj (unsigned char op_type, SLang_Object_Type *obja_ptr)
{
   if (op_type != _SLANG_BCST_ASSIGN)
     {
	if (-1 == perform_lvalue_operation (op_type, obja_ptr))
	  return -1;
     }
#if _SLANG_OPTIMIZE_FOR_SPEED
   if (0 == _SLclass_is_scalar_type [obja_ptr->data_type])
#endif
     SLang_free_object (obja_ptr);

   return SLang_pop(obja_ptr);
}

static int
set_struct_lvalue (SLBlock_Type *bc_blk)
{
   SLang_Object_Type *obja_ptr;

   if (NULL == (obja_ptr = _SLstruct_get_assign_obj (bc_blk->b.s_blk)))
     return -1;

   return set_lvalue_obj (bc_blk->bc_sub_type, obja_ptr);
}

static int
set_intrin_lvalue (SLBlock_Type *bc_blk)
{
   unsigned char op_type;
   SLang_Object_Type obja;
   SLang_Class_Type *cl;
   SLang_Intrin_Var_Type *ivar;
   VOID_STAR intrinsic_addr;
   unsigned char intrinsic_type;
   
   ivar = bc_blk->b.nt_ivar_blk;

   intrinsic_type = ivar->type;
   intrinsic_addr = ivar->addr;

   op_type = bc_blk->bc_sub_type;

   cl = _SLclass_get_class (intrinsic_type);
   
   if (op_type != _SLANG_BCST_ASSIGN) 
     {
	/* We want to get the current value into obja.  This is the
	 * easiest way.
	 */
	if ((-1 == (*cl->cl_push) (intrinsic_type, intrinsic_addr))
	    || (-1 == SLang_pop (&obja)))
	  return -1;
	
	(void) perform_lvalue_operation (op_type, &obja);
	SLang_free_object (&obja);
	
	if (SLang_Error)
	  return -1;
     }

   return (*cl->cl_pop) (intrinsic_type, intrinsic_addr);
}

int _SLang_deref_assign (_SLang_Ref_Type *ref)
{
   SLang_Object_Type *objp;
   SLang_Name_Type *nt;
   SLBlock_Type blk;

   if (ref->is_global == 0)
     {
	objp = ref->v.local_obj;
	if (objp > Local_Variable_Frame)
	  {
	     SLang_verror (SL_UNDEFINED_NAME, "Local variable reference is out of scope");
	     return -1;
	  }
	return set_lvalue_obj (_SLANG_BCST_ASSIGN, objp);
     }

   nt = ref->v.nt;
   switch (nt->name_type)
     {
      case SLANG_GVARIABLE:
	if (-1 == set_lvalue_obj (_SLANG_BCST_ASSIGN, 
				  &((SLang_Global_Var_Type *)nt)->obj))
	  {
	     do_name_type_error (nt);
	     return -1;
	  }
	break;

      case SLANG_IVARIABLE:
	blk.b.nt_blk = nt;
	blk.bc_sub_type = _SLANG_BCST_ASSIGN;
	if (-1 == set_intrin_lvalue (&blk))
	  {
	     do_name_type_error (nt);
	     return -1;
	  }
	break;
	
      case SLANG_LVARIABLE:
	SLang_Error = SL_INTERNAL_ERROR;
	/* set_intrin_lvalue (&blk); */
	return -1;

      case SLANG_RVARIABLE:
      default:
	SLang_verror (SL_READONLY_ERROR, "deref assignment to %s not allowed", nt->name);
	return -1;
     }
   
   return 0;
}



static void set_deref_lvalue (SLBlock_Type *bc_blk)
{
   SLang_Object_Type *objp;
   _SLang_Ref_Type *ref;

   switch (bc_blk->bc_sub_type)
     {
      case SLANG_LVARIABLE:
	objp =  (Local_Variable_Frame - bc_blk->b.i_blk);
	break;
      case SLANG_GVARIABLE:
	objp = &bc_blk->b.nt_gvar_blk->obj;
	break;
      default:
	SLang_Error = SL_INTERNAL_ERROR;
	return;
     }

   if (-1 == _SLpush_slang_obj (objp))
     return;

   if (-1 == _SLang_pop_ref (&ref))
     return;
   (void) _SLang_deref_assign (ref);
   _SLang_free_ref (ref);
}


static void trace_dump (char *format, char *name, SLang_Object_Type *objs, int n, int dir)
{
   unsigned int len;
   char buf[256];
   char prefix [52];
   
   len = Trace_Mode - 1;
   if (len + 2 >= sizeof (prefix))
     len = sizeof (prefix) - 2;

   SLMEMSET (prefix, ' ', len);
   prefix[len] = 0;

   (SLang_Dump_Routine) (prefix);
   sprintf (buf, format, name, n);
   (*SLang_Dump_Routine) (buf);
   
   if (n > 0)
     {
	prefix[len] = ' ';
	len++;
	prefix[len] = 0;
   
	_SLdump_objects (prefix, objs, n, dir);
     }
}

/*  Pop a data item from the stack and return a pointer to it.
 *  Strings are not freed from stack so use another routine to do it.
 */
static VOID_STAR pop_pointer (SLang_Object_Type *obj, unsigned char type)
{
   if (-1 == SLang_pop (obj))
     return NULL;
   
   if (type != obj->data_type)
     {
	if (type != 0)
	  {
	     SLang_push (obj);
	     if (-1 == _SLclass_typecast (type, 1, 0))
	       return NULL;
	     if (-1 == SLang_pop (obj))
	       return NULL;
	  }
	type = obj->data_type;
     }

   if (
#if _SLANG_OPTIMIZE_FOR_SPEED
       _SLclass_is_scalar_type [type]
#else
       SLANG_CLASS_TYPE_SCALAR == _SLclass_get_class (type)->cl_class_type
#endif
       )
     return (VOID_STAR) &obj->v;
   else
     return obj->v.p_val;
}

/* This is ugly.  Does anyone have a advice for a cleaner way of doing
 * this??
 */
typedef void (*VF0_Type)(void);
typedef void (*VF1_Type)(VOID_STAR);
typedef void (*VF2_Type)(VOID_STAR, VOID_STAR);
typedef void (*VF3_Type)(VOID_STAR, VOID_STAR, VOID_STAR);
typedef void (*VF4_Type)(VOID_STAR, VOID_STAR, VOID_STAR, VOID_STAR);
typedef void (*VF5_Type)(VOID_STAR, VOID_STAR, VOID_STAR, VOID_STAR, VOID_STAR);
typedef void (*VF6_Type)(VOID_STAR, VOID_STAR, VOID_STAR, VOID_STAR, VOID_STAR, VOID_STAR);
typedef void (*VF7_Type)(VOID_STAR, VOID_STAR, VOID_STAR, VOID_STAR, VOID_STAR, VOID_STAR, VOID_STAR);
typedef long (*LF0_Type)(void);
typedef long (*LF1_Type)(VOID_STAR);
typedef long (*LF2_Type)(VOID_STAR, VOID_STAR);
typedef long (*LF3_Type)(VOID_STAR, VOID_STAR, VOID_STAR);
typedef long (*LF4_Type)(VOID_STAR, VOID_STAR, VOID_STAR, VOID_STAR);
typedef long (*LF5_Type)(VOID_STAR, VOID_STAR, VOID_STAR, VOID_STAR, VOID_STAR);
typedef long (*LF6_Type)(VOID_STAR, VOID_STAR, VOID_STAR, VOID_STAR, VOID_STAR, VOID_STAR);
typedef long (*LF7_Type)(VOID_STAR, VOID_STAR, VOID_STAR, VOID_STAR, VOID_STAR, VOID_STAR, VOID_STAR);
#if SLANG_HAS_FLOAT
typedef double (*FF0_Type)(void);
typedef double (*FF1_Type)(VOID_STAR);
typedef double (*FF2_Type)(VOID_STAR, VOID_STAR);
typedef double (*FF3_Type)(VOID_STAR, VOID_STAR, VOID_STAR);
typedef double (*FF4_Type)(VOID_STAR, VOID_STAR, VOID_STAR, VOID_STAR);
typedef double (*FF5_Type)(VOID_STAR, VOID_STAR, VOID_STAR, VOID_STAR, VOID_STAR);
typedef double (*FF6_Type)(VOID_STAR, VOID_STAR, VOID_STAR, VOID_STAR, VOID_STAR, VOID_STAR);
typedef double (*FF7_Type)(VOID_STAR, VOID_STAR, VOID_STAR, VOID_STAR, VOID_STAR, VOID_STAR, VOID_STAR);
#endif


static int execute_intrinsic_fun (SLang_Intrin_Fun_Type *objf)
{
#if SLANG_HAS_FLOAT
   double xf;
#endif
   VOID_STAR p[SLANG_MAX_INTRIN_ARGS];
   SLang_Object_Type objs[SLANG_MAX_INTRIN_ARGS];
   SLang_Class_Type *cl;
   long ret;
   unsigned char type;
   unsigned int argc;
   unsigned int i;
   FVOID_STAR fptr;
   unsigned char *arg_types;
   int stk_depth;

   fptr = objf->i_fun;
   argc = objf->num_args;
   type = objf->return_type;
   arg_types = objf->arg_types;

   if (argc > SLANG_MAX_INTRIN_ARGS)
     {
	SLang_verror(SL_APPLICATION_ERROR, 
		     "Intrinsic function %s requires too many parameters", objf->name);
	return -1;
     }

   if (-1 == increment_frame_pointer ())
     return -1;
   
   stk_depth = -1;
   if (Trace_Mode && (_SLang_Trace > 0))
     {
	int nargs;

	stk_depth = _SLstack_depth ();

	nargs = SLang_Num_Function_Args;
	if (nargs == 0)
	  nargs = (int)argc;

	stk_depth -= nargs;

	if (stk_depth >= 0)
	  trace_dump (">>%s (%d args)\n",
		      objf->name,
		      _SLStack_Pointer - nargs,
		      nargs,
		      1);
     }

   i = argc;
   while (i != 0)
     {
	i--;
	if (NULL == (p[i] = pop_pointer (objs + i, arg_types[i])))
	  goto free_and_return;
     }

   ret = 0;
#if SLANG_HAS_FLOAT
   xf = 0.0;
#endif

   switch (argc)
     {
      case 0:
	if (type == SLANG_VOID_TYPE) ((VF0_Type) fptr) ();
#if SLANG_HAS_FLOAT
	else if (type == SLANG_DOUBLE_TYPE) xf = ((FF0_Type) fptr)();
#endif
	else ret = ((LF0_Type) fptr)();
	break;

      case 1:
	if (type == SLANG_VOID_TYPE) ((VF1_Type) fptr)(p[0]);
#if SLANG_HAS_FLOAT
	else if (type == SLANG_DOUBLE_TYPE) xf =  ((FF1_Type) fptr)(p[0]);
#endif
	else ret =  ((LF1_Type) fptr)(p[0]);
	break;

      case 2:
	if (type == SLANG_VOID_TYPE)  ((VF2_Type) fptr)(p[0], p[1]);
#if SLANG_HAS_FLOAT
	else if (type == SLANG_DOUBLE_TYPE) xf = ((FF2_Type) fptr)(p[0], p[1]);
#endif
	else ret = ((LF2_Type) fptr)(p[0], p[1]);
	break;

      case 3:
	if (type == SLANG_VOID_TYPE) ((VF3_Type) fptr)(p[0], p[1], p[2]);
#if SLANG_HAS_FLOAT
	else if (type == SLANG_DOUBLE_TYPE) xf = ((FF3_Type) fptr)(p[0], p[1], p[2]);
#endif
	else ret = ((LF3_Type) fptr)(p[0], p[1], p[2]);
	break;

      case 4:
	if (type == SLANG_VOID_TYPE) ((VF4_Type) fptr)(p[0], p[1], p[2], p[3]);
#if SLANG_HAS_FLOAT
	else if (type == SLANG_DOUBLE_TYPE) xf = ((FF4_Type) fptr)(p[0], p[1], p[2], p[3]);
#endif
	else ret = ((LF4_Type) fptr)(p[0], p[1], p[2], p[3]);
	break;

      case 5:
	if (type == SLANG_VOID_TYPE) ((VF5_Type) fptr)(p[0], p[1], p[2], p[3], p[4]);
#if SLANG_HAS_FLOAT
	else if (type == SLANG_DOUBLE_TYPE) xf = ((FF5_Type) fptr)(p[0], p[1], p[2], p[3], p[4]);
#endif
	else ret = ((LF5_Type) fptr)(p[0], p[1], p[2], p[3], p[4]);
	break;

      case 6:
	if (type == SLANG_VOID_TYPE) ((VF6_Type) fptr)(p[0], p[1], p[2], p[3], p[4], p[5]);
#if SLANG_HAS_FLOAT
	else if (type == SLANG_DOUBLE_TYPE) xf = ((FF6_Type) fptr)(p[0], p[1], p[2], p[3], p[4], p[5]);
#endif
	else ret = ((LF6_Type) fptr)(p[0], p[1], p[2], p[3], p[4], p[5]);
	break;

      case 7:
	if (type == SLANG_VOID_TYPE) ((VF7_Type) fptr)(p[0], p[1], p[2], p[3], p[4], p[5], p[6]);
#if SLANG_HAS_FLOAT
	else if (type == SLANG_DOUBLE_TYPE) xf = ((FF7_Type) fptr)(p[0], p[1], p[2], p[3], p[4], p[5], p[6]);
#endif
	else ret = ((LF7_Type) fptr)(p[0], p[1], p[2], p[3], p[4], p[5], p[6]);
	break;
     }

   if (type != SLANG_VOID_TYPE)
     {
	cl = _SLclass_get_class (type);
   
	switch (cl->cl_class_type)
	  {
	   case SLANG_CLASS_TYPE_SCALAR:
#if SLANG_HAS_FLOAT
	     if (type == SLANG_DOUBLE_TYPE)
	       (void) SLang_push_double (xf);
	     else
#endif
	       (void) (*cl->cl_push) (type, (VOID_STAR) &ret);
	     break;

	   default:
	     if ((VOID_STAR) ret == NULL)
	       {
		  if (SLang_Error == 0)
		    SLang_Error = SL_INTRINSIC_ERROR;
	       }
	     else (void) (*cl->cl_push) (type, (VOID_STAR) &ret);
	  }
     }

   if (stk_depth >= 0)
     {
	stk_depth = _SLstack_depth () - stk_depth;

	trace_dump ("<<%s (returning %d values)\n",
		      objf->name,
		      _SLStack_Pointer - stk_depth,
		      stk_depth,
		      1);
     }
	
   free_and_return:
   while (i < argc)
     {
	SLang_free_object (objs + i);
	i++;
     }

   return decrement_frame_pointer ();
}

static int inner_interp(register SLBlock_Type *);

/* Switch_Obj_Ptr points to the NEXT available free switch object */
static SLang_Object_Type Switch_Objects[SLANG_MAX_NESTED_SWITCH];
static SLang_Object_Type *Switch_Obj_Ptr = Switch_Objects;
static SLang_Object_Type *Switch_Obj_Max = Switch_Objects + SLANG_MAX_NESTED_SWITCH;

static void 
lang_do_loops (unsigned char stype, SLBlock_Type *block, unsigned int num_blocks)
{
   int i, ctrl;
   int first, last;
   SLBlock_Type *blks[4];
   char *loop_name;

   for (i = 0; i < (int) num_blocks; i++)
     {
	if (block[i].bc_main_type != _SLANG_BC_BLOCK)
	  {
	     SLang_verror (SL_SYNTAX_ERROR, "Bytecode is not a looping block");
	     return;
	  }
	blks[i] = block[i].b.blk;
     }

   block = blks[0];

   switch (stype)
     {
      case _SLANG_BCST_WHILE:
	loop_name = "while";

	if (num_blocks != 2) 
	  goto wrong_num_blocks_error;
	
	while (1)
	  {
	     if (SLang_Error)
	       goto return_error;

	     inner_interp (block);
	     if (Lang_Break) break;

	     if (-1 == _SLang_pop_i_val (SLANG_INT_TYPE, &ctrl))
	       goto return_error;

	     if (ctrl == 0) break;

	     inner_interp (blks[1]);
	     if (Lang_Break) break;
	     Lang_Break_Condition = Lang_Continue = 0;
	  }
	break;
   
      case _SLANG_BCST_DOWHILE:
	loop_name = "do...while";

	if (num_blocks != 2) 
	  goto wrong_num_blocks_error;

	while (1)
	  {
	     if (SLang_Error)
	       goto return_error;

	     Lang_Break_Condition = Lang_Continue = 0;

	     inner_interp (block);
	     if (Lang_Break) break;
	     inner_interp (blks[1]);
	     if (-1 == _SLang_pop_i_val (SLANG_INT_TYPE, &ctrl))
	       goto return_error;

	     if (ctrl == 0) break;
	  }
	break;

      case _SLANG_BCST_CFOR:
	loop_name = "for";

	/* we need 4 blocks: first 3 control, the last is code */
	if (num_blocks != 4) goto wrong_num_blocks_error;

	inner_interp (block);
	while (1)
	  {
	     if (SLang_Error)
	       goto return_error;

	     inner_interp(blks[1]);       /* test */
	     if (-1 == _SLang_pop_i_val (SLANG_INT_TYPE, &ctrl))
	       goto return_error;

	     if (ctrl == 0) break;
	     inner_interp(blks[3]);       /* code */
	     if (Lang_Break) break;
	     inner_interp(blks[2]);       /* bump */
	     Lang_Break_Condition = Lang_Continue = 0;
	  }
	break;

      case _SLANG_BCST_FOR:
	loop_name = "_for";

	if (num_blocks != 1)
	  goto wrong_num_blocks_error;

	/* 3 elements: first, last, step */
	if ((-1 == _SLang_pop_i_val (SLANG_INT_TYPE, &ctrl))
	    || (-1 == _SLang_pop_i_val (SLANG_INT_TYPE, &last))
	    || (-1 == _SLang_pop_i_val (SLANG_INT_TYPE, &first)))
	  goto return_error;
	
	i = first;
	while (1)
	  {
	     /* It is ugly to have this test here but I do not know of a 
	      * simple way to do this without using two while loops.
	      */
	     if (ctrl >= 0) 
	       {
		  if (i > last) break;
	       }
	     else if (i < last) break;

	     if (SLang_Error) goto return_error;

	     _SLang_push_i_val (SLANG_INT_TYPE, i);
	     inner_interp (block);
	     if (Lang_Break) break;
	     Lang_Break_Condition = Lang_Continue = 0;
	     
	     i += ctrl;
	  }
	break;

      case _SLANG_BCST_LOOP:
	loop_name = "loop";
	if (num_blocks != 1)
	  goto wrong_num_blocks_error;

	if (-1 == _SLang_pop_i_val (SLANG_INT_TYPE, &ctrl))
	  goto return_error;
	while (ctrl > 0)
	  {
	     ctrl--;

	     if (SLang_Error)
	       goto return_error;

	     inner_interp (block);
	     if (Lang_Break) break;
	     Lang_Break_Condition = Lang_Continue = 0;
	  }
	break;

      case _SLANG_BCST_FOREVER:
	loop_name = "forever";

	if (num_blocks != 1)
	  goto wrong_num_blocks_error;
	
	while (1)
	  {
	     if (SLang_Error)
	       goto return_error;

	     inner_interp (block);
	     if (Lang_Break) break;
	     Lang_Break_Condition = Lang_Continue = 0;
	  }
	break;

      default:  SLang_verror(SL_INTERNAL_ERROR, "Unknown loop type");
	return;
     }
   Lang_Break = Lang_Continue = 0;
   Lang_Break_Condition = Lang_Return;
   return;
   
   wrong_num_blocks_error:
   SLang_verror (SL_SYNTAX_ERROR, "Wrong number of blocks for '%s' construct", loop_name);
   
   /* drop */
   return_error:
   do_traceback (loop_name, 0);
}

static void lang_do_and_orelse (unsigned char stype, SLBlock_Type *addr, SLBlock_Type *addr_max)
{
   int test = 0;
   int is_or;
   
   is_or = (stype == _SLANG_BCST_ORELSE);

   while (addr <= addr_max)
     {
	inner_interp (addr->b.blk);
	if (SLang_Error 
	    || Lang_Break_Condition
	    || (-1 == _SLang_pop_i_val (SLANG_INT_TYPE, &test)))
	  return;
	
	if (is_or == (test != 0))
	  break;
	
	/* if (((stype == _SLANG_BCST_ANDELSE) && (test == 0))
	 *   || ((stype == _SLANG_BCST_ORELSE) && test))
	 * break;
	 */

	addr++;
     }
   _SLang_push_i_val (SLANG_INT_TYPE, test);
}

static void do_else_if (SLBlock_Type *zero_block, SLBlock_Type *non_zero_block)
{
   int test;
   
   if (-1 == _SLang_pop_i_val (SLANG_INT_TYPE, &test))
     return;
   
   if (test == 0)
     non_zero_block = zero_block;
   
   if (non_zero_block != NULL)
     inner_interp (non_zero_block->b.blk);
}


int _SLang_trace_fun (char *f)
{
   if (NULL == (f = SLang_create_slstring (f)))
     return -1;

   SLang_free_slstring (Trace_Function);
   Trace_Function = f;
   _SLang_Trace = 1;
   return 0;
}

int _SLdump_objects (char *prefix, SLang_Object_Type *x, unsigned int n, int dir)
{
   char buf[256];
   char *s;
   SLang_Class_Type *cl;
   
   while (n)
     {
	cl = _SLclass_get_class (x->data_type);
	(*SLang_Dump_Routine) (prefix);

	sprintf (buf, "[%s]:", cl->cl_name);
	(*SLang_Dump_Routine)(buf);

	if (NULL != (s = _SLstringize_object (x)))
	  (*SLang_Dump_Routine)(s);
	
	(*SLang_Dump_Routine)("\n");
	
	SLang_free_slstring (s);

	x += dir;
	n--;
     }
   return 0;
}




			 
static SLBlock_Type *Exit_Block_Ptr;
static SLBlock_Type *Global_User_Block[5];
static SLBlock_Type **User_Block_Ptr = Global_User_Block;
char *_SLang_Current_Function_Name = NULL;

static int execute_slang_fun (_SLang_Function_Type *fun)
{
   register unsigned int i;
   register SLang_Object_Type *frame, *lvf;
   register unsigned int n_locals;
   SLBlock_Type *val;
   SLBlock_Type *exit_block_save;
   SLBlock_Type **user_block_save;
   SLBlock_Type *user_blocks[5];
   char *save_fname;

   exit_block_save = Exit_Block_Ptr;
   user_block_save = User_Block_Ptr;
   User_Block_Ptr = user_blocks;
   *(user_blocks) = NULL;
   *(user_blocks + 1) = NULL;
   *(user_blocks + 2) = NULL;
   *(user_blocks + 3) = NULL;
   *(user_blocks + 4) = NULL;

   Exit_Block_Ptr = NULL;

   save_fname = _SLang_Current_Function_Name;
   _SLang_Current_Function_Name = fun->name;

   increment_frame_pointer ();

   /* need loaded?  */
   if (fun->nlocals == AUTOLOAD_NUM_LOCALS)
     {
	if (-1 == SLang_load_file((char *) fun->addr)) goto the_return;
	if (fun->nlocals == AUTOLOAD_NUM_LOCALS)
	  {
	     SLang_verror (SL_UNDEFINED_NAME, "%s: Function did not autoload",
			   _SLang_Current_Function_Name);
             goto the_return;
	  }
     }

   n_locals = fun->nlocals;
   val = fun->addr;

   /* let the error propagate through since it will do no harm
    and allow us to restore stack. */
   
   /* set new stack frame */
   lvf = frame = Local_Variable_Frame;
   i = n_locals;
   if ((lvf + i) > Local_Variable_Stack + SLANG_MAX_LOCAL_STACK)
     {
	SLang_verror(SL_STACK_OVERFLOW, "%s: Local Variable Stack Overflow",
		     _SLang_Current_Function_Name);
	goto the_return;
     }

   while (i--)
     {
	lvf++;
	lvf->data_type = SLANG_UNDEFINED_TYPE;
     }
   Local_Variable_Frame = lvf;

   /* read values of function arguments */
   i = fun->nargs;
   while (i > 0)
     {
	i--;
	(void) SLang_pop (Local_Variable_Frame - i);
     }

   if (SLang_Enter_Function != NULL) (*SLang_Enter_Function)(_SLang_Current_Function_Name);

   if (_SLang_Trace)
     {
	int stack_depth;

	stack_depth = _SLstack_depth ();

	if ((Trace_Function != NULL)
	    && (0 == strcmp (Trace_Function, _SLang_Current_Function_Name))
	    && (Trace_Mode == 0))
	  Trace_Mode = 1;

	if (Trace_Mode)
	  {
	     /* The local variable frame grows backwards */
	     trace_dump (">>%s (%d args)\n",
			 _SLang_Current_Function_Name,
			 Local_Variable_Frame,
			 (int) fun->nargs,
			 -1);
	     Trace_Mode++;
	  }

	inner_interp(val);
	Lang_Break_Condition = Lang_Return = Lang_Break = 0;
	if (Exit_Block_Ptr != NULL) inner_interp(Exit_Block_Ptr);

	if (Trace_Mode)
	  {
	     Trace_Mode--;
	     stack_depth = _SLstack_depth () - stack_depth;
	     
	     trace_dump ("<<%s (returning %d values)\n",
			 _SLang_Current_Function_Name,
			 _SLStack_Pointer - stack_depth,
			 stack_depth, 
			 1);

	     if (Trace_Mode == 1)
	       Trace_Mode = 0;
	  }
     }
   else
     {
	inner_interp(val);
	Lang_Break_Condition = Lang_Return = Lang_Break = 0;
	if (Exit_Block_Ptr != NULL) inner_interp(Exit_Block_Ptr);
     }

   if (SLang_Exit_Function != NULL) (*SLang_Exit_Function)(_SLang_Current_Function_Name);

   if (SLang_Error)
     do_traceback(fun->name, n_locals);

   /* free local variables.... */
   lvf = Local_Variable_Frame;
   while (lvf > frame)
     {
#if _SLANG_OPTIMIZE_FOR_SPEED
	if (0 == _SLclass_is_scalar_type [lvf->data_type])
#endif
	  SLang_free_object (lvf);
	lvf--;
     }
   Local_Variable_Frame = lvf;

   the_return:
   Lang_Break_Condition = Lang_Return = Lang_Break = 0;
   Exit_Block_Ptr = exit_block_save;
   User_Block_Ptr = user_block_save;
   _SLang_Current_Function_Name = save_fname;
   decrement_frame_pointer ();

   if (SLang_Error)
     return -1;

   return 0;
}

static void do_traceback (char *name, unsigned int locals)
{
   char buf[512];
   char *s;
   unsigned int i;
   SLang_Object_Type *objp;
   unsigned short stype;

   if (SLang_Traceback == 0)
     return;

   sprintf(buf, "S-Lang Traceback: %s\n", name);

   (*SLang_Dump_Routine)(buf);
   if ((locals == 0)
       || (SLang_Traceback < 0))
     return;

   (*SLang_Dump_Routine)("  Local Variables:\n");

   for (i = 0; i < locals; i++)
     {
	SLang_Class_Type *cl;
	char *class_name;

	objp = Local_Variable_Frame - i;
	stype = objp->data_type;

	s = _SLstringize_object (objp);
	cl = _SLclass_get_class (stype);
	class_name = cl->cl_name;

	sprintf (buf, "\t$%d: Type: %s,\tValue:\t", i, class_name);
	(*SLang_Dump_Routine)(buf);

	if (s == NULL) (*SLang_Dump_Routine)("??");
	else
	  {
	     if (SLANG_STRING_TYPE == stype) (*SLang_Dump_Routine) ("\"");
	     (*SLang_Dump_Routine)(s);
	     if (SLANG_STRING_TYPE == stype) (*SLang_Dump_Routine) ("\"");
	     SLang_free_slstring (s);
	  }
	(*SLang_Dump_Routine)("\n");
     }
}

static void do_app_unary (SLang_App_Unary_Type *nt)
{
   if (-1 == do_unary (nt->unary_op, nt->name_type))
     do_traceback (nt->name, 0);
}

int _SLang_dereference_ref (_SLang_Ref_Type *ref)
{
   SLBlock_Type bc_blks[2];
   SLang_Name_Type *nt;

   if (ref == NULL)
     {
	SLang_Error = SL_INTERNAL_ERROR;
	return -1;
     }
   
   if (ref->is_global == 0)
     {
	SLang_Object_Type *obj = ref->v.local_obj;
	if (obj > Local_Variable_Frame)
	  {
	     SLang_verror (SL_UNDEFINED_NAME, "Local variable deref is out of scope");
	     return -1;
	  }
	return _SLpush_slang_obj (ref->v.local_obj);
     }
   
   nt = ref->v.nt;
   bc_blks[0].b.nt_blk = nt;
   bc_blks[0].bc_main_type = nt->name_type;
   bc_blks[1].bc_main_type = 0;
   inner_interp(bc_blks);
   return 0;
}

#ifdef SLANG_STATS
static unsigned long stat_counts[256];
#endif

void (*SLang_Interrupt)(void);
static int Last_Error;
void (*SLang_User_Clear_Error)(void);
void _SLang_clear_error (void)
{
   if (Last_Error <= 0)
     {
	Last_Error = 0;
	return;
     }
   Last_Error--;
   if (SLang_User_Clear_Error != NULL) (*SLang_User_Clear_Error)();
}

int _SLpush_slang_obj (SLang_Object_Type *obj)
{
   unsigned char subtype;
   SLang_Class_Type *cl;

   subtype = obj->data_type;

#if _SLANG_OPTIMIZE_FOR_SPEED
   if (_SLclass_is_scalar_type[subtype])
     return SLang_push (obj);
#endif

   cl = _SLclass_get_class (subtype);
   return (*cl->cl_push) (subtype, (VOID_STAR) &obj->v);
}

static int push_intrinsic_variable (SLang_Intrin_Var_Type *ivar)
{
   SLang_Class_Type *cl;
   unsigned char stype;

   stype = ivar->type;
   cl = _SLclass_get_class (stype);

   if (-1 == (*cl->cl_push_intrinsic) (stype, ivar->addr))
     {
	do_name_type_error ((SLang_Name_Type *) ivar);
	return -1;
     }
   return 0;
}

static int dereference_object (void)
{
   SLang_Object_Type obj;
   SLang_Class_Type *cl;
   unsigned char type;
   int ret;

   if (-1 == SLang_pop (&obj))
     return -1;

   type = obj.data_type;

   cl = _SLclass_get_class (type);
   ret = (*cl->cl_dereference)(type, (VOID_STAR) &obj.v);

   SLang_free_object (&obj);
   return ret;
}

static int case_function (void)
{
   unsigned char type;
   SLang_Object_Type obj;
   SLang_Object_Type *swobjptr;
   
   swobjptr = Switch_Obj_Ptr - 1;
   
   if ((swobjptr < Switch_Objects)
       || (0 == (type = swobjptr->data_type)))
     {
	SLang_verror (SL_SYNTAX_ERROR, "Misplaced 'case' keyword");
	return -1;
     }
   
   if (-1 == SLang_pop (&obj))
     return -1;

   if (obj.data_type != type)
     (void) _SLang_push_i_val (SLANG_INT_TYPE, 0);
   else
     (void) do_binary_ab (SLANG_EQ, swobjptr, &obj);
   SLang_free_object (&obj);
   return 0;
}

int SLang_start_arg_list (void)
{
   if (Frame_Pointer_Depth < SLANG_MAX_RECURSIVE_DEPTH)
     {
	Frame_Pointer_Stack [Frame_Pointer_Depth] = (unsigned int) (Frame_Pointer - _SLRun_Stack);
	Frame_Pointer = _SLStack_Pointer;
     }
   else SLang_verror (SL_STACK_OVERFLOW, "Frame Stack Overflow");
   Frame_Pointer_Depth++;
   Next_Function_Num_Args = 0;
   if (SLang_Error) return -1;
   return 0;
}

int SLang_end_arg_list (void)
{
   if (Frame_Pointer_Depth == 0)
     {
	SLang_verror (SL_STACK_UNDERFLOW, "Frame Stack Underflow");
	return -1;
     }
   Frame_Pointer_Depth--;
   if (Frame_Pointer_Depth < SLANG_MAX_RECURSIVE_DEPTH)
     {
	Next_Function_Num_Args = (int) (_SLStack_Pointer - Frame_Pointer);
	Frame_Pointer = _SLRun_Stack + Frame_Pointer_Stack [Frame_Pointer_Depth];
     }
   return 0;
}


static int 
do_inner_interp_error (SLBlock_Type *err_block,
		       SLBlock_Type *addr_start, 
		       SLBlock_Type *addr)
{
   int save_err, slerr;

   /* Someday I can use the these variable to provide extra information 
    * about what went wrong.
    */
   (void) addr_start;
   (void) addr;
   
   if (err_block == NULL)
     goto return_error;

   if (SLang_Error < 0)		       /* errors less than 0 are severe */
     goto return_error;
   
   save_err = Last_Error++;
   slerr = SLang_Error;
   SLang_Error = 0;
   inner_interp (err_block->b.blk);

   if (Last_Error <= save_err)
     {
	/* Caught error and cleared it */
	Last_Error = save_err;
	if (Lang_Break_Condition == 0)
	  return 0;
     }
   else
     {
	Last_Error = save_err;
	SLang_Error = slerr;
     }
   
   return_error:
#if _SLANG_HAS_DEBUG_CODE
   while (addr >= addr_start)
     {
	if (addr->bc_main_type == _SLANG_BC_LINE_NUM)
	  {
	     char buf[256];
	     sprintf (buf, "(Error occured on line %u)", addr->b.i_blk);
	     do_traceback (buf, 0);
	     break;
	  }
	addr--;
     }
#endif
   return -1;
}


/* inner interpreter */
/* The return value from this function is only meaningful when it is used
 * to process blocks for the switch statement.  If it returns 0, the calling
 * routine should pass the next block to it.  Otherwise it will
 * return non-zero, with or without error.
 */
static int inner_interp (SLBlock_Type *addr_start)
{
   SLBlock_Type *block, *err_block, *addr;

   /* for systems that have no real interrupt facility (e.g. go32 on dos) */
   if (SLang_Interrupt != NULL) (*SLang_Interrupt)();

   block = err_block = NULL;
   addr = addr_start;

   while (1)
     {	
	switch (addr->bc_main_type)
	  {
	   case 0:
	     return 1;

	   case _SLANG_BC_CALL_DIRECT:
	     (*addr->b.call_function) ();
	     break;

	   case _SLANG_BC_CALL_DIRECT_FRAME:
	     if ((0 == SLang_end_arg_list ())
		 && (0 == increment_frame_pointer ()))
	       {
		  (*addr->b.call_function) ();
		  decrement_frame_pointer ();
	       }
	     break;

	   case _SLANG_BC_LVARIABLE:
	     _SLpush_slang_obj (Local_Variable_Frame - addr->b.i_blk);
	     break;

	   case _SLANG_BC_INTRINSIC:
	     execute_intrinsic_fun (addr->b.nt_ifun_blk);
	     if (SLang_Error)
	       do_traceback(addr->b.nt_ifun_blk->name, 0);
	     break;

	   case _SLANG_BC_FUNCTION:
	     execute_slang_fun (addr->b.nt_fun_blk);
	     if (Lang_Break_Condition) goto handle_break_condition;
	     break;

	   case _SLANG_BC_BINARY:
	     do_binary (addr->b.i_blk);
	     break;

	   case _SLANG_BC_UNARY:
	     do_unary (addr->b.i_blk, _SLANG_BC_UNARY);
	     break;

	   case _SLANG_BC_MATH_UNARY:
	   case _SLANG_BC_APP_UNARY:
	     do_app_unary (addr->b.nt_unary_blk);
	     break;
	     
#if _SLANG_OPTIMIZE_FOR_SPEED
	   case _SLANG_BC_LITERAL_INT:
	     _SLang_push_i_val (SLANG_INT_TYPE, addr->b.i_blk);
	     break;

	   case _SLANG_BC_LITERAL_STR:
	     SLang_push_string (addr->b.s_blk);
	     break;
#else
	   case _SLANG_BC_LITERAL_STR:
	   case _SLANG_BC_LITERAL_INT:
#endif
	   case _SLANG_BC_LITERAL:
	       {
		  SLang_Class_Type *cl = _SLclass_get_class (addr->bc_sub_type);
		  (*cl->cl_push_literal) (addr->bc_sub_type, (VOID_STAR) &addr->b.ptr_blk);
	       }
	     break;
	     
	   case _SLANG_BC_BLOCK:
	     switch (addr->bc_sub_type)
	       {
		case _SLANG_BCST_ERROR_BLOCK:
		  err_block = addr;
		  break;

		case _SLANG_BCST_EXIT_BLOCK:
		  Exit_Block_Ptr = addr->b.blk;
		  break;

		case _SLANG_BCST_USER_BLOCK0:
		case _SLANG_BCST_USER_BLOCK1:
		case _SLANG_BCST_USER_BLOCK2:
		case _SLANG_BCST_USER_BLOCK3:
		case _SLANG_BCST_USER_BLOCK4:
		  User_Block_Ptr[addr->bc_sub_type - _SLANG_BCST_USER_BLOCK0] = addr->b.blk;
		  break;

		case _SLANG_BCST_LOOP:
		case _SLANG_BCST_WHILE:
		case _SLANG_BCST_FOR:
		case _SLANG_BCST_FOREVER:
		case _SLANG_BCST_CFOR:
		case _SLANG_BCST_DOWHILE:
		  if (block == NULL) block = addr;
		  lang_do_loops(addr->bc_sub_type, block, 1 + (unsigned int) (addr - block));
		  block = NULL;
		  break;

		case _SLANG_BCST_IFNOT:
		  do_else_if (addr, NULL);
		  break;

		case _SLANG_BCST_IF:
		  do_else_if (NULL, addr);
		  break;

		case _SLANG_BCST_ELSE:
		  do_else_if (addr, block);
		  block = NULL;
		  break;

		case _SLANG_BCST_SWITCH:
		  if (Switch_Obj_Ptr == Switch_Obj_Max)
		    {
		       SLang_doerror("switch nesting too deep");
		       break;
		    }
		  (void) SLang_pop (Switch_Obj_Ptr);
		  Switch_Obj_Ptr++;

		  if (block == NULL) block = addr;
		  while ((SLang_Error == 0)
			 && (block <= addr)
			 && (Lang_Break_Condition == 0)
			 && (0 == inner_interp (block->b.blk)))
		    block++;
		  Switch_Obj_Ptr--;
		  SLang_free_object (Switch_Obj_Ptr);
		  Switch_Obj_Ptr->data_type = 0;
		  block = NULL;
		  break;
		       
		case _SLANG_BCST_ANDELSE:
		case _SLANG_BCST_ORELSE:
		  if (block == NULL) block = addr;
		  lang_do_and_orelse (addr->bc_sub_type, block, addr);
		  block = NULL;
		  break;

		default:
		  if (block == NULL) block =  addr;
		  break;
	       }
	     if (Lang_Break_Condition) goto handle_break_condition;
	     break;

	   case _SLANG_BC_SET_LOCAL_LVALUE:
	     set_lvalue_obj (addr->bc_sub_type, Local_Variable_Frame - addr->b.i_blk);
	     break;
	   case _SLANG_BC_SET_GLOBAL_LVALUE:
	     if (-1 == set_lvalue_obj (addr->bc_sub_type, &addr->b.nt_gvar_blk->obj))
	       do_name_type_error (addr->b.nt_blk);
	     break;
	   case _SLANG_BC_SET_STRUCT_LVALUE:
	     set_struct_lvalue (addr);
	     break;
	   case _SLANG_BC_SET_INTRIN_LVALUE:
	     set_intrin_lvalue (addr);
	     break;

	   case _SLANG_BC_DEREF_ASSIGN:
	     set_deref_lvalue (addr);
	     break;

	   case _SLANG_BC_GVARIABLE:
	     if (-1 == _SLpush_slang_obj (&addr->b.nt_gvar_blk->obj))
	       do_name_type_error (addr->b.nt_blk);
	     break;

	   case _SLANG_BC_IVARIABLE:
	   case _SLANG_BC_RVARIABLE:
	     push_intrinsic_variable (addr->b.nt_ivar_blk);
	     break;

	   case _SLANG_BC_RETURN:
	     Lang_Break_Condition = Lang_Return = Lang_Break = 1; return 1;
	   case _SLANG_BC_BREAK:
	     Lang_Break_Condition = Lang_Break = 1; return 1;
	   case _SLANG_BC_CONTINUE:
	     Lang_Break_Condition = Lang_Continue = 1; return 1;

	   case _SLANG_BC_EXCH:
	     (void) _SLreverse_stack (2);
	     break;

	   case _SLANG_BC_LABEL:
	       {
		  int test;
		  if ((0 == _SLang_pop_i_val (SLANG_INT_TYPE, &test))
		      && (test == 0))
		    return 0;
	       }
	     break;

	   case _SLANG_BC_LOBJPTR:
	     (void)_SLang_push_ref (0, (VOID_STAR)(Local_Variable_Frame - addr->b.i_blk));
	     break;

	   case _SLANG_BC_GOBJPTR:
	     (void)_SLang_push_ref (1, (VOID_STAR)addr->b.nt_blk);
	     break;

	   case _SLANG_BC_X_USER0:
	   case _SLANG_BC_X_USER1:
	   case _SLANG_BC_X_USER2:
	   case _SLANG_BC_X_USER3:
	   case _SLANG_BC_X_USER4:
	     if (User_Block_Ptr[addr->bc_main_type - _SLANG_BC_X_USER0] != NULL)
	       {
		  inner_interp(User_Block_Ptr[addr->bc_main_type - _SLANG_BC_X_USER0]);
	       }
	     else SLang_verror(SL_SYNTAX_ERROR, "No block for X_USERBLOCK");
	     if (Lang_Break_Condition) goto handle_break_condition;
	     break;

	   case _SLANG_BC_X_ERROR:
	     if (err_block != NULL)
	       {
		  inner_interp(err_block->b.blk);
		  if (SLang_Error) err_block = NULL;
	       }
	     else SLang_verror(SL_SYNTAX_ERROR, "No ERROR_BLOCK");
	     if (Lang_Break_Condition) goto handle_break_condition;
	     break;

	   case _SLANG_BC_FIELD:
	     (void) _SLstruct_get_field (addr->b.s_blk);
	     break;

#if _SLANG_HAS_DEBUG_CODE
	   case _SLANG_BC_LINE_NUM:
	     break;
#endif
	   default :
	     SLang_verror (SL_INTERNAL_ERROR, "Byte-Code 0x%X is not valid", addr->bc_main_type);
	  }

	if (SLang_Error)
	  {
	     if (-1 == do_inner_interp_error (err_block, addr_start, addr))
	       return 1;
	     /* Otherwise, error cleared.  Continue onto next bytecode.
	      * Someday I need to add something to indicate where the 
	      * next statement begins since continuing on the next
	      * bytecode is not really what is desired.
	      */
	  }
	addr++;
     }
   
   handle_break_condition:
   /* Get here if Lang_Break_Condition != 0, which implies that either
    * Lang_Return, Lang_Break, or Lang_Continue is non zero
    */
   if (Lang_Return)
     Lang_Break = 1;

   return 1;
}

/*}}}*/

/* The functions below this point are used to implement the parsed token
 * to byte-compiled code.
 */

static SLang_Name_Type *Globals_Hash_Table [SLGLOBALS_HASH_TABLE_SIZE];
static SLang_Name_Type *Locals_Hash_Table [SLLOCALS_HASH_TABLE_SIZE];
static int Local_Variable_Number;
static unsigned int Function_Args_Number;
int _SLang_Auto_Declare_Globals = 0;

static SLBlock_Type SLShort_Blocks[3];
/* These are initialized in add_table below.  I cannot init a Union!! */

static int Lang_Defining_Function;
typedef struct
{
   int block_type;
   SLBlock_Type *block;		       /* beginning of block definition */
   SLBlock_Type *block_ptr;	       /* current location */
   SLBlock_Type *block_max;	       /* end of definition */
}
Block_Context_Type;

static Block_Context_Type Block_Context_Stack [SLANG_MAX_BLOCK_STACK_LEN];
static unsigned int Block_Context_Stack_Len;

static SLBlock_Type *Compile_ByteCode_Ptr;
static SLBlock_Type *This_Compile_Block;
static SLBlock_Type *This_Compile_Block_Max;
static int This_Compile_Block_Type;
#define COMPILE_BLOCK_TYPE_FUNCTION	1
#define COMPILE_BLOCK_TYPE_BLOCK	2
#define COMPILE_BLOCK_TYPE_TOP_LEVEL	3

/* If it returns 0, DO NOT FREE p */
static int lang_free_branch (SLBlock_Type *p)
{
   if ((p == SLShort_Blocks)
       || (p == SLShort_Blocks + 1)
       || (p == SLShort_Blocks + 2)
       )
     return 0;

   while (1)
     {
	SLang_Class_Type *cl;

        switch (p->bc_main_type)
	  {
	   case _SLANG_BC_BLOCK:
	     if (lang_free_branch(p->b.blk))
	       SLfree((char *)p->b.blk);
	     break;

	   case _SLANG_BC_LITERAL:
	   case _SLANG_BC_LITERAL_STR:
	     /* No user types should be here. */
	     cl = _SLclass_get_class (p->bc_sub_type);
	     (*cl->cl_byte_code_destroy) (p->bc_sub_type, (VOID_STAR) &p->b.ptr_blk);
	     break;

	   case _SLANG_BC_FIELD:
	   case _SLANG_BC_SET_STRUCT_LVALUE:
	     SLang_free_slstring (p->b.s_blk);
	     break;

	   default:
	     break;

	   case 0:
	     return 1;
	  }
	p++;
     }
}

static int push_block_context (int type)
{
   Block_Context_Type *c;
   unsigned int num;
   SLBlock_Type *b;

   if (Block_Context_Stack_Len == SLANG_MAX_BLOCK_STACK_LEN)
     {
	SLang_verror (SL_STACK_OVERFLOW, "Block stack overflow");
	return -1;
     }

   num = 5;    /* 40 bytes */
   if (NULL == (b = (SLBlock_Type *) SLcalloc (num, sizeof (SLBlock_Type))))
     return -1;

   c = Block_Context_Stack + Block_Context_Stack_Len;
   c->block = This_Compile_Block;
   c->block_ptr = Compile_ByteCode_Ptr;
   c->block_max = This_Compile_Block_Max;
   c->block_type = This_Compile_Block_Type;

   Compile_ByteCode_Ptr = This_Compile_Block = b;
   This_Compile_Block_Max = b + num;
   This_Compile_Block_Type = type;

   Block_Context_Stack_Len += 1;
   return 0;
}

static int pop_block_context (void)
{
   Block_Context_Type *c;

   if (Block_Context_Stack_Len == 0)
     return -1;

   Block_Context_Stack_Len -= 1;
   c = Block_Context_Stack + Block_Context_Stack_Len;

   This_Compile_Block = c->block;
   This_Compile_Block_Max = c->block_max;
   This_Compile_Block_Type = c->block_type;
   Compile_ByteCode_Ptr = c->block_ptr;

   return 0;
}

/* The only way the push/pop_context functions can get called is via
 * an eval type function.  That can only happen when executed from a
 * top level block.  This means that Compile_ByteCode_Ptr can always be
 * rest back to the beginning of a block.
 */
int _SLcompile_push_context (void)
{
   if (-1 == push_block_context (COMPILE_BLOCK_TYPE_TOP_LEVEL))
     return -1;

   return 0;
}

int _SLcompile_pop_context (void)
{
   if (This_Compile_Block_Type == COMPILE_BLOCK_TYPE_TOP_LEVEL)
     {
	Compile_ByteCode_Ptr->bc_main_type = 0;
	if (lang_free_branch (This_Compile_Block))
	  SLfree ((char *) This_Compile_Block);
     }

   if (-1 == pop_block_context ())
     return -1;

   if (This_Compile_Block == NULL)
     return 0;

   if (This_Compile_Block_Type != COMPILE_BLOCK_TYPE_TOP_LEVEL)
     {
	SLang_verror (SL_INTERNAL_ERROR, "Not at top-level");
	return -1;
     }

   return 0;
}

/*{{{ Hash and Name Table Functions */

static SLang_Name_Type *locate_name_in_table (char *name, unsigned long hash,
					      SLang_Name_Type **table, unsigned int table_size)
{
   SLang_Name_Type *t;
   char ch;

   t = table [(unsigned int) (hash % table_size)];
   ch = *name++;

   while (t != NULL)
     {
	if ((ch == t->name[0])
	    && (0 == strcmp (t->name + 1, name)))
	  break;

	t = t->next;
     }

   return t;
}

/* check syntax.  Allowed chars are: $!_?AB..Zab..z0-9 */
static int lang_check_name (char *name)
{
   register char *p, ch;

   p = name;
   while ((ch = *p++) != 0)
     {
	if ((ch >= 'a') && (ch <= 'z')) continue;
	if ((ch >= 'A') && (ch <= 'Z')) continue;
	if ((ch >= '0') && (ch <= '9')) continue;
	if ((ch == '_') || (ch == '$') || (ch == '!') || (ch == '?')) continue;
	SLang_verror (SL_SYNTAX_ERROR, "%s: bad name syntax", name);
	return -1;
     }
   return 0;
}

static SLang_Name_Type *locate_hashed_name (char *name, unsigned long hash)
{
   SLang_Name_Type *t;

   if (Lang_Defining_Function)
     {
	t = locate_name_in_table (name, hash, Locals_Hash_Table, SLLOCALS_HASH_TABLE_SIZE);
	if (t != NULL)
	  return t;
     }

   return locate_name_in_table (name, hash, Globals_Hash_Table, SLGLOBALS_HASH_TABLE_SIZE);
}


static SLang_Name_Type *
add_name_to_hash_table (char *name, unsigned long hash, 
			unsigned int sizeof_obj, unsigned char name_type,
			SLang_Name_Type **table, unsigned int table_size,
			int check_existing)
{
   SLang_Name_Type *t;

   if (check_existing)
     {
	t = locate_name_in_table (name, hash, table, table_size);
	if (t != NULL)
	  return t;
     }

   if (-1 == lang_check_name(name))
     return NULL;

   t = (SLang_Name_Type *) SLmalloc (sizeof_obj);
   if (t == NULL)
     return t;

   memset ((char *) t, 0, sizeof_obj);
   if (NULL == (t->name = _SLstring_dup_hashed_string (name, hash)))
     {
	SLfree ((char *) t);
	return NULL;
     }
   t->name_type = name_type;

   hash = hash % table_size;
   t->next = table [(unsigned int)hash];
   table [(unsigned int) hash] = t;

   return t;
}

static SLang_Name_Type *
add_global_name (char *name, unsigned long hash, 
		 unsigned char name_type, unsigned int sizeof_obj)
{
   SLang_Name_Type *nt;
   
   nt = locate_name_in_table (name, hash, Globals_Hash_Table, SLGLOBALS_HASH_TABLE_SIZE);
   if (nt != NULL)
     {
	if (nt->name_type == name_type)
	  return nt;
	
	SLang_verror (SL_DUPLICATE_DEFINITION, "%s cannot be re-defined", name);
	return NULL;
     }
   
   return add_name_to_hash_table (name, hash, sizeof_obj, name_type,
				  Globals_Hash_Table, SLGLOBALS_HASH_TABLE_SIZE, 
				  0);
}

int SLadd_intrinsic_function (char *name, FVOID_STAR addr, unsigned char ret_type, 
			      unsigned int nargs, ...)
{
   SLang_Intrin_Fun_Type *f;
   va_list ap;
   unsigned int i;

   if (nargs > SLANG_MAX_INTRIN_ARGS)
     {
	SLang_verror (SL_APPLICATION_ERROR, "Function %s requires too many arguments", name);
	return -1;
     }

   f = (SLang_Intrin_Fun_Type *) add_global_name (name, _SLcompute_string_hash (name),
						  SLANG_INTRINSIC, sizeof (SLang_Intrin_Fun_Type));
   if (f == NULL)
     return -1;

   f->i_fun = addr;
   f->num_args = nargs;
   f->return_type = ret_type;

   va_start (ap, nargs);
   for (i = 0; i < nargs; i++)
     f->arg_types [i] = va_arg (ap, unsigned int);
   va_end (ap);

   return 0;
}

int SLadd_intrinsic_variable (char *name, VOID_STAR addr, unsigned char data_type, int ro)
{
   SLang_Intrin_Var_Type *v;
   
   v = (SLang_Intrin_Var_Type *)add_global_name (name,
						 _SLcompute_string_hash (name),
						 (ro ? SLANG_RVARIABLE : SLANG_IVARIABLE),
						 sizeof (SLang_Intrin_Var_Type));
   if (v == NULL)
     return -1;
   
   v->addr = addr;
   v->type = data_type;
   return 0;
}


static int 
add_slang_function (char *name, unsigned long hash, 
		    unsigned int num_args, unsigned int num_locals,
		    SLBlock_Type *addr)
{
   _SLang_Function_Type *f;
   
   f = (_SLang_Function_Type *)add_global_name (name, hash,
						SLANG_FUNCTION, 
						sizeof (_SLang_Function_Type));
   if (f == NULL) return -1;

   if (f->addr != NULL)
     {
	if (f->nlocals == AUTOLOAD_NUM_LOCALS)	      
	  SLang_free_slstring ((char *)f->addr); /* autoloaded filename */
	else
	  if (lang_free_branch(f->addr)) 
	    SLfree((char *)f->addr);
     }
   
   f->addr = addr;
   f->nlocals = num_locals;
   f->nargs = num_args;
   
   return 0;
}


int SLang_autoload (char *name, char *file)
{
   _SLang_Function_Type *f;
   unsigned long hash;
   
   hash = _SLcompute_string_hash (name);
   f = (_SLang_Function_Type *)locate_name_in_table (name, hash, Globals_Hash_Table, SLGLOBALS_HASH_TABLE_SIZE);

   if ((f != NULL)
       && (f->name_type == SLANG_FUNCTION)
       && (f->addr != NULL)
       && (f->nlocals != AUTOLOAD_NUM_LOCALS))
     {
	/* already loaded */
	return 0;
     }
   
   file = SLang_create_slstring (file);
   if (-1 == add_slang_function (name, hash, 0, AUTOLOAD_NUM_LOCALS, (SLBlock_Type *) file))
     {
	SLang_free_slstring (file);
	return -1;
     }

   return 0;
}

static SLang_Name_Type *locate_global_name (char *name)
{
   unsigned long hash;

   hash = _SLcompute_string_hash (name);
   return locate_name_in_table (name, hash, Globals_Hash_Table, SLGLOBALS_HASH_TABLE_SIZE);
}

/*}}}*/

static void free_local_variable_table (void)
{
   unsigned int i;
   SLang_Name_Type *t, *t1;

   for (i = 0; i < SLLOCALS_HASH_TABLE_SIZE; i++)
     {
	t = Locals_Hash_Table [i];
	while (t != NULL)
	  {
	     SLang_free_slstring (t->name);
	     t1 = t->next;
	     SLfree ((char *) t);
	     t = t1;
	  }
	Locals_Hash_Table [i] = NULL;
     }
   Local_Variable_Number = 0;
}

/* call inner interpreter or return for more */
static void lang_try_now(void)
{
   Compile_ByteCode_Ptr++;
   if (This_Compile_Block_Type != COMPILE_BLOCK_TYPE_TOP_LEVEL)
     return;

   Compile_ByteCode_Ptr->bc_main_type = 0;  /* so next command stops after this */

   /* now do it */
   inner_interp (This_Compile_Block);
   (void) lang_free_branch (This_Compile_Block);
   Compile_ByteCode_Ptr = This_Compile_Block;
}

int SLexecute_function (SLang_Name_Type *nt)
{
   unsigned char type;
   char *name;

   type = nt->name_type;
   name = nt->name;

   if (type == SLANG_FUNCTION) 
     execute_slang_fun ((_SLang_Function_Type *) nt);
   else if (type == SLANG_INTRINSIC)
     execute_intrinsic_fun ((SLang_Intrin_Fun_Type *) nt);
   else 
     {
	SLang_verror (SL_TYPE_MISMATCH, "%s is not a function", name);
	return -1;
     }

   if (SLang_Error)
     {
	SLang_verror (SLang_Error, "Error while executing %s", name);
	return -1;
     }

   return 1;
}

int SLang_execute_function (char *name)
{
   SLang_Name_Type *entry;

   if (NULL == (entry = locate_global_name (name)))
     return 0;

   return SLexecute_function (entry);
}

/* return S-Lang function or NULL */
SLang_Name_Type *SLang_get_function (char *name)
{
   SLang_Name_Type *entry;

   if (NULL == (entry = locate_global_name (name)))
     return NULL;

   if ((entry->name_type == SLANG_FUNCTION)
       || (entry->name_type == SLANG_INTRINSIC))
     return entry;

   return NULL;
}

static void lang_begin_function (void)
{
   if (This_Compile_Block_Type != COMPILE_BLOCK_TYPE_TOP_LEVEL)
     {
	SLang_verror (SL_SYNTAX_ERROR, "Function nesting is illegal");
	return;
     }
   Lang_Defining_Function = 1;
   (void) push_block_context (COMPILE_BLOCK_TYPE_FUNCTION);
}

/* name will be NULL if the object is to simply terminate the function
 * definition.  See SLang_restart.
 */
static int lang_define_function (char *name, unsigned long hash)
{
   if (This_Compile_Block_Type != COMPILE_BLOCK_TYPE_FUNCTION)
     {
	SLang_verror (SL_SYNTAX_ERROR, "Premature end of function");
	return -1;
     }

   /* terminate function */
   Compile_ByteCode_Ptr->bc_main_type = 0;

   if (name != NULL)
     {
	(void) add_slang_function (name, hash,
				   Function_Args_Number,
				   Local_Variable_Number,
				   This_Compile_Block);
     }

   free_local_variable_table ();

   Function_Args_Number = 0;
   Lang_Defining_Function = 0;

   if (SLang_Error) return -1;
   /* SLang_restart will finish this if there is a slang error. */

   pop_block_context ();

   /* A function is only defined at top-level */
   if (This_Compile_Block_Type != COMPILE_BLOCK_TYPE_TOP_LEVEL)
     {
	SLang_verror (SL_INTERNAL_ERROR, "Not at top-level");
	return -1;
     }
   Compile_ByteCode_Ptr = This_Compile_Block;
   return 0;
}

static void lang_end_block (void)
{
   SLBlock_Type *node, *branch;
   unsigned char mtype;

   if (This_Compile_Block_Type != COMPILE_BLOCK_TYPE_BLOCK)
     {
	SLang_verror (SL_SYNTAX_ERROR, "Not defining a block");
	return;
     }

   /* terminate the block */
   Compile_ByteCode_Ptr->bc_main_type = 0;
   branch = This_Compile_Block;

   /* Try to save some space by using the cached blocks. */
   if (Compile_ByteCode_Ptr == branch + 1)
     {
	mtype = branch->bc_main_type;
	if (((mtype == _SLANG_BC_BREAK)
	     || (mtype == _SLANG_BC_CONTINUE)
	     || (mtype == _SLANG_BC_RETURN))
	    && (SLang_Error == 0))
	  {
	     SLfree ((char *)branch);
	     branch = SLShort_Blocks + (int) (mtype - _SLANG_BC_RETURN);
	  }
     }

   pop_block_context ();
   node = Compile_ByteCode_Ptr++;

   node->bc_main_type = _SLANG_BC_BLOCK;
   node->bc_sub_type = 0;
   node->b.blk = branch;
}

static int lang_begin_block (void)
{
   return push_block_context (COMPILE_BLOCK_TYPE_BLOCK);
}

static int lang_check_space (void)
{
   unsigned int n;
   SLBlock_Type *p;

   if (NULL == (p = This_Compile_Block))
     {
	SLang_verror (SL_INTERNAL_ERROR, "Top-level block not present");
	return -1;
     }

   /* Allow 1 extra for terminator */
   if (Compile_ByteCode_Ptr + 1 < This_Compile_Block_Max)
     return 0;

   n = (unsigned int) (This_Compile_Block_Max - p);

   /* enlarge the space by 2 objects */
   n += 2;

   if (NULL == (p = (SLBlock_Type *) SLrealloc((char *)p, n * sizeof(SLBlock_Type))))
     return -1;

   This_Compile_Block_Max = p + n;
   n = (unsigned int) (Compile_ByteCode_Ptr - This_Compile_Block);
   This_Compile_Block = p;
   Compile_ByteCode_Ptr = p + n;

   return 0;
}

/* returns positive number if name is a function or negative number if it
 is a variable.  If it is intrinsic, it returns magnitude of 1, else 2 */
int SLang_is_defined(char *name)
{
   SLang_Name_Type *t;

   if (NULL == (t = locate_global_name (name)))
     return 0;

   switch (t->name_type)
     {
      case SLANG_FUNCTION: return 2;
      case SLANG_GVARIABLE: return -2;

      case SLANG_RVARIABLE:
      case SLANG_IVARIABLE:
	return -1;

      case SLANG_INTRINSIC:
      default:
	return 1;
     }
}

static int add_global_variable (char *name, unsigned long hash)
{
   SLang_Name_Type *g;

   /* Note the importance of checking if it is already defined or not.  For example,
    * suppose X is defined as an intrinsic variable.  Then S-Lang code like:
    * !if (is_defined("X")) { variable X; }
    * will not result in a global variable X.  On the other hand, this would
    * not be an issue if 'variable' statements always were not processed
    * immediately.  That is, as it is now, 'if (0) {variable ZZZZ;}' will result
    * in the variable ZZZZ being defined because of the immediate processing.
    * The current solution is to do: if (0) { eval("variable ZZZZ;"); }
    */
   hash = _SLcompute_string_hash (name);
   g = locate_name_in_table (name, hash, Globals_Hash_Table, SLGLOBALS_HASH_TABLE_SIZE);
   
   if ((g != NULL) && (g->name_type == SLANG_GVARIABLE))
     return 0;
   
   if (NULL == add_global_name (name, hash, SLANG_GVARIABLE,
				sizeof (SLang_Global_Var_Type)))
     return -1;

   return 0;
}

int SLadd_global_variable (char *name)
{
   return add_global_variable (name, _SLcompute_string_hash (name));
}

static int add_hashed_variable (char *name, unsigned long hash)
{
   SLang_Local_Var_Type *t;

   if (Lang_Defining_Function == 0)
     return add_global_variable (name, hash);

   /* local variable */
   if (Local_Variable_Number >= SLANG_MAX_LOCAL_VARIABLES)
     {
	SLang_verror (SL_SYNTAX_ERROR, "Too many local variables");
	return -1;
     }

   if (NULL != locate_name_in_table (name, hash, Locals_Hash_Table, SLLOCALS_HASH_TABLE_SIZE))
     {
	SLang_verror (SL_SYNTAX_ERROR, "Local variable %s has already been defined", name);
	return -1;
     }

   t = (SLang_Local_Var_Type *) 
     add_name_to_hash_table (name, hash, 
			     sizeof (SLang_Local_Var_Type), SLANG_LVARIABLE,
			     Locals_Hash_Table, SLLOCALS_HASH_TABLE_SIZE, 0);
   if (t == NULL)
     return -1;

   t->local_var_number = Local_Variable_Number;
   Local_Variable_Number++;
   return 0;
}


static void (*Compile_Mode_Function) (_SLang_Token_Type *);
static void compile_basic_token_mode (_SLang_Token_Type *);

/* if an error occurs, discard current object, block, function, etc... */
void SLang_restart (int localv)
{
   int save = SLang_Error;

   SLang_Error = SL_UNKNOWN_ERROR;

   _SLcompile_ptr = _SLcompile;
   Compile_Mode_Function = compile_basic_token_mode;

   Lang_Break = Lang_Continue = Lang_Return = 0;
   Trace_Mode = 0;

   while (This_Compile_Block_Type == COMPILE_BLOCK_TYPE_BLOCK)
     lang_end_block();

   if (This_Compile_Block_Type == COMPILE_BLOCK_TYPE_FUNCTION)
     {
	/* Terminate function definition and free variables */
	lang_define_function (NULL, 0);
	if (lang_free_branch (This_Compile_Block))
	  SLfree((char *)This_Compile_Block);
     }
   Lang_Defining_Function = 0;

   SLang_Error = save;

   if (SLang_Error == SL_STACK_OVERFLOW)
     {
	/* This loop guarantees that the stack is properly cleaned. */
	while (_SLStack_Pointer != _SLRun_Stack)
	  {
	     SLdo_pop ();
	  }
     }

   while ((This_Compile_Block_Type != COMPILE_BLOCK_TYPE_TOP_LEVEL)
	  && (0 == pop_block_context ()))
     ;

   if (localv)
     {
	Next_Function_Num_Args = SLang_Num_Function_Args = 0;
	Local_Variable_Frame = Local_Variable_Stack;
	Recursion_Depth = 0;
	Frame_Pointer = _SLStack_Pointer;
	Frame_Pointer_Depth = 0;
	Switch_Obj_Ptr = Switch_Objects;
	while (Switch_Obj_Ptr < Switch_Obj_Max)
	  {
	     SLang_free_object (Switch_Obj_Ptr);
	     Switch_Obj_Ptr++;
	  }
	Switch_Obj_Ptr = Switch_Objects;
     }
}

static void compile_directive (unsigned char sub_type)
{
   /* This function is called only from compile_directive_mode which is
    * only possible when a block is available.
    */

   /* use BLOCK */
   Compile_ByteCode_Ptr--;
   Compile_ByteCode_Ptr->bc_sub_type = sub_type;

   lang_try_now ();
}

static void compile_unary (int op)
{
   Compile_ByteCode_Ptr->bc_main_type = _SLANG_BC_UNARY;
   Compile_ByteCode_Ptr->b.i_blk = op;
   Compile_ByteCode_Ptr->bc_sub_type = 0;

   lang_try_now ();
}

static void compile_binary (int op)
{
   Compile_ByteCode_Ptr->bc_main_type = _SLANG_BC_BINARY;
   Compile_ByteCode_Ptr->b.i_blk = op;
   Compile_ByteCode_Ptr->bc_sub_type = 0;

   lang_try_now ();
}

static void compile_hashed_identifier (char *name, unsigned long hash)
{
   SLang_Name_Type *entry;
   unsigned char name_type;

   entry = locate_hashed_name (name, hash);

   if (entry == NULL)
     {
	int i = 0;

	/* Perhaps it is a user/error block */
	if (0 == strcmp (name, "EXECUTE_ERROR_BLOCK"))
	  name_type = _SLANG_BC_X_ERROR;
	else if ((0 == strncmp ("X_USER_BLOCK", name, 12))
		 && ((i = name[12]) < '5') && (i >= '0')
		 && (name[13] == 0))
	  name_type = _SLANG_BC_X_USER0 + (i - '0');
	else
	  {
	     SLang_verror (SL_UNDEFINED_NAME, "%s is undefined", name);
	     return;
	  }

	Compile_ByteCode_Ptr->bc_main_type = name_type;
	Compile_ByteCode_Ptr->b.blk = NULL;
	lang_try_now ();
	return;
     }

   name_type = entry->name_type;
   Compile_ByteCode_Ptr->bc_main_type = name_type;

   if (name_type == SLANG_LVARIABLE)
     Compile_ByteCode_Ptr->b.i_blk = ((SLang_Local_Var_Type *) entry)->local_var_number;
   else
     Compile_ByteCode_Ptr->b.nt_blk = entry;

   lang_try_now ();
}

static void compile_simple (unsigned char main_type)
{
   Compile_ByteCode_Ptr->bc_main_type = main_type;
   Compile_ByteCode_Ptr->bc_sub_type = 0;
   Compile_ByteCode_Ptr->b.blk = NULL;
   lang_try_now ();
}

static void compile_identifier (char *name)
{
   compile_hashed_identifier (name, _SLcompute_string_hash (name));
}

static void compile_call_direct (int (*f) (void), unsigned char byte_code)
{
   Compile_ByteCode_Ptr->b.call_function = f;
   Compile_ByteCode_Ptr->bc_main_type = byte_code;
   Compile_ByteCode_Ptr->bc_sub_type = 0;
   lang_try_now ();
}

static void compile_integer (int i)
{
   Compile_ByteCode_Ptr->b.i_blk = i;
   Compile_ByteCode_Ptr->bc_main_type = _SLANG_BC_LITERAL_INT;
   Compile_ByteCode_Ptr->bc_sub_type = SLANG_INT_TYPE;
   
   lang_try_now ();
}

#if SLANG_HAS_FLOAT
static void compile_double (char *str, unsigned char type)
{
   double d;
   unsigned int factor = 1;
   double *ptr;

   if (1 != sscanf (str, "%lf", &d))
     {
	SLang_verror (SL_SYNTAX_ERROR, "Unable to convert %s to double", str);
	return;
     }

#if SLANG_HAS_COMPLEX
   if (type == SLANG_COMPLEX_TYPE) factor = 2;
#endif
   if (NULL == (ptr = (double *) SLmalloc(factor * sizeof(double))))
     return;

   Compile_ByteCode_Ptr->b.f_blk = ptr;
#if SLANG_HAS_COMPLEX
   if (type == SLANG_COMPLEX_TYPE)
     *ptr++ = 0;
#endif
   *ptr = d;

   Compile_ByteCode_Ptr->bc_main_type = _SLANG_BC_LITERAL;
   Compile_ByteCode_Ptr->bc_sub_type = type;
   lang_try_now ();
}
#endif

static void compile_string (char *s, unsigned long hash)
{
   if (NULL == (Compile_ByteCode_Ptr->b.s_blk = _SLstring_dup_hashed_string (s, hash)))
     return;

   Compile_ByteCode_Ptr->bc_main_type = _SLANG_BC_LITERAL_STR;
   Compile_ByteCode_Ptr->bc_sub_type = SLANG_STRING_TYPE;

   lang_try_now ();
}

/* assign_type is one of _SLANG_BCST_ASSIGN, ... values */
static void compile_assign (unsigned char assign_type,
			    char *name, unsigned long hash)
{
   SLang_Name_Type *v;
   unsigned char main_type;
   SLang_Class_Type *cl;

   v = locate_hashed_name (name, hash);
   if (v == NULL)
     {
	if ((_SLang_Auto_Declare_Globals == 0)
	    || (This_Compile_Block_Type != COMPILE_BLOCK_TYPE_TOP_LEVEL)
	    || (assign_type != _SLANG_BCST_ASSIGN))
	  {
	     SLang_verror (SL_UNDEFINED_NAME, "%s is undefined", name);
	     return;
	  }

	if ((-1 == add_hashed_variable (name, hash))
	    || (NULL == (v = locate_hashed_name (name, hash))))
	  return;
     }

   switch (v->name_type)
     {
      case SLANG_LVARIABLE:
	main_type = _SLANG_BC_SET_LOCAL_LVALUE;
	Compile_ByteCode_Ptr->b.i_blk = ((SLang_Local_Var_Type *) v)->local_var_number;
	break;

      case SLANG_GVARIABLE:
	main_type = _SLANG_BC_SET_GLOBAL_LVALUE;
	Compile_ByteCode_Ptr->b.nt_blk = v;
	break;

      case SLANG_IVARIABLE:
	cl = _SLclass_get_class (((SLang_Intrin_Var_Type *)v)->type);
	if (cl->cl_class_type != SLANG_CLASS_TYPE_SCALAR)
	  {
	     SLang_verror (SL_SYNTAX_ERROR, "Assignment to %s is not allowed", name);
	     return;
	  }
	main_type = _SLANG_BC_SET_INTRIN_LVALUE;
	Compile_ByteCode_Ptr->b.nt_blk = v;
	break;

      case SLANG_RVARIABLE:
	SLang_verror (SL_READONLY_ERROR, "%s is read-only", name);
	return;

      default:
	SLang_verror (SL_DUPLICATE_DEFINITION, "%s already defined", name);
	return;
     }

   Compile_ByteCode_Ptr->bc_sub_type = assign_type;
   Compile_ByteCode_Ptr->bc_main_type = main_type;

   lang_try_now ();
}

static void compile_deref_assign (char *name, unsigned long hash)
{
   SLang_Name_Type *v;

   v = locate_hashed_name (name, hash);

   if (v == NULL)
     {
	SLang_verror (SL_UNDEFINED_NAME, "%s is undefined", name);
	return;
     }

   switch (v->name_type)
     {
      case SLANG_LVARIABLE:
	Compile_ByteCode_Ptr->b.i_blk = ((SLang_Local_Var_Type *) v)->local_var_number;
	break;

      case SLANG_GVARIABLE:
	Compile_ByteCode_Ptr->b.nt_blk = v;
	break;

      default:
	/* FIXME!! This could be made to work.  It is not a priority because
	 * I cannot imagine application intrinsics which are references.
	 */
	SLang_verror (SL_NOT_IMPLEMENTED, "Deref assignment to %s is not allowed", name);
	return;
     }

   Compile_ByteCode_Ptr->bc_sub_type = v->name_type;
   Compile_ByteCode_Ptr->bc_main_type = _SLANG_BC_DEREF_ASSIGN;

   lang_try_now ();
}

static void 
compile_struct_assign (_SLang_Token_Type *t)
{
   Compile_ByteCode_Ptr->bc_sub_type = _SLANG_BCST_ASSIGN + (t->type - _STRUCT_ASSIGN_TOKEN);
   Compile_ByteCode_Ptr->bc_main_type = _SLANG_BC_SET_STRUCT_LVALUE;
   Compile_ByteCode_Ptr->b.s_blk = _SLstring_dup_hashed_string (t->v.s_val, t->hash);
   lang_try_now ();
}

static void compile_dot(_SLang_Token_Type *t)
{
   Compile_ByteCode_Ptr->bc_main_type = _SLANG_BC_FIELD;
   Compile_ByteCode_Ptr->b.s_blk = _SLstring_dup_hashed_string(t->v.s_val, t->hash);
   lang_try_now ();
}


static void compile_ref (char *name, unsigned long hash)
{
   SLang_Name_Type *entry;
   unsigned char main_type;

   if (NULL == (entry = locate_hashed_name (name, hash)))
     {
	SLang_verror (SL_UNDEFINED_NAME, "%s is undefined", name);
	return;
     }

   main_type = entry->name_type;

   if (main_type == SLANG_LVARIABLE)
     {
	main_type = _SLANG_BC_LOBJPTR;
	Compile_ByteCode_Ptr->b.i_blk = ((SLang_Local_Var_Type *)entry)->local_var_number;
     }
   else
     {
	main_type = _SLANG_BC_GOBJPTR;
	Compile_ByteCode_Ptr->b.nt_blk = entry;
     }

   Compile_ByteCode_Ptr->bc_main_type = main_type;
   lang_try_now ();
}

static void compile_break (unsigned char break_type,
			   int requires_block, int requires_fun,
			   char *str)
{
   if ((requires_fun
	&& (Lang_Defining_Function == 0))
       || (requires_block
	   && (This_Compile_Block_Type != COMPILE_BLOCK_TYPE_BLOCK)))
     {
	SLang_verror (SL_SYNTAX_ERROR, "misplaced %s", str);
	return;
     }

   Compile_ByteCode_Ptr->bc_main_type = break_type;
   Compile_ByteCode_Ptr->bc_sub_type = 0;

   lang_try_now ();
}

static void compile_variable_mode (_SLang_Token_Type *t)
{
   if (t->type == IDENT_TOKEN)
     add_hashed_variable (t->v.s_val, t->hash);
   else if (t->type == CBRACKET_TOKEN)
     {
	Compile_Mode_Function = compile_basic_token_mode;
     }
   else
     SLang_verror (SL_SYNTAX_ERROR, "Misplaced token in variable list");
}

static void compile_function_mode (_SLang_Token_Type *t)
{
   if (-1 == lang_check_space ())
     return;

   if (t->type != IDENT_TOKEN)
     SLang_verror (SL_SYNTAX_ERROR, "Expecting function name");
   else
     lang_define_function (t->v.s_val, t->hash);

   Compile_Mode_Function = compile_basic_token_mode;
}

/* The only allowed tokens are the directives and another block start.
 * The mode is only active if a block is available.  The inner_interp routine
 * expects such safety checks.
 */
static void compile_directive_mode (_SLang_Token_Type *t)
{
   int bc_sub_type;

   if (-1 == lang_check_space ())
     return;

   bc_sub_type = -1;

   switch (t->type)
     {
      case FOREVER_TOKEN:
	bc_sub_type = _SLANG_BCST_FOREVER;
	break;

      case IFNOT_TOKEN:
	bc_sub_type = _SLANG_BCST_IFNOT;
	break;

      case IF_TOKEN:
	bc_sub_type = _SLANG_BCST_IF;
	break;

      case ANDELSE_TOKEN:
	bc_sub_type = _SLANG_BCST_ANDELSE;
	break;

      case SWITCH_TOKEN:
	bc_sub_type = _SLANG_BCST_SWITCH;
	break;

      case EXITBLK_TOKEN:
	if (Lang_Defining_Function == 0)
	  {
	     SLang_verror (SL_SYNTAX_ERROR, "misplaced EXIT_BLOCK");
	     break;
	  }
	bc_sub_type = _SLANG_BCST_EXIT_BLOCK;
	break;

      case ERRBLK_TOKEN:
	if (This_Compile_Block_Type == COMPILE_BLOCK_TYPE_TOP_LEVEL)
	  {
	     SLang_verror (SL_SYNTAX_ERROR, "misplaced ERROR_BLOCK");
	     break;
	  }
	bc_sub_type = _SLANG_BCST_ERROR_BLOCK;
	break;

      case USRBLK0_TOKEN:
      case USRBLK1_TOKEN:
      case USRBLK2_TOKEN:
      case USRBLK3_TOKEN:
      case USRBLK4_TOKEN:
	if (This_Compile_Block_Type == COMPILE_BLOCK_TYPE_TOP_LEVEL)
	  {
	     SLang_verror (SL_SYNTAX_ERROR, "misplaced USER_BLOCK");
	     break;
	  }
	bc_sub_type = _SLANG_BCST_USER_BLOCK0 + (t->type - USRBLK0_TOKEN);
	break;

      case ELSE_TOKEN:
	bc_sub_type = _SLANG_BCST_ELSE;
	break;

      case LOOP_TOKEN:
	bc_sub_type = _SLANG_BCST_LOOP;
	break;

      case DOWHILE_TOKEN:
	bc_sub_type = _SLANG_BCST_DOWHILE;
	break;

      case WHILE_TOKEN:
	bc_sub_type = _SLANG_BCST_WHILE;
	break;

      case ORELSE_TOKEN:
	bc_sub_type = _SLANG_BCST_ORELSE;
	break;

      case _FOR_TOKEN:
	bc_sub_type = _SLANG_BCST_FOR;
	break;

      case FOR_TOKEN:
	bc_sub_type = _SLANG_BCST_CFOR;
	break;

      case OBRACE_TOKEN:
	lang_begin_block ();
	break;

      default:
	SLang_verror (SL_SYNTAX_ERROR, "Expecting directive token.  Found 0x%X", t->type);
	break;
     }

   /* Reset this pointer first because compile_directive may cause a
    * file to be loaded.
    */
   Compile_Mode_Function = compile_basic_token_mode;

   if (bc_sub_type != -1)
     compile_directive (bc_sub_type);
}

static unsigned int Assign_Mode_Type;
static void compile_assign_mode (_SLang_Token_Type *t)
{
   if (t->type != IDENT_TOKEN)
     {
	SLang_verror (SL_SYNTAX_ERROR, "Expecting identifier for assignment");
	return;
     }

   compile_assign (Assign_Mode_Type, t->v.s_val, t->hash);
   Compile_Mode_Function = compile_basic_token_mode;
}

static void compile_basic_token_mode (_SLang_Token_Type *t)
{
   if (-1 == lang_check_space ())
     return;

   switch (t->type)
     {
      case STATIC_TOKEN:
      case PUSH_TOKEN:
      case NOP_TOKEN:
      case EOF_TOKEN:
      case READONLY_TOKEN:
      case DO_TOKEN:
      case VARIABLE_TOKEN:
      case DEFINE_TOKEN:
      case SEMICOLON_TOKEN:
      default:
	SLang_verror (SL_SYNTAX_ERROR, "Unknown or unsupported token type 0x%X", t->type);
	break;

      case DEREF_TOKEN:
	compile_call_direct (dereference_object, _SLANG_BC_CALL_DIRECT);
	break;

      case STRUCT_TOKEN:
	compile_call_direct (_SLstruct_define_struct, _SLANG_BC_CALL_DIRECT);
	break;

      case TYPEDEF_TOKEN:
	compile_call_direct (_SLstruct_define_typedef, _SLANG_BC_CALL_DIRECT);
	break;

      case DOT_TOKEN:		       /* X . field */
	compile_dot (t);
	break;

      case COMMA_TOKEN:
	break;			       /* do nothing */

      case IDENT_TOKEN:
	compile_hashed_identifier (t->v.s_val, t->hash);
	break;

      case _REF_TOKEN:
	compile_ref (t->v.s_val, t->hash);
	break;

      case ARG_TOKEN:
	compile_call_direct (SLang_start_arg_list, _SLANG_BC_CALL_DIRECT);
	break;

      case EARG_TOKEN:
	compile_call_direct (SLang_end_arg_list, _SLANG_BC_CALL_DIRECT);
	break;

      case COLON_TOKEN:
	if (This_Compile_Block_Type == COMPILE_BLOCK_TYPE_BLOCK)
	  compile_simple (_SLANG_BC_LABEL);
	else SLang_Error = SL_SYNTAX_ERROR;
	break;

      case POP_TOKEN:
	compile_call_direct (SLdo_pop, _SLANG_BC_CALL_DIRECT);
	break;

      case CASE_TOKEN:
	if (This_Compile_Block_Type != COMPILE_BLOCK_TYPE_BLOCK)
	  SLang_verror (SL_SYNTAX_ERROR, "Misplaced 'case'");
	else
	  compile_call_direct (case_function, _SLANG_BC_CALL_DIRECT);
	break;

      case CHAR_TOKEN:
      case INT_TOKEN:
	compile_integer (t->v.i_val);
	break;

#if SLANG_HAS_FLOAT
      case DOUBLE_TOKEN:
	compile_double (t->v.s_val, SLANG_DOUBLE_TYPE);
	break;
#endif
#if SLANG_HAS_COMPLEX
      case COMPLEX_TOKEN:
	compile_double (t->v.s_val, SLANG_COMPLEX_TYPE);
	break;
#endif

      case STRING_TOKEN:
	compile_string (t->v.s_val, t->hash);
	break;

      case _NULL_TOKEN:
	compile_identifier ("NULL");
	break;

      case _INLINE_ARRAY_TOKEN:
	compile_call_direct (_SLarray_inline_array, _SLANG_BC_CALL_DIRECT_FRAME);
	break;

      case _INLINE_IMPLICIT_ARRAY_TOKEN:
	compile_call_direct (_SLarray_inline_implicit_array, _SLANG_BC_CALL_DIRECT_FRAME);
	break;

      case ARRAY_TOKEN:
	compile_call_direct (_SLarray_aget, _SLANG_BC_CALL_DIRECT_FRAME);
	break;

	/* Note: I need to add the other _ARRAY assign tokens. */
      case _ARRAY_PLUSEQS_TOKEN:
      case _ARRAY_MINUSEQS_TOKEN:
      case _ARRAY_POST_MINUSMINUS_TOKEN:
      case _ARRAY_MINUSMINUS_TOKEN:
      case _ARRAY_POST_PLUSPLUS_TOKEN:
      case _ARRAY_PLUSPLUS_TOKEN:
	SLang_verror (SL_NOT_IMPLEMENTED, "Array assignment op not implemented");
	break;

      case _ARRAY_ASSIGN_TOKEN:
	compile_call_direct (_SLarray_aput, _SLANG_BC_CALL_DIRECT_FRAME);
	break;

      case _STRUCT_ASSIGN_TOKEN:
      case _STRUCT_PLUSEQS_TOKEN:
      case _STRUCT_MINUSEQS_TOKEN:
      case _STRUCT_POST_MINUSMINUS_TOKEN:
      case _STRUCT_MINUSMINUS_TOKEN:
      case _STRUCT_POST_PLUSPLUS_TOKEN:
      case _STRUCT_PLUSPLUS_TOKEN:
	compile_struct_assign (t);
	break;

      case _SCALAR_ASSIGN_TOKEN:
      case _SCALAR_PLUSEQS_TOKEN:
      case _SCALAR_MINUSEQS_TOKEN:
      case _SCALAR_POST_MINUSMINUS_TOKEN:
      case _SCALAR_MINUSMINUS_TOKEN:
      case _SCALAR_POST_PLUSPLUS_TOKEN:
      case _SCALAR_PLUSPLUS_TOKEN:
	compile_assign (_SLANG_BCST_ASSIGN + (t->type - _SCALAR_ASSIGN_TOKEN),
			t->v.s_val, t->hash);
	break;

      case _DEREF_ASSIGN_TOKEN:
	compile_deref_assign (t->v.s_val, t->hash);
	break;

 	/* For processing RPN tokens */
      case ASSIGN_TOKEN:
      case PLUSEQS_TOKEN:
      case MINUSEQS_TOKEN:
      case POST_MINUSMINUS_TOKEN:
      case MINUSMINUS_TOKEN:
      case POST_PLUSPLUS_TOKEN:
      case PLUSPLUS_TOKEN:
	Compile_Mode_Function = compile_assign_mode;
	Assign_Mode_Type = _SLANG_BCST_ASSIGN + (t->type - ASSIGN_TOKEN);
	break;

      case LT_TOKEN:
	compile_binary (SLANG_LT);
	break;

      case LE_TOKEN:
	compile_binary (SLANG_LE);
	break;

      case GT_TOKEN:
	compile_binary (SLANG_GT);
	break;

      case GE_TOKEN:
	compile_binary (SLANG_GE);
	break;

      case EQ_TOKEN:
	compile_binary (SLANG_EQ);
	break;

      case NE_TOKEN:
	compile_binary (SLANG_NE);
	break;

      case AND_TOKEN:
	compile_binary (SLANG_AND);
	break;

      case ADD_TOKEN:
	compile_binary (SLANG_PLUS);
	break;

      case SUB_TOKEN:
	compile_binary (SLANG_MINUS);
	break;

      case MUL_TOKEN:
	compile_binary (SLANG_TIMES);
	break;

      case DIV_TOKEN:
	compile_binary (SLANG_DIVIDE);
	break;

      case POW_TOKEN:
	compile_binary (SLANG_POW);
	break;

      case BXOR_TOKEN:
	compile_binary (SLANG_BXOR);
	break;

      case BAND_TOKEN:
	compile_binary (SLANG_BAND);
	break;

      case BOR_TOKEN:
	compile_binary (SLANG_BOR);
	break;

      case SHR_TOKEN:
	compile_binary (SLANG_SHR);
	break;

      case SHL_TOKEN:
	compile_binary (SLANG_SHL);
	break;

      case MOD_TOKEN:
	compile_binary (SLANG_MOD);
	break;

      case OR_TOKEN:
	compile_binary (SLANG_OR);
	break;

      case MUL2_TOKEN:
	compile_unary (SLANG_MUL2);
	break;

      case NOT_TOKEN:
	compile_unary (SLANG_NOT);
	break;

      case ABS_TOKEN:
	compile_unary (SLANG_ABS);
	break;

      case BNOT_TOKEN:
	compile_unary (SLANG_BNOT);
	break;

      case CHS_TOKEN:
	compile_unary (SLANG_CHS);
	break;

      case SQR_TOKEN:
	compile_unary (SLANG_SQR);
	break;

      case SIGN_TOKEN:
	compile_unary (SLANG_SIGN);
	break;

      case BREAK_TOKEN:
	compile_break (_SLANG_BC_BREAK, 1, 0, "break");
	break;

      case RETURN_TOKEN:
	compile_break (_SLANG_BC_RETURN, 0, 1, "return");
	break;

      case CONT_TOKEN:
	compile_break (_SLANG_BC_CONTINUE, 1, 0, "continue");
	break;

      case EXCH_TOKEN:
	compile_break (_SLANG_BC_EXCH, 0, 0, "");   /* FIXME!! */
	break;

      case OBRACKET_TOKEN:
	Compile_Mode_Function = compile_variable_mode;
	break;

      case OPAREN_TOKEN:
	lang_begin_function ();
	break;

      case CPAREN_TOKEN:
	if (Lang_Defining_Function)
	  Compile_Mode_Function = compile_function_mode;
	else SLang_Error = SL_SYNTAX_ERROR;
	break;

      case CBRACE_TOKEN:
	lang_end_block ();
	Compile_Mode_Function = compile_directive_mode;
	break;

      case OBRACE_TOKEN:
	lang_begin_block ();
	break;

      case FARG_TOKEN:
	Function_Args_Number = Local_Variable_Number;
	break;

#if _SLANG_HAS_DEBUG_CODE
      case LINE_NUM_TOKEN:
	Compile_ByteCode_Ptr->bc_main_type = _SLANG_BC_LINE_NUM;
	Compile_ByteCode_Ptr->b.i_blk = t->v.i_val;
	lang_try_now ();
	break;
#endif
     }
}

void _SLcompile (_SLang_Token_Type *t)
{
   if (SLang_Error == 0)
     {
	if (Compile_Mode_Function != compile_basic_token_mode)
	  {
	     if (Compile_Mode_Function == NULL)
	       Compile_Mode_Function = compile_basic_token_mode;
#if _SLANG_HAS_DEBUG_CODE
	     if (t->type == LINE_NUM_TOKEN)
	       {
		  compile_basic_token_mode (t);
		  return;
	       }
#endif
	  }

	(*Compile_Mode_Function) (t);
     }

   if (SLang_Error)
     {
	Compile_Mode_Function = compile_basic_token_mode;
	SLang_restart (0);
     }
}

void (*_SLcompile_ptr)(_SLang_Token_Type *) = _SLcompile;

static int init_interpreter (void)
{
   _SLRun_Stack = (SLang_Object_Type *) SLcalloc (SLANG_MAX_STACK_LEN,
						  sizeof (SLang_Object_Type));
   if (_SLRun_Stack == NULL)
     return -1;

   _SLStack_Pointer = _SLRun_Stack;
   _SLStack_Pointer_Max = _SLRun_Stack + SLANG_MAX_STACK_LEN;

   SLShort_Blocks[0].bc_main_type = _SLANG_BC_RETURN;
   SLShort_Blocks[1].bc_main_type = _SLANG_BC_BREAK;
   SLShort_Blocks[2].bc_main_type = _SLANG_BC_CONTINUE;

   Num_Args_Stack = (int *) SLmalloc (sizeof (int) * SLANG_MAX_RECURSIVE_DEPTH);
   if (Num_Args_Stack == NULL)
     {
	SLfree ((char *) _SLRun_Stack);
	return -1;
     }
   Recursion_Depth = 0;
   Frame_Pointer_Stack = (unsigned int *) SLmalloc (sizeof (unsigned int) * SLANG_MAX_RECURSIVE_DEPTH);
   if (Frame_Pointer_Stack == NULL)
     {
	SLfree ((char *) _SLRun_Stack);
	SLfree ((char *)Num_Args_Stack);
	return -1;
     }
   Frame_Pointer_Depth = 0;
   Frame_Pointer = _SLRun_Stack;

   return 0;
}

static int add_generic_table (SLang_Name_Type *table, char *pp_name, 
			      unsigned int entry_len)
{
   SLang_Name_Type *t;
   char *name;
   static int init = 0;

   if (init == 0)
     {
	if (-1 == init_interpreter ())
	  return -1;

	init = 1;
     }

   if ((pp_name != NULL)
       && (-1 == SLdefine_for_ifdef (pp_name)))
     return -1;

   t = table;
   while (NULL != (name = t->name))
     {
	unsigned long hash;

	/* Backward compatibility: '.' WAS used as hash marker */
	if (*name == '.')
	  {
	     name++;
	     t->name = name;
	  }

	if (NULL == SLang_create_static_slstring (name))
	  return -1;

	hash = _SLcompute_string_hash (name);
	hash = hash % SLGLOBALS_HASH_TABLE_SIZE;

	t->next = Globals_Hash_Table [(unsigned int) hash];
	Globals_Hash_Table [(unsigned int) hash] = t;

	t = (SLang_Name_Type *) ((char *)t + entry_len);
     }

   return 0;
}

int SLadd_intrin_fun_table (SLang_Intrin_Fun_Type *tbl, char *pp)
{
   return add_generic_table ((SLang_Name_Type *) tbl, pp, sizeof (SLang_Intrin_Fun_Type));
}

int SLadd_intrin_var_table (SLang_Intrin_Var_Type *tbl, char *pp)
{
   return add_generic_table ((SLang_Name_Type *) tbl, pp, sizeof (SLang_Intrin_Var_Type));
}

int SLadd_app_unary_table (SLang_App_Unary_Type *tbl, char *pp)
{
   return add_generic_table ((SLang_Name_Type *) tbl, pp, sizeof (SLang_App_Unary_Type));
}

int SLadd_math_unary_table (SLang_Math_Unary_Type *tbl, char *pp)
{
   return add_generic_table ((SLang_Name_Type *) tbl, pp, sizeof (SLang_Math_Unary_Type));
}


/* what is a bitmapped value: 
 * 1 intrin fun
 * 2 user fun
 * 4 intrin var
 * 8 user defined var
 */
int _SLang_apropos (char *s, unsigned int what)
{
   SLang_Name_Type *t;
   unsigned int i, n;
   int all;
   
   all = (*s == 0);

   n = 0;
   for (i = 0; i < SLGLOBALS_HASH_TABLE_SIZE; i++)
     {
	t = Globals_Hash_Table [i];

	while (t != NULL)
	  {
	     char *name;

	     name = t->name;
	     if (all || (NULL != strstr (name, s))) 
	       {
		  unsigned int ok;

		  switch (t->name_type)
		    {
		     case SLANG_GVARIABLE:
		       ok = (what & 8);
		       break;
		     case SLANG_IVARIABLE: 
		     case SLANG_RVARIABLE:
		       ok = (what & 4);
		       break;
		     case SLANG_FUNCTION:
		       ok = (what & 2);
		       break;
		     default:
		       ok = (what & 1);
		       break;
		    }
		  
		  if (ok)
		    {
		       if (-1 == SLang_push_string (name))
			 {
			    SLang_free_slstring (s);
			    SLdo_pop_n (n);
			    return -1;
			 }
		       n++;
		    }
	       }
	     t = t->next;
	  }
     }
   return n;
}

[ RETURN TO DIRECTORY ]