/* 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 #include # 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: */ STATIC void readgrammar(int, char *[]); STATIC void doparse(register p_file); STATIC void comfatal(void); extern void UNLINK(string); extern void RENAME(string, string); extern void TMPNAM(string); extern string libpath(string); extern void conflchecks(void); extern void do_compute(void); extern void gencode(int); int main(int argc, register string argv[]) { register string arg; TMPNAM(f_temp); TMPNAM(f_pars); /* 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; #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--; } #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 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); } exit(EXIT_SUCCESS); } STATIC void readgrammar(int argc, 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", NULL); } if (nerrors) comfatal(); } STATIC void doparse(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; } void error(int lineno, string s, string t) { /* * Just an error message */ ++nerrors; if (!lineno) lineno = 1; fprintf(stderr, "\"%s\", line %d: ", f_input, lineno); fprintf(stderr, s, t); fputs("\n", stderr); } void warning(int lineno, string s, string t) { /* * Just a warning */ if (wflag) return; if (!lineno) lineno = 1; fprintf(stderr, "\"%s\", line %d: (Warning) ", f_input, lineno); fprintf(stderr, s, t); fputs("\n", stderr); } void fatal(int lineno, string s, string t) { /* * Fatal error */ error(lineno, s, t); comfatal(); } STATIC void comfatal(void) { /* * Some common code for exit on errors */ if (fact != NULL) { fclose(fact); UNLINK(f_temp); } if (fpars != NULL) fclose(fpars); UNLINK(f_pars); exit(EXIT_FAILURE); } void copyfile(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 copyto(string target, string source) { FILE *fsource; FILE *ftarget; int c; ftarget = fopen(target,"wb+"); if (ftarget == NULL) { fatal(0, "Cannot open file %s, call an expert", target); } fsource = fopen(source,"rb"); if (fsource == NULL) { fatal(0, "Cannot open file %s, call an expert", source); } while ((c = getc(fsource)) != EOF) putc(c, ftarget); fclose(fsource); fclose(ftarget); } void install(string target, string 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); copyto(target, f_pars); 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); } copyto(target, f_pars); } }