/***********************************************************************
     "d2h.o": lotos Data language to C Header.
***********************************************************************/

/***********************************
  (C) Copyright 1992-1993; dit/upm
   Distributed under the conditions stated in the
   TOPO General Public License (see file LICENSE)
 ***********************************
 $Log: d2h.c,v $
 * Revision 2.9  1995/01/17  09:55:24  lotos
 * dynamic table sizing
 *
 * Revision 2.8  1994/03/09  15:55:33  lotos
 * include ctype.h
 *
 * Revision 2.7  1993/06/10  14:04:22  lotos
 * new annotation CALL
 *
 * Revision 2.6  1993/05/28  10:51:50  lotos
 * opcion -c tiene niveles
 *
 * Revision 2.5  1993/05/25  11:02:30  lotos
 * anotaciones ldcend y ldcinit pasan al principio
 *
 * Revision 2.4  1993/01/18  18:16:12  lotos
 * distribution issues
 *
 * Revision 2.3  1993/01/12  18:37:36  lotos
 * portability issues
 *
 * Revision 2.2  1992/11/17  18:35:08  lotos
 * separate internal (kdatum) and external (udatum)
 * new annotation ldc at the very and of the spec
 * new annotation ldcinit at the beginning
 *
 * Revision 2.1  1992/10/14  18:16:00  lotos
 * new philosophy to write ADTs,
 * every new expression is supposed to be pointed once,
 * in order to share a value, it has to be kd_copy'ed
 * functions assume their arguments are absolutely theirs
 *
 * Revision 1.6  1992/10/14  18:07:46  lotos
 * forget ophuscation
 *
 * Revision 1.5  1992/09/02  14:04:23  lotos
 * avoid casting on LHS (for portability)
 *
 * Revision 1.4  92/05/06  18:46:06  lotos
 * main() and exit(): fixed to shut lint up!
 *
 * Revision 1.3  92/02/29  13:26:59  lotos
 * flags for pretty printing: optional
 *
 * Revision 1.2  92/01/15  12:45:06  lotos
 * ready to distribute
 *
 * Revision 1.1  91/10/02  17:00:19  lotos
 * Initial revision
 *
 ***********************************/

#include "version.h"
#ifndef lint
static char rcsid[]= "$Id: d2h.c,v 2.9 1995/01/17 09:55:24 lotos Exp $";
#endif

#define d2h_IMP

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "d2h.hh"

/* KJT 11/03/98: added for NS/OS */

#ifdef __NeXT__
char *strdup(s) register char *s; {

  register char *ns;

  return(((ns=malloc(strlen(s)+1))==0)?0:strcpy(ns,s)); }
#endif

/* translation initialization */

PRIVATE sREC *
trn_init ()
{
  init_indt(prog_flag.indt_incr);
  indent();
  (void) printf("#%s %s\n", C_IFNDEF, HEAD_NAME);
  indent();
  (void) printf("#%s %s\n", C_DEFINE, HEAD_NAME);
  (void) printf("\n");
  indent();
  (void) printf("#%s <%s>\n", C_INCLUDE, KAOS_H);
  ign_empty_rec= TRUE;
  return get_rec();
}

/* translates ldc annotation (at the beginning) */

PRIVATE sREC *
trn_ldc_ant (rec)
  sREC *rec;
{
  abort_if(rec == NULL ||
	   nofld(rec) == 0)
  if (field(rec, 1, SYM) != BEGIN_ANNOTATION ||
      field(rec, 2, SYM) != LDC_ANNOTATION)
    return rec;
  abort_if(field(rec, 2, SYM) != LDC_ANNOTATION)
  (void) printf("\n");
  for (;;)
  { switch (rec= get_rec(), field(rec, 1, SYM))
    { case LINE_QUOTE:
	(void) printf("%s\n", field(rec, 2, STR) + 1);
	continue;
      case END_ANNOTATION:
	break;
      default:
	abort_if(TRUE)
	break;
    }
    break;
  }
  return get_rec();
}

/* skips ldcinit annotation */

PRIVATE sREC *
skp_ldi_ant (rec)
  sREC *rec;
{
  abort_if(rec == NULL ||
	   nofld(rec) == 0)
  if (field(rec, 1, SYM) != BEGIN_ANNOTATION ||
      field(rec, 2, SYM) != LDCINIT_ANNOTATION)
    return rec;
  while (rec= get_rec(),
	 field(rec, 1, SYM) != END_ANNOTATION)
    continue;
  return get_rec();
}

