/* Copyright (c) 1998, 1999, 2001 John E. Davis
 * This file is part of the S-Lang library.
 *
 * You may distribute under the terms of either the GNU General Public
 * License or the Perl Artistic License.
 */

#include "slinclud.h"

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

static SLang_Load_Type *LLT;
int _SLang_Compile_Line_Num_Info;

static void free_token (_SLang_Token_Type *t)
{
   register unsigned int nrefs = t->num_refs;

   if (nrefs == 0)
     return;

   if (nrefs == 1)
     {
	if (t->free_sval_flag)
	  {
	     if (t->type == BSTRING_TOKEN)
	       SLbstring_free (t->v.b_val);
	     else
	       _SLfree_hashed_string (t->v.s_val, strlen (t->v.s_val), t->hash);
	     t->v.s_val = NULL;
	  }
     }

   t->num_refs = nrefs - 1;
}

static void init_token (_SLang_Token_Type *t)
{
   memset ((char *) t, 0, sizeof (_SLang_Token_Type));
#if _SLANG_HAS_DEBUG_CODE
   t->line_number = -1;
#endif
}

/* Allow room for one push back of a token.  This is necessary for
 * multiple assignment.
 */
static unsigned int Use_Next_Token;
static _SLang_Token_Type Next_Token;
#if _SLANG_HAS_DEBUG_CODE
static int Last_Line_Number = -1;
#endif

static int unget_token (_SLang_Token_Type *ctok)
{
   if (SLang_Error)
     return -1;
   if (Use_Next_Token != 0)
     {
	_SLparse_error ("unget_token failed", ctok, 0);
	return -1;
     }

   Use_Next_Token++;
   Next_Token = *ctok;
   init_token (ctok);
   return 0;
}

static int get_token (_SLang_Token_Type *ctok)
{
   if (ctok->num_refs)
     free_token (ctok);

   if (Use_Next_Token)
     {
	Use_Next_Token--;
	*ctok = Next_Token;
	return ctok->type;
     }

   return _SLget_token (ctok);
}

static int compile_token (_SLang_Token_Type *t)
{
#if _SLANG_HAS_DEBUG_CODE
   if (_SLang_Compile_Line_Num_Info
       && (t->line_number != Last_Line_Number)
       && (t->line_number != -1))
     {
	_SLang_Token_Type tok;
	tok.type = LINE_NUM_TOKEN;
	tok.v.long_val = Last_Line_Number = t->line_number;
	(*_SLcompile_ptr) (&tok);
     }
#endif
   (*_SLcompile_ptr) (t);
   return 0;
}

typedef struct
{
#define USE_PARANOID_MAGIC	0
#if USE_PARANOID_MAGIC
   unsigned long magic;
#endif
   _SLang_Token_Type *stack;
   unsigned int len;
   unsigned int size;
}
Token_List_Type;

#define MAX_TOKEN_LISTS 16
static Token_List_Type Token_List_Stack [MAX_TOKEN_LISTS];
static unsigned int Token_List_Stack_Depth = 0;
static Token_List_Type *Token_List = NULL;

static void init_token_list (Token_List_Type *t)
{
   t->size = 0;
   t->len = 0;
   t->stack = NULL;
#if USE_PARANOID_MAGIC
   t->magic = 0xABCDEF12;
#endif
}

static void free_token_list (Token_List_Type *t)
{
   _SLang_Token_Type *s;

   if (t == NULL)
     return;
#if USE_PARANOID_MAGIC
   if (t->magic != 0xABCDEF12)
     {
	SLang_doerror ("Magic error.");
	return;
     }
#endif
   s = t->stack;
   if (s != NULL)
     {
	_SLang_Token_Type *smax = s + t->len;
	while (s != smax)
	  {
	     if (s->num_refs) free_token (s);
	     s++;
	  }

	SLfree ((char *) t->stack);
     }

   memset ((char *) t, 0, sizeof (Token_List_Type));
}

static Token_List_Type *push_token_list (void)
{
   if (Token_List_Stack_Depth == MAX_TOKEN_LISTS)
     {
	_SLparse_error ("Token list stack size exceeded", NULL, 0);
	return NULL;
     }

   Token_List = Token_List_Stack + Token_List_Stack_Depth;
   Token_List_Stack_Depth++;
   init_token_list (Token_List);
   return Token_List;
}

static int pop_token_list (int do_free)
{
   if (Token_List_Stack_Depth == 0)
     {
	if (SLang_Error == 0)
	  _SLparse_error ("Token list stack underflow", NULL, 0);
	return -1;
     }
   Token_List_Stack_Depth--;

   if (do_free) free_token_list (Token_List);

   if (Token_List_Stack_Depth != 0)
     Token_List = Token_List_Stack + (Token_List_Stack_Depth - 1);
   else
     Token_List = NULL;

   return 0;
}

static int check_token_list_space (Token_List_Type *t, unsigned int delta_size)
{
   _SLang_Token_Type *st;
   unsigned int len;
#if USE_PARANOID_MAGIC
   if (t->magic != 0xABCDEF12)
     {
	SLang_doerror ("Magic error.");
	return -1;
     }
#endif
   len = t->len + delta_size;
   if (len <= t->size) return 0;

   if (delta_size < 4)
     {
	delta_size = 4;
	len = t->len + delta_size;
     }

   st = (_SLang_Token_Type *) SLrealloc((char *) t->stack,
					len * sizeof(_SLang_Token_Type));
   if (st == NULL)
     {
	_SLparse_error ("Malloc error", NULL, 0);
	return -1;
     }

   memset ((char *) (st + t->len), 0, delta_size);

   t->stack = st;
   t->size = len;
   return 0;
}

static int append_token (_SLang_Token_Type *t)
{
   if (-1 == check_token_list_space (Token_List, 1))
     return -1;

   Token_List->stack [Token_List->len] = *t;
   Token_List->len += 1;
   t->num_refs = 0;		       /* stealing it */
   return 0;
}

static int append_token_of_type (unsigned char t)
{
   _SLang_Token_Type *tok;

   if (-1 == check_token_list_space (Token_List, 1))
     return -1;

   /* The memset when the list was created ensures that the other fields
    * are properly initialized.
    */
   tok = Token_List->stack + Token_List->len;
   init_token (tok);
   tok->type = t;
   Token_List->len += 1;
   return 0;
}

static _SLang_Token_Type *get_last_token (void)
{
   unsigned int len;

   if ((Token_List == NULL)
       || (0 == (len = Token_List->len)))
     return NULL;

   len--;
   return Token_List->stack + len;
}

