/* l2xirtne.c  LTX2X interpreter  Routine parser */
/*             parse programs and declared procedures and functions */
/*  Written by: Peter Wilson, CUA  [email protected]                */
/*  This code is partly based on algorithms presented by Ronald Mak in */
/*  "Writing Compilers & Interpreters", John Wiley & Sons, 1991        */

#include <stdio.h>
#include "l2xicmon.h"
#include "l2xierr.h"
#include "l2xiscan.h"
#include "l2xisymt.h"
#include "l2xiprse.h"
#include "l2xiidbg.h"
#include "l2xiexec.h"
#ifndef l2xicpr_h
#include "l2xicpr.h"        /* extern token code lists */
#endif

/* EXTERNALS */

extern int line_number;;
extern long exec_stmt_count;

extern TOKEN_CODE token;
extern char word_string[];
extern SYMTAB_NODE_PTR symtab_display[];
extern int level;

extern ICT *code_buffer;
extern ICT *code_bufferp;

extern STACK_ITEM *stack;
extern STACK_ITEM_PTR tos;
extern STACK_ITEM_PTR stack_frame_basep;
extern STACK_ITEM_PTR maxtos;
extern TYPE_STRUCT_PTR get_type();

/* GLOBALS */

char buffer[MAX_PRINT_LINE_LENGTH];

/* FORWARDS */

SYMTAB_NODE_PTR formal_parm_list();
SYMTAB_NODE_PTR procedure_header();
SYMTAB_NODE_PTR function_header();
ICT *create_code_segment();



/***************************************************************************/
/* init_stack   initialise the runtime stack                               */

init_stack()
{

 entry_debug("init_stack");

 /* allocate runtime stack */
 stack = alloc_array(STACK_ITEM, MAX_STACK_SIZE);
 stack_frame_basep = tos = stack;
 stack_frame_debug();

 maxtos = tos;               /* current max top of stack */
 /* initialise the program's stack frame */
 level = 1;
 stack_frame_basep = tos + 1;
 stack_frame_debug();
 push_integer(0);                   /* function return value */
 push_address(NULL);                /* static link */
 push_address(NULL);                /* dynamic link */
 push_address(NULL);                /* return address */


 exit_debug("init_stack");
 return;
}                                                        /* end init_stack */
/***************************************************************************/


/***************************************************************************/
/* create_dummy_prog()   create a dummy program symbol table node          */
/*                    Based on program and program_header                  */
/*     Must be called BEFORE any scanning or parsing                       */
/* returns pointer to program id node                                      */

SYMTAB_NODE_PTR create_dummy_prog()
{
 SYMTAB_NODE_PTR program_idp;             /* program id */
 entry_debug("creat_dummy_prog");

  /* make up fake program name */
   strcpy(word_string, "_PrOgRaM");
   search_and_enter_local_symtab(program_idp);
   program_idp->defn.key = PROG_DEFN;
   program_idp->defn.info.routine.key = DECLARED;
   program_idp->defn.info.routine.parm_count = 0;
   program_idp->defn.info.routine.total_parm_size = 0;
   program_idp->defn.info.routine.total_local_size = 0;
   program_idp->typep = &dummy_type;
   program_idp->label_index = 0;

 enter_scope(NULL);

 /* no program parameters */

 program_idp->defn.info.routine.locals = NULL;
 program_idp->defn.info.routine.parms = NULL;

 exit_debug("create_dummy_prog");
 return(program_idp);
}                                                 /* end create_dummy_prog */
/***************************************************************************/





/***************************************************************************/
/* a_function     Process an EXPRESS function                              */
/*                FUNCTION <header> <body> END_FUNCTION ;                  */
/*    at entry, token is FUNCTION                                          */
/*    at exit,  token is after END_FUNCTION ;                              */

