/* l2xistd.c  LTX2X interpreter  Parsing for calls to standard 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"


#define DEFAULT_NUMERIC_FIELD_WIDTH 10
#define DEFAULT_PRECISION 2

/* EXTERNALS */

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

/* FORWARDS */

TYPE_STRUCT_PTR eof_eoln(), abs_sqr(), arctan_cos_exp_ln_sin_sqrt(),
               pred_succ(), odd(), ord(), round_trunc();
TYPE_STRUCT_PTR atan(), exists_etc(), nvl_etc();
TYPE_STRUCT_PTR rexpr_etc(), hibound_etc(), length_etc();

/***************************************************************************/
/* standard_routine_call (rtn_idp) Process call to standard function       */
/* return pointer to type structure of the call                            */

TYPE_STRUCT_PTR standard_routine_call(rtn_idp)
SYMTAB_NODE_PTR rtn_idp;           /* routine id */
{

 switch (rtn_idp->defn.info.routine.key) {
   case READ:
   case READLN: {
     read_readln(rtn_idp);
     return(NULL);
   }
   case WRITE:
   case WRITELN: {
     write_writeln(rtn_idp);
     return(NULL);
   }
   case EOFF:
   case EOLN: {
     return(eof_eoln(rtn_idp));
   }
   case ABS:          /* real or int arg -> real or int */
             {
     return(abs_sqr());
   }
   case COS:          /* real or int arg -> real */
   case EXP:
   case SIN:
   case SQRT:
   case XACOS:
   case XASIN:
   case XLOG:
   case XLOG2:
   case XLOG10:
   case XTAN: {
     return(arctan_cos_exp_ln_sin_sqrt());
   }
   case XATAN: {
     return(atan());
   }
   case ODD:  {       /* int arg -> boolean */
     return(odd());
   }
   case ROUND:        /* real arg -> int */
   case TRUNC: {
     return(round_trunc());
   }
   case L2XPRINT:
   case L2XPRINTLN: {               /* extra for ltx2x */
     print_println(rtn_idp);
     return(NULL);
   }
   case L2XSYSTEM: {                /* extra for ltx2x */
     system_etc(rtn_idp);
     return(NULL);
   }
   case L2XREXPR: {                 /* extra for ltx2x two strings -> boolean */
     return(rexpr_etc());
   }
   case XEXISTS: {    /* any arg -> boolean */
     return(exists_etc());
   }
   case XNVL: {       /* two args -> one of these */
     return(nvl_etc());
   }
   case XHIBOUND:         /* agg arg -> int */
   case XHIINDEX:
   case XLOBOUND:
   case XLOINDEX:
   case XSIZEOF: {
     return(hibound_etc());
   }
   case XLENGTH: {        /* string arg -> int */
     return(length_etc());
   }
   case XINSERT:
   case XREMOVE: {
     insert_etc(rtn_idp);
     return(NULL);
   }
   case XBLENGTH:                 /* unimplemented EXPRESS functions */
   case XFORMAT:
   case XROLESOF:
   case XTYPEOF:
   case XUSEDIN:
   case XVALUE:
   case XVALUE_IN:
   case XVALUE_UNIQUE: {
     error(UNIMPLEMENTED_FEATURE);
     return(NULL);
   }
   default : {    /* should not be here */
     error(UNEXPECTED_TOKEN);
     return(NULL);
   }
 }  /* end switch */
}                                             /* end standard_routine_call */
/***************************************************************************/


/***************************************************************************/
/* read_readln(rtn_idp) Process call to read or readln                     */

