/* l2xierr.c  LTX2X interpreter error handler */
/*  Written by: Peter Wilson, CUA  pwilson@cme.nist.gov                */
/*  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"

/* externals */

extern char *tokenp;
extern BOOLEAN print_flag;
extern char source_buffer[];
extern char *bufferp;

/* error messages: keyed to enum types in error.h from l2xiertc.h */

char *error_messages[] = {
#define petc(a, b) b,
#define pwtc(a, b)
#define rtetc(a, b)
#define rtwtc(a, b)
#include "l2xiertc.h"
#undef petc
#undef pwtc
#undef rtetc
#undef rtwtc
      };


char *warning_messages[] = {
#define petc(a, b) 
#define pwtc(a, b) b,
#define rtetc(a, b)
#define rtwtc(a, b)
#include "l2xiertc.h"
#undef petc
#undef pwtc
#undef rtetc
#undef rtwtc
      };

char *runtime_error_messages[] = {
#define petc(a, b) 
#define pwtc(a, b)
#define rtetc(a, b) b,
#define rtwtc(a, b)
#include "l2xiertc.h"
#undef petc
#undef pwtc
#undef rtetc
#undef rtwtc
      };

char *runtime_warning_messages[] = {
#define petc(a, b) 
#define pwtc(a, b)
#define rtetc(a, b)
#define rtwtc(a, b) b,
#include "l2xiertc.h"
#undef petc
#undef pwtc
#undef rtetc
#undef rtwtc
      };

/* stack types */
typedef enum {
#define fotc(a, b, c, d) 
#define sotc(a, b, c, d) a,
#define sftc(a, b, c, d) a,
#include "l2xisftc.h"
#undef fotc
#undef sotc
#undef sftc
} STACK_TYPE;


/* LOCALS */
#define EOS '\0'

/* GLOBALS  */

int isynt_error_count = 0;   /* number of syntax errors */
int isynt_warn_count = 0;    /* number of syntax warnings */
int irun_error_count = 0;    /* number of runtime errors */
int irun_warn_count = 0;     /* number of runtime warnings */
char message_buffer[MAX_PRINT_LINE_LENGTH];

/***************************************************************************/
/* error(code)   Print an arrow under the error, then the error message    */

error(code)
ERROR_CODE code;     /* error code */
{
  extern int buffer_offset;
  char *message = error_messages[code];
  int offset = buffer_offset - 2;

  ++isynt_error_count;

  /* print the arrow pointing to just scanned token */
  if (print_flag) offset += 8;
  sprintf(message_buffer, "%*s^\n", offset, " ");
  if (print_flag) {
    print_line(message_buffer);
  }
  else {
     print_error(message_buffer);
   }

  /* print the error message */
  sprintf(message_buffer, " ***ERROR: %s.\n", message);
  if (print_flag) {
    print_line(message_buffer);
  }
  else {
     print_error(message_buffer);
   }

  *tokenp = EOS;

  if (isynt_error_count > MAX_SYNTAX_ERRORS) {
    sprintf(message_buffer, "Too many syntax errors.\n");
    if (print_flag) {
      print_line(message_buffer);
    }
    else {
      print_error(message_buffer);
    }
    exit(-TOO_MANY_SYNTAX_ERRORS);
  }

}                                                             /* end error */
/***************************************************************************/



/***************************************************************************/
/* compile_warning(code)   Print an arrow under the error, then the        */
/*                         warning message                                 */

compile_warning(code)
WARNING_CODE code;     /* warning code */
{
  extern int buffer_offset;
  char *message = warning_messages[code];
  int offset = buffer_offset - 2;

  ++isynt_warn_count;

  /* print the arrow pointing to just scanned token */
  if (print_flag) offset += 8;
  sprintf(message_buffer, "%*s^\n", offset, " ");
  if (print_flag) {
    print_line(message_buffer);
  }
  else {
     print_error(message_buffer);
   }

  /* print the warning message */
  sprintf(message_buffer, " ***WARNING: %s.\n", message);
  if (print_flag) {
    print_line(message_buffer);
  }
  else {
     print_error(message_buffer);
   }

  *tokenp = EOS;

}                                                           /* end warning */
/***************************************************************************/



/***************************************************************************/
/* runtime_error(code)   Print a runtime error message and then debug      */

runtime_error(code)  
RUNTIME_ERROR_CODE code;     /* error code */
{
  extern int exec_line_number; 
  extern long exec_stmt_count;
  char *message = runtime_error_messages[code];
  extern BOOLEAN debugger_command_flag;

  ++irun_error_count;

  if (SLD_OFF) {    /* source level debugger disabled -- abort */
    sprintf(message_buffer, "\n*** RUNTIME ERROR in line %d: %s\n",
             exec_line_number, message);
    print_error(message_buffer);
    sprintf(message_buffer, "\nUnsuccessful completion. %ld statements executed.\n\n",
                             exec_stmt_count);
    print_error(message_buffer);
    exit(-code);    
  }

  if (debugger_command_flag) {
    print_error(message);
    print_error("\n");
  }
  else {
    sprintf(message_buffer, "\n*** RUNTIME ERROR in line %d: %s\n",
             exec_line_number, message);
    print_error(message_buffer);
    read_debugger_command();
  }

}                                                     /* end runtime_error */
/***************************************************************************/



