/* Copyright (c) 1991 by the Vrije Universiteit, Amsterdam, the Netherlands.
 * For full copyright and restrictions on use see the file COPYING in the top
 * level of the LLgen tree.
 */

/*
 *  L L G E N
 *
 *  An Extended LL(1) Parser Generator
 *
 *  Author : Ceriel J.H. Jacobs
 */

/*
 * main.c
 * Contains main program, and some error message routines
 */

#include <stdlib.h>
#include <string.h>
# include "types.h"
# include "io.h"
# include "extern.h"
# include "sets.h"
# include "assert.h"

# ifndef NORCSID
static string rcsid6 = "$Id$";
# endif

/* In this file the following routines are defined: */
extern int	main();
STATIC		readgrammar();
STATIC		doparse();
extern		error();
extern		fatal();
extern		comfatal();
extern		copyfile();
extern void install();
extern char	*mktemp();
extern char	*sbrk();

main(argc,argv) register string	argv[]; {
	register string arg;
	string libpath();
	char	*beg_sbrk = 0;

	/* Initialize */

	assval = 0400;
	/* read options */

	while (argc >= 2 && (arg = argv[1], *arg == '-')) {
		while (*++arg) {
			switch(*arg) {
			  case 'j':
			  case 'J':
				jmptable_option = 1;
				if (*++arg)
					min_cases_for_jmptable = atoi(arg);
				break;
			  case 'w':
			  case 'W':
				wflag = 1;
				continue;
			  case 'v':
			  case 'V':
				verbose++;
				continue;
			  case 'l':
			  case 'L':
				low_percentage = atoi(++arg);
				break;
			  case 'h':
			  case 'H':
				high_percentage = atoi(++arg);
				break;
# ifndef NDEBUG
			  case 'd':
			  case 'D':
				debug++;
				continue;
			  case 'r':
			  case 'R':
				if (rec_file) {
					fprintf(stderr,"duplicate -r flag\n");
					exit(1);
				}
				rec_file = ++arg;
				break;
			  case 'i':
			  case 'I':
				if (incl_file) {
					fprintf(stderr,"duplicate -i flag\n");
					exit(1);
				}
				incl_file = ++arg;
				break;
#endif /* not NDEBUG */
			  case 'x':
			  case 'X':
				ntneeded = 1;
				ntprint = 1;
				continue;
			  case 'a':
			  case 'A':
				ansi_c = 1;
				continue;
#ifdef NON_CORRECTING
			  case 'n':
			  case 'N':
				non_corr = 1;
				continue;
			  case 's':
			  case 'S':
				subpars_sim = 1;
				continue;
#endif
			  case 'g':
			  case 'G':
				strip_grammar = 1;
				continue;
			  default:
				fprintf(stderr,"illegal option : %c\n",*arg);
				exit(1);
			}
			break;
		}
		argv++;
		argc--;
	}

	if (verbose) beg_sbrk = sbrk(0);

#ifdef NON_CORRECTING
	if ((subpars_sim) && (!non_corr)) {
	    fprintf(stderr,"option -s illegal without -n, turned off\n");
	    subpars_sim = 0;
	}
#endif

	/*
	 * Now check wether the sets should include nonterminals
	 */
	if (verbose == 2) ntneeded = 1;
	/*
	 * Initialise
	 */
# ifndef NDEBUG
	if (!rec_file) {
# endif
		rec_file = libpath("rec");
# ifndef NDEBUG
	}
	if (!incl_file) {
# endif
		incl_file = libpath("incl");
# ifndef NDEBUG
	}
# endif
#ifdef NON_CORRECTING
	if (non_corr) {
	    nc_incl_file = libpath("nc_incl");
	    nc_rec_file = libpath ("nc_rec");
	}
#endif
	mktemp(f_temp);
	mktemp(f_pars);
	if ((fact = fopen(f_temp,"w")) == NULL) {
		fputs("Cannot create temporary\n",stderr);
		exit(1);
	}
	name_init();
	readgrammar(argc,argv);
	sprintf(f_out, OUTFILE, prefix ? prefix : "LL");

	/* for the following two filenames only one L is used; historical
	   reasons ...
	*/
	sprintf(f_include, HFILE, prefix ? prefix : "L");
	sprintf(f_rec, RFILE, prefix ? prefix : "L");
#ifdef NON_CORRECTING
	if (non_corr)
	    sprintf(f_nc, NCFILE, prefix ? prefix : "L");
#endif
	setinit(ntneeded);
	maxnt = &nonterms[nnonterms];
	maxt = &tokens[ntokens];
	/*
	 * Now, the grammar is read. Do some computations
	 */
	co_reach();		/* Check for undefined and unreachable */
	if (nerrors) comfatal();
	do_compute();
	conflchecks();
	if (nerrors) comfatal();
	fclose(fact);
	if (argc-- == 1) {
		fputs("No code generation for input from standard input\n",
		      stderr);
	}
	else	gencode(argc);
	UNLINK(f_temp);
	UNLINK(f_pars);
	if (verbose) {
		fprintf(stderr, "number of nonterminals: %d\n", nnonterms);
		fprintf(stderr, "number of tokens: %d\n", ntokens);
		fprintf(stderr, "number of term structures: %d\n", nterms);
		fprintf(stderr, "number of alternation structures: %d\n", nalts);
		fprintf(stderr, "total memory used: %ld\n", (long)(sbrk(0) - beg_sbrk));
	}
	exit(0);
}