a_function()
{
 SYMTAB_NODE_PTR rtn_idp;       /* routine id */
 entry_debug("a_function");

 rtn_idp = function_header();

 /* sync. Should be ; */
 synchronize(follow_header_list, declaration_start_list, statement_start_list);
 if_token_get(SEMICOLON);
 else if (token_in(declaration_start_list) || token_in(statement_start_list))
   error(MISSING_SEMICOLON);

 /* block or forward */
 if (strcmp(word_string, "forward") != 0) {
   rtn_idp->defn.info.routine.key = DECLARED;
   analyze_routine_header(rtn_idp);
   rtn_idp->defn.info.routine.locals = NULL;
   function_body(rtn_idp);

   rtn_idp->defn.info.routine.code_segment = create_code_segment();
   analyze_block(rtn_idp->defn.info.routine.code_segment);
   if_token_get_else_error(XEND_FUNCTION, MISSING_END_FUNCTION);
   if_token_get_else_error(SEMICOLON, MISSING_SEMICOLON);
 }
 else {
   get_token();
   rtn_idp->defn.info.routine.key = FORWARD;
   analyze_routine_header(rtn_idp);
 }

 rtn_idp->defn.info.routine.local_symtab = exit_scope();

 exit_debug("a_function");
 return;

}                                                        /* end A_FUNCTION */
/***************************************************************************/



/***************************************************************************/
/* function_body(rtn_idp)   Process body of a function                     */
/*       at entry, token is after ; ending the header                      */
/*       at exit, token is after a ;  and should be END_FUNCTION           */

function_body(rtn_idp)
SYMTAB_NODE_PTR rtn_idp;                  /* routine id */
{
 extern BOOLEAN block_flag;
 entry_debug("function_body");

 if (token_in(declaration_start_list)) {
   declarations(rtn_idp);
/*    synchronize(follow_decls_list, NULL, NULL); */
 }
  /* possibly need an else skip_declarations(rtn_idp); here */

 block_flag = TRUE;
 /* possibly empty list of statements */
 if (token_in(statement_start_list)) {
   crunch_token();
   statements();
   crunch_statement_marker();
   change_crunched_token(END_OF_STATEMENTS);
 }
 block_flag = FALSE;

/*  if_token_get_else_error(XEND_FUNCTION, MISSING_END_FUNCTION);
*  if_token_get_else_error(SEMICOLON, MISSING_SEMICOLON);
*/

 exit_debug("function_body");
 return;
}                                                     /* end FUNCTION_BODY */
/***************************************************************************/




/***************************************************************************/
/* a_procedure()  Process EXPRESS procedure                                */
/*      FUN/PROC <routine_header> ; <block>                                */
/*      at entry, token is PROCEDURE                                       */
/*      at exit, token is past final ;                                     */

a_procedure()
{
 SYMTAB_NODE_PTR rtn_idp;       /* routine id */
 entry_debug("a_procedure");

 rtn_idp = procedure_header();

 /* sync. Should be ; */
 synchronize(follow_header_list, declaration_start_list, statement_start_list);
 if_token_get(SEMICOLON);
 else if (token_in(declaration_start_list) || token_in(statement_start_list))
   error(MISSING_SEMICOLON);

 /* block or forward */
 if (strcmp(word_string, "forward") != 0) {
   rtn_idp->defn.info.routine.key = DECLARED;
   analyze_routine_header(rtn_idp);
   rtn_idp->defn.info.routine.locals = NULL;
   function_body(rtn_idp);

   rtn_idp->defn.info.routine.code_segment = create_code_segment();
   analyze_block(rtn_idp->defn.info.routine.code_segment);
   if_token_get_else_error(XEND_PROCEDURE, MISSING_END_PROCEDURE);
   if_token_get_else_error(SEMICOLON, MISSING_SEMICOLON);
 }
 else {
   get_token();
   rtn_idp->defn.info.routine.key = FORWARD;
   analyze_routine_header(rtn_idp);
 }

 rtn_idp->defn.info.routine.local_symtab = exit_scope();

 exit_debug("a_procedure");
 return;
}                                                       /* end A_PROCEDURE */
/***************************************************************************/



/***************************************************************************/
/* procedure_header()  Process a procedure header                          */
/*                     PROCEDURE <id>                                      */
/*                  or PROCEDURE <id> ( <parm-list> )                      */
/* returns pointer to the procedure id node.                               */

