/* web2c -- convert the pseudo-Pascal generated by Tangle to C.
   The output depends on many C macros and some postprocessing by other
   programs.
 
  Arguments:
        -f:     force strict interpretation of semantics of for stmt
                (never used with TeX and friends)
        -t:     special optimizations for tex.p->tex*.c
        -m:     special optimizations for mf.p->mf*.c
        -c:     supply the base part of the name of the coerce.h file
        -h:     supply the name of the standard header file
        -d:     generate some additional debugging output
 
  The majority of this program (which includes ptoc.yacc and ptoc.lex)
  was written by Tomas Rokicki, with modifications by Tim Morgan, et al. */

#include "web2c.h"
#ifdef RISCOS
#include "Y_tab.h"
#else
#include "y.tab.h"
#endif


/* Changing this value will probably stimulate bugs in some
   preprocessors -- those which want to put the expansion of a macro
   entirely on one line.  */
#define max_line_length 78
#define max_strings 20000
#define hash_prime 101
#define sym_table_size 3000
#define unused 271828

/* Says whether to give voluminous progress reports.  */
boolean debug = false;
int indent = 0;
int line_pos = 0;
int last_brace = 0;
int block_level = 0;
int last_tok;
int tex = 0, strict_for = 0, mf = 0;

char safe_string[80];
char var_list[200];
char field_list[200];
char last_id[80];
char z_id[80];
char next_temp[] = "zzzaa";
#ifdef RISCOS
char coerce_name[100] = "h.coerce";
#else
char coerce_name[100] = "coerce.h";
#endif

long last_i_num;
int ii, l_s;
long lower_bound, upper_bound;
FILE *std;
FILE *coerce;
int pf_count = 1;

char *std_header = "h.null";    /* Default include filename */

char strings[max_strings];
int hash_list[hash_prime];
short global = 1;
struct sym_entry sym_table[sym_table_size];
int next_sym_free = -1, next_string_free = 0;
int mark_sym_free, mark_string_free;

extern int yyleng;

void
find_next_temp ()
{
    next_temp[4]++;
    if (next_temp[4] > 'z') {
        next_temp[4] = 'a';
        next_temp[3]++;
    }
}

void
normal ()
{
    std = stdout;
}

void
new_line ()
{
    if (!std)
        return;
    if (line_pos > 0) {
        (void) putc('\n', std);
        line_pos = 0;
    }
}

void
indent_line ()
{
    new_line ();
}


/* Output the string S to `std'.  */

void
my_output (s)
  char *s;
{
  int len = strlen (s);
  int less_indent = 0;

  if (!std)
    return;

  if (line_pos + len > max_line_length)
    indent_line ();

  if (indent > 1 && (strcmp(s, "case") == 0 || strcmp(s, "default") == 0))
    less_indent = 2;

  while (line_pos < indent * 2 - less_indent)
    {
      fputs ("  ", std);
      line_pos += 2;
    }

  (void) fprintf (std, "%s ", s);
  line_pos += len + 1;

  last_brace = (s[0] == '}');
}

void
semicolon ()
{
    if (!last_brace) {
        my_output(";");
        indent_line();
        last_brace = 1;
    }
}


/* Since a syntax error can never be recovered from, we exit here with
   bad status.  */

int
yyerror (s)
    char *s;
{
    fflush(stdout);
    fprintf(stderr, " %s\n", s);
    fprintf(stderr, "Last token = %d, ", last_tok);
    fprintf(stderr, "error buffer = `%s',\n\t", yytext);
    fprintf(stderr, "last id = `%s' (", last_id);
    ii = search_table(last_id);
    if (ii == -1)
        fprintf(stderr, "not in symbol table");
    else
        switch (sym_table[ii].typ) {
        case undef_id_tok:
            fprintf(stderr, "undefined");
            break;
        case var_id_tok:
            fprintf(stderr, "variable");
            break;
        case const_id_tok:
            fprintf(stderr, "constant");
            break;
        case type_id_tok:
            fprintf(stderr, "type");
            break;
        case proc_id_tok:
            fprintf(stderr, "parameterless procedure");
            break;
        case proc_param_tok:
            fprintf(stderr, "procedure with parameters");
            break;
        case fun_id_tok:
            fprintf(stderr, "parameterless function");
            break;
        case fun_param_tok:
            fprintf(stderr, "function with parameters");
            break;
        default:
            fprintf(stderr, "unknown!");
            break;
        }
    fprintf (stderr, ").\n");
    uexit (0);

    /* To stop silly warnings.  */
    return 0;
}

int hash(id) char *id;
{
    register int i = 0, j;
    for (j = 0; id[j] != 0; j++)
        i = (i + i + id[j]) % hash_prime;
    return (i);
}

int search_table(id) char *id;
{
    int ptr;
    ptr = hash_list[hash(id)];
    while (ptr != -1) {
        if (strcmp(id, sym_table[ptr].id) == 0)
            return (ptr);
        else
            ptr = sym_table[ptr].next;
    }
    return -1;
}


/* Add ID to the symbol table.  Leave it up to the caller to assign to
   the `typ' field.  Return the index into the `sym_table' array.  */
int
add_to_table (id)
  char *id;
{
    int h, ptr;
    h = hash (id);
    ptr = hash_list[h];
    hash_list[h] = ++next_sym_free;
    sym_table[next_sym_free].next = ptr;
    sym_table[next_sym_free].val = unused;
    sym_table[next_sym_free].id = strings + next_string_free;
    sym_table[next_sym_free].var_formal = false;
    sym_table[next_sym_free].var_not_needed = false;
    strcpy (strings + next_string_free, id);
    next_string_free += strlen (id) + 1;
    return next_sym_free;
}

void
remove_locals ()
{
    int h, ptr;
    for (h = 0; h < hash_prime; h++) {
        next_sym_free = mark_sym_free;
        next_string_free = mark_string_free;
        ptr = hash_list[h];
        while (ptr > next_sym_free)
            ptr = sym_table[ptr].next;
        hash_list[h] = ptr;
    }
    global = 1;
}

void
mark ()
{
    mark_sym_free = next_sym_free;
    mark_string_free = next_string_free;
    global = 0;
}


void
initialize ()
{
    register int i;

    for (i = 0; i < hash_prime; hash_list[i++] = -1)
      ;

    normal ();

    coerce = fopen (coerce_name, "w");
    if (coerce == NULL)
      {
        perror (coerce_name);
        uexit (0);
      }
}

int
main (argc, argv)
  int argc;
  char *argv[];
{
    int error, i;
    
    for (i=1; i < argc; i++) if (argv[i][0] == '-') switch (argv[i][1]) {
    case 't':
        tex = true;
        break;
    case 'm':
        mf = true;
        break;
    case 'f':
        strict_for = true;
        break;
    case 'h':
        std_header = &argv[i][2];
        break;
    case 'd':
        debug = true;
        break;
    case 'c':
#ifdef RISCOS
        sprintf(coerce_name, "h.%s", &argv[i][2]);
#else
        sprintf(coerce_name, "%s.h", &argv[i][2]);
#endif
        break;
    default:
        fprintf(stderr, "Unknown option %s, ignored\n", argv[i]);
        break;
    }
    else {
        fprintf(stderr, "Unknown option %s, ignored\n", argv[i]);
    }
    
    initialize ();
    error = yyparse ();
    new_line ();

    fclose (coerce);
    
    if (debug)
      fprintf(stderr, "%d symbols.\n", next_sym_free);

    uexit (error);
}
