680 lines
		
	
	
	
		
			13 KiB
		
	
	
	
		
			Text
		
	
	
	
	
	
			
		
		
	
	
			680 lines
		
	
	
	
		
			13 KiB
		
	
	
	
		
			Text
		
	
	
	
	
	
/* Copyright (c) 1991 by the Vrije Universiteit, Amsterdam, the Netherlands.
 | 
						|
 * All rights reserved.
 | 
						|
 */
 | 
						|
 | 
						|
/*
 | 
						|
 *  L L G E N
 | 
						|
 *
 | 
						|
 *  An Extended LL(1) Parser Generator
 | 
						|
 *
 | 
						|
 *  Author : Ceriel J.H. Jacobs
 | 
						|
 */
 | 
						|
 | 
						|
/*
 | 
						|
 * LLgen.g
 | 
						|
 * Defines the grammar of LLgen.
 | 
						|
 * Some routines that build the internal structure are also included
 | 
						|
 */
 | 
						|
 | 
						|
{
 | 
						|
# include "types.h"
 | 
						|
# include "io.h"
 | 
						|
# include "extern.h"
 | 
						|
# include "assert.h"
 | 
						|
# include "cclass.h"
 | 
						|
 | 
						|
# ifndef NORCSID
 | 
						|
static string	rcsid = "$Header$";
 | 
						|
# endif
 | 
						|
p_mem		alloc(), ralloc();
 | 
						|
string		store();
 | 
						|
p_gram		search();
 | 
						|
long		ftell();
 | 
						|
 | 
						|
static int	nparams;		/* parameter count for nonterminals */
 | 
						|
static int	acount;			/* count #of global actions */
 | 
						|
static int	order;
 | 
						|
static p_term t_list;
 | 
						|
static int t_cnt;
 | 
						|
static p_gram	alt_table;
 | 
						|
static int	n_alts;
 | 
						|
static int	max_alts;
 | 
						|
#define ALTINCR 32
 | 
						|
 | 
						|
static p_gram	rule_table;
 | 
						|
static int	n_rules;
 | 
						|
static int	max_rules;
 | 
						|
#define RULEINCR	32
 | 
						|
 | 
						|
/* Here are defined : */
 | 
						|
STATIC 		newnorder();
 | 
						|
STATIC 		newtorder();
 | 
						|
STATIC		copyact();
 | 
						|
STATIC		mkalt();
 | 
						|
STATIC		mkterm();
 | 
						|
STATIC p_gram	copyrule();
 | 
						|
/* and of course LLparse() */
 | 
						|
 | 
						|
STATIC
 | 
						|
newnorder(index) {
 | 
						|
	static int porder;
 | 
						|
 | 
						|
	if (norder != -1) {
 | 
						|
		nonterms[porder].n_next = index;
 | 
						|
	}
 | 
						|
	else	norder = index;
 | 
						|
	porder = index;
 | 
						|
	nonterms[porder].n_next = -1;
 | 
						|
}
 | 
						|
 | 
						|
STATIC
 | 
						|
newtorder(index) {
 | 
						|
	static int porder;
 | 
						|
 | 
						|
	if (torder != -1) {
 | 
						|
		tokens[porder].t_next = index;
 | 
						|
	}
 | 
						|
	else	torder = index;
 | 
						|
	porder = index;
 | 
						|
	tokens[porder].t_next = -1;
 | 
						|
}
 | 
						|
 | 
						|
p_init()
 | 
						|
{
 | 
						|
	alt_table = (p_gram )alloc(ALTINCR*sizeof(t_gram));
 | 
						|
	n_alts = 0;
 | 
						|
	max_alts = ALTINCR;
 | 
						|
	rule_table = (p_gram )alloc(RULEINCR*sizeof(t_gram));
 | 
						|
	n_rules = 0;
 | 
						|
	max_rules = RULEINCR;
 | 
						|
}
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
%start	LLparse, spec;
 | 
						|
 | 
						|
spec	:		{	acount = 0; p_init(); }
 | 
						|
	  [ %persistent def ]*
 | 
						|
			{	/*
 | 
						|
				 * Put an endmarker in temporary file
 | 
						|
				 */
 | 
						|
				putc('\0', fact);
 | 
						|
				putc('\0', fact);
 | 
						|
				free((p_mem) rule_table);
 | 
						|
				free((p_mem) alt_table);
 | 
						|
			}
 | 
						|
	;
 | 
						|
 | 
						|
def			{	register string p; }
 | 
						|
	: rule
 | 
						|
	  /*
 | 
						|
	   * A grammar rule
 | 
						|
	   */
 | 
						|
	| C_TOKEN listel [ ',' listel ]* ';'
 | 
						|
	  /*
 | 
						|
	   * A token declaration
 | 
						|
	   */
 | 
						|
	| C_START C_IDENT
 | 
						|
			{	p = store(lextoken.t_string); }
 | 
						|
	  ',' C_IDENT ';'
 | 
						|
	  /*
 | 
						|
	   * A start symbol declaration
 | 
						|
	   */
 | 
						|
			{	/*
 | 
						|
				 * Put the declaration in the list
 | 
						|
				 * of start symbols
 | 
						|
				 */
 | 
						|
				register p_gram temp;
 | 
						|
				register p_start ff;
 | 
						|
 | 
						|
				temp = search(NONTERM,lextoken.t_string,BOTH);
 | 
						|
				ff = (p_start) alloc(sizeof(t_start));
 | 
						|
				ff->ff_nont = g_getcont(temp);
 | 
						|
				ff->ff_name = p;
 | 
						|
				ff->ff_next = start;
 | 
						|
				start = ff;
 | 
						|
				while (ff = ff->ff_next) {
 | 
						|
					if (! strcmp(p, ff->ff_name)) {
 | 
						|
						error(linecount, "\"%s\" already used in a %%start", p);
 | 
						|
						break;
 | 
						|
					}
 | 
						|
				}
 | 
						|
			}
 | 
						|
	| C_LEXICAL C_IDENT
 | 
						|
	  /*
 | 
						|
	   * Declaration of a name for the lexical analyser.
 | 
						|
	   * May appear only once
 | 
						|
	   */
 | 
						|
			{	if (!lexical) {
 | 
						|
					lexical = store(lextoken.t_string);
 | 
						|
				}
 | 
						|
				else	error(linecount,"Duplicate %%lexical");
 | 
						|
			}
 | 
						|
	  ';'
 | 
						|
	| C_PREFIX C_IDENT
 | 
						|
	  /*
 | 
						|
	   * Prefix of external names (default: LL)
 | 
						|
	   */
 | 
						|
			{	if (!prefix) {
 | 
						|
					prefix = store(lextoken.t_string);
 | 
						|
					if (strlen(prefix) > 6) {
 | 
						|
						error(linecount,
 | 
						|
							"%%prefix too long");
 | 
						|
						prefix[6] = 0;
 | 
						|
					}
 | 
						|
				}
 | 
						|
				else	error(linecount,"Duplicate %%prefix");
 | 
						|
			}
 | 
						|
	  ';'
 | 
						|
	| C_ONERROR C_IDENT
 | 
						|
			{	if (! onerror) {
 | 
						|
					onerror = store(lextoken.t_string);
 | 
						|
				}
 | 
						|
				else	error(linecount,"Duplicate %%onerror");
 | 
						|
			}
 | 
						|
	  ';'
 | 
						|
	| action(0)	{	acount++; }
 | 
						|
	  /*
 | 
						|
	   * A global C-declaration
 | 
						|
	   */
 | 
						|
	| firsts
 | 
						|
	  /*
 | 
						|
	   * declarations for macros
 | 
						|
	   */
 | 
						|
	;
 | 
						|
 | 
						|
listel	: C_IDENT	{	p_gram temp = search(TERMINAL,lextoken.t_string,ENTERING);
 | 
						|
				newtorder(g_getcont(temp));
 | 
						|
				tokens[g_getcont(temp)].t_lineno = linecount;
 | 
						|
			}
 | 
						|
	;
 | 
						|
 | 
						|
rule			{	register p_nont p;
 | 
						|
				p_gram rr;
 | 
						|
				register p_gram temp;
 | 
						|
			}
 | 
						|
	: /*
 | 
						|
	   * grammar for a production rule
 | 
						|
	   */
 | 
						|
	  C_IDENT	{	temp = search(NONTERM,lextoken.t_string,BOTH);
 | 
						|
				p = &nonterms[g_getcont(temp)];
 | 
						|
				if (p->n_rule) {
 | 
						|
					error(linecount,
 | 
						|
"Nonterminal %s already defined", lextoken.t_string);
 | 
						|
				}
 | 
						|
				/*
 | 
						|
				 * Remember the order in which the nonterminals
 | 
						|
				 * were defined. Code must be generated in that
 | 
						|
				 * order to keep track with the actions on the
 | 
						|
				 * temporary file
 | 
						|
				 */
 | 
						|
				newnorder(p - nonterms);
 | 
						|
				p->n_count = acount;
 | 
						|
				acount = 0;
 | 
						|
				p->n_lineno = linecount;
 | 
						|
				p->n_off = ftell(fact);
 | 
						|
			}
 | 
						|
	  [ params	{	if (nparams > 0) {
 | 
						|
					p->n_flags |= PARAMS;
 | 
						|
					if (nparams > 15) {
 | 
						|
						error(linecount,"Too many parameters");
 | 
						|
					}
 | 
						|
					else	setntparams(p,nparams);
 | 
						|
				}
 | 
						|
			}
 | 
						|
	  ]?
 | 
						|
	  [ action(0)	{	p->n_flags |= LOCALS; }
 | 
						|
	  ]?
 | 
						|
	  ':' productions(&rr) ';'
 | 
						|
			/*
 | 
						|
			 * Do not use p->n_rule now! The nonterms array
 | 
						|
			 * might have been re-allocated.
 | 
						|
			 */
 | 
						|
			{	nonterms[g_getcont(temp)].n_rule = rr;}
 | 
						|
	;
 | 
						|
 | 
						|
action(int n;)
 | 
						|
	/*
 | 
						|
	 * The parameter n is non-zero when the opening and closing
 | 
						|
	 * bracket must be copied along with the action
 | 
						|
	 */
 | 
						|
	: '{'		{	copyact('{','}',n,0); }
 | 
						|
	  '}'
 | 
						|
	;
 | 
						|
 | 
						|
productions(p_gram *p;)
 | 
						|
	/*
 | 
						|
	 * One or more alternatives
 | 
						|
	 */
 | 
						|
	{	p_gram		prod;
 | 
						|
		int		conflres = 0;
 | 
						|
		int		t = 0;
 | 
						|
		int		haddefault = 0;
 | 
						|
		int		altcnt = 0;
 | 
						|
		int		o_lc, n_lc;
 | 
						|
	} :
 | 
						|
			{	o_lc = linecount; }
 | 
						|
	  simpleproduction(p,&conflres)
 | 
						|
			{	if (conflres & DEF) haddefault = 1; }
 | 
						|
	  [
 | 
						|
	    [ '|'	{	n_lc = linecount; }
 | 
						|
	      simpleproduction(&prod,&t)
 | 
						|
			{	if (n_alts >= max_alts-2) {
 | 
						|
					alt_table = (p_gram ) ralloc(
 | 
						|
						(p_mem) alt_table,
 | 
						|
						(unsigned)(max_alts+=ALTINCR)*sizeof(t_gram));
 | 
						|
				}
 | 
						|
				if (t & DEF) {
 | 
						|
					if (haddefault) {
 | 
						|
						error(n_lc,
 | 
						|
		"More than one %%default in alternation");
 | 
						|
					}
 | 
						|
					haddefault = 1;
 | 
						|
				}
 | 
						|
				mkalt(*p,conflres,o_lc,&alt_table[n_alts++]);
 | 
						|
				altcnt++;
 | 
						|
				o_lc = n_lc;
 | 
						|
				conflres = t;
 | 
						|
				t = 0;
 | 
						|
				*p = prod;
 | 
						|
			}
 | 
						|
	    ]+		{	if (conflres & ~DEF) {
 | 
						|
					error(n_lc,
 | 
						|
		"Resolver on last alternative not allowed");
 | 
						|
				}
 | 
						|
				mkalt(*p,conflres,n_lc,&alt_table[n_alts++]);
 | 
						|
				altcnt++;
 | 
						|
				g_settype((&alt_table[n_alts]),EORULE);
 | 
						|
				*p = copyrule(&alt_table[n_alts-altcnt],altcnt+1);
 | 
						|
			}
 | 
						|
	  |
 | 
						|
			{	if (conflres & ~DEF) {
 | 
						|
					error(o_lc,
 | 
						|
		"No alternation conflict resolver allowed here");
 | 
						|
				}
 | 
						|
				/*
 | 
						|
				if (conflres & DEF) {
 | 
						|
					error(o_lc,
 | 
						|
		"No %%default allowed here");
 | 
						|
				}
 | 
						|
				*/
 | 
						|
			}
 | 
						|
	  ]
 | 
						|
			{	n_alts -= altcnt; }
 | 
						|
	;
 | 
						|
{
 | 
						|
 | 
						|
STATIC
 | 
						|
mkalt(prod,condition,lc,res) p_gram prod; register p_gram res; {
 | 
						|
	/*
 | 
						|
	 * Create an alternation and initialise it.
 | 
						|
	 */
 | 
						|
	register p_link		l;
 | 
						|
	static p_link list;
 | 
						|
	static int cnt;
 | 
						|
 | 
						|
	if (! cnt) {
 | 
						|
		cnt = 50;
 | 
						|
		list = (p_link) alloc(50 * sizeof(t_link));
 | 
						|
	}
 | 
						|
	cnt--;
 | 
						|
	l = list++;
 | 
						|
	l->l_rule = prod;
 | 
						|
	l->l_flag = condition;
 | 
						|
	g_setlink(res,l);
 | 
						|
	g_settype(res,ALTERNATION);
 | 
						|
	res->g_lineno = lc;
 | 
						|
	nalts++;
 | 
						|
}
 | 
						|
}
 | 
						|
 | 
						|
simpleproduction(p_gram *p; register int *conflres;)
 | 
						|
	{	t_gram		elem;
 | 
						|
		int		elmcnt = 0;
 | 
						|
		int		cnt, kind;
 | 
						|
		int		termdeleted = 0;
 | 
						|
	} :
 | 
						|
	  [ C_DEFAULT	{	*conflres = DEF; }
 | 
						|
	  ]?
 | 
						|
	  [
 | 
						|
	    /*
 | 
						|
	     * Optional conflict reslover
 | 
						|
	     */
 | 
						|
	      C_IF expr {	*conflres |= COND; }
 | 
						|
	    | C_PREFER	{	*conflres |= PREFERING; }
 | 
						|
	    | C_AVOID	{	*conflres |= AVOIDING; }
 | 
						|
	  ]?
 | 
						|
	  [ %persistent elem(&elem)
 | 
						|
			{	if (n_rules >= max_rules-2) {
 | 
						|
					rule_table = (p_gram) ralloc(
 | 
						|
						  (p_mem) rule_table,
 | 
						|
						  (unsigned)(max_rules+=RULEINCR)*sizeof(t_gram));
 | 
						|
				}
 | 
						|
				kind = FIXED;
 | 
						|
				cnt = 0;
 | 
						|
			}
 | 
						|
	    [ repeats(&kind, &cnt)
 | 
						|
			{	if (g_gettype(&elem) != TERM) {
 | 
						|
					rule_table[n_rules] = elem;
 | 
						|
					g_settype((&rule_table[n_rules+1]),EORULE);
 | 
						|
					mkterm(copyrule(&rule_table[n_rules],2),
 | 
						|
					       0,
 | 
						|
					       elem.g_lineno,
 | 
						|
					       &elem);
 | 
						|
				}
 | 
						|
			}
 | 
						|
	    |
 | 
						|
			{ if (g_gettype(&elem) == TERM) {
 | 
						|
				register p_term q = g_getterm(&elem);
 | 
						|
 | 
						|
				if (! (q->t_flags & RESOLVER) &&
 | 
						|
				    g_gettype(q->t_rule) != ALTERNATION &&
 | 
						|
				    g_gettype(q->t_rule) != EORULE) {
 | 
						|
				    while (g_gettype(q->t_rule) != EORULE) {
 | 
						|
					rule_table[n_rules++] = *q->t_rule++;
 | 
						|
					elmcnt++;
 | 
						|
					if (n_rules >= max_rules-2) {
 | 
						|
					    rule_table = (p_gram) ralloc(
 | 
						|
						  (p_mem) rule_table,
 | 
						|
						  (unsigned)(max_rules+=RULEINCR)*sizeof(t_gram));
 | 
						|
					}
 | 
						|
				    }
 | 
						|
				    elem = *--(q->t_rule);
 | 
						|
				    n_rules--;
 | 
						|
				    elmcnt--;
 | 
						|
				    if (q == t_list - 1) {
 | 
						|
					t_list--;
 | 
						|
					nterms--;
 | 
						|
					t_cnt++;
 | 
						|
				    }
 | 
						|
				    termdeleted = 1;
 | 
						|
				}
 | 
						|
			  }
 | 
						|
			}
 | 
						|
	    ]		{	if (!termdeleted && g_gettype(&elem) == TERM) {
 | 
						|
					register p_term q;
 | 
						|
 | 
						|
					q = g_getterm(&elem);
 | 
						|
					r_setkind(q,kind);
 | 
						|
					r_setnum(q,cnt);
 | 
						|
					if ((q->t_flags & RESOLVER) &&
 | 
						|
					    (kind == PLUS || kind == FIXED)) {
 | 
						|
						error(linecount,
 | 
						|
		"%%while not allowed in this term");
 | 
						|
					}
 | 
						|
					/*
 | 
						|
					 * A persistent fixed term is the same
 | 
						|
					 * as a non-persistent fixed term.
 | 
						|
					 * Should we complain?
 | 
						|
					if ((q->t_flags & PERSISTENT) &&
 | 
						|
					    kind == FIXED) {
 | 
						|
						error(linecount,
 | 
						|
							"Illegal %%persistent");
 | 
						|
					}
 | 
						|
					*/
 | 
						|
				}
 | 
						|
				termdeleted = 0;
 | 
						|
				elmcnt++;
 | 
						|
				rule_table[n_rules++] = elem;
 | 
						|
			}
 | 
						|
	  ]*		{	register p_term q;
 | 
						|
 | 
						|
				g_settype((&rule_table[n_rules]),EORULE);
 | 
						|
				*p = 0;
 | 
						|
				n_rules -= elmcnt;
 | 
						|
				if (g_gettype(&rule_table[n_rules]) == TERM &&
 | 
						|
				    elmcnt == 1) {
 | 
						|
					q = g_getterm(&rule_table[n_rules]);
 | 
						|
					if (r_getkind(q) == FIXED &&
 | 
						|
					    r_getnum(q) == 0) {
 | 
						|
						*p = q->t_rule;
 | 
						|
					}
 | 
						|
				}
 | 
						|
				if (!*p) *p = copyrule(&rule_table[n_rules],
 | 
						|
						elmcnt+1);
 | 
						|
			}
 | 
						|
	;
 | 
						|
{
 | 
						|
 | 
						|
STATIC
 | 
						|
mkterm(prod,flags,lc,result) p_gram prod; register p_gram result; {
 | 
						|
	/*
 | 
						|
	 * Create a term, initialise it and return
 | 
						|
	 * a grammar element containing it
 | 
						|
	 */
 | 
						|
	register p_term		q;
 | 
						|
 | 
						|
	if (! t_cnt) {
 | 
						|
		t_cnt = 50;
 | 
						|
		t_list = (p_term) alloc(50 * sizeof(t_term));
 | 
						|
	}
 | 
						|
	t_cnt--;
 | 
						|
	q = t_list++;
 | 
						|
	q->t_rule = prod;
 | 
						|
	q->t_contains = 0;
 | 
						|
	q->t_flags = flags;
 | 
						|
	g_settype(result,TERM);
 | 
						|
	g_setterm(result,q);
 | 
						|
	result->g_lineno = lc;
 | 
						|
	nterms++;
 | 
						|
}
 | 
						|
}
 | 
						|
 | 
						|
elem (register p_gram pres;)
 | 
						|
	{	register int	t = 0;
 | 
						|
		p_gram		p1;
 | 
						|
		int		ln;
 | 
						|
		p_gram		pe;
 | 
						|
	} :
 | 
						|
	  '['		{	ln = linecount; }
 | 
						|
	  [ C_WHILE expr	{	t |= RESOLVER; }
 | 
						|
	  ]?
 | 
						|
	  [ C_PERSISTENT	{	t |= PERSISTENT; }
 | 
						|
	  ]?
 | 
						|
	  productions(&p1)
 | 
						|
	  ']'		{
 | 
						|
				mkterm(p1,t,ln,pres);
 | 
						|
			}
 | 
						|
	|
 | 
						|
	  C_IDENT	{	pe = search(UNKNOWN,lextoken.t_string,BOTH);
 | 
						|
				*pres = *pe;
 | 
						|
			}
 | 
						|
	  [ params	{	if (nparams > 15) {
 | 
						|
					error(linecount,"Too many parameters");
 | 
						|
				} else	g_setnpar(pres,nparams);
 | 
						|
				if (g_gettype(pres) == TERMINAL) {
 | 
						|
					error(linecount,
 | 
						|
						"Terminal with parameters");
 | 
						|
				}
 | 
						|
			}
 | 
						|
	  ]?
 | 
						|
	| C_LITERAL	{	pe = search(LITERAL,lextoken.t_string,BOTH);
 | 
						|
				*pres = *pe;
 | 
						|
			}
 | 
						|
	|		{	g_settype(pres,ACTION);
 | 
						|
				pres->g_lineno = linecount;
 | 
						|
			}
 | 
						|
	  action(1)
 | 
						|
	;
 | 
						|
 | 
						|
params
 | 
						|
{
 | 
						|
	long off = ftell(fact);
 | 
						|
}
 | 
						|
	: '('		{	copyact('(',')',0,0); }
 | 
						|
	  ')'
 | 
						|
			{	if (nparams == 0) {
 | 
						|
					fseek(fact, off, 0);
 | 
						|
				}
 | 
						|
			}
 | 
						|
	;
 | 
						|
 | 
						|
expr	: '('		{	copyact('(',')',1,0); }
 | 
						|
	  ')'
 | 
						|
	;
 | 
						|
 | 
						|
repeats(int *kind; int *cnt;)	{	int t1 = 0; } :
 | 
						|
	  [
 | 
						|
	    '?'		{	*kind = OPT; }
 | 
						|
	  | [ '*'	{	*kind = STAR; }
 | 
						|
	    | '+'	{	*kind = PLUS; }
 | 
						|
	    ]
 | 
						|
	    number(&t1)?
 | 
						|
			{	if (t1 == 1) {
 | 
						|
					t1 = 0;
 | 
						|
					if (*kind == STAR) *kind = OPT;
 | 
						|
					if (*kind == PLUS) *kind = FIXED;
 | 
						|
				}
 | 
						|
			}
 | 
						|
	  | number(&t1)
 | 
						|
	  ]		{	*cnt = t1; }
 | 
						|
	;
 | 
						|
 | 
						|
number(int *t;)
 | 
						|
	: C_NUMBER
 | 
						|
			{	*t = lextoken.t_num;
 | 
						|
				if (*t <= 0 || *t >= 8192) {
 | 
						|
					error(linecount,"Illegal number");
 | 
						|
				}
 | 
						|
			}
 | 
						|
	;
 | 
						|
 | 
						|
firsts	{	register string p; }
 | 
						|
	: C_FIRST C_IDENT
 | 
						|
			{	p = store(lextoken.t_string); }
 | 
						|
	  ',' C_IDENT ';'
 | 
						|
			{	/*
 | 
						|
				 * Store this %first in the list belonging
 | 
						|
				 * to this input file
 | 
						|
				 */
 | 
						|
				p_gram temp;
 | 
						|
				register p_first ff;
 | 
						|
 | 
						|
				temp = search(NONTERM,lextoken.t_string,BOTH);
 | 
						|
				ff = (p_first) alloc(sizeof(t_first));
 | 
						|
				ff->ff_nont = g_getcont(temp);
 | 
						|
				ff->ff_name = p;
 | 
						|
				ff->ff_next = pfile->f_firsts;
 | 
						|
				pfile->f_firsts = ff;
 | 
						|
			}
 | 
						|
	;
 | 
						|
{
 | 
						|
 | 
						|
STATIC
 | 
						|
copyact(ch1,ch2,flag,level) char ch1,ch2; {
 | 
						|
	/*
 | 
						|
	 * Copy an action to file f. Opening bracket is ch1, closing bracket
 | 
						|
	 * is ch2.
 | 
						|
	 * If flag != 0, copy opening and closing parameters too.
 | 
						|
	 */
 | 
						|
	static int	text_seen = 0;
 | 
						|
	register	FILE *f;
 | 
						|
	register	ch;		/* Current char */
 | 
						|
	register	match;		/* used to read strings */
 | 
						|
	int		saved;		/* save linecount */
 | 
						|
 | 
						|
	f = fact;
 | 
						|
	if (!level) {
 | 
						|
		saved = linecount;
 | 
						|
		text_seen = 0;
 | 
						|
		nparams = 0;			/* count comma's */
 | 
						|
		putc('\0',f);
 | 
						|
		fprintf(f,"# line %d \"%s\"\n", linecount,f_input);
 | 
						|
	}
 | 
						|
	if (level || flag) putc(ch1,f);
 | 
						|
	for (;;) {
 | 
						|
		ch = input();
 | 
						|
		if (ch == ch2) {
 | 
						|
			if (!level) {
 | 
						|
				unput(ch);
 | 
						|
				if (text_seen) nparams++;
 | 
						|
			}
 | 
						|
			if (level || flag) putc(ch,f);
 | 
						|
			return;
 | 
						|
		}
 | 
						|
		switch(ch) {
 | 
						|
		  case ')':
 | 
						|
		  case '}':
 | 
						|
		  case ']':
 | 
						|
			error(linecount,"Parentheses mismatch");
 | 
						|
			break;
 | 
						|
		  case '(':
 | 
						|
			text_seen = 1;
 | 
						|
			copyact('(',')',flag,level+1);
 | 
						|
			continue;
 | 
						|
		  case '{':
 | 
						|
			text_seen = 1;
 | 
						|
			copyact('{','}',flag,level+1);
 | 
						|
			continue;
 | 
						|
		  case '[':
 | 
						|
			text_seen = 1;
 | 
						|
			copyact('[',']',flag,level+1);
 | 
						|
			continue;
 | 
						|
		  case '/':
 | 
						|
			ch = input();
 | 
						|
			unput(ch);
 | 
						|
			if (ch == '*') {
 | 
						|
				putc('/', f);
 | 
						|
				skipcomment(1);
 | 
						|
				continue;
 | 
						|
			}
 | 
						|
			ch = '/';
 | 
						|
			text_seen = 1;
 | 
						|
			break;
 | 
						|
		  case ';':
 | 
						|
		  case ',':
 | 
						|
			if (! level && text_seen) {
 | 
						|
				text_seen = 0;
 | 
						|
				nparams++;
 | 
						|
			}
 | 
						|
			break;
 | 
						|
		  case '\'':
 | 
						|
		  case '"' :
 | 
						|
			/*
 | 
						|
			 * watch out for brackets in strings, they do not
 | 
						|
			 * count !
 | 
						|
			 */
 | 
						|
			text_seen = 1;
 | 
						|
			match = ch;
 | 
						|
			putc(ch,f);
 | 
						|
			while((ch = input())) {
 | 
						|
				if (ch == match) break;
 | 
						|
				if (ch == '\\') {
 | 
						|
					putc(ch,f);
 | 
						|
					ch = input();
 | 
						|
				}
 | 
						|
				if (ch == '\n') {
 | 
						|
					error(linecount,"Newline in string");
 | 
						|
					unput(match);
 | 
						|
				}
 | 
						|
				putc(ch,f);
 | 
						|
			}
 | 
						|
			if (ch == match) break;
 | 
						|
			/* Fall through */
 | 
						|
		    case EOF :
 | 
						|
			if (!level) error(saved,"Action does not terminate");
 | 
						|
			return;
 | 
						|
		    default:
 | 
						|
			if (c_class[ch] != ISSPA) text_seen = 1;
 | 
						|
		}
 | 
						|
		putc(ch,f);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
STATIC p_gram
 | 
						|
copyrule(p,length) register p_gram p; {
 | 
						|
	/*
 | 
						|
	 * Returns a pointer to a grammar rule that was created in
 | 
						|
	 * p. The space pointed to by p can now be reused
 | 
						|
	 */
 | 
						|
	register p_gram t;
 | 
						|
	p_gram rule;
 | 
						|
 | 
						|
	t = (p_gram) alloc((unsigned) length * sizeof(t_gram));
 | 
						|
	rule = t;
 | 
						|
	while (length--) {
 | 
						|
		*t++ = *p++;
 | 
						|
	}
 | 
						|
	return rule;
 | 
						|
}
 | 
						|
}
 |