/* skips ldc annotation (at the end) */

PRIVATE sREC *
skp_lde_ant (rec)
  sREC *rec;
{
  abort_if(rec == NULL ||
	   nofld(rec) == 0)
  if (field(rec, 1, SYM) != BEGIN_ANNOTATION ||
      field(rec, 2, SYM) != LDCEND_ANNOTATION)
    return rec;
  while (rec= get_rec(),
	 field(rec, 1, SYM) != END_ANNOTATION)
    continue;
  return get_rec();
}

PRIVATE int max_srt= -1;
PRIVATE sREC **srt_dcl= NULL;

/* translates sort declarations */

PRIVATE sREC *
trn_srt_dcls (rec)
  sREC *rec;
{
  int srt_uid;
  int i;

  abort_if(rec == NULL ||
	   nofld(rec) == 0 ||
	   field(rec, 1, SYM) != BEGIN_SORTS_DECL)
  abort_if(max_srt != -1)
  max_srt= field(rec, 2, INT);
  abort_if(srt_dcl != NULL)
  talloc(srt_dcl, max_srt + 1);
  for (i= 0; i <= max_srt; ++i)
    srt_dcl[i]= NULL;
  for (;;)
  { switch (rec= get_rec(), field(rec, 1, SYM))
    { case COMMENT:
	continue;
      case SORT_DECL:
	srt_uid= -field(rec, 3, INT);
	abort_if(max_srt < srt_uid)
	srt_dcl[srt_uid]= dup_rec(rec);
	continue;
      case BEGIN_ANNOTATION:
      case LINE_QUOTE:
      case END_ANNOTATION:
	continue;
      case END_SORTS_DECL:
	break;
      default:
	abort_if(TRUE)
	break;
    }
    break;
  }
  return get_rec();
}

/* translates operation declarations */

