/*
 * This program converts (mostly) 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 part of the name of the coerce.h file
 *      -h:     supply the name of the standard header file
 *      -a:     generate ANSI-style function prototypes
 *
 * The majority of this program (which includes ptoc.yacc and ptoc.lex)
 * was written by Tomas Rokicki, with modifications by Tim Morgan.
 */

#include <stdio.h>
#include "w2cy.h"
#include "w2c.h"

#define max_line_length (78)
#define ex_32 (2)
#define ex_real (3)
#define max(a,b) ((a>b)?a:b)

int last_tok;
int tex = 0, strict_for = 0, mf = 0;

char safe_string[80],  vvoid [] = "void\0";

char field_list[200];
char last_id[80];
char next_temp[] = "zzzaa";
/* Default names */
char coerce_name[100] = "coerce.h";
char comm_file[100] = "w2c.defines" ;


long last_i_num;
int ii, l_s;
long lower_bound, upper_bound;
extern FILE *fopen();

int pf_count = 1;
#ifdef  ANSI
boolean ansi=TRUE;
#else
boolean ansi=FALSE;
#endif
char *std_header="w2c-ext.h";       /* Default include filename */

extern int yyleng;
extern int yylineno;

#define id_char(x) \
    ((x >= 'a' && x <= 'z') || (x >= 'A' && x <= 'Z') || \
    (x >= '0' && x <= '9'))

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

/* Normally, output goes to std. These functions switch output 
on or off, or divert it.*/

int indent = 0;
int line_pos=0, str_pos=0;
int last_brace = 0;
int block_level = 0;
boolean want_space = FALSE, str_space = FALSE ;
char *sink;
FILE *std;
FILE *coerce;

void normal()
{
  std = stdout;
  sink = 0;
}

void silent()
{
  std = 0 ;
  sink = 0;
}

/*Divert output to string s */
void to_string(s) char *s;
{
  std = 0 ;
  sink = s;
  *s = '\0' ;
  str_space = FALSE ;
  str_pos = 0 ;
}

void new_line()
{if (std && (line_pos > 0)) 
   {
     if ( fputc('\n', std) == EOF) perror( "fputc");
     line_pos = 0;
     want_space = FALSE ;
   }
else if (sink && (str_pos > 0)) 
  {
    (void) strcat( sink, "\n") ;
    str_pos = 0;
    str_space = FALSE ;
  }
 }

void my_output(s) char *s;
{
  int len = strlen(s);
  int less_indent = 0;
  char c = s[len-1], *n ;
  if (len==0) return ;
  last_brace = ( c == '}');
  if (std) {
    if ((line_pos + len > max_line_length) || 
	( *s == '#' && line_pos > 0))
      new_line();
    if (indent > 1 && (strcmp(s, "case") == 0 || strcmp(s, "default") == 0))
      less_indent = 2;
    if( *s != '#')
      /*CPP commands must not be indented*/
      while (line_pos < indent*2 - less_indent) {
	if ( fputs("  ", std) == EOF) perror( "fputs");
	line_pos += 2;
      }
    if (want_space && id_char (*s))
      { line_pos += 1;
	if ( fputc(' ', std) == EOF) perror( "fputc");
      }
    if ( fputs(s, std) == EOF) perror( "fputs");
    want_space = id_char(c) ;
    n = rindex( s, '\n') ;
    if (n) line_pos = strlen(n) -1 ;
    else line_pos += len ;
  }
  else if (sink) {
    if ((str_pos + len > max_line_length) || ( *s == '#'))
      new_line();
    /*     if (indent > 1 && (strcmp(s, "case") == 0 || strcmp(s, "default") == 0))
	   less_indent = 2;
	   if( *s != '#') */
    /*CPP commands must not be indented*/
    /*      while (line_pos < indent*2 - less_indent) {
	    (void) strcat ( sink, " ") ;
	    line_pos += 2;
	    } */
    if (str_space && id_char (*s))
      { str_pos += 1;
	(void) strcat ( sink, " ") ;
      }
    (void) strcat(sink , s);
    str_space = id_char(c) ;
    n = rindex( s, '\n') ;
    if (n) str_pos = strlen(n) -1 ;
    else str_pos += len ;
  }
}

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

