// Copyright (c) 1993-1999 Microsoft Corporation

#include <stdlib.h>
#include "y2.h"
#include <string.h>
#include <ctype.h>
/*
 * YSETUP.C  -- Modified for use with DECUS LEX
 *              Variable "yylval" resides in yylex(), not in yypars();
 *              Therefore, is defined "extern" here.
 *
 *              Also, the command line processing for the Decus version
 *              has been changed.  A new switch has been added to allow
 *              specification of the "table" file name(s), and unused
 *              switch processing removed.
 *
 *                               NOTE
 *              This probably won't run on UNIX any more.
 *
 * Bob Denny 27-Aug-81
 * Bob Denny 22-Mar-82 (01) Added header line, changes for 'new' DECUS library
 * Bob Denny 12-Apr-83 (02) Make filename[] size per #define'd FNAMESIZE so
 *                          VAX filenames won't blow out.  Conditionalize
 *                          time handling for banner.  Make filespec buffer
 *                          static for safety, since global "infile" is
 *                          pointed to it.
 * Scott Guthery 15-May-83  (03) Fixed up option flag handling for RT-11
 *				 23-Dec-83  Adapted for IBM PC/XT & DeSmet C compiler
 */

static char filename[FNAMESIZE];

int i;
SSIZE_T lev, t, j, ty;
int c;
SSIZE_T tempty;
SSIZE_T *p;
int defsw, infsw, ssw = 0;
char actname[8];
char *cp;
char *pszPrefix = NULL;

void
setup(argc,argv)
int argc;
char *argv[];
   {
   char finsave[FNAMESIZE];

   defsw = infsw = 0;
   foutput = NULL;
   fdefine = NULL;

   argc--;
   argv++;
   while( argc && **argv == '-' )
      {
      while( *++(*argv) )
         {
         switch( toupper(**argv) )
            {
         case 'I':
            infsw++;
            continue;
         case 'H':
            defsw++;
            continue;

		 case 'S':
			ssw++;
			infsw++;
			continue;

         case 'T':
            if (!--argc) {
                fprintf(stderr, "-t requires an argument\n");
                usage();
            } else {
                argv++;
                if (pszPrefix) {
                    free(pszPrefix);
                }
		pszPrefix = MIDL_STRDUP(*argv);
                goto next_arg;  // I hate myself
            }
            break;

         default:
            fprintf(stderr, "Illegal option: %c\n", *argv[i]);
            usage();
            }
         }
next_arg:
      argc--;
      argv++;
      }

   if(!argc) {
      fprintf(stderr, "No input file specified\n");
      usage();               /* Catch no filename given */
   }

/*
 * Now open the input file with a default extension of ".Y",
 * then replace the period in argv[1] with a null, so argv[1]
 * can be used to form the table, defs and info filenames.
 */

   if (!(cp = strrchr(argv[i], '\\')) && !(cp = strrchr(argv[i], ':'))) {
       cp = argv[i];
   }

   cp = strrchr(cp, '.');

   if(!cp) {
      strcpy(filename, argv[i]); strcat(filename, ".Y");
   } else {
      strcpy(filename, argv[i]);
      *cp = '\0';
   }

   strcpy(finsave, filename);
   if((finput=fopen( filename, "r" )) == NULL )
      error( "cannot open input file \"%s\"", filename );

/*
 * Now open the .H and .I files if requested.
 */

   if(defsw)
      {
      strcpy(filename, argv[i]); strcat(filename, ".H");
      fdefine = fopen(filename, "w");
      if(fdefine == NULL) error("cannot open defs file\"%s\"", filename);
      }

   if(infsw)
      {
      strcpy(filename, argv[i]); strcat(filename, ".I");
      foutput = fopen(filename, "w");
      if(foutput == NULL) error("cannot open info file\"%s\"", filename);
      }
/*
 * Now the "table" output C file.
 */
   strcpy(filename, argv[i]); strcat(filename, ".C");
   ftable = fopen(filename, "w");
   if( ftable == NULL ) error( "cannot open table file \"%s\"", filename);

/*
 * Finally, the temp files.
 */
   ftemp = fopen( TEMPNAME, "w" );
   if( ftemp==NULL ) error( "cannot open temp file" );
   faction = fopen( ACTNAME, "w" );
   if( faction==NULL ) error( "cannot open action file" );


/*
 * Now put the full filename of the input file into
 * the "filename" buffer for cpyact(), and point the
 * global cell "infile" at it.
 */
   strcpy(filename, finsave);
   infile = filename;
/*
 * Put out a header line at the beginning of the 'table' file.
 */
fprintf(ftable, "/*\n * Created by CSD YACC (IBM PC) from \"%s\" */\n",
        infile);
/*
 * Complete  initialization.
 */
   cnamp = cnames;
   defin(0,"$end");
   extval = 0400;
   defin(0,"error");
   defin(1,"$accept");
   mem=mem0;
   lev = 0;
   ty = 0;
   i=0;

   yyparse();
   }