PRIVATE sREC *
trn_opn_dcls (rec)
  sREC *rec;
{
  static int max_opn= -1;
  static char **cmt_opn_dcl= NULL;
  static sREC **opn_dcl= NULL;
  static char **call_opn_dcl= NULL;
  int opn_uid;
  sREC *opn_info;
  char *opn_cmt= NULL;
  char *opn_call;
  int opn_1st= TRUE;
  int call_1st= TRUE;
  char *opn_name;
  int opn_sort;
  int opn_args;
  int max_opn_args= 0;
  int *opn_sarg= NULL;
  int ant_type;
  char *ant_line, *ant_link, *ant_text;
  int i, j;

  abort_if(rec == NULL ||
	   nofld(rec) == 0 ||
	   field(rec, 1, SYM) != BEGIN_OPERATIONS_DECL)
  abort_if(max_opn != -1)
  max_opn= field(rec, 2, INT);
  abort_if(cmt_opn_dcl != NULL)
  talloc(cmt_opn_dcl, max_opn + 1);
  for (j= 0; j <= max_opn; ++j)
    cmt_opn_dcl[j]= NULL;
  abort_if(opn_dcl != NULL)
  talloc(opn_dcl, max_opn + 1);
  for (j= 0; j <= max_opn; ++j)
    opn_dcl[j]= NULL;
  abort_if(call_opn_dcl != NULL)
  talloc(call_opn_dcl, max_opn + 1);
  for (j= 0; j <= max_opn; ++j)
    call_opn_dcl[j]= NULL;
  for (;;)
  { switch (rec= get_rec(), field(rec, 1, SYM))
    { case COMMENT:
	if (prog_flag.add_cmt > 0)
	  opn_cmt= strdup(field(rec, 2, STR) + 1);
	continue;
      case OPERATION_DECL:
	opn_uid= field(rec, 3, INT);
	abort_if(max_opn < opn_uid)
	opn_dcl[opn_uid]= dup_rec(rec);
	if (opn_cmt != NULL)
	{ cmt_opn_dcl[opn_uid]= opn_cmt;
	  opn_cmt= NULL;
	}
	continue;
      case BEGIN_ANNOTATION:
	ant_type= field(rec, 2, SYM);
	ant_text= NULL;
	switch (ant_type)
	{ case CALL_ANNOTATION:
	    ant_link= " \\\n";
	    break;
	  default:
	    ant_link= "\n";
	    break;
	}
	continue;
      case LINE_QUOTE:
	ant_line= field(rec, 2, STR) + 1;
	if (ant_text == NULL)
	  ant_text= strdup(ant_line);
	else
	{ trealloc(ant_text, strlen(ant_text) + strlen(ant_line) +
			     strlen(ant_link) + 1);
	  (void) strcat(ant_text, ant_link);
	  (void) strcat(ant_text, ant_line);
	}
	continue;
      case END_ANNOTATION:
	switch (ant_type)
	{ case CALL_ANNOTATION:
	    call_opn_dcl[opn_uid]= ant_text;
	    break;
	  default:
	    abort_if(TRUE)
	    break;
	}
	continue;
      case END_OPERATIONS_DECL:
	break;
      default:
	abort_if(TRUE)
	break;
    }
    break;
  }
  for (j= 0; j <= max_opn; ++j)
  { opn_info= opn_dcl[j];
    opn_cmt= cmt_opn_dcl[j];
    if (opn_info == NULL)
      continue;
    if (field(opn_info, 5, SYM) != DEFINED)
      continue;
    if (opn_1st)
    { (void) printf("\n");
      if (prog_flag.add_cmt > 1)
      { indent();
	(void) printf("/* %s %s */\n", CMT_BEGIN, CMT_OPN_DCLS);
      }
      opn_1st= FALSE;
    }
    else if (prog_flag.add_cmt > 0)
      (void) printf("\n");
    opn_name= sym_name(field(opn_info, 2, SYM));
    opn_sort= -field(opn_info, 6, INT);
    opn_args= field(opn_info, 7, INT);
    if (opn_args > max_opn_args)
    { max_opn_args= opn_args;
      if (opn_sarg == NULL)
	talloc(opn_sarg, max_opn_args);
      else
	trealloc(opn_sarg, max_opn_args);
    }
    for (i= 0; i < opn_args; ++i)
      opn_sarg[i]= -field(opn_info, 8 + i, INT);
    if (opn_cmt != NULL)
    { indent();
      (void) printf("/* %s */\n", opn_cmt);
    }
    indent();
    if (field(srt_dcl[opn_sort], 5, SYM) == DEFINED)
      (void) printf("%s", K_DATUM);
    else
      (void) printf("%s", U_DATUM);
    (void) printf(" %s(", opn_name);
    if (prog_flag.add_cmt > 0)
    { (void) printf("/* ");
      if (opn_args == 0)
	(void) printf("%s", C_VOID);
      else
	for (i= 1;; ++i)
	{ if (field(srt_dcl[opn_sarg[i - 1]], 5, SYM) == DEFINED)
	    (void) printf("%s", K_DATUM);
	  else
	    (void) printf("%s", U_DATUM);
	  (void) printf(" %s%d", ARG_ROOT, i);
	  if (i == opn_args)
	    break;
	  (void) printf(", ");
	}
      (void) printf(" */");
    }
    (void) printf(");\n");
  }
  if (!opn_1st)
  { if (prog_flag.add_cmt > 1)
    { indent();
      (void) printf("/* %s %s */\n", CMT_END, CMT_OPN_DCLS);
    }
    if (opn_sarg != NULL)
    { tfree(opn_sarg);
      max_opn_args= 0;
      opn_sarg= NULL;
    }
  }
  for (j= 0; j <= max_opn; ++j)
  { opn_info= opn_dcl[j];
    opn_cmt= prog_flag.add_cmt > 0? cmt_opn_dcl[j]: NULL;
    opn_call= call_opn_dcl[j];
    if (opn_info == NULL)
      continue;
    if (opn_call == NULL)
      continue;
    if (call_1st)
    { (void) printf("\n");
      if (prog_flag.add_cmt > 1)
      { indent();
	(void) printf("/* %s %s */\n", CMT_BEGIN, CMT_CALL_DFNS);
      }
      call_1st= FALSE;
    }
    else
      (void) printf("\n");
    if (opn_cmt != NULL)
    { indent();
      (void) printf("/* %s */\n", opn_cmt);
    }
    opn_name= sym_name(field(opn_info, 2, SYM));
    opn_sort= -field(opn_info, 6, INT);
    opn_args= field(opn_info, 7, INT);
    indent();
    (void) printf("#%s %s(", C_DEFINE, opn_name);
    if (opn_args > 0)
      for (i= 1;; ++i)
      { (void) printf("%s%d", ARG_ROOT, i);
	if (i == opn_args)
	  break;
	(void) printf(", ");
      }
    (void) printf(") (");
    {
      char *p, *q, *r;

      for (p= opn_call; (q= strchr(p, '$')) != NULL; p= q)
      { (void) printf("%.*s", q - p, p);
	if (*(q + 1) == '$')
	{ (void) printf("$");
	  q+= 2;
	}
	else
	{ if (q > opn_call && isascii(*(q - 1)) &&
	      (isalnum(*(q - 1)) || *(q - 1) == '_'))
	    (void) printf(" ");
	  (void) printf("%s", ARG_ROOT);
	  for (r= ++q; isascii(*r) && isdigit(*r); ++r)
	    ;
	  abort_if(r == q)
	  (void) printf("%.*s", r - q, q);
	  if (*r != '\0' && isascii(*r) &&
	      (isalnum(*r) || *r == '_'))
	    (void) printf(" ");
	  q= r;
	}
      }
      (void) printf("%s", p);
    }
    (void) printf(")\n");
  }
  if (!call_1st)
    if (prog_flag.add_cmt > 1)
    { indent();
      (void) printf("/* %s %s */\n", CMT_END, CMT_CALL_DFNS);
    }
  return get_rec();
}