/* This function does NOT free the list. */
static int compile_token_list_with_fun (int dir, Token_List_Type *list,
					int (*f)(_SLang_Token_Type *))
{
   _SLang_Token_Type *t0, *t1;

   if (list == NULL)
     return -1;

   if (f == NULL)
     f = compile_token;

   t0 = list->stack;
   t1 = t0 + list->len;

   if (dir < 0)
     {
	/* backwards */

	while ((SLang_Error == 0) && (t1 > t0))
	  {
	     t1--;
	     (*f) (t1);
	  }
	return 0;
     }

   /* forward */
   while ((SLang_Error == 0) && (t0 < t1))
     {
	(*f) (t0);
	t0++;
     }
   return 0;
}

static int compile_token_list (void)
{
   if (Token_List == NULL)
     return -1;

   compile_token_list_with_fun (1, Token_List, NULL);
   pop_token_list (1);
   return 0;
}

/* Take all elements in the list from pos2 to the end and exchange them
 * with the elements at pos1, e.g.,
 * ...ABCDEabc ==> ...abcABCDE
 * where pos1 denotes A and pos2 denotes a.
 */
static int token_list_element_exchange (unsigned int pos1, unsigned int pos2)
{
   _SLang_Token_Type *s, *s1, *s2;
   unsigned int len, nloops;

   if (Token_List == NULL)
     return -1;

   s = Token_List->stack;
   len = Token_List->len;

   if ((s == NULL) || (len == 0)
       || (pos2 >= len))
     return -1;

   /* This may not be the most efficient algorithm but the number to swap
    * is most-likely going to be small, e.g, 3
    * The algorithm is to rotate the list.  The particular rotation
    * direction was chosen to make insert_token fast.
    * It works like:
    * @ ABCabcde --> BCabcdeA --> CabcdeAB -->  abcdefAB
    * which is optimal for Abcdef sequence produced by function calls.
    *
    * Profiling indicates that nloops is almost always 1, whereas the inner
    * loop can loop many times (e.g., 9 times).
    */

   s2 = s + (len - 1);
   s1 = s + pos1;
   nloops = pos2 - pos1;

   while (nloops)
     {
	_SLang_Token_Type save;

	s = s1;
	save = *s;

	while (s < s2)
	  {
	     *s = *(s + 1);
	     s++;
	  }
	*s = save;

	nloops--;
     }
   return 0;
}

#if 0
static int insert_token (_SLang_Token_Type *t, unsigned int pos)
{
   if (-1 == append_token (t))
     return -1;

   return token_list_element_exchange (pos, Token_List->len - 1);
}
#endif
static void compile_token_of_type (unsigned char t)
{
   _SLang_Token_Type tok;

#if _SLANG_HAS_DEBUG_CODE
   tok.line_number = -1;
#endif
   tok.type = t;
   compile_token(&tok);
}

static void statement (_SLang_Token_Type *);
static void compound_statement (_SLang_Token_Type *);
static void expression_with_parenthesis (_SLang_Token_Type *);
static void handle_semicolon (_SLang_Token_Type *);
static void statement_list (_SLang_Token_Type *);
static void variable_list (_SLang_Token_Type *, unsigned char);
static void struct_declaration (_SLang_Token_Type *);
static void define_function_args (_SLang_Token_Type *);
static void typedef_definition (_SLang_Token_Type *);
static void function_args_expression (_SLang_Token_Type *, int);
static void expression (_SLang_Token_Type *);
static void expression_with_commas (_SLang_Token_Type *, int);
static void simple_expression (_SLang_Token_Type *);
static void unary_expression (_SLang_Token_Type *);
static void postfix_expression (_SLang_Token_Type *);
static int check_for_lvalue (unsigned char, _SLang_Token_Type *);
/* static void primary_expression (_SLang_Token_Type *); */
static void block (_SLang_Token_Type *);
static void inline_array_expression (_SLang_Token_Type *);
static void array_index_expression (_SLang_Token_Type *);
static void do_multiple_assignment (_SLang_Token_Type *);
static void try_multiple_assignment (_SLang_Token_Type *);
#if 0
static void not_implemented (char *what)
{
   char err [256];
   sprintf (err, "Expression not implemented: %s", what);
   _SLparse_error (err, NULL, 0);
}
#endif
static void rpn_parse_line (_SLang_Token_Type *tok)
{
   do
     {
	  /* multiple RPN tokens possible when the file looks like:
	   * . <end of line>
	   * . <end of line>
	   */
	if (tok->type != RPN_TOKEN)
	  compile_token (tok);
	free_token (tok);
     }
   while (EOF_TOKEN != _SLget_rpn_token (tok));
}

static int get_identifier_token (_SLang_Token_Type *tok)
{
   if (IDENT_TOKEN == get_token (tok))
     return IDENT_TOKEN;

   _SLparse_error ("Expecting identifier", tok, 0);
   return tok->type;
}

static void define_function (_SLang_Token_Type *ctok, unsigned char type)
{
   _SLang_Token_Type fname;
   
   switch (type)
     {
      case STATIC_TOKEN:
	type = DEFINE_STATIC_TOKEN;
	break;
	
      case PUBLIC_TOKEN:
	type = DEFINE_PUBLIC_TOKEN;
	break;
	
      case PRIVATE_TOKEN:
	type = DEFINE_PRIVATE_TOKEN;
     }

   init_token (&fname);
   if (IDENT_TOKEN != get_identifier_token (&fname))
     {
	free_token (&fname);
	return;
     }

   compile_token_of_type(OPAREN_TOKEN);
   get_token (ctok);
   define_function_args (ctok);
   compile_token_of_type(FARG_TOKEN);

   if (ctok->type == OBRACE_TOKEN)
     compound_statement(ctok);

   else if (ctok->type != SEMICOLON_TOKEN)
     {
	_SLparse_error("Expecting {", ctok, 0);
	free_token (&fname);
	return;
     }

   fname.type = type;
   compile_token (&fname);
   free_token (&fname);
}