STATIC
readgrammar(argc,argv) char *argv[]; {
	/*
	 * Do just what the name suggests : read the grammar
	 */
	register p_file	p;
	p_mem		alloc();

	linecount = 0;
	f_input = "no filename";
	/*
	 * Build the file structure
	 */
	files = p = (p_file) alloc((unsigned) (argc+1) * sizeof(t_file));
	if (argc-- == 1) {
		finput = stdin;
		f_input = "standard input";
		doparse(p++);
	} else {
		while (argc--) {
			if ((finput = fopen(f_input=argv[1],"r")) == NULL) {
				fatal(0,e_noopen,f_input);
			}
			doparse(p++);
			argv++;
			fclose(finput);
		}
	}
	maxfiles = p;
	if (! lexical) lexical = "yylex";
	/*
	 * There must be a start symbol!
	 */
	if (! nerrors && start == 0) {
		fatal(linecount,"Missing %%start");
	}
	if (nerrors) comfatal();
}

STATIC
doparse(p) register p_file p; {

	linecount = 0;
	p->f_name = f_input;
	p->f_firsts = 0;
	pfile = p;
	torder = -1;
	norder = -1;
	LLparse();
	p->f_nonterminals = norder;
	p->f_terminals = torder;
}

/* VARARGS1 */
error(lineno,s,t,u) string	s,t,u; {
	/*
	 * Just an error message
	 */

	++nerrors;
	if (!lineno) lineno = 1;
	fprintf(stderr,"\"%s\", line %d: ",f_input, lineno);
	fprintf(stderr,s,t,u);
	fputs("\n",stderr);
}

/* VARARGS1 */
void
warning(lineno,s,t,u) string	s,t,u; {
	/*
	 * Just a warning
	 */

	if (wflag) return;
	if (!lineno) lineno = 1;
	fprintf(stderr,"\"%s\", line %d: (Warning) ",f_input, lineno);
	fprintf(stderr,s,t,u);
	fputs("\n",stderr);
}

/* VARARGS1 */
fatal(lineno,s,t,u) string	s,t,u; {
	/*
	 * Fatal error
	 */
	error(lineno,s,t,u);
	comfatal();
}

comfatal() {
	/*
	 * Some common code for exit on errors
	 */
	if (fact != NULL) {
		fclose(fact);
		UNLINK(f_temp);
	}
	if (fpars != NULL) fclose(fpars);
	UNLINK(f_pars);
	exit(1);
}

copyfile(file) string file; {
	/*
	 * Copies a file indicated by the parameter to filedescriptor fpars.
	 */
	register int	c;
	register FILE	*f;

	if ((f = fopen(file,"r")) == NULL) {
		fatal(0,"Cannot open library file %s, call an expert",file);
	}
	while ((c = getc(f)) != EOF) putc(c,fpars);
	fclose(f);
}

void
install(target, source) string target, source; {
	/*
	 * Copy the temporary file generated from source to target
	 * if allowed (which means that the target must be generated
	 * by LLgen from the source, or that the target is not present
	 */
	register int	c1, c2;
	register FILE	*f1, *f2;
	int		cnt;

	/*
	 * First open temporary, generated for source
	 */
	if ((f1 = fopen(f_pars,"r")) == NULL) {
		fatal(0,e_noopen,f_pars);
	}
	/*
	 * Now open target for reading
	 */
	if ((f2 = fopen(target,"r")) == NULL) {
		fclose(f1);
		RENAME(f_pars, target);
		return;
	}
	/*
	 * Compute length of LLgen identification string. The target must
	 * start with that!
	 */
	cnt = strlen(LLgenid) + strlen(source) - 2;
	/*
	 * Now compare the target with the temporary
	 */
	do {
		c1 = getc(f1);
		c2 = getc(f2);
		if (cnt >= 0) cnt--;
	} while (c1 == c2 && c1 != EOF);
	fclose(f1);
	fclose(f2);
	/*
	 * Here, if c1 != c2 the target must be recreated
	 */
	if (c1 != c2) {
		if (cnt >= 0) {
			fatal(0,"%s : not a file generated by LLgen",target);
		}
		RENAME(f_pars,target);
	}
}