/* translation ending */

PRIVATE sREC *
trn_end (rec)
  sREC *rec;
{
  (void) printf("\n");
  indent();
  (void) printf("#%s\n", C_ENDIF);
  return rec;
}

/* writes program usage */

PRIVATE void
usage ()
{
  (void) fprintf(stderr, "usage: %s", prog_name);
  (void) fprintf(stderr, " [ -h ]");
  (void) fprintf(stderr, " [ -c[0-2] ]");
  (void) fprintf(stderr, " [ -i[num] ]");
  (void) fprintf(stderr, "\n");
}

/* writes program help */

PRIVATE void
help ()
{
  usage();
  (void) fprintf(stderr, "       -h        this help message\n");
  (void) fprintf(stderr, "       -c[0-2]   comment insertion level ");
  (void) fprintf(stderr, "(default 1)\n");
  (void) fprintf(stderr, "       -i[num]   indentation level size ");
  (void) fprintf(stderr, "(range 0-%u, default %u)\n",
		 MAX_INDT_INCR, DFL_INDT_INCR);
}

/* processes command line arguments */

PRIVATE void
proc_arg (argc, argv)
  int argc;
  char *argv[];
{
  int any_error= FALSE;
  int i, j;

  prog_name= strrchr(argv[0], '/');
  if (prog_name == NULL)
    prog_name= argv[0];
  else
    ++prog_name;
  for (i= 1; !any_error && i < argc; ++i)
    if (argv[i][j= 0] == '-')
      switch (argv[i][++j])
      { case 'h':
	  prog_flag.put_help= TRUE;
	  if (argv[i][++j] != '\0')
	    any_error= TRUE;
	  break;
	case 'c':
	{ int argcnt;
	  unsigned value;
	  char end;

	  argcnt= sscanf(&argv[i][++j], "%u %c", &value, &end);
	  if (argcnt == EOF)
	    prog_flag.add_cmt= 1;
	  else if (argcnt == 1 && value <= 2)
	    prog_flag.add_cmt= value;
	  else
	    any_error= TRUE;
	  break;
	}
	case 'i':
	{ int argcnt;
	  unsigned value;
	  char end;

	  argcnt= sscanf(&argv[i][++j], "%u %c", &value, &end);
	  if (argcnt == EOF)
	    prog_flag.indt_incr= DFL_INDT_INCR;
	  else if (argcnt == 1 && value <= MAX_INDT_INCR)
	    prog_flag.indt_incr= value;
	  else
	    any_error= TRUE;
	  break;
	}
	default:
	  any_error= TRUE;
	  break;
      }
    else
      any_error= TRUE;
  if (any_error)
  { usage();
    exit(2);
  }
  if (prog_flag.put_help)
  { help();
    exit(0);
  }
}

PUBLIC int
main (argc, argv)
  int argc;
  char *argv[];
{
  sREC *rec;

  proc_arg(argc, argv);
  rec= trn_init();
  rec= trn_ldc_ant(rec);
  rec= skp_ldi_ant(rec);
  rec= skp_lde_ant(rec);
  rec= trn_srt_dcls(rec);
  rec= trn_opn_dcls(rec);
  rec= trn_end(rec);
  exit(0);
  return 0;
}