read_readln(rtn_idp)
SYMTAB_NODE_PTR rtn_idp;
{
 TYPE_STRUCT_PTR actual_parm_tp;      /* actual param type */

 /* parameters are optional for readln */
 if (token == LPAREN) {
   do {
     get_token();
     /* actuals should be variables, but parse anyway */
     if (token == IDENTIFIER) {
       SYMTAB_NODE_PTR idp;
       search_and_find_all_symtab(idp);
       actual_parm_tp = base_type(variable(idp, VARPARM_USE));
/*        if (actual_parm_tp->form != SCALAR_FORM) error(INCOMPATIBLE_TYPES); */
       if (actual_parm_tp != integer_typep &&
           actual_parm_tp != real_typep &&
           actual_parm_tp != logical_typep &&
           actual_parm_tp != string_typep) {
         error(INCOMPATIBLE_TYPES);
       }
     }
     else {
       actual_parm_tp = expression();
       error(INVALID_VAR_PARM);
     }
    /* 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);
 } /* end if */
 else {
   if (rtn_idp->defn.info.routine.key == READ) error(WRONG_NUMBER_OF_PARMS);
 }

}                                                       /* end read_readln */
/***************************************************************************/



/***************************************************************************/
/* write_writeln(rtn_idp) Process call to write or writeln                 */
/*                        Each actual parameter can be:                    */
/*                        <expr> or                                        */
/*                        <expr> : <expr> or                               */
/*                        <expr> : <expr> : <expr>                         */

write_writeln(rtn_idp)
SYMTAB_NODE_PTR rtn_idp;
{
 TYPE_STRUCT_PTR actual_parm_tp;             /* actual parm type */
 TYPE_STRUCT_PTR field_width_tp, precision_tp;

 /* params are optional for writeln */
 if (token == LPAREN) {
   do {
     get_token();
     actual_parm_tp = base_type(expression());

     if ((actual_parm_tp->form != SCALAR_FORM) &&
         (actual_parm_tp != logical_typep) &&
         (actual_parm_tp->form != STRING_FORM) &&
         (actual_parm_tp->form != ENUM_FORM))
       error(INVALID_EXPRESSION);

     /* optional field width expression */
     if (token == COLON) {
       get_token();
       field_width_tp = base_type(expression());
       if (field_width_tp != integer_typep) error(INCOMPATIBLE_TYPES);

       /* optional precision spec */
       if (token == COLON) {
         get_token();
         precision_tp = base_type(expression());
         if (precision_tp != integer_typep) error(INCOMPATIBLE_TYPES);

       } /* end colon if */
     } /* end colon if */
     /* 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);
 } /* end if */
 else {
   if (rtn_idp->defn.info.routine.key == WRITE) error(WRONG_NUMBER_OF_PARMS);
 }

}                                                     /* end write_writeln */
/***************************************************************************/



/***************************************************************************/
/* print_println(rtn_idp) Process call to print or println                 */
/*                        Each actual parameter can be:                    */
/*                        <expr> or                                        */
/*                        <expr> : <expr> or                               */
/*                        <expr> : <expr> : <expr>                         */
/*    At this point, identical to write_writeln                            */

print_println(rtn_idp)
SYMTAB_NODE_PTR rtn_idp;
{
 TYPE_STRUCT_PTR actual_parm_tp;             /* actual parm type */
 TYPE_STRUCT_PTR field_width_tp, precision_tp;

 /* params are optional for println */
 if (token == LPAREN) {
   do {
     get_token();
     actual_parm_tp = base_type(expression());

     if ((actual_parm_tp->form != SCALAR_FORM) &&
         (actual_parm_tp != logical_typep) &&
         (actual_parm_tp->form != STRING_FORM) &&
         (actual_parm_tp->form != ENUM_FORM))
       error(INVALID_EXPRESSION);

     /* optional field width expression */
     if (token == COLON) {
       get_token();
       field_width_tp = base_type(expression());
       if (field_width_tp != integer_typep) error(INCOMPATIBLE_TYPES);

       /* optional precision spec */
       if (token == COLON) {
         get_token();
         precision_tp = base_type(expression());
         if (precision_tp != integer_typep) error(INCOMPATIBLE_TYPES);

       } /* end colon if */
     } /* end colon if */
     /* 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);
 } /* end if */
 else {
   if (rtn_idp->defn.info.routine.key == WRITE) error(WRONG_NUMBER_OF_PARMS);
 }

}                                                     /* end print_println */
/***************************************************************************/



/***************************************************************************/
/* eof_eoln(rtn_idp)  Process call to eof or eoln. No parameters.          */
/* return boolean result.                                                  */

TYPE_STRUCT_PTR eof_eoln(rtn_idp)
SYMTAB_NODE_PTR rtn_idp;
{
 TYPE_STRUCT_PTR result_tp = logical_typep;

 if (token == LPAREN) {
   error(WRONG_NUMBER_OF_PARMS);
   actual_parm_list(rtn_idp, FALSE);
 }
 return(result_tp);
}                                                          /* end eof_eoln */
/***************************************************************************/



/***************************************************************************/
/* system_etc()         Process call to system, etc                        */
/*                      fun('string')                                      */
/*              One string parameter, no return value                      */
/*    at entry, token is `fun'                                             */
/*    at exit, token is after closing )                                    */

system_etc(rtn_idp)
SYMTAB_NODE_PTR rtn_idp;                 /* routine id */
{
 TYPE_STRUCT_PTR actual_parm_tp;         /* actual parm type */

 if (token == LPAREN) {
   get_token();
   actual_parm_tp = base_type(expression());
   if (actual_parm_tp != string_typep &&
         (actual_parm_tp->form != STRING_FORM)) {
     error(INVALID_EXPRESSION);
   }
   if_token_get_else_error(RPAREN, MISSING_RPAREN);
 }
 else {
   error(WRONG_NUMBER_OF_PARMS);
 }

 return;
}                                                        /* end SYSTEM_ETC */
/***************************************************************************/



/***************************************************************************/
/* length_etc()         Process call to length, etc                        */
/*                      fun('string')                                      */
/*              One string parameter, integer return value                 */
/*    at entry, token is `fun'                                             */
/*    at exit, token is after closing )                                    */

TYPE_STRUCT_PTR length_etc()
{
 TYPE_STRUCT_PTR actual_parm_tp;         /* actual parm type */
 TYPE_STRUCT_PTR result_tp = integer_typep;  /* result type */
 if (token == LPAREN) {
   get_token();
   actual_parm_tp = base_type(expression());
   if (actual_parm_tp != string_typep &&
         (actual_parm_tp->form != STRING_FORM)) {
     error(INVALID_EXPRESSION);
   }
   if_token_get_else_error(RPAREN, MISSING_RPAREN);
 }
 else {
   error(WRONG_NUMBER_OF_PARMS);
 }

 return(result_tp);
}                                                        /* end LENGTH_ETC */
/***************************************************************************/



/***************************************************************************/
/* hibound_etc()         Process call to hibound, etc                      */
/*                      fun(agg)                                           */
/*              One aggregate parameter, integer return value              */
/*    at entry, token is `fun'                                             */
/*    at exit, token is after closing )                                    */

TYPE_STRUCT_PTR hibound_etc()
{
 TYPE_STRUCT_PTR actual_parm_tp;         /* actual parm type */
 TYPE_STRUCT_PTR result_tp = integer_typep;  /* result type */
 if (token == LPAREN) {
   get_token();
   actual_parm_tp = base_type(expression());
   if ((actual_parm_tp->form != ARRAY_FORM) &&
       (actual_parm_tp->form != BAG_FORM) &&
       (actual_parm_tp->form != LIST_FORM) &&
       (actual_parm_tp->form != SET_FORM) ) {
     error(INVALID_EXPRESSION);
   }
   if_token_get_else_error(RPAREN, MISSING_RPAREN);
 }
 else {
   error(WRONG_NUMBER_OF_PARMS);
 }

 return(result_tp);
}                                                       /* end HIBOUND_ETC */
/***************************************************************************/



/***************************************************************************/
/* rexpr_etc()         Process call to rexpr, etc                          */
/*                      fun('string', 'string')                            */
/*              Two string parameters, boolean return value                */
/*    at entry, token is `fun'                                             */
/*    at exit, token is after closing )                                    */

TYPE_STRUCT_PTR rexpr_etc()
{
 TYPE_STRUCT_PTR actual_parm_tp;         /* actual parm type */
 TYPE_STRUCT_PTR result_tp = logical_typep; /* result type */

 if (token == LPAREN) {
   get_token();
   actual_parm_tp = base_type(expression());
   if (actual_parm_tp != string_typep &&
       actual_parm_tp->form != STRING_FORM) {
     error(INVALID_EXPRESSION);
   }
   if_token_get_else_error(COMMA, MISSING_COMMA);
   actual_parm_tp = base_type(expression());
   if (actual_parm_tp != string_typep &&
       actual_parm_tp->form != STRING_FORM) {
     error(INVALID_EXPRESSION);
   }
   if_token_get_else_error(RPAREN, MISSING_RPAREN);
 }
 else {
   error(WRONG_NUMBER_OF_PARMS);
 }

 return(result_tp);
}                                                         /* end REXPR_ETC */
/***************************************************************************/



/***************************************************************************/
/* exists_etc   Process call to exists, etc                                */
/*              fun(any) -> boolean                                        */
/*          any type parm -> boolean result                                */

TYPE_STRUCT_PTR exists_etc()
{
 TYPE_STRUCT_PTR parm_tp;                   /* actual param type */
 TYPE_STRUCT_PTR result_tp = logical_typep; /* result type */

 if (token == LPAREN) {
   get_token();
   if_token_get_else_error(RPAREN, MISSING_RPAREN);
 }
 else error(WRONG_NUMBER_OF_PARMS);

 return(result_tp);
}                                                        /* end EXISTS_ETC */
/***************************************************************************/



/***************************************************************************/
/* nvl_etc    Process NVL, etc                                             */
/*            fun(p1, p2) -> p1 or p2                                      */
/*            Two args, any type, returns one of them                      */

TYPE_STRUCT_PTR nvl_etc()
{
 TYPE_STRUCT_PTR parm_tp;                 /* actual param type */

 if (token == LPAREN) {
   get_token();
   parm_tp = base_type(expression());

   if_token_get_else_error(COMMA, MISSING_COMMA);
       /* PERHAPS SHOULD CHECK FOR ASSIGNMENT COMPATIBILITY */
/*
*    if (parm_tp != base_type(expression()) ) {
*      error(INCOMPATIBLE_TYPES);
*    }
*/
   if_token_get_else_error(RPAREN, MISSING_RPAREN);
 }
 else error(WRONG_NUMBER_OF_PARMS);

 return(parm_tp);
}                                                           /* end NVL_ETC */
/***************************************************************************/



/***************************************************************************/
/* abs_sqr  Process call to abs or sqr.                                    */
/*          integer parm -> integer result                                 */
/*          real parm -> real result                                       */

TYPE_STRUCT_PTR abs_sqr()
{
 TYPE_STRUCT_PTR parm_tp;                 /* actual param type */
 TYPE_STRUCT_PTR result_tp;               /* result type */

 if (token == LPAREN) {
   get_token();
   parm_tp = base_type(expression());

   if ((parm_tp != integer_typep) && (parm_tp != real_typep)) {
     error(INCOMPATIBLE_TYPES);
     result_tp = real_typep;
   }
   else result_tp = parm_tp;

   if_token_get_else_error(RPAREN, MISSING_RPAREN);
 }
 else error(WRONG_NUMBER_OF_PARMS);

 return(result_tp);
}                                                           /* end abs_sqr */
/***************************************************************************/



/***************************************************************************/
/* arctan_cos_exp_ln_sin_sqrt  Process call to these                       */
/*          integer parm -> real result                                    */
/*          real parm -> real result                                       */

TYPE_STRUCT_PTR arctan_cos_exp_ln_sin_sqrt()
{
 TYPE_STRUCT_PTR parm_tp;                 /* actual param type */

 if (token == LPAREN) {
   get_token();
   parm_tp = base_type(expression());

   if ((parm_tp != integer_typep) && (parm_tp != real_typep)) {
     error(INCOMPATIBLE_TYPES);
   }
   if_token_get_else_error(RPAREN, MISSING_RPAREN);
 }
 else error(WRONG_NUMBER_OF_PARMS);

 return(real_typep);
}                                        /* end arctan_cos_exp_ln_sin_sqrt */
/***************************************************************************/



/***************************************************************************/
/* atan  Process call to these                                             */
/*              fun(p1, p2)                                                */
/*          integer parm -> real result                                    */
/*          real parm -> real result                                       */

TYPE_STRUCT_PTR atan()
{
 TYPE_STRUCT_PTR parm_tp;                 /* actual param type */

 if (token == LPAREN) {
   get_token();
   parm_tp = base_type(expression());

   if ((parm_tp != integer_typep) && (parm_tp != real_typep)) {
     error(INCOMPATIBLE_TYPES);
   }
   if_token_get_else_error(COMMA, MISSING_COMMA);
   parm_tp = base_type(expression());
   if ((parm_tp != integer_typep) && (parm_tp != real_typep)) {
     error(INCOMPATIBLE_TYPES);
   }
   if_token_get_else_error(RPAREN, MISSING_RPAREN);
 }
 else error(WRONG_NUMBER_OF_PARMS);

 return(real_typep);
}                                                              /* end ATAN */
/***************************************************************************/





/***************************************************************************/
/* odd                 Process call to odd.                                */
/*          integer parm -> boolean result                                 */

TYPE_STRUCT_PTR odd()
{
 TYPE_STRUCT_PTR parm_tp;                 /* actual param type */
 TYPE_STRUCT_PTR result_tp = logical_typep;

 if (token == LPAREN) {
   get_token();
   parm_tp = base_type(expression());

   if (parm_tp != integer_typep) {
     error(INCOMPATIBLE_TYPES);
   }

   if_token_get_else_error(RPAREN, MISSING_RPAREN);
 }
 else error(WRONG_NUMBER_OF_PARMS);

 return(logical_typep);
}                                                               /* end odd */
/***************************************************************************/



/***************************************************************************/
/* round_trunc  Process call to round or trunc.                            */
/*          real parm -> integer result                                    */

TYPE_STRUCT_PTR round_trunc()
{
 TYPE_STRUCT_PTR parm_tp;                 /* actual param type */

 if (token == LPAREN) {
   get_token();
   parm_tp = base_type(expression());

   if (parm_tp != real_typep) {
     error(INCOMPATIBLE_TYPES);
   }

   if_token_get_else_error(RPAREN, MISSING_RPAREN);
 }
 else error(WRONG_NUMBER_OF_PARMS);

 return(integer_typep);
}                                                       /* end round_trunc */
/***************************************************************************/



/***************************************************************************/
/* insert_etc   Process a call to INSERT, etc                              */
/*              list procedures                                            */
/*       INSERT(LIST, GENERIC, INTEGER)                                    */
/*       REMOVE(LIST, INTEGER)                                             */

insert_etc(rtn_idp)
SYMTAB_NODE_PTR rtn_idp;
{
 TYPE_STRUCT_PTR parm_tp;               /* actual parm type */

 if (token == LPAREN) {
   get_token();
   parm_tp = base_type(expression());
   if (parm_tp->form != LIST_FORM) {
     error(INCOMPATIBLE_TYPES);
   }
   if_token_get_else_error(COMMA, MISSING_COMMA);
   if (rtn_idp->defn.info.routine.key == XINSERT) {
     expression();
     if_token_get_else_error(COMMA, MISSING_COMMA);
   }
   parm_tp = base_type(expression());
   if (parm_tp != integer_typep) {
     error(INCOMPATIBLE_TYPES);
   }
   if_token_get_else_error(RPAREN, MISSING_RPAREN);
 }
 else error(WRONG_NUMBER_OF_PARMS);

}                                                        /* end INSERT_ETC */
/***************************************************************************/