/* statement:
 *	 compound-statement
 *	 if ( expression ) statement
 *	 if ( expression ) statement else statement
 *	 !if ( expression ) statement
 *	 loop ( expression ) statement
 *	 _for ( expression ) statement
 *       foreach ( expression ) statement
 *       foreach (expression ) using (expression-list) statement
 *	 while ( expression ) statement
 *	 do statement while (expression) ;
 *	 for ( expressionopt ; expressionopt ; expressionopt ) statement
 *	 ERROR_BLOCK statement
 *	 EXIT_BLOCK statement
 *	 USER_BLOCK0 statement
 *	 USER_BLOCK1 statement
 *	 USER_BLOCK2 statement
 *	 USER_BLOCK3 statement
 *	 USER_BLOCK4 statement
 *	 forever statement
 *	 break ;
 *	 continue ;
 *	 return expressionopt ;
 *	 variable variable-list ;
 *	 struct struct-decl ;
 *	 define identifier function-args ;
 *	 define identifier function-args compound-statement
 *	 switch ( expression ) statement
 *	 rpn-line
 *	 at-line
 *	 push ( expression )
 *	 ( expression ) = expression ;
 *	 expression ;
 *       expression :
 */

/* Note: This function does not return with a new token.  It is up to the
 * calling routine to handle that.
 */
static void statement (_SLang_Token_Type *ctok)
{
   unsigned char type;

   if (SLang_Error)
     return;

   LLT->parse_level += 1;

   switch (ctok->type)
     {
      case OBRACE_TOKEN:
	compound_statement (ctok);
	break;

      case IF_TOKEN:
      case IFNOT_TOKEN:
	type = ctok->type;
	get_token (ctok);
	expression_with_parenthesis (ctok);
	block (ctok);

	if (ELSE_TOKEN != get_token (ctok))
	  {
	     compile_token_of_type (type);
	     unget_token (ctok);
	     break;
	  }
	get_token (ctok);
	block (ctok);
	if (type == IF_TOKEN) type = ELSE_TOKEN; else type = NOTELSE_TOKEN;
	compile_token_of_type (type);
	break;

      /* case IFNOT_TOKEN: */
      case LOOP_TOKEN:
      case _FOR_TOKEN:
	type = ctok->type;
	get_token (ctok);
	expression_with_parenthesis (ctok);
	block (ctok);
	compile_token_of_type (type);
	break;

      case FOREACH_TOKEN:
	get_token (ctok);
	expression_with_parenthesis (ctok);

	if (NULL == push_token_list ())
	  break;

	append_token_of_type (ARG_TOKEN);
	if (ctok->type == USING_TOKEN)
	  {
	     if (OPAREN_TOKEN != get_token (ctok))
	       {
		  _SLparse_error ("Expected 'using ('", ctok, 0);
		  break;
	       }
	     get_token (ctok);
	     function_args_expression (ctok, 0);
	  }
	append_token_of_type (EARG_TOKEN);

	compile_token_list ();

	block (ctok);
	compile_token_of_type (FOREACH_TOKEN);
	break;

      case WHILE_TOKEN:
	get_token (ctok);
	compile_token_of_type (OBRACE_TOKEN);
	expression_with_parenthesis (ctok);
	compile_token_of_type (CBRACE_TOKEN);
	block (ctok);
	compile_token_of_type (WHILE_TOKEN);
	break;

      case DO_TOKEN:
	get_token (ctok);
	block (ctok);

	if (WHILE_TOKEN != get_token (ctok))
	  {
	     _SLparse_error("Expecting while", ctok, 0);
	     break;
	  }

	get_token (ctok);

	compile_token_of_type (OBRACE_TOKEN);
	expression_with_parenthesis (ctok);
	compile_token_of_type (CBRACE_TOKEN);
	compile_token_of_type (DOWHILE_TOKEN);
	handle_semicolon (ctok);
	break;

      case FOR_TOKEN:

	/* Look for (exp_opt ; exp_opt ; exp_opt ) */

	if (OPAREN_TOKEN != get_token (ctok))
	  {
	     _SLparse_error("Expecting (.", ctok, 0);
	     break;
	  }

	if (NULL == push_token_list ())
	  break;

	append_token_of_type (OBRACE_TOKEN);
	if (SEMICOLON_TOKEN != get_token (ctok))
	  {
	     expression (ctok);
	     if (ctok->type != SEMICOLON_TOKEN)
	       {
		  _SLparse_error("Expecting ;", ctok, 0);
		  break;
	       }
	  }
	append_token_of_type (CBRACE_TOKEN);

	append_token_of_type (OBRACE_TOKEN);
	if (SEMICOLON_TOKEN != get_token (ctok))
	  {
	     expression (ctok);
	     if (ctok->type != SEMICOLON_TOKEN)
	       {
		  _SLparse_error("Expecting ;", ctok, 0);
		  break;
	       }
	  }
	append_token_of_type (CBRACE_TOKEN);

	append_token_of_type (OBRACE_TOKEN);
	if (CPAREN_TOKEN != get_token (ctok))
	  {
	     expression (ctok);
	     if (ctok->type != CPAREN_TOKEN)
	       {
		  _SLparse_error("Expecting ).", ctok, 0);
		  break;
	       }
	  }
	append_token_of_type (CBRACE_TOKEN);

	compile_token_list ();

	get_token (ctok);
	block (ctok);
	compile_token_of_type (FOR_TOKEN);
	break;

      case ERRBLK_TOKEN:
      case EXITBLK_TOKEN:
      case USRBLK0_TOKEN:
      case USRBLK1_TOKEN:
      case USRBLK2_TOKEN:
      case USRBLK3_TOKEN:
      case USRBLK4_TOKEN:
      case FOREVER_TOKEN:
	type = ctok->type;
	get_token (ctok);
	block (ctok);
	compile_token_of_type (type);
	break;

      case BREAK_TOKEN:
      case CONT_TOKEN:
	compile_token_of_type (ctok->type);
	get_token (ctok);
	handle_semicolon (ctok);
	break;

      case RETURN_TOKEN:
	if (SEMICOLON_TOKEN != get_token (ctok))
	  {
	     if (NULL == push_token_list ())
	       break;

	     expression (ctok);

	     if (ctok->type != SEMICOLON_TOKEN)
	       {
		  _SLparse_error ("Expecting ;", ctok, 0);
		  break;
	       }
	     compile_token_list ();
	  }
	compile_token_of_type (RETURN_TOKEN);
	handle_semicolon (ctok);
	break;

      case STATIC_TOKEN:
      case PRIVATE_TOKEN:
      case PUBLIC_TOKEN:
	type = ctok->type;
	get_token (ctok);
	if (ctok->type == VARIABLE_TOKEN)
	  {
	     get_token (ctok);
	     variable_list (ctok, type);
	     handle_semicolon (ctok);
	     break;
	  }
	if (ctok->type == DEFINE_TOKEN)
	  {
	     define_function (ctok, type);
	     break;
	  }
	_SLparse_error ("Expecting 'variable' or 'define'", ctok, 0);
	break;

      case VARIABLE_TOKEN:
	get_token (ctok);
	variable_list (ctok, OBRACKET_TOKEN);
	handle_semicolon (ctok);
	break;

      case TYPEDEF_TOKEN:
	get_token (ctok);
	if (NULL == push_token_list ())
	  break;
	typedef_definition (ctok);
	compile_token_list ();

	handle_semicolon (ctok);
	break;

      case DEFINE_TOKEN:
	define_function (ctok, DEFINE_TOKEN);
	break;

      case SWITCH_TOKEN:
	get_token (ctok);
	expression_with_parenthesis (ctok);

	while ((SLang_Error == 0)
	       && (OBRACE_TOKEN == ctok->type))
	  {
	     compile_token_of_type (OBRACE_TOKEN);
	     compound_statement (ctok);
	     compile_token_of_type (CBRACE_TOKEN);
	     get_token (ctok);
	  }
	compile_token_of_type (SWITCH_TOKEN);
	unget_token (ctok);
	break;

      case EOF_TOKEN:
	break;
#if 0
      case PUSH_TOKEN:
	get_token (ctok);
	expression_list_with_parenthesis (ctok);
	handle_semicolon (ctok);
	break;
#endif

      case SEMICOLON_TOKEN:
	handle_semicolon (ctok);
	break;

      case RPN_TOKEN:
	if (POUND_TOKEN == get_token (ctok))
	  _SLcompile_byte_compiled ();
	else if (ctok->type != EOF_TOKEN)
	  rpn_parse_line (ctok);
	break;

      case OPAREN_TOKEN:	       /* multiple assignment */
	try_multiple_assignment (ctok);
	if (ctok->type == COLON_TOKEN)
	  compile_token_of_type (COLON_TOKEN);
	else handle_semicolon (ctok);
	break;

      default:

	if (NULL == push_token_list ())
	  break;

	expression (ctok);
	compile_token_list ();

	if (ctok->type == COLON_TOKEN)
	  compile_token_of_type (COLON_TOKEN);
	else handle_semicolon (ctok);
	break;
     }

   LLT->parse_level -= 1;
}