SYMTAB_NODE_PTR procedure_header()
{
 SYMTAB_NODE_PTR proc_idp;             /* procedure id */
 SYMTAB_NODE_PTR parm_listp;           /* formal param list */
 int parm_count;
 int total_parm_size;
 BOOLEAN forward_flag = FALSE;         /* TRUE iff forward */
 entry_debug("procedure_header");

 get_token();

 /* if proc id has already been declared in this scope, */
 /* it must be a forward */
 if (token == IDENTIFIER) {
   search_local_symtab(proc_idp);
   if (proc_idp == NULL) {
     enter_local_symtab(proc_idp);
     proc_idp->defn.key = PROC_DEFN;
     proc_idp->defn.info.routine.total_local_size = 0;
     proc_idp->typep = &dummy_type;
     proc_idp->label_index = 0;
   }
   else if ((proc_idp->defn.key == PROC_DEFN) &&
            (proc_idp->defn.info.routine.key == FORWARD))
     forward_flag = TRUE;
   else error(REDEFINED_IDENTIFIER);

   get_token();
 }
 else error(MISSING_IDENTIFIER);

 /* sync. Should be ( or ; */
 synchronize(follow_proc_id_list, declaration_start_list, statement_start_list);
 enter_scope(NULL);

 /* optional formal parameters, if FORWARD shouldn't be any, but parse */
 /* for error recovery */
 if (token == LPAREN) {
   parm_listp = formal_parm_list(&parm_count, &total_parm_size);
   if (forward_flag) error(ALREADY_FORWARDED);
   else {
     proc_idp->defn.info.routine.parm_count = parm_count;
     proc_idp->defn.info.routine.total_parm_size = total_parm_size;
     proc_idp->defn.info.routine.parms = parm_listp;
   }
 }
 else if (!forward_flag) {
   proc_idp->defn.info.routine.parm_count = 0;
   proc_idp->defn.info.routine.total_parm_size = 0;
   proc_idp->defn.info.routine.parms = NULL;
 }

 proc_idp->typep = NULL;
 exit_debug("procedure_header");
 return(proc_idp);
}                                                  /* end procedure_header */
/***************************************************************************/



/***************************************************************************/
/* function_header()  Process a function header                            */
/*                     FUNCTION <id> : <type-id>                           */
/*                  or FUNCTION <id> ( <parm-list> ) : <type-id>           */
/* returns pointer to the function id node.                                */

SYMTAB_NODE_PTR function_header()
{
 SYMTAB_NODE_PTR func_idp, type_idp;   /* function and type id */
 SYMTAB_NODE_PTR parm_listp;           /* formal param list */
 int parm_count;
 int total_parm_size;
 BOOLEAN forward_flag = FALSE;         /* TRUE iff forward */
 entry_debug("function_header");

 get_token();

 /* if func id has already been declared in this scope, */
 /* it must be a forward */
 if (token == IDENTIFIER) {
   search_local_symtab(func_idp);
   if (func_idp == NULL) {
     enter_local_symtab(func_idp);
     func_idp->defn.key = FUNC_DEFN;
     func_idp->defn.info.routine.total_local_size = 0;
     func_idp->typep = &dummy_type;
     func_idp->label_index = 0;
   }
   else if ((func_idp->defn.key == FUNC_DEFN) &&
            (func_idp->defn.info.routine.key == FORWARD))
     forward_flag = TRUE;
   else error(REDEFINED_IDENTIFIER);

   get_token();
 }
 else error(MISSING_IDENTIFIER);

 /* sync. Should be ( or : or ; */
 synchronize(follow_func_id_list, declaration_start_list, statement_start_list);
 enter_scope(NULL);

 /* optional formal parameters, if FORWARD shouldn't be any, but parse */
 /* for error recovery */
 if (token == LPAREN) {
   parm_listp = formal_parm_list(&parm_count, &total_parm_size);
   if (forward_flag) error(ALREADY_FORWARDED);
   else {
     func_idp->defn.info.routine.parm_count = parm_count;
     func_idp->defn.info.routine.total_parm_size = total_parm_size;
     func_idp->defn.info.routine.parms = parm_listp;
   }
 }
 else if (!forward_flag) {
   func_idp->defn.info.routine.parm_count = 0;
   func_idp->defn.info.routine.total_parm_size = 0;
   func_idp->defn.info.routine.parms = NULL;
 }

 /* for a forward, should not be a type, but parse anyway */
 if (!forward_flag || (token == COLON)) {
   if_token_get_else_error(COLON, MISSING_COLON);

/*     changed for EXPRESS
   if (token == IDENTIFIER) {
     search_and_find_all_symtab(type_idp);
     if (type_idp->defn.key != TYPE_DEFN) error(INVALID_TYPE);
     if (!forward_flag) func_idp->typep = type_idp->typep;
     get_token();
   }
   else {
     error(MISSING_IDENTIFIER);
     func_idp->typep = &dummy_type;
   }
*/
   if (!forward_flag) func_idp->typep = get_type();
   get_token();
   if (forward_flag) error(ALREADY_FORWARDED);
 }

 exit_debug("function_header");
 return(func_idp);
}                                                  /* end function_header */
/***************************************************************************/