void yyerror(s) char *s;
{
    (void) fflush(stdout);
    (void) fprintf(stderr, " %s\n", s);
    (void) fprintf(stderr, "Last token - %d\n", last_tok);
    (void) fprintf(stderr, "Error buffer = %s\n", yytext);
    (void) fprintf(stderr, "Last id - %s   Line number %d\n", last_id, yylineno);
    ii = search_table(last_id);
    if (ii == -1)
        (void) fprintf(stderr, "Not in symbol table!\n");
    else
        switch (sym_table[ii].typ) {
        case undef_id_tok:
            (void) fprintf(stderr, "Undefined\n");
            break;
        case var_id_tok:
            (void) fprintf(stderr, "Variable\n");
            break;
        case const_id_tok:
            (void) fprintf(stderr, "Constant\n");
            break;
        case type_id_tok:
            (void) fprintf(stderr, "Type\n");
            break;
        case proc_id_tok:
            (void) fprintf(stderr, "Procedure\n");
            break;
        case proc_forwd_tok:
            (void) fprintf(stderr, "Procedure forward\n");
            break; 
        case fun_id_tok:
            (void) fprintf(stderr, "Function\n");
            break;
        case file_id_tok:
            (void) fprintf(stderr, "File id\n");
            break;
      case fun_forwd_tok:
            (void) fprintf(stderr, "Function forward\n");
            break; 
        default:
            (void) fprintf(stderr, "Unknown!\n");
            break;
        }
    (void) fprintf(stderr, "Next sym free    =  %d\n", last_sym_used);
/*    (void) fprintf(stderr, "Mark sym         =  %d\n", mark_sym_used);*/
    (void) fprintf(stderr, "Next string free =  %d\n", next_string_free);
/*    (void) fprintf(stderr, "Mark string      =  %d\n", mark_string_free);*/
}

/*Other error messages*/
void err_mes(s) char *s ;
{
  (void) fprintf(stderr, "error: %s    line number  %d\n", s, yylineno); 
}

/*
Managing the table of identifiers. The ids themselves are stored in strings.
They are found via the hash table hash_list. If ss is an id, then
hash_list[hash(ss)] points to the beginning of a list of ids in sym_table. 
This list contains all ids with the same hash, linked via their next fields. 
last_sym_used (previously mis-named next_sym_free) addresses the latest used
place in sym_table. 
*/

#define max_strings (20000)
#define hash_prime (101)
#define sym_table_size (3000)
#define max_stack (100)

char strings[max_strings];
int hash_list[hash_prime];
short sym_stack[max_stack], string_stack[max_stack];
int depth = 0 ;
#ifdef  MS_DOS
struct sym_entry huge sym_table[sym_table_size];
#else
struct sym_entry sym_table[sym_table_size];
#endif

int last_sym_used = 0, next_string_free = 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);
}

/*Find the hash code and run along its linked list*/
int search_table(id) char *id;
{
  int ptr;
  ptr = hash_list[hash(id)];
  while (ptr) {
    if (strcmp(id, sym_table[ptr].id) == 0)
      return (ptr);
    else
      ptr = sym_table[ptr].next;
  }
  return (0);
}

#define unused (271828)

struct sym_entry dummy =
  { 0, 0, 0, unused, 0, 0, 0, 0, 0, 0, FALSE, FALSE} ;

/*Insert the new id at the front of its hash code's linked list. This hides any
previous use of the id.*/
int add_to_table(id) char *id;
{
  int h, ptr;
  h = hash(id);
  ptr = hash_list[h];
  hash_list[h] = (++last_sym_used);
  sym_table[last_sym_used] = dummy ;
  sym_table[last_sym_used].next = ptr;
  sym_table[last_sym_used].val = unused;
  sym_table[last_sym_used].id = strings + next_string_free;
  (void) strcpy(strings + next_string_free, id);
  next_string_free += strlen(id) + 1;
  return (last_sym_used);
}

/*Mark all current ids as belonging to the previous stack frame*/
void mark()
{
    sym_stack[depth-1]  = last_sym_used;
    string_stack[depth-1] = next_string_free;
}

/*Push a stack frame*/
void push_stack()
{
  if (depth >= max_stack) {
    err_mes ("Fatal: stack overflow");
    exit(1);
  }
  sym_stack[depth]  = last_sym_used;
  string_stack[depth] = next_string_free;
  depth ++ ;
}

/*Hide all ids from p to q inclusive. */
void hide_ids( p, q)
int p, q;
{
  int h, ptr, n;
  for (h = 0; h < hash_prime; h++) {
    ptr = hash_list[h];
    while (ptr >= p && ptr <= q)
      ptr = sym_table[ptr].next;
    hash_list[h] = ptr;
    while (ptr)
      {
	n=ptr;
	ptr = sym_table[ptr].next;
	while (ptr >= p && ptr <= q)
	  ptr = sym_table[ptr].next;
	sym_table[n].next = ptr ;
      }
  }
}

/*Pop a stack frame*/
void pop_stack()
{
  if (depth == 0) {
    err_mes ("Fatal: POP at level 0");
    exit(1);
  }
  depth--;
  hide_ids( sym_stack[depth] +1, last_sym_used ) ;
  last_sym_used = sym_stack[depth] ;
  next_string_free = string_stack[depth] ;
}