static void block (_SLang_Token_Type *ctok)
{
   compile_token_of_type (OBRACE_TOKEN);
   statement (ctok);
   compile_token_of_type (CBRACE_TOKEN);
}

/*
 * statement-list:
 *	 statement
 *	 statement-list statement
 */
static void statement_list (_SLang_Token_Type *ctok)
{
   while ((SLang_Error == 0)
	  && (ctok->type != CBRACE_TOKEN)
	  && (ctok->type != EOF_TOKEN))
     {
	statement(ctok);
	get_token (ctok);
     }
}

/* compound-statement:
 *	 { statement-list }
 */
static void compound_statement (_SLang_Token_Type *ctok)
{
   /* ctok->type is OBRACE_TOKEN here */
   get_token (ctok);
   statement_list(ctok);
   if (CBRACE_TOKEN != ctok->type)
     {
	_SLparse_error ("Expecting '}'", ctok, 0);
	return;
     }
}

/* This function is only called from statement. */
static void expression_with_parenthesis (_SLang_Token_Type *ctok)
{
   if (ctok->type != OPAREN_TOKEN)
     {
	_SLparse_error("Expecting (", ctok, 0);
	return;
     }

   if (NULL == push_token_list ())
     return;

   get_token (ctok);
   expression (ctok);

   if (ctok->type != CPAREN_TOKEN)
     _SLparse_error("Expecting )", ctok, 0);

   compile_token_list ();

   get_token (ctok);
}

static void handle_semicolon (_SLang_Token_Type *ctok)
{
   if ((ctok->type == SEMICOLON_TOKEN)
       || (ctok->type == EOF_TOKEN))
     return;

   _SLparse_error ("Expecting ;", ctok, 0);
}

void _SLparse_start (SLang_Load_Type *llt)
{
   _SLang_Token_Type ctok;
   SLang_Load_Type *save_llt;
   unsigned int save_use_next_token;
   _SLang_Token_Type save_next_token;
   Token_List_Type *save_list;
#if _SLANG_HAS_DEBUG_CODE
   int save_last_line_number = Last_Line_Number;

   Last_Line_Number = -1;
#endif
   save_use_next_token = Use_Next_Token;
   save_next_token = Next_Token;
   save_list = Token_List;
   save_llt = LLT;
   LLT = llt;

   init_token (&Next_Token);
   Use_Next_Token = 0;
   init_token (&ctok);
   get_token (&ctok);

   llt->parse_level = 0;
   statement_list (&ctok);

   if ((SLang_Error == 0)
       && (ctok.type != EOF_TOKEN))
     _SLparse_error ("Parse ended prematurely", &ctok, 0);
   

   if (SLang_Error)
     {
	if (SLang_Error < 0)	       /* severe error */
	  save_list = NULL;

	while (Token_List != save_list)
	  {
	     if (-1 == pop_token_list (1))
	       break;		       /* ??? when would this happen? */
	  }
     }

   free_token (&ctok);
   LLT = save_llt;
   if (Use_Next_Token)
     free_token (&Next_Token);
   Use_Next_Token = save_use_next_token;
   Next_Token = save_next_token;
#if _SLANG_HAS_DEBUG_CODE
   Last_Line_Number = save_last_line_number;
#endif
}

/* variable-list:
 * 	variable-decl
 * 	variable-decl variable-list
 *
 * variable-decl:
 * 	identifier
 * 	identifier = simple-expression
 */
static void variable_list (_SLang_Token_Type *name_token, unsigned char variable_type)
{
   int declaring;
   _SLang_Token_Type tok;

   if (name_token->type != IDENT_TOKEN)
     {
	_SLparse_error ("Expecting a variable name", name_token, 0);
	return;
     }

   declaring = 0;
   do
     {
	if (declaring == 0)
	  {
	     declaring = 1;
	     compile_token_of_type (variable_type);
	  }

	compile_token (name_token);

	init_token (&tok);
	if (ASSIGN_TOKEN == get_token (&tok))
	  {
	     compile_token_of_type (CBRACKET_TOKEN);
	     declaring = 0;

	     get_token (&tok);

	     push_token_list ();
	     simple_expression (&tok);
	     compile_token_list ();

	     name_token->type = _SCALAR_ASSIGN_TOKEN;
	     compile_token (name_token);
	  }

	free_token (name_token);
	*name_token = tok;
     }
   while ((name_token->type == COMMA_TOKEN)
	  && (IDENT_TOKEN == get_token (name_token)));

   if (declaring) compile_token_of_type (CBRACKET_TOKEN);
}

