/* ------------------------------------------------------------------------
@NAME       : error.c
@DESCRIPTION: Anything relating to reporting or recording errors and 
              warnings.
@GLOBALS    : errclass_names
              errclass_counts
@CALLS      : 
@CREATED    : 1996/08/28, Greg Ward
@MODIFIED   : 
@VERSION    : $Id: error.c,v 1.10 1997/09/07 02:36:28 greg Exp $
@COPYRIGHT  : Copyright (c) 1996-97 by Gregory P. Ward.  All rights reserved.

              This file is part of the btparse library.  This library is
              free software; you can redistribute it and/or modify it under
              the terms of the GNU Library General Public License as
              published by the Free Software Foundation; either version 2
              of the License, or (at your option) any later version.
-------------------------------------------------------------------------- */

#include "bt_config.h"
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include "btparse.h"
#include "error.h"
#include "my_dmalloc.h"


char *errclass_names[NUM_ERRCLASSES] = 
{
   NULL,                        /* E_NOTIFY */
   "warning",                   /* E_CONTENT */ 
   "warning",                   /* E_STRUCTURAL */
   "warning",                   /* E_LEXWARN */
   "error",                     /* E_LEXERR */
   "syntax error",              /* E_SYNTAX */
   "fatal error",               /* E_FATAL */
   "internal error"             /* E_INTERNAL */
};

int errclass_counts[NUM_ERRCLASSES] = { 0, 0, 0, 0, 0, 0, 0, 0 };


/*
 * vprint_message()
 * 
 * low-level driver for printing errors, warnings, etc. of any kind.
 * Depends on no global variables (except errclass_names), does no policing
 * or decision -- just prints the damn thing.
 *
 * Inputs: filename  - name of file where error found
 *         line      - line number in file (1-based)
 *         column    - column in line (0-based; currently ignored)
 *         show_line - print the offending line? (currently ignored)
 *         errclass  - class of error message (content warning, structure 
 *                     warning, lexer error, syntax error)
 *         format    - a printf format for your message
 *         arglist   - the "..." to fill in your format
 * 
 * TO DO: in the lexer (?) we need to build a data structure that will keep
 *        track of where each line of the file begins.  Then, whenever we
 *        find an error, we remember it for later.  When the file is done,
 *        we go back to print error messages; for each one, we can quickly
 *        seek to the start of the offending line using the line-start
 *        data.
 */
static void
vprint_message (char    *filename,
                int      line,
                int      column,
                int      show_line,
                errclass_t errclass,
                char    *format,
                va_list  arglist)
{
   char  *name;

   if (filename)
      fprintf (stderr, filename);
   if (filename && line > 0)
      fprintf (stderr, ", ");
   if (line > 0)
      fprintf (stderr, "line %d", line);

   name = errclass_names[(int) errclass];
   if (name)
   {
      if (filename || line > 0)
         fprintf (stderr, ", ");
      fprintf (stderr, name);
   }

   if (filename || line > 0 || name)
      fprintf (stderr, ": ");

   vfprintf (stderr, format, arglist);
   fputc ('\n', stderr);
}


/*------------------------------------------------------------------------
 * Functions that call vprint_message(), and are called from elsewhere.
 * These freely use the InputFilename global variable, and modify the
 * errclass_counts array.
 * 
 * There are three kinds of error functions, all of which are sufficiently
 * similar to be generated by macros, but sufficiently different that there
 * is one generating macro for each kind of function.  Sigh.  They are:
 *    - ordinary error functions, which print no filename or line number info
 *      (these are: notify(), fatal_error(), internal_error())
 *    - parser error functions, which get filename from global InputFilename
 *      and line number from zzline
 *      (lexical_warning(), lexical_error(), syntax_error()) 
 *    - AST error functions, which get filename from global InputFilename
 *      and line number from a passed-in AST node
 *      (content_warning(), structural_warning())
 *-----------------------------------------------------------------------*/

#define ERRFUNC(func,class,action)              \
void func (char *format, ...)                   \
{                                               \
   va_list    arglist;                          \
                                                \
   va_start (arglist, format);                  \
   vprint_message (NULL, 0, -1, 0,              \
                   class, format, arglist);     \
   errclass_counts[(int) class]++;              \
   va_end (arglist);                            \
   action;                                      \
}

#define PARSER_ERRFUNC(func,class,action)               \
void func (char *format, ...)                           \
{                                                       \
   extern char *InputFilename;                          \
   extern int  zzline, zzendcol;                        \
   va_list    arglist;                                  \
                                                        \
   va_start (arglist, format);                          \
   vprint_message (InputFilename, zzline, zzendcol, 1,  \
                  class, format, arglist);              \
   errclass_counts[(int) class]++;                      \
   va_end (arglist);                                    \
   action;                                              \
}