/***************************************************************************/
/* runtime_warning(code)   Print a runtime warning message and then debug  */

runtime_warning(code)  
RUNTIME_WARNING_CODE code;     /* warning code */
{
  extern int exec_line_number; 
  extern long exec_stmt_count;
  char *message = runtime_warning_messages[code];
  extern BOOLEAN debugger_command_flag;

  if (INVALID_STACK_ACCESS == code) return;

  ++irun_warn_count;

  if (SLD_OFF) {    /* source level debugger disabled */
    sprintf(message_buffer, "\n*** RUNTIME WARNING in line %d: %s\n",
             exec_line_number, message);
    print_error(message_buffer);
    return;
  }

  if (debugger_command_flag) {
    print_error(message);
    print_error("\n");
  }
  else {
    sprintf(message_buffer, "\n*** RUNTIME WARNING in line %d: %s\n",
             exec_line_number, message);
    print_error(message_buffer);
    read_debugger_command();
  }
  return;
}                                                   /* end runtime_warning */
/***************************************************************************/



/***************************************************************************/
/* stack_warning(etype, ftype)   Print a runtime warning message about     */
/*               stack access and then debug                               */

stack_warning(etype, ftype)  
STACK_TYPE etype;                 /* expected type */
STACK_TYPE ftype;                 /* type actually found */
{
  extern int exec_line_number; 
  extern long exec_stmt_count;
  RUNTIME_WARNING_CODE code1, code2;
  extern BOOLEAN debugger_command_flag;

  code1 = RUNTIME_WARN;
  switch (etype) {           /* report the expected type */
    case STKINT:
    case STKREA: {
      code1 = EXPECTED_NUMBER;
      break;
    }
    case STKLOG: {
      code1 = EXPECTED_LOGICAL;
      break;
    }
    case STKADD: {
      code1 = EXPECTED_ADDRESS;
      break;
    }
    case STKARY: {
      code1 = EXPECTED_ARRAY;
      break;
    }
    case STKBAG: {
      code1 = EXPECTED_BAG;
      break;
    }
    case STKLST: {
      code1 = EXPECTED_LIST;
      break;
    }
    case STKSET: {
      code1 = EXPECTED_SET;
      break;
    }
    case STKSTR: {
      code1 = EXPECTED_STRING;
      break;
    }
    case STKENT: {
      code1 = EXPECTED_ENTITY;
      break;
    }
    case STKUDF: {
      code1 = EXPECTED_UDF;
      break;
    }
  }  /* finished expected type */

  code2 = RUNTIME_WARN;
  switch (ftype) {           /* report the found type */
    case STKINT:
    case STKREA: {
      code2 = FOUND_NUMBER;
      break;
    }
    case STKLOG: {
      code2 = FOUND_LOGICAL;
      break;
    }
    case STKADD: {
      code2 = FOUND_ADDRESS;
      break;
    }
    case STKARY: {
      code2 = FOUND_ARRAY;
      break;
    }
    case STKBAG: {
      code2 = FOUND_BAG;
      break;
    }
    case STKLST: {
      code2 = FOUND_LIST;
      break;
    }
    case STKSET: {
      code2 = FOUND_SET;
      break;
    }
    case STKSTR: {
      code2 = FOUND_STRING;
      break;
    }
    case STKENT: {
      code2 = FOUND_ENTITY;
      break;
    }
    case STKUDF: {
      code2 = FOUND_UDF;
      break;
    }
  }  /* finished found type */



  if (SLD_OFF) {    /* source level debugger disabled */
    sprintf(message_buffer, "\n*** RUNTIME WARNING in line %d:\n    %s\n    %s\n",
             exec_line_number, 
             runtime_warning_messages[code1],
             runtime_warning_messages[code2]);
    print_error(message_buffer);
    return;
  }

  if (debugger_command_flag) {
    sprintf(message_buffer, "\n    %s\n    %s\n",
             runtime_warning_messages[code1],
             runtime_warning_messages[code2]);
    print_error(message_buffer);
  }
  else {
    sprintf(message_buffer, "\n*** RUNTIME WARNING in line %d:\n    %s\n    %s\n",
             exec_line_number, 
             runtime_warning_messages[code1],
             runtime_warning_messages[code2]);
    print_error(message_buffer);
    read_debugger_command();
  }
  return;
}                                                     /* end STACK_WARNING */
/***************************************************************************/



/***************************************************************************/
/* print_error(string) Prints to error file(s)                             */

print_error(string)
char string[];
{

  fprintf(ferr, "%s", string);
  fflush(ferr);
  fprintf(stderr, "%s", string);
  fflush(stderr);
  if (filout != stdout) {
    fprintf(filout, "%s", string);
    fflush(filout);
  }
  return;
}                                                       /* end print_error */
/***************************************************************************/