/* struct-declaration:
 * 	struct { struct-field-list };
 *
 * struct-field-list:
 * 	struct-field-name , struct-field-list
 * 	struct-field-name
 *
 * Generates code: "field-name-1" ... "field-name-N" N STRUCT_TOKEN
 */
static void struct_declaration (_SLang_Token_Type *ctok)
{
   int n;
   _SLang_Token_Type num_tok;

   if (ctok->type != OBRACE_TOKEN)
     {
	_SLparse_error ("Expecting {", ctok, 0);
	return;
     }

   n = 0;
   while (IDENT_TOKEN == get_token (ctok))
     {
	n++;
	ctok->type = STRING_TOKEN;
	append_token (ctok);
	if (COMMA_TOKEN != get_token (ctok))
	  break;
     }

   if (ctok->type != CBRACE_TOKEN)
     {
	_SLparse_error ("Expecting }", ctok, 0);
	return;
     }
   if (n == 0)
     {
	_SLparse_error ("struct requires at least 1 field", ctok, 0);
	return;
     }

   init_token (&num_tok);
   num_tok.type = INT_TOKEN;
   num_tok.v.long_val = n;
   append_token (&num_tok);
   append_token_of_type (STRUCT_TOKEN);

   get_token (ctok);
}

/* struct-declaration:
 * 	typedef struct { struct-field-list } Type_Name;
 *
 * struct-field-list:
 * 	struct-field-name , struct-field-list
 * 	struct-field-name
 *
 * Generates code: "field-name-1" ... "field-name-N" N STRUCT_TOKEN typedef
 */
static void typedef_definition (_SLang_Token_Type *t)
{

   if (t->type != STRUCT_TOKEN)
     {
	_SLparse_error ("Expecting `struct'", t, 0);
	return;
     }
   get_token (t);

   struct_declaration (t);
   if (t->type != IDENT_TOKEN)
     {
	_SLparse_error ("Expecting identifier", t, 0);
	return;
     }

   t->type = STRING_TOKEN;
   append_token (t);
   append_token_of_type (TYPEDEF_TOKEN);

   get_token (t);
}

/* function-args:
 * 	( args-dec-opt )
 *
 * args-decl-opt:
 * 	identifier
 * 	args-decl , identifier
 */
static void define_function_args (_SLang_Token_Type *ctok)
{
   if (CPAREN_TOKEN == get_token (ctok))
     {
	get_token (ctok);
	return;
     }

   compile_token_of_type(OBRACKET_TOKEN);

   while ((SLang_Error == 0)
	  && (ctok->type == IDENT_TOKEN))
     {
	compile_token (ctok);
	if (COMMA_TOKEN != get_token (ctok))
	  break;

	get_token (ctok);
     }

   if (CPAREN_TOKEN != ctok->type)
     {
	_SLparse_error("Expecting )", ctok, 0);
	return;
     }
   compile_token_of_type(CBRACKET_TOKEN);

   get_token (ctok);
}

void try_multiple_assignment (_SLang_Token_Type *ctok)
{
   /* This is called with ctok->type == OPAREN_TOKEN.  We have no idea
    * what follows this.  There are various possibilities such as:
    * @  () = x;
    * @  ( expression ) = x;
    * @  ( expression ) ;
    * @  ( expression ) OP expression;
    * @  ( expression ) [expression] = expression;
    * and only the first two constitute a multiple assignment.  The last
    * two forms create the difficulty.
    *
    * Here is the plan.  First parse (expression) and then check next token.
    * If it is an equal operator, then it will be parsed as a multiple
    * assignment.  In fact, that is the easy part.
    *
    * The hard part stems from the fact that by parsing (expression), we
    * have effectly truncated the parse if (expression) is part of a binary
    * or unary expression.  Somehow, the parsing must be resumed.  The trick
    * here is to use a dummy literal that generates no code: NO_OP_LITERAL
    * Using it, we just call 'expression' and proceed.
    */

   if (NULL == push_token_list ())
     return;

   get_token (ctok);

   if (ctok->type != CPAREN_TOKEN)
     {
	expression_with_commas (ctok, 1);
	if (ctok->type != CPAREN_TOKEN)
	  {
	     _SLparse_error ("Expecting )", ctok, 0);
	     return;
	  }
     }

   switch (get_token (ctok))
     {
      case ASSIGN_TOKEN:
      case PLUSEQS_TOKEN:
      case MINUSEQS_TOKEN:
      case TIMESEQS_TOKEN:
      case DIVEQS_TOKEN:
      case BOREQS_TOKEN:
      case BANDEQS_TOKEN:
	do_multiple_assignment (ctok);
	pop_token_list (1);
	break;

      default:
	unget_token (ctok);
	ctok->type = NO_OP_LITERAL;
	expression (ctok);
	compile_token_list ();
	break;
     }
}

/* Note:  expression never gets compiled directly.  Rather, it gets
 *        appended to the token list and then compiled by a calling
 *        routine.
 */

/* expression:
 *	 simple_expression
 *	 simple-expression , expression
 *       <none>
 */
static void expression_with_commas (_SLang_Token_Type *ctok, int save_comma)
{
   while (SLang_Error == 0)
     {
	if (ctok->type != COMMA_TOKEN)
	  {
	     if (ctok->type == CPAREN_TOKEN)
	       return;

	     simple_expression (ctok);

	     if (ctok->type != COMMA_TOKEN)
	       break;
	  }
	if (save_comma) append_token (ctok);
	get_token (ctok);
     }
}

static void expression (_SLang_Token_Type *ctok)
{
   expression_with_commas (ctok, 0);
}

/* priority levels of binary operations */
static unsigned char Binop_Level[] =
{
/* ADD_TOKEN */		2,
/* SUB_TOKEN */		2,
/* MUL_TOKEN */		1,
/* DIV_TOKEN */		1,
/* LT_TOKEN */		4,
/* LE_TOKEN */		4,
/* GT_TOKEN */		4,
/* GE_TOKEN */		4,
/* EQ_TOKEN */		5,
/* NE_TOKEN */		5,
/* AND_TOKEN */		9,
/* OR_TOKEN */		10,
/* MOD_TOKEN */		1,
/* BAND_TOKEN */	6,
/* SHL_TOKEN */		3,
/* SHR_TOKEN */		3,
/* BXOR_TOKEN */	7,
/* BOR_TOKEN */		8,
/* POUND_TOKEN */	1  /* Matrix Multiplication */
};

/* % Note: simple-expression groups operators OP1 at same level.  The
 * % actual implementation will not do this.
 * simple-expression:
 *	 unary-expression
 *	 binary-expression BINARY-OP unary-expression
 *       andelse xxelse-expression-list
 *       orelse xxelse-expression-list
 *
 * xxelse-expression-list:
 * 	{ expression }
 * 	xxelse-expression-list { expression }
 * binary-expression:
 *      unary-expression
 *      unary-expression BINARY-OP binary-expression
 */
static void simple_expression (_SLang_Token_Type *ctok)
{
   unsigned char type;
   unsigned char op_stack [64];
   unsigned char level_stack [64];
   unsigned char level;
   unsigned int op_num;

   switch (ctok->type)
     {
      case ANDELSE_TOKEN:
      case ORELSE_TOKEN:
	type = ctok->type;
	if (OBRACE_TOKEN != get_token (ctok))
	  {
	     _SLparse_error ("Expecting '{'", ctok, 0);
	     return;
	  }

	while (ctok->type == OBRACE_TOKEN)
	  {
	     append_token (ctok);
	     get_token (ctok);
	     expression (ctok);
	     if (CBRACE_TOKEN != ctok->type)
	       {
		  _SLparse_error("Expecting }", ctok, 0);
		  return;
	       }
	     append_token (ctok);
	     get_token (ctok);
	  }
	append_token_of_type (type);
	return;

	/* avoid unary-expression if possible */
      case STRING_TOKEN:
	append_token (ctok);
	get_token (ctok);
	break;

      default:
	unary_expression (ctok);
	break;
     }

   if (SEMICOLON_TOKEN == (type = ctok->type))
     return;

   op_num = 0;

   while ((SLang_Error == 0)
	  && (IS_BINARY_OP(type)))
     {
	level = Binop_Level[type - FIRST_BINARY_OP];

	while ((op_num > 0) && (level_stack [op_num - 1] <= level))
	  append_token_of_type (op_stack [--op_num]);

	if (op_num >= sizeof (op_stack) - 1)
	  {
	     _SLparse_error ("Binary op stack overflow", ctok, 0);
	     return;
	  }

	op_stack [op_num] = type;
	level_stack [op_num] = level;
	op_num++;

	get_token (ctok);
	unary_expression (ctok);
	type = ctok->type;
     }

   while (op_num > 0)
     append_token_of_type(op_stack[--op_num]);
}

/* unary-expression:
 *	 postfix-expression
 *	 ++ postfix-expression
 *	 -- postfix-expression
 *	 case unary-expression
 *	 OP3 unary-expression
 *	 (OP3: + - ~ & not @)
 *
 * Note:  This grammar permits: case case case WHATEVER
 */
static void unary_expression (_SLang_Token_Type *ctok)
{
   unsigned char save_unary_ops [16];
   unsigned int num_unary_ops;
   unsigned char type;
   _SLang_Token_Type *last_token;

   num_unary_ops = 0;
   while (SLang_Error == 0)
     {
	type = ctok->type;

	switch (type)
	  {
	   case PLUSPLUS_TOKEN:
	   case MINUSMINUS_TOKEN:
	     get_token (ctok);
	     postfix_expression (ctok);
	     check_for_lvalue (type, NULL);
	     goto out_of_switch;

	   case ADD_TOKEN:
	     get_token (ctok);	       /* skip it-- it's unary here */
	     break;

	   case SUB_TOKEN:
	     (void) get_token (ctok);
	     if (IS_INTEGER_TOKEN (ctok->type))
	       {
		  ctok->v.long_val = -ctok->v.long_val;
		  break;
	       }

	     if (num_unary_ops == 16)
	       goto stack_overflow_error;
	     save_unary_ops [num_unary_ops++] = CHS_TOKEN;
	     break;

	   case DEREF_TOKEN:
	   case BNOT_TOKEN:
	   case NOT_TOKEN:
	   case CASE_TOKEN:
	     if (num_unary_ops == 16)
	       goto stack_overflow_error;

	     save_unary_ops [num_unary_ops++] = type;
	     get_token (ctok);
	     break;

	     /* Try to avoid ->postfix_expression->primary_expression
	      * subroutine calls.
	      */
	   case STRING_TOKEN:
	     append_token (ctok);
	     get_token (ctok);
	     goto out_of_switch;

	   default:
	     postfix_expression (ctok);
	     goto out_of_switch;
	  }
     }

   out_of_switch:
   if (num_unary_ops == 0)
     return;

   if ((DEREF_TOKEN == save_unary_ops[num_unary_ops - 1])
       && (NULL != (last_token = get_last_token ()))
       && (IS_ASSIGN_TOKEN(last_token->type)))
     {
	/* FIXME: Priority=medium
	 * This needs generalized so that things like @a.y = 1 will work properly.
	 */
	if ((num_unary_ops != 1)
	    || (last_token->type != _SCALAR_ASSIGN_TOKEN))
	  {
	     SLang_verror (SL_NOT_IMPLEMENTED, 
			   "Only derefence assignments to simple variables are possible");
	     return;
	  }

	last_token->type += (_DEREF_ASSIGN_TOKEN - _SCALAR_ASSIGN_TOKEN);
	return;
     }

   while (num_unary_ops)
     {
	num_unary_ops--;
	append_token_of_type (save_unary_ops [num_unary_ops]);
     }
   return;

   stack_overflow_error:
   _SLparse_error ("Too many unary operators.", ctok, 0);
}

static int combine_namespace_tokens (_SLang_Token_Type *a, _SLang_Token_Type *b)
{
   char *sa, *sb, *sc;
   unsigned int lena, lenb;
   unsigned long hash;

   /* This is somewhat of a hack.  Combine the TWO identifier names
    * (NAMESPACE) and (name) into the form NAMESPACE->name.  Then when the
    * byte compiler compiles the object it will not be found.  It will then
    * check for this hack and make the appropriate namespace lookup.
    */

   sa = a->v.s_val;
   sb = b->v.s_val;

   lena = strlen (sa);
   lenb = strlen (sb);

   sc = SLmalloc (lena + lenb + 3);
   if (sc == NULL)
     return -1;

   strcpy (sc, sa);
   strcpy (sc + lena, "->");
   strcpy (sc + lena + 2, sb);

   sb = _SLstring_make_hashed_string (sc, lena + lenb + 2, &hash);
   SLfree (sc);
   if (sb == NULL)
     return -1;

   /* I can free this string because no other token should be referencing it.
    * (num_refs == 1).
    */
   _SLfree_hashed_string (sa, lena, a->hash);
   a->v.s_val = sb;
   a->hash = hash;

   return 0;
}