#define AST_ERRFUNC(func,class,action)                          \
void func (AST *ast, char *format, ...)                         \
{                                                               \
   extern char *InputFilename;                                  \
   va_list    arglist;                                          \
                                                                \
   va_start (arglist, format);                                  \
   vprint_message (InputFilename, ast->line, ast->offset, 1,    \
                   class, format, arglist);                     \
                                                                \
   errclass_counts[(int) class]++;                              \
   va_end (arglist);                                            \
   action;                                                      \
}

#define GENERAL_ERRFUNC(func,class,action)              \
void func (char *filename, int line, char *format, ...) \
{                                                       \
   va_list    arglist;                                  \
                                                        \
   va_start (arglist, format);                          \
   vprint_message (filename, line, -1, 1,               \
                   class, format, arglist);             \
                                                        \
   errclass_counts[(int) class]++;                      \
   va_end (arglist);                                    \
   action;                                              \
}
   

ERRFUNC        (notify,             E_NOTIFY,     return)
ERRFUNC        (fatal_error,        E_FATAL,      exit(1))
ERRFUNC        (internal_error,     E_INTERNAL,   abort())
PARSER_ERRFUNC (lexical_warning,    E_LEXWARN,    return)
PARSER_ERRFUNC (lexical_error,      E_LEXERR,     return)
PARSER_ERRFUNC (syntax_error,       E_SYNTAX,     return)
AST_ERRFUNC    (content_warning,    E_CONTENT,    return)
AST_ERRFUNC    (structural_warning, E_STRUCTURAL, return)
GENERAL_ERRFUNC(name_warning,       E_CONTENT,    return)

#undef ERRFUNC
#undef PARSER_ERRFUNC
#undef AST_ERRFUNC
#undef GENERAL_ERRFUNC


/* ------------------------------------------------------------------------
@NAME       : reset_error_counts()
@INPUT      : 
@OUTPUT     : 
@RETURNS    : 
@DESCRIPTION: Resets all the error counters to zero.
@GLOBALS    : 
@CALLS      : 
@CREATED    : 1997/01/08, GPW
@MODIFIED   : 
-------------------------------------------------------------------------- */
void reset_error_counts (void)
{
   int  i;

   for (i = 0; i < NUM_ERRCLASSES; i++)
      errclass_counts[i] = 0;
}


/* ------------------------------------------------------------------------
@NAME       : get_error_count()
@INPUT      : errclass
@OUTPUT     : 
@RETURNS    : 
@DESCRIPTION: Returns number of errors seen in the specified class.
@GLOBALS    : errclass_counts
@CALLS      : 
@CREATED    : 
@MODIFIED   : 
-------------------------------------------------------------------------- */
int get_error_count (errclass_t errclass)
{
   return errclass_counts[errclass];
}


/* ------------------------------------------------------------------------
@NAME       : get_error_counts()
@INPUT      : counts - pointer to an array big enough to hold all the
                       counts, or NULL to make get_error_counts alloc it
@OUTPUT     : 
@RETURNS    : counts - either the passed-in pointer, or the newly-
                       allocated array if you pass in NULL
@DESCRIPTION: Returns a newly-allocated array with the number of errors
              in each error class, indexed by the members of the
              eclass_t enum.
@GLOBALS    : errclass_counts
@CALLS      : 
@CREATED    : 1997/01/06, GPW
@MODIFIED   : 
-------------------------------------------------------------------------- */
int *get_error_counts (int *counts)
{
   int    i;

   if (counts == NULL)
      counts = (int *) malloc (sizeof (int) * NUM_ERRCLASSES);
   for (i = 0; i < NUM_ERRCLASSES; i++)
      counts[i] = errclass_counts[i];

   return counts;
}


/* ------------------------------------------------------------------------
@NAME       : error_status
@INPUT      : saved_counts - an array of error counts as returned by 
                             get_error_counts, or NULL to not compare
                             to a previous watermark
@OUTPUT     : 
@RETURNS    : 
@DESCRIPTION: Computes a bitmap where a bit is set for each error class
              that has more errors now than it used to have (or, if 
              saved_counts is NULL, the bit is set of there are have been
              any errors in the corresponding error class).

              Eg. "x & (1<<E_SYNTAX)" (where x is returned by error_status)
              is true if there have been any syntax errors.
@GLOBALS    : 
@CALLS      : 
@CREATED    : 
@MODIFIED   : 
-------------------------------------------------------------------------- */
ushort error_status (int *saved_counts)
{
   int     i;
   ushort  status;

   status = 0;

   if (saved_counts)
   {
      for (i = 0; i < NUM_ERRCLASSES; i++)
         status |= ( (errclass_counts[i] > saved_counts[i]) << i);
   }
   else
   {
      for (i = 0; i < NUM_ERRCLASSES; i++)
         status |= ( (errclass_counts[i] > 0) << i);
   }

   return status;
}