/***************************************************************************/
/* formal_parm_list(countp, total_size) Process formal parameter list      */
/*                 ( VAR <id-list> : <type> ;                              */
/*                       <id-list> : <type> ; ... )                        */
/* return a pointer to the head of the parameter id list                   */

SYMTAB_NODE_PTR formal_parm_list(countp, total_sizep)
int *countp;              /* ptr to count of parameters */
int *total_sizep;         /* ptr to total byte size of parameters */
{
 SYMTAB_NODE_PTR parm_idp, first_idp, last_idp;   /* parm ids */
 SYMTAB_NODE_PTR prev_last_idp = NULL;            /* last id of list */
 SYMTAB_NODE_PTR parm_listp = NULL;               /* parm list */
 SYMTAB_NODE_PTR type_idp;                        /* type id */
 TYPE_STRUCT_PTR parm_tp;                         /* parm type */
 DEFN_KEY parm_defn;                              /* parm definition */
 int parm_count = 0;                              /* count of parms */
 int parm_offset = STACK_FRAME_HEADER_SIZE;
 entry_debug("formal_parm_list");

 get_token();

 /* loop to process declarations seperated by ; */
 while ((token == IDENTIFIER) || (token == VAR)) {
   first_idp = NULL;
   /* VAR parm? */
   if (token == VAR) {
     parm_defn = VARPARM_DEFN;
     get_token();
   }
   else parm_defn = VALPARM_DEFN;

   /* <id list> */
   while (token == IDENTIFIER) {
     search_and_enter_local_symtab(parm_idp);
     parm_idp->defn.key = parm_defn;
     parm_idp->label_index = 0;
     ++parm_count;

     if (parm_listp == NULL) parm_listp = parm_idp;

     /* link parms together */
     if (first_idp == NULL) first_idp = last_idp = parm_idp;
     else {
       last_idp->next = parm_idp;
       last_idp = parm_idp;
     }
     get_token();
     if_token_get(COMMA);
   }

   if_token_get_else_error(COLON, MISSING_COLON);

/* changed following for EXPRESS
   if (token == IDENTIFIER) {
     search_and_find_all_symtab(type_idp);
     if (type_idp->defn.key != TYPE_DEFN) error(INVALID_TYPE);
     parm_tp = type_idp->typep;
     get_token();
   }
   else {
     error(MISSING_IDENTIFIER);
     parm_tp = &dummy_type;
   }
*/
   parm_tp = get_type();
   get_token();

   /* assign the offset and the type to all parm ids in the sublist */
   for (parm_idp = first_idp; parm_idp != NULL; parm_idp = parm_idp->next) {
     parm_idp->typep = parm_tp;
     parm_idp->defn.info.data.offset = parm_offset++;
   }

   /* link this sublist to the list of all parm ids */
   if (prev_last_idp != NULL) prev_last_idp->next = first_idp;
   prev_last_idp = last_idp;

   /* sync: Should be ; or ) */
   synchronize(follow_parms_list, NULL, NULL);
   if_token_get(SEMICOLON);
 } /* end while */

 if_token_get_else_error(RPAREN, MISSING_RPAREN);
 *countp = parm_count;
 *total_sizep = parm_offset - STACK_FRAME_HEADER_SIZE;

 exit_debug("formal_parm_list");
 return(parm_listp);
}                                                  /* end formal_parm_list */
/***************************************************************************/



/***************************************************************************/
/* routine_call(rtn_idp, parm_check_flag) Process a call to a procedure    */
/*                                        or function                      */
/* return pointer to the type structure of the call                        */

TYPE_STRUCT_PTR routine_call(rtn_idp, parm_check_flag)
SYMTAB_NODE_PTR rtn_idp;                 /* routine id */
BOOLEAN parm_check_flag;                 /* if TRUE then check parms */
{
 TYPE_STRUCT_PTR declared_routine_call(), standard_routine_call();
 entry_debug("routine_call");

 if ((rtn_idp->defn.info.routine.key == DECLARED) ||
     (rtn_idp->defn.info.routine.key == FORWARD) ||
     (!parm_check_flag)) {
   exit_debug("routine_call");
   return(declared_routine_call(rtn_idp, parm_check_flag));
 }
 else {
   exit_debug("routine_call");
   return(standard_routine_call(rtn_idp));
 }
}                                                      /* end routine_call */
/***************************************************************************/