static void append_identifier_token (_SLang_Token_Type *ctok)
{
   _SLang_Token_Type *last_token;

   append_token (ctok);

   if (NAMESPACE_TOKEN != get_token (ctok))
     return;

   if (IDENT_TOKEN != get_token (ctok))
     {
	_SLparse_error ("Expecting name-space identifier", ctok, 0);
	return;
     }

   last_token = get_last_token ();
   if (-1 == combine_namespace_tokens (last_token, ctok))
     return;

   (void) get_token (ctok);
}

static int get_identifier_expr_token (_SLang_Token_Type *ctok)
{
   _SLang_Token_Type next_token;

   if (IDENT_TOKEN != get_identifier_token (ctok))
     return -1;

   init_token (&next_token);
   if (NAMESPACE_TOKEN != get_token (&next_token))
     {
	unget_token (&next_token);
	return IDENT_TOKEN;
     }

   if (IDENT_TOKEN != get_identifier_token (&next_token))
     {
	free_token (&next_token);
	return -1;
     }

   if (-1 == combine_namespace_tokens (ctok, &next_token))
     {
	free_token (&next_token);
	return -1;
     }
   free_token (&next_token);
   return IDENT_TOKEN;
}

/* postfix-expression:
 *	 primary-expression
 *	 postfix-expression [ expression ]
 *	 postfix-expression ( function-args-expression )
 *	 postfix-expression . identifier
 *       postfix-expression ^ unary-expression
 *	 postfix-expression ++
 *	 postfix-expression --
 *	 postfix-expression = simple-expression
 *	 postfix-expression += simple-expression
 *	 postfix-expression -= simple-expression
 *
 * primary-expression:
 *	literal
 *	identifier-expr
 *	( expression_opt )
 * 	[ inline-array-expression ]
 * 	&identifier-expr
 *      struct-definition
 *      __tmp(identifier-expr)
 *
 * identifier-expr:
 *      identifier
 *      identifier->identifier
 */
static void postfix_expression (_SLang_Token_Type *ctok)
{
   unsigned int start_pos, end_pos;
   unsigned char type;

   if (Token_List == NULL)
     return;

   start_pos = Token_List->len;

   switch (ctok->type)
     {
      case IDENT_TOKEN:
	append_identifier_token (ctok);
	break;

      case CHAR_TOKEN:
      case SHORT_TOKEN:
      case INT_TOKEN:
      case LONG_TOKEN:
      case UCHAR_TOKEN:
      case USHORT_TOKEN:
      case UINT_TOKEN:
      case ULONG_TOKEN:
      case STRING_TOKEN:
      case BSTRING_TOKEN:
#ifdef SLANG_HAS_FLOAT
      case DOUBLE_TOKEN:
      case FLOAT_TOKEN:
#endif
#ifdef SLANG_HAS_COMPLEX
      case COMPLEX_TOKEN:
#endif
	append_token (ctok);
	get_token (ctok);
	break;

      case OPAREN_TOKEN:
	if (CPAREN_TOKEN != get_token (ctok))
	  {
	     expression (ctok);
	     if (ctok->type != CPAREN_TOKEN)
	       _SLparse_error("Expecting )", ctok, 0);
	  }
	get_token (ctok);
	break;

      case BAND_TOKEN:
	if (IDENT_TOKEN != get_identifier_expr_token (ctok))
	  break;

	ctok->type = _REF_TOKEN;
	append_token (ctok);
	get_token (ctok);
	break;

      case OBRACKET_TOKEN:
	get_token (ctok);
	inline_array_expression (ctok);
	break;

      case NO_OP_LITERAL:
	/* This token was introduced by try_multiple_assignment.  There,
	 * a new token_list was pushed and (expression) was evaluated.
	 * NO_OP_LITERAL represents the result of expression.  However,
	 * we need to tweak the start_pos variable to point to the beginning
	 * of the token list to complete the equivalence.
	 */
	start_pos = 0;
	get_token (ctok);
	break;

      case STRUCT_TOKEN:
	get_token (ctok);
	struct_declaration (ctok);
	break;

      case TMP_TOKEN:
	get_token (ctok);
	if (ctok->type == OPAREN_TOKEN)
	  {
	     if (IDENT_TOKEN == get_identifier_expr_token (ctok))
	       {
		  ctok->type = TMP_TOKEN;
		  append_token (ctok);
		  get_token (ctok);
		  if (ctok->type == CPAREN_TOKEN)
		    {
		       get_token (ctok);
		       break;
		    }
	       }
	  }
	_SLparse_error ("Expecting form __tmp(NAME)", ctok, 0);
	break;

      default:
	if (IS_INTERNAL_FUNC(ctok->type))
	  {
	     append_token (ctok);
	     get_token (ctok);
	  }
	else
	  _SLparse_error("Expecting a PRIMARY", ctok, 0);
     }

   while (SLang_Error == 0)
     {
	end_pos = Token_List->len;
	type = ctok->type;
	switch (type)
	  {
	   case OBRACKET_TOKEN:	       /* X[args] ==> [args] X ARRAY */
	     get_token (ctok);
	     append_token_of_type (ARG_TOKEN);
	     if (ctok->type != CBRACKET_TOKEN) 
	       array_index_expression (ctok);

	     if (ctok->type != CBRACKET_TOKEN)
	       {
		  _SLparse_error ("Expecting ']'", ctok, 0);
		  return;
	       }
	     get_token (ctok);
	     /* append_token_of_type (EARG_TOKEN); -- ARRAY_TOKEN implicitely does this */
	     token_list_element_exchange (start_pos, end_pos);
	     append_token_of_type (ARRAY_TOKEN);
	     break;

	   case OPAREN_TOKEN:
	     /* f(args) ==> args f */
	     if (CPAREN_TOKEN != get_token (ctok))
	       {
		  function_args_expression (ctok, 1);
		  token_list_element_exchange (start_pos, end_pos);
	       }
	     else get_token (ctok);
	     break;

	   case DOT_TOKEN:
	     /* S.a ==> "a" S DOT
	      * This means that if S is X[b], then X[b].a ==> a b X ARRAY DOT
	      * and f(a).X[b].c ==> "c" b "X" a f . ARRAY .
	      * Also, f(a).X[b] = g(x); ==> x g b "X" a f .
	      */
	     if (IDENT_TOKEN != get_identifier_token (ctok))
	       return;

	     ctok->type = DOT_TOKEN;
	     append_token (ctok);
	     get_token (ctok);
	     break;

	   case PLUSPLUS_TOKEN:
	   case MINUSMINUS_TOKEN:
	     check_for_lvalue (type, NULL);
	     get_token (ctok);
	     break;

	   case ASSIGN_TOKEN:
	   case PLUSEQS_TOKEN:
	   case MINUSEQS_TOKEN:
	   case TIMESEQS_TOKEN:
	   case DIVEQS_TOKEN:
	   case BOREQS_TOKEN:
	   case BANDEQS_TOKEN:
	     check_for_lvalue (type, NULL);
	     get_token (ctok);
	     simple_expression (ctok);
	     token_list_element_exchange (start_pos, end_pos);
	     break;

	   case POW_TOKEN:
	     get_token (ctok);
	     unary_expression (ctok);
	     append_token_of_type (POW_TOKEN);
	     break;

	   default:
	     return;
	  }
     }
}

static void function_args_expression (_SLang_Token_Type *ctok, int handle_num_args)
{
   unsigned char last_type, this_type;

   if (handle_num_args) append_token_of_type (ARG_TOKEN);

   last_type = COMMA_TOKEN;

   while (SLang_Error == 0)
     {
	this_type = ctok->type;

	switch (this_type)
	  {
	   case COMMA_TOKEN:
	     if (last_type == COMMA_TOKEN)
	       append_token_of_type (_NULL_TOKEN);
	     get_token (ctok);
	     break;

	   case CPAREN_TOKEN:
	     if (last_type == COMMA_TOKEN)
	       append_token_of_type (_NULL_TOKEN);
	     if (handle_num_args) append_token_of_type (EARG_TOKEN);
	     get_token (ctok);
	     return;

	   default:
	     simple_expression (ctok);
	     if ((ctok->type != COMMA_TOKEN)
		 && (ctok->type != CPAREN_TOKEN))
	       {
		  _SLparse_error ("Expecting ')'", ctok, 0);
		  break;
	       }
	  }
	last_type = this_type;
     }
}

static int check_for_lvalue (unsigned char eqs_type, _SLang_Token_Type *ctok)
{
   unsigned char type;

   if ((ctok == NULL)
       && (NULL == (ctok = get_last_token ())))
     return -1;

   type = ctok->type;

   eqs_type -= ASSIGN_TOKEN;

   if (type == IDENT_TOKEN)
     eqs_type += _SCALAR_ASSIGN_TOKEN;
   else if (type == ARRAY_TOKEN)
     eqs_type += _ARRAY_ASSIGN_TOKEN;
   else if (type == DOT_TOKEN)
     eqs_type += _STRUCT_ASSIGN_TOKEN;
   else
     {
	_SLparse_error ("Expecting LVALUE", ctok, 0);
	return -1;
     }

   ctok->type = eqs_type;
   return 0;
}

static void array_index_expression (_SLang_Token_Type *ctok)
{
   unsigned int num_commas;

   num_commas = 0;
   while (1)
     {
	switch (ctok->type)
	  {
	   case COLON_TOKEN:
	     if (num_commas)
	       _SLparse_error ("Misplaced ':'", ctok, 0);
	     return;
	     
	   case TIMES_TOKEN:
	     append_token_of_type (_INLINE_WILDCARD_ARRAY_TOKEN);
	     get_token (ctok);
	     break;
	     
	   case COMMA_TOKEN:
	     _SLparse_error ("Misplaced ','", ctok, 0);
	     return;
	     
	   default:
	     simple_expression (ctok);
	  }
	
	if (ctok->type != COMMA_TOKEN)
	  return;
	num_commas++;
	get_token (ctok);
     }
}

/* inline-array-expression:
 *    array_index_expression
 *    simple_expression : simple_expression
 *    simple_expression : simple_expression : simple_expression
 */
static void inline_array_expression (_SLang_Token_Type *ctok)
{
   int num_colons = 0;

   append_token_of_type (ARG_TOKEN);

   if (ctok->type == COLON_TOKEN)	       /* [:...] */
     append_token_of_type (_NULL_TOKEN);
   else if (ctok->type != CBRACKET_TOKEN) 
     array_index_expression (ctok);

   if (ctok->type == COLON_TOKEN)
     {
	num_colons++;
	if ((COLON_TOKEN == get_token (ctok))
	    || (ctok->type == CBRACKET_TOKEN))
	  append_token_of_type (_NULL_TOKEN);
	else
	  simple_expression (ctok);

	if (ctok->type == COLON_TOKEN)
	  {
	     num_colons++;
	     get_token (ctok);
	     simple_expression (ctok);
	  }
     }

   if (ctok->type != CBRACKET_TOKEN)
     {
	_SLparse_error ("Expecting ']'", ctok, 0);
	return;
     }

   /* append_token_of_type (EARG_TOKEN); */
   if (num_colons)
     append_token_of_type (_INLINE_IMPLICIT_ARRAY_TOKEN);
   else
     append_token_of_type (_INLINE_ARRAY_TOKEN);
   get_token (ctok);
}

static void do_multiple_assignment (_SLang_Token_Type *ctok)
{
   _SLang_Token_Type *s;
   unsigned int i, k, len;
   unsigned char assign_type;

   assign_type = ctok->type;

   /* The LHS token list has already been pushed.  Here we do the RHS
    * so push to another token list, process it, then come back to
    * LHS for assignment.
    */
   if (NULL == push_token_list ())
     return;

   get_token (ctok);
   expression (ctok);
   compile_token_list ();

   if (SLang_Error)
     return;

   /* Finally compile the LHS of the assignment expression
    * that has been saved.
    */
   s = Token_List->stack;
   len = Token_List->len;

   if (len == 0)
     {
	compile_token_of_type (POP_TOKEN);
	return;
     }

   while (len > 0)
     {
	/* List is of form:
	 *    a , b, c d e, f , g , , , h ,
	 * The missing expressions will be replaced by a POP
	 * ,,a
	 */

	/* Start from back looking for a COMMA */
	k = len - 1;
	if (s[k].type == COMMA_TOKEN)
	  {
	     compile_token_of_type (POP_TOKEN);
	     len = k;
	     continue;
	  }

	if (-1 == check_for_lvalue (assign_type, s + k))
	  return;

	i = 0;
	while (1)
	  {
	     if (s[k].type == COMMA_TOKEN)
	       {
		  i = k + 1;
		  break;
	       }

	     if (k == 0)
	       break;

	     k--;
	  }

	while (i < len)
	  {
	     compile_token (s + i);
	     i++;
	  }

	len = k;
     }

   if (s[0].type == COMMA_TOKEN)
     compile_token_of_type (POP_TOKEN);
}