/*Un-hide ids from p to q. Each id goes at the head of its hash code's list. 
*/
void unhide_ids( p, q)
int p, q;
{
  int h, n, m; char *i ;
  for (n = p; n <=q ; n++) {
    i = sym_table[n].id ;
    h = add_to_table(i);
    m = sym_table[h].next ;
    sym_table[h] = sym_table[n] ;
    sym_table[h].next = m ;
  }
}

/*Mark a proc. as 'forward'. fun points to the proc's name; the formal params 
go from fun+1 to last_sym_used. They must be hidden for later re-use. */
void forward( fun)
int fun ;
{
  sym_table[fun].var_formal = TRUE ; 
  hide_ids( sym_table[fun].val_sym , sym_table[fun].upper_sym);
  if (sym_table[fun].typ == fun_id_tok)
    sym_table[fun].typ = fun_forwd_tok ;
  else if (sym_table[fun].typ == proc_id_tok)
    sym_table[fun].typ = proc_forwd_tok ;
  else err_mes( "FORWARD on non-proc") ;
  mark();
}

/*Do this when a forward proc is being defined*/
void un_forward( fun)
int fun ;
{
  unhide_ids(
	     sym_table[fun].val_sym,
	     sym_table[fun].upper_sym) ;
  if (sym_table[fun].typ == fun_forwd_tok)
    sym_table[fun].typ = fun_id_tok ;
  else if (sym_table[fun].typ == proc_forwd_tok)
    sym_table[fun].typ = proc_id_tok ;
  else err_mes( "FORWARD on non-proc") ;
}

/*Generate prototype and call macro for function fun. The call macro is needed
for some Pascal var parameters; but it is too complicated to decide exactly when
it is needed, so we simply use a macro whenever the fun. has parameters.*/
void do_prototype( fun, extrn)
int fun ;
boolean extrn;
{
  int i, m, n; char *result, *name, z_id[100];
  struct sym_entry cur_sym;
  cur_sym = sym_table[fun] ;
  m = cur_sym.val_sym ;
  n = cur_sym.upper_sym ; 
  if (cur_sym.arg_typ) 
    result = cur_sym.arg_typ ;
  else result = vvoid ;
  name = cur_sym.id ;
  if (n==0)			/*no parameters*/
    strcpy( z_id, name) ;
  else if ( sprintf( z_id, "z%s", name) == NULL) perror( "sprintf");

  /* ANSI style prototype */
  if ( fprintf( coerce, "\n#ifdef ANSI \n" ) == EOF) perror( "fprintf");
  if ( fprintf( coerce, "%s%s %s(", 
	       (extrn)?"extern ":"" , result, z_id) == EOF) perror( "fprintf") ;
  if (n == 0) { if ( fprintf(coerce, "void") == EOF) perror( "fprintf"); }
  else {
    for (i=m; i<=n; i++) {
      if ( fprintf( coerce, " %s%s",
		   sym_table[i].arg_typ ,
		   (sym_table[i].need_var)?"*":"")
	  == EOF) perror( "fprintf");
      if (i<n) if ( fputc(',', coerce) == EOF) perror( "fputc") ;
    }}
  if ( fprintf(coerce, ");\n#else /* not ANSI*/ \n") == EOF) perror( "fprintf"); 

  /* Non-ANSI style prototype */
  if ( fprintf( coerce, "%s%s %s();\n", 
	       (extrn)?"extern ":"" , result, z_id) == EOF) perror( "fprintf") ;
  if ( fprintf( coerce, "#endif	/* ANSI*/ \n") == EOF) perror( "fprintf"); 

  /* This part defines the call macro*/
  if (n!=0) {
    if ( fprintf(coerce, "#define %s(", name) == EOF) perror( "fprintf");
    for (i=m; i<=n; i++) {
      if ( fprintf(coerce, " %s%s", 
		   sym_table[i].id, 
		   (i<n)?",":"" ) == EOF) perror( "fprintf");
    }
    if ( fprintf(coerce, ") %s( ", z_id) == EOF) perror( "fprintf");
    for (i=m; i<=n; i++) {
      if (i > m)
	if ( fprintf(coerce, ", ") == EOF) perror( "fprintf") ;
      cur_sym =sym_table[i] ;
      if ((cur_sym.top_type==is_int) 
	  || (cur_sym.top_type==is_point)
	  || (cur_sym.top_type==is_real)
	  ||  cur_sym.need_var ) {
	if ( fprintf(coerce, "(%s%s) %s(%s)",
		     cur_sym.arg_typ,
		     cur_sym.need_var?"*":"",
		     cur_sym.need_var?"&":"",
		     cur_sym.id ) == EOF) perror( "fprintf") ;}
      else
	if ( fprintf(coerce, "%s",
		     cur_sym.id ) == EOF) perror( "fprintf") ;
    }
    if ( fprintf(coerce, ")\n") == EOF) perror( "fprintf");
  }
  if ( fflush(coerce) == EOF) perror( "fflush");
}

/*Write the function header for fun*/
void gen_function_head(fun) 
int fun ;
{ int i, m, n; char *result, *name, z_id[100], out_string[300];
  struct sym_entry cur_sym;
  cur_sym = sym_table[fun] ;
  if ( fflush(std) == EOF) perror( "fflush");
  m = cur_sym.val_sym ;
  n = cur_sym.upper_sym ; 
  if (cur_sym.arg_typ) 
    result = cur_sym.arg_typ ;
  else result = vvoid ;
  name = cur_sym.id ;
  if (n==0)			/*no parameters*/
    strcpy( z_id, name) ;
  else if ( sprintf( z_id, "z%s", name) == NULL) perror( "sprintf");

  /* This part defines the memory word stuff.*/
  if (tex) {
    if (uses_mem && uses_eqtb){
      if ( fprintf(coerce,
		   "#define %s_regmem register memoryword *mem=zmem, *eqtb=zeqtb;\n",
		   my_routine) == EOF) perror( "fprintf");}
    else if (uses_mem){
      if ( fprintf(coerce,
		   "#define %s_regmem register memoryword *mem=zmem;\n",
		   my_routine) == EOF) perror( "fprintf");}
    else if (uses_eqtb){
      if ( fprintf(coerce,
		   "#define %s_regmem register memoryword *eqtb=zeqtb;\n",
		   my_routine) == EOF) perror( "fprintf");}
    else{
      if ( fprintf(coerce,
		   "#define %s_regmem\n",
		   my_routine) == EOF) perror( "fprintf");}
  }

  /* This part defines the actual header -- ANSI--*/
  new_line(); my_output("\n") ;
  my_output( "#ifdef ANSI \n");
  if ( sprintf( out_string, "%s %s(", 
	       result, z_id) == NULL) perror( "sprintf") ;
  my_output(out_string);
  if (n) {
    for (i=m; i<=n; i++) {
      if ( sprintf( out_string, " %s %s%s%s",
		   sym_table[i].arg_typ,
		   (sym_table[i].need_var)?"*":"" ,
		   sym_table[i].id,
		   (i<n)?",":"" ) == NULL) perror( "sprintf") ;
      my_output(out_string);
    }}
  else my_output( "void") ;
  my_output( ")\n") ;

  /* Not ANSI*/
  my_output( "#else		/* not ANSI*/ \n"); 
  if ( sprintf( out_string, "%s %s(", 
	       result, z_id) == NULL) perror( "sprintf") ;
  my_output(out_string);
  if (n) {
    for (i=m; i<=n; i++) {
      if ( sprintf( out_string, " %s%s",
		   sym_table[i].id,
		   (i<n)?",":"" ) == NULL) perror( "sprintf") ;
      my_output(out_string);
    }}
  my_output( ")\n") ;
  if (n) {
    for (i=m; i<=n; i++) {
      if ( sprintf( out_string, " %s %s%s;\n",
		   sym_table[i].arg_typ,
		   (sym_table[i].need_var)?"*":"" ,
		   sym_table[i].id ) == NULL) perror( "sprintf") ;
      my_output(out_string);
    }}
  my_output( "#endif		/* ANSI*/ \n"); 
}

/* Deal with input. Idea is to read the common defs and then the Pascal 
file (which comes from stdin) This lets us give the line no. of errors
*/
extern FILE *yyin;

/* Called by LEX on end-of-file*/
int yywrap()
{
  if (yyin == stdin) 
    return (1) ;
  else {
    yyin = stdin ;
    yylineno = 1 ;
    return (0);
  }}

void initialize()
{
  register int i;
  for (i = 0; i < hash_prime; hash_list[i++] = 0);
  for ( i=0; i<=max_stack; sym_stack[i++] = 0) ;
  normal();
  if(( coerce = fopen(coerce_name, "w")) == NULL)
    perror( "Open coerce");
  if(( yyin = fopen(comm_file, "r")) == NULL)
    perror( "Open common");
}

#ifdef  ANSI
void
#endif
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 'a':
        ansi = TRUE;
        break;
    case 'c':
        { if ( sprintf(coerce_name, "%s.h", &argv[i][2]) == NULL) perror( "sprintf");}
        break;
    case 'd':
        strcpy(comm_file,  &argv[i][2]) ;
        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();
    (void) fprintf(stderr, "\n%d symbols.\n", last_sym_used);
    exit(error);
}