void
yyparse( void )
   {
   /* sorry -- no yacc parser here.....
                we must bootstrap somehow... */

   for( t=gettok();  t!=MARK && t!= ENDFILE; )
      {
      switch( t )
         {

      case ';':
         t = gettok();
         break;

      case START:
         if( (t=gettok()) != IDENTIFIER )
            {
            error( "bad %%start construction" );
            }
         start = chfind(1,tokname);
         t = gettok();
         continue;

      case TYPEDEF:
         if( (t=gettok()) != TYPENAME ) error( "bad syntax in %%type" );
         ty = numbval;
         for(;;)

            {
            t = gettok();
            switch( t )
               {

            case IDENTIFIER:
               if( (t=chfind( 1, tokname ) ) < NTBASE )
                  {
                  j = TYPE( toklev[t] );
                  if( j!= 0 && j != ty )
                     {
                     error( "type redeclaration of token %s",
                     tokset[t].name );
                     }
                  else SETTYPE( toklev[t],ty);
                  }
               else
                  {
                  j = nontrst[t-NTBASE].tvalue;
                  if( j != 0 && j != ty )
                     {
                     error( "type redeclaration of nonterminal %s",
                     nontrst[t-NTBASE].name );
                     }
                  else nontrst[t-NTBASE].tvalue = ty;
                  }
            case ',':
               continue;

            case ';':
               t = gettok();
               break;
            default:
               break;
               }
            break;
            }
         continue;

      case UNION:
         /* copy the union declaration to the output */
         cpyunion();
         t = gettok();
         continue;

      case LEFT:
      case BINARY:
      case RIGHT:
         ++i;
      case TERM:
         lev = t-TERM;  /* nonzero means new prec. and assoc. */
         ty = 0;

         /* get identifiers so defined */

         t = gettok();
         if( t == TYPENAME )
            {
            /* there is a type defined */
            ty = numbval;
            t = gettok();
            }

         for(;;)
            {
            switch( t )
               {

            case ',':
               t = gettok();
               continue;

            case ';':
               break;

            case IDENTIFIER:
               j = chfind(0,tokname);
               if( lev )
                  {
                  if( ASSOC(toklev[j]) ) error( "redeclaration of precedence of%s", tokname );
                  SETASC(toklev[j],lev);
                  SETPLEV(toklev[j],i);
                  }
               if( ty )
                  {
                  if( TYPE(toklev[j]) ) error( "redeclaration of type of %s", tokname );
                  SETTYPE(toklev[j],ty);
                  }
               if( (t=gettok()) == NUMBER )
                  {
                  tokset[j].value = numbval;
                  if( j < ndefout && j>2 )
                     {
                     error( "please define type number of %s earlier",
                     tokset[j].name );
                     }
                  t=gettok();
                  }
               continue;

               }

            break;
            }

         continue;

      case LCURLY:
         defout();
         cpycode();
         t = gettok();
         continue;

      default:
	     printf("Unrecognized character: %o\n", t);
         error( "syntax error" );

         }

      }

   if( t == ENDFILE )
      {
      error( "unexpected EOF before %%" );
      }

   /* t is MARK */

   defout();

   fprintf( ftable,"#define yyclearin yychar = -1\n" );
   fprintf( ftable,"#define yyerrok yyerrflag = 0\n" );
/*
   fprintf( ftable,"extern int yychar;\nextern short yyerrflag;\n" );
*/
   fprintf( ftable,"#ifndef YYMAXDEPTH\n#define YYMAXDEPTH 150\n#endif\n" );
   if(!ntypes)
      fprintf( ftable,  "#ifndef YYSTYPE\n#define YYSTYPE int\n#endif\n" );
#ifdef unix
   fprintf( ftable,  "YYSTYPE yylval, yyval;\n" );
#else
   fprintf( ftable, "extern YYSTYPE yylval;  /*CSD & DECUS LEX */\n");
   fprintf( ftable, "YYSTYPE yyval;          /*CSD & DECUS LEX */\n");
#endif
   prdptr[0]=mem;
   /* added production */
   *mem++ = NTBASE;
   *mem++ = start;  /* if start is 0, we will overwrite with the lhs of the firstrule */
   *mem++ = 1;
   *mem++ = 0;
   prdptr[1]=mem;
   while( (t=gettok()) == LCURLY ) cpycode();
   if( t != C_IDENTIFIER ) error( "bad syntax on first rule" );
   if( !start ) prdptr[0][1] = chfind(1,tokname);

   /* read rules */

   while( t!=MARK && t!=ENDFILE )
      {

      /* process a rule */

      if( t == '|' )
         {
         *mem++ = *prdptr[nprod-1];
         }
      else if( t == C_IDENTIFIER )
         {
         *mem = chfind(1,tokname);
         if( *mem < NTBASE ) error( "token illegal on LHS of grammar rule" );
         ++mem;
         }
      else error( "illegal rule: missing semicolon or | ?" );

      /* read rule body */
      t = gettok();
more_rule:
      while( t == IDENTIFIER )
         {
         *mem = chfind(1,tokname);
         if( *mem<NTBASE ) levprd[nprod] = toklev[*mem];
         ++mem;
         t = gettok();
         }
      if( t == PREC )

         {
         if( gettok()!=IDENTIFIER) error( "illegal %%prec syntax" );
         j = chfind(2,tokname);
         if( j>=NTBASE)error("nonterminal %s illegal after %%prec", nontrst[j-NTBASE].name);
         levprd[nprod]=toklev[j];
         t = gettok();
         }
      if( t == '=' )
         {
         levprd[nprod] |= ACTFLAG;
         fprintf( faction, "\ncase %d:", nprod );
         cpyact( mem-prdptr[nprod]-1 );
         fprintf( faction, " break;" );
         if( (t=gettok()) == IDENTIFIER )
            {
            /* action within rule... */
            sprintf( actname, "$$%d", nprod );
            j = chfind(1,actname);  /* make it a nonterminal */
            /* the current rule will become rule number nprod+1 */
            /* move the contents down, and make room for the null */
            for( p=mem; p>=prdptr[nprod]; --p ) p[2] = *p;
            mem += 2;
            /* enter null production for action */
            p = prdptr[nprod];
            *p++ = j;
            *p++ = -nprod;

            /* update the production information */
            levprd[nprod+1] = levprd[nprod] & ~ACTFLAG;
            levprd[nprod] = ACTFLAG;

            if( ++nprod >= NPROD ) error( "more than %d rules", NPROD );
            prdptr[nprod] = p;

            /* make the action appear in the original rule */
            *mem++ = j;

            /* get some more of the rule */

            goto more_rule;
            }

         }

      while( t == ';' ) t = gettok();

      *mem++ = -nprod;

      /* check that default action is reasonable */

      if( ntypes && !(levprd[nprod]&ACTFLAG) && nontrst[*prdptr[nprod]-NTBASE].tvalue )
         {
         /* no explicit action, LHS has value */
         /*01*/
         tempty = prdptr[nprod][1];
         if( tempty < 0 ) error( "must return a value, since LHS has a type" );
         else if( tempty >= NTBASE ) tempty = nontrst[tempty-NTBASE].tvalue;
         else tempty = TYPE( toklev[tempty] );
         if( tempty != nontrst[*prdptr[nprod]-NTBASE].tvalue )
            {
            error( "default action causes potential type clash" );
            }

         }
      if( ++nprod >= NPROD ) error( "more than %d rules", NPROD );
      prdptr[nprod] = mem;
      levprd[nprod]=0;
      }
   /* end of all rules */
   fprintf(faction, "/* End of actions */"); /* Properly terminate the last line */
   finact();
   if( t == MARK )
      {
      writeline(ftable);
      while( (c=unix_getc(finput)) != EOF ) putc( c, ftable );
      }
   fclose( finput );
   }

void
usage( void )

   {
   fprintf(stderr,"UNIX YACC (CSD Variant):\n");
   fprintf(stderr,"   yacc -hist tag infile\n\n");
   fprintf(stderr,"Switches:\n");
   fprintf(stderr,"   -h     Create definitions header file\n");
   fprintf(stderr,"   -i     Create parser description file\n");
   fprintf(stderr,"   -t tag Prepends tag to tables\n");
   fprintf(stderr,"   -s     Generates extended tables (MIDL specific) \n\n");
   fprintf(stderr,"Default input file extension is \".Y\"\n");
   fprintf(stderr,"Defs file same name, \".H\" extension.\n");
   fprintf(stderr,"Info file same name, \".I\" extension.\n");
   fprintf(stderr,"Extended Tables in file \"extable.[h1/h2/h3]\".\n");
   fprintf(stderr,"Specifying -s switch also enables the -i switch\n");
   exit(EX_ERR);
   }