/***************************************************************************/
/* declared_routine_call(rtn_idp, parm_check_flag) Process a call to a     */
/*                                      declared function or procedure     */
/*                       <id> or                                           */
/*                       <id> ( <parm-list> )                              */
/*                               The actual params are checked against the */
/*                               formal params for type and number.        */
/* return pointer to type structure of the call                            */

TYPE_STRUCT_PTR declared_routine_call(rtn_idp, parm_check_flag)
SYMTAB_NODE_PTR rtn_idp;                /* routine id */
BOOLEAN parm_check_flag;                /* if TRUE then check parms */
{
 entry_debug("declared_routine_call");

 actual_parm_list(rtn_idp, parm_check_flag);

 exit_debug("declared_routine_call");
 return(rtn_idp->defn.key == PROC_DEFN ? NULL : rtn_idp->typep);
}                                             /* end declared_routine_call */
/***************************************************************************/



/***************************************************************************/
/* actual_parm_list(rtn_idp, parm_check_flag) Process actual param list    */
/*                           ( <expr-list> )                               */

actual_parm_list(rtn_idp, parm_check_flag)
SYMTAB_NODE_PTR rtn_idp;                /* routine id */
BOOLEAN parm_check_flag;                /* if TRUE then check parms */
{
 SYMTAB_NODE_PTR formal_parm_idp;
 DEFN_KEY formal_parm_defn;
 TYPE_STRUCT_PTR formal_parm_tp, actual_parm_tp;
 entry_debug("actual_parm_list");

 if (parm_check_flag) formal_parm_idp = rtn_idp->defn.info.routine.parms;

 if (token == LPAREN) {
   /* loop to process actual param expressions */
   do {
     /* get info on corresponding formal params */
     if (parm_check_flag && (formal_parm_idp != NULL)) {
       formal_parm_defn = formal_parm_idp->defn.key;
       formal_parm_tp = formal_parm_idp->typep;
     }

     get_token();

     /* Actual and formal parms must be consistent. */
     /* Actual parm may be an expression */
     if ((formal_parm_idp == NULL) ||
         (formal_parm_defn == VALPARM_DEFN) ||
         (!parm_check_flag)) {
       actual_parm_tp = expression();
       if (parm_check_flag &&
           (formal_parm_idp != NULL) &&
           (!is_assign_type_compatible(formal_parm_tp, actual_parm_tp)))
         error(INCOMPATIBLE_TYPES);
     }

     /* Now the same for VAR params */
     else {
       if (token == IDENTIFIER) {
         SYMTAB_NODE_PTR idp;

         search_and_find_all_symtab(idp);
         actual_parm_tp = variable(idp, VARPARM_USE);

         if (formal_parm_tp != actual_parm_tp) error(INCOMPATIBLE_TYPES);
       }
       else {  /* not a variable, but parse anyway */
         actual_parm_tp = expression();
         error(INVALID_VAR_PARM);
       }
     }

     /* check if there are more actuals than formals */
     if (parm_check_flag) {
       if (formal_parm_idp == NULL) error(WRONG_NUMBER_OF_PARMS);
       else formal_parm_idp = formal_parm_idp->next;
     }

     /* sync. Should be , or ) */
     synchronize(follow_parm_list, statement_end_list, NULL);

   } while (token == COMMA);  /* end do */
   if_token_get_else_error(RPAREN, MISSING_RPAREN);
 }

 /* check for fewer actuals than formals */
 if (parm_check_flag && (formal_parm_idp != NULL)) error(WRONG_NUMBER_OF_PARMS);

 exit_debug("actual_parm_list");
 return;
}                                                  /* end actual_parm_list */
/***************************************************************************/



/***************************************************************************/
/* block(rtn_idp)  Process a block, which consists of declarations         */
/*                                  followed by a compound statement       */

old_block(rtn_idp)
SYMTAB_NODE_PTR rtn_idp;
{
 extern BOOLEAN block_flag;
 entry_debug("block");

 declarations(rtn_idp);

 /* sync. Should be ; */
 synchronize(follow_decls_list, NULL, NULL);
 if (token != BEGIN) error(MISSING_BEGIN);

 crunch_token();

 block_flag = TRUE;
 compound_statement();
 block_flag = FALSE;

 exit_debug("block");
 return;
}                                                             /* end block */
/***************************************************************************/