/***********************************************************************
*                                                                      *
*               This software is part of the ast package               *
*           Copyright (c) 1986-2006 AT&T Knowledge Ventures            *
*                      and is licensed under the                       *
*                  Common Public License, Version 1.0                  *
*                      by AT&T Knowledge Ventures                      *
*                                                                      *
*                A copy of the License is available at                 *
*            http://www.opensource.org/licenses/cpl1.0.txt             *
*         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
*                                                                      *
*              Information and Software Systems Research               *
*                            AT&T Research                             *
*                           Florham Park NJ                            *
*                                                                      *
*                 Glenn Fowler <gsf@research.att.com>                  *
*                                                                      *
***********************************************************************/
#pragma prototyped
/*
 * Glenn Fowler
 * AT&T Research
 *
 * miscellaneous preprocessor support
 */

#include "pplib.h"

/*
 * macro symbol def|ref
 */

struct ppsymbol*
pprefmac(char* name, int ref)
{
	register struct ppsymbol*	sym;

	if (!(sym = ppsymget(pp.symtab, name)) && (ref <= REF_NORMAL && pp.macref || ref == REF_CREATE || ref == REF_DELETE && (pp.mode & (INIT|READONLY))))
	{
		if ((pp.state & COMPILE) && pp.truncate && strlen(name) > pp.truncate)
			name[pp.truncate] = 0;
		sym = ppsymset(pp.symtab, NiL);
	}
	if (sym && ref <= REF_NORMAL)
	{
		if (pp.macref) (*pp.macref)(sym, error_info.file, error_info.line, ref == REF_NORMAL && (pp.state & CONDITIONAL) ? REF_IF : ref, 0L);
		if (!sym->macro) sym = 0;
	}
#if COMPATIBLE
	if (!(pp.state & COMPATIBILITY))
#endif
	if (ref == REF_IF && sym && (sym->flags & SYM_PREDEFINED) && *name != '_' && !(pp.mode & (HOSTED|INACTIVE)))
	{
		if (pp.state & STRICT)
		{
			error(1, "%s: obsolete predefined symbol reference disabled", name);
			return(0);
		}
		error(1, "%s: obsolete predefined symbol referenced", name);
	}
	return(sym);
}

/*
 * common predicate assertion operations
 * op is DEFINE or UNDEF
 */

void
ppassert(int op, char* pred, char* args)
{
	register struct pplist*		a;
	register struct ppsymbol*	sym;
	register struct pplist*		p;
	register struct pplist*		q;

	if (!args) switch (op)
	{
	case DEFINE:
		goto mark;
	case UNDEF:
		a = 0;
		goto unmark;
	}
	if (a = (struct pplist*)hashget(pp.prdtab, pred))
	{
		p = 0;
		q = a;
		while (q)
		{
			if (streq(q->value, args))
			{
				if (op == DEFINE) return;
				q = q->next;
				if (p) p->next = q;
				else a = q;
			}
			else
			{
				p = q;
				q = q->next;
			}
		}
		if (op == UNDEF)
		{
		unmark:
			hashput(pp.prdtab, pred, a);
			if (sym = ppsymref(pp.symtab, pred))
				sym->flags &= ~SYM_PREDICATE;
			return;
		}
	}
	if (op == DEFINE)
	{
		p = newof(0, struct pplist, 1, 0);
		p->next = a;
		p->value = strdup(args);
		hashput(pp.prdtab, NiL, p);
	mark:
		if ((pp.state & COMPILE) && pp.truncate) return;
		if (sym = ppsymset(pp.symtab, pred))
			sym->flags |= SYM_PREDICATE;
	}
}

/*
 * parse a predicate argument list
 * the args are placed in pp.args
 * the first non-space/paren argument token type is returned
 * forms:
 *
 *	predicate <identifier>			type=T_ID
 *	predicate ( <identifier> )		type=T_ID
 *	predicate ( )				type=0
 *	predicate ( <balanced-paren-list> )	type=T_STRING
 *	otherwise				type=<other>
 */

int
pppredargs(void)
{
	register int	c;
	register int	n;
	register int	type;
	char*		pptoken;

	pptoken = pp.token;
	pp.token = pp.args;
	switch (type = pplex())
	{
	case '(':
		type = 0;
		n = 1;
		pp.state |= HEADER;
		pp.state &= ~STRIP;
		c = pplex();
		pp.state &= ~NOSPACE;
		for (;;)
		{
			switch (c)
			{
			case '(':
				n++;
				break;
			case '\n':
				ungetchr(c);
				error(2, "missing %d )%s in predicate argument list", n, n == 1 ? "" : "'s");
				type = 0;
				goto done;
			case ')':
				if (!--n) goto done;
				break;
			}
			pp.token = pp.toknxt;
			if (c != ' ')
			{
				if (type) type = T_STRING;
				else type = (c == T_ID) ? T_ID : T_STRING;
			}
			c = pplex();
		}
	done:
		pp.state &= ~HEADER;
		pp.state |= NOSPACE|STRIP;
		if (pp.token > pp.args && *(pp.token - 1) == ' ') pp.token--;
		*pp.token = 0;
		break;
	case '\n':
		ungetchr('\n');
		type = 0;
		break;
	}
	pp.token = pptoken;
	return(type);
}

/*
 * sync output line number
 */

int
ppsync(void)
{
	long	m;

	if ((pp.state & (ADD|HIDDEN)))
	{
		if (pp.state & ADD)
		{
			pp.state &= ~ADD;
			m = pp.addp - pp.addbuf;
			pp.addp = pp.addbuf;
			ppprintf("%-.*s", m, pp.addbuf);
		}
		if (pp.linesync)
		{
			if ((pp.state & SYNCLINE) || pp.hidden >= MAXHIDDEN)
			{
				pp.hidden = 0;
				pp.state &= ~(HIDDEN|SYNCLINE);
				if (error_info.line)
					(*pp.linesync)(error_info.line, error_info.file);
			}
			else
			{
				m = pp.hidden;
				pp.hidden = 0;
				pp.state &= ~HIDDEN;
				while (m-- > 0)
					ppputchar('\n');
			}
		}
		else
		{
			pp.hidden = 0;
			pp.state &= ~HIDDEN;
			ppputchar('\n');
		}
	}
	return 0;
}
