1/***********************************************************************
2*                                                                      *
3*               This software is part of the ast package               *
4*          Copyright (c) 1986-2009 AT&T Intellectual Property          *
5*                      and is licensed under the                       *
6*                  Common Public License, Version 1.0                  *
7*                    by AT&T Intellectual Property                     *
8*                                                                      *
9*                A copy of the License is available at                 *
10*            http://www.opensource.org/licenses/cpl1.0.txt             *
11*         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
12*                                                                      *
13*              Information and Software Systems Research               *
14*                            AT&T Research                             *
15*                           Florham Park NJ                            *
16*                                                                      *
17*                 Glenn Fowler <gsf@research.att.com>                  *
18*                                                                      *
19***********************************************************************/
20#pragma prototyped
21/*
22 * Glenn Fowler
23 * AT&T Research
24 *
25 * preprocessor macro call
26 */
27
28#include "pplib.h"
29
30#include <ctype.h>
31
32/*
33 * call a macro by pushing its value on the input stream
34 * only the macro token itself has been consumed
35 * -1 returned if macro disabled
36 *  0 returned if tok==0 and sym->mac->value to be copied to output by caller
37 *  1 returned if value pushed on input
38 */
39
40int
41ppcall(register struct ppsymbol* sym, int tok)
42{
43	register int			c;
44	register char*			p;
45	register char*			q;
46	register struct ppmacro*	mac;
47	int				n;
48	int				m;
49	int				ret;
50	int				old_hidden;
51	int				last_line;
52	long				old_state;
53	char*				last_file;
54	char*				old_next;
55	char*				old_token;
56	struct ppmacstk*		mp;
57	struct ppinstk*			old_in;
58	struct ppinstk*			kp;
59	struct pptuple*			tp;
60
61	ret = -1;
62	sym->flags |= SYM_NOTICED;
63	if (mac = sym->macro)
64	{
65		count(macro);
66		if ((sym->flags & SYM_PREDICATE) && (pp.state & (CONDITIONAL|WARN)) == (CONDITIONAL|WARN))
67			error(1, "%s: macro definition overrides assertion: use #%s ...", sym->name, sym->name);
68		if (sym->flags & SYM_DISABLED)
69#if COMPATIBLE
70			if ((pp.state & (COMPATIBILITY|TRANSITION)) != COMPATIBILITY || !mac->arity)
71#endif
72		{
73			pp.mode |= MARKMACRO;
74#if COMPATIBLE
75			if ((pp.state & (COMPATIBILITY|STRICT)) == (COMPATIBILITY|STRICT))
76				error(1, "%s: macro recursion inhibited", sym->name);
77#endif
78			goto disable;
79		}
80		if ((sym->flags & SYM_PREDEFINED) && !(pp.mode & (HOSTED|INACTIVE)))
81		{
82#if COMPATIBLE
83			if (*sym->name != '_' && !(pp.state & COMPATIBILITY))
84#else
85			if (*sym->name != '_')
86#endif
87			{
88				if (pp.state & STRICT)
89				{
90					error(1, "%s: obsolete predefined symbol expansion disabled", sym->name);
91					goto disable;
92				}
93				error(1, "%s: obsolete predefined symbol expanded%s", sym->name, (pp.state & DIRECTIVE) ? "" : " outside of directive");
94			}
95			else if (!(pp.state & DIRECTIVE) && mac->value && (ppisdig(*mac->value) || *mac->value == '#'))
96				error(1, "%s: predefined symbol expanded outside of directive", sym->name);
97		}
98		debug((-5, "macro %s = %s", sym->name, mac->value));
99		if (pp.macref)
100			(*pp.macref)(sym, error_info.file, error_info.line, (pp.state & CONDITIONAL) ? REF_IF : REF_NORMAL, 0L);
101		if (tp = mac->tuple)
102		{
103			old_state = pp.state;
104			pp.state |= DEFINITION|NOSPACE;
105			old_token = pp.token;
106			n = 2 * MAXTOKEN;
107			pp.token = p = oldof(0, char, 0, n);
108			q = p + MAXTOKEN;
109			*pp.token++ = ' ';
110			old_hidden = pp.hidden;
111			while (c = pplex())
112			{
113				if (c == '\n')
114				{
115					pp.hidden++;
116					pp.state |= HIDDEN|NEWLINE;
117					old_state |= HIDDEN|NEWLINE;
118					error_info.line++;
119				}
120				else if (c == '#')
121				{
122					ungetchr(c);
123					break;
124				}
125				else
126				{
127					for (;;)
128					{
129						if (streq(pp.token, tp->token))
130						{
131							if (!(tp = tp->match))
132								break;
133							if (!tp->nomatch)
134							{
135								free(p);
136								pp.state = old_state;
137								pp.token = old_token;
138								PUSH_TUPLE(sym, tp->token);
139								ret = 1;
140								goto disable;
141							}
142						}
143						else if (!(tp = tp->nomatch))
144							break;
145					}
146					if (!tp)
147					{
148						pp.token = pp.toknxt;
149						break;
150					}
151				}
152				if ((pp.token = pp.toknxt) > q)
153				{
154					c = pp.token - p;
155					p = newof(p, char, n += MAXTOKEN, 0);
156					q = p + n - MAXTOKEN;
157					pp.token = p + c;
158				}
159				*pp.token++ = ' ';
160			}
161			if (pp.token > p && *(pp.token - 1) == ' ')
162				pp.token--;
163			if (pp.hidden != old_hidden)
164				*pp.token++ = '\n';
165			else
166				*pp.token++ = ' ';
167			*pp.token = 0;
168			pp.state = old_state;
169			pp.token = old_token;
170			if (*p)
171				PUSH_RESCAN(p);
172			else
173				free(p);
174			if (!mac->value)
175				goto disable;
176		}
177		if (sym->flags & SYM_FUNCTION)
178		{
179			/*
180			 * a quick and dirty '(' peek to avoid possibly
181			 * inappropriate ungetchr()'s below
182			 */
183
184			for (p = pp.in->nextchr; isspace(*p); p++);
185			if ((c = *p) != '(' && c != '/' && c != 0 && c != MARK)
186				goto disable;
187			old_next = (c == MARK) ? pp.in->nextchr : NiL;
188			old_token = pp.token;
189			mp = pp.macp->next;
190			if ((pp.token = (char*)&mp->arg[mac->arity + 1]) > pp.maxmac)
191				error(3, "%s: too many nested function-like macros", sym->name);
192			old_hidden = pp.hidden;
193			old_state = pp.state;
194			pp.state |= DEFINITION|FILEPOP|NOSPACE;
195			while ((c = pplex()) == '\n')
196			{
197				pp.hidden++;
198				pp.state |= HIDDEN|NEWLINE;
199				old_state |= HIDDEN|NEWLINE;
200				error_info.line++;
201			}
202			if (c != '(')
203			{
204				pp.state = old_state;
205				if (old_next)
206					pp.in->nextchr = old_next;
207				else
208				{
209					if (c)
210					{
211						p = pp.toknxt;
212						while (p > pp.token)
213							ungetchr(*--p);
214#if COMPATIBLE
215						if ((pp.state & (COMPATIBILITY|STRICT)) == (COMPATIBILITY|STRICT))
216							error(1, "%s: macro arguments omitted", sym->name);
217#endif
218						if (c == T_ID && !(pp.state & HIDDEN))
219							ungetchr(' ');
220					}
221					if (pp.hidden != old_hidden)
222					{
223						ungetchr('\n');
224						error_info.line--;
225						if (pp.hidden && !--pp.hidden)
226							pp.state &= ~HIDDEN;
227					}
228				}
229				pp.token = old_token;
230				goto disable;
231			}
232			pp.state = old_state;
233
234			/*
235			 * arg[i][-1] is an extra char for each actual i
236			 * for a possible ungetchr('"') during IN_QUOTE
237			 * arg[i][-1]==0 if arg i need not be expanded
238			 * arg[0][-2] holds the actual arg count
239			 */
240
241			c = 0;
242			m = 0;
243			n = 0;
244			mp = pp.macp->next;
245			p = pp.token = (char*)&mp->arg[mac->arity + 1];
246			pp.state |= COLLECTING|NOEXPAND;
247			pp.state &= ~FILEPOP;
248			sym->flags |= SYM_ACTIVE;
249			old_in = pp.in;
250			last_line = error_info.line;
251			last_file = error_info.file;
252			mp->line = error_info.line;
253#if MACKEYARGS
254			if (pp.option & KEYARGS)
255			{
256				for (c = 0; c < mac->arity; c++)
257					mp->arg[c] = mac->args.key[c].value + 1;
258				mp->arg[0]++;
259			}
260			else
261#endif
262			{
263				*++p = ' ';
264				mp->arg[0] = ++p;
265			}
266#if MACKEYARGS
267		keyarg:
268			if (pp.option & KEYARGS)
269			{
270				pp.state |= NOSPACE;
271				switch (pplex())
272				{
273				case T_ID:
274					break;
275				case ')':	/* no actual key args */
276					if (!(pp.state & NOEXPAND))
277						pp.state |= NOEXPAND;
278					for (c = 0; c < mac->arity; c++)
279						mp->arg[c][-1] = 0;
280					c = 0;
281					goto endactuals;
282				default:
283					error(3, "%s: invalid keyword macro argument", pp.token);
284					break;
285				}
286				for (c = 0; c < mac->arity; c++)
287					if (streq(pp.token, mac->args.key[c].name)) break;
288				if (c >= mac->arity)
289					error(2, "%s: invalid macro argument keyword", pp.token);
290				if (pplex() != '=')
291					error(2, "= expected in keyword macro argument");
292				pp.state &= ~NOSPACE;
293				if (!c)
294					p++;
295				pp.token = mp->arg[c] = ++p;
296			}
297#endif
298			for (;;)
299			{
300				if ((pp.mactop = pp.token = p) >= pp.maxmac)
301					error(3, "%s: too many nested function-like macros", sym->name);
302				switch (pplex())
303				{
304				case '(':
305					n++;
306					break;
307				case ')':
308					if (!n--)
309					{
310						if (p > mp->arg[c] && *(p - 1) == ' ')
311							p--;
312						if (p > mp->arg[c] && *(p - 1) == '\\')
313						{
314							for (q = mp->arg[c]; q < p; q++)
315								if (*q == '\\')
316									q++;
317							if (q > p)
318								*p++ = '\\';
319						}
320#if MACKEYARGS
321						*p = 0;
322						m++;
323#endif
324						goto endactuals;
325					}
326					break;
327				case ',':
328					if (!n && (m++, (c < mac->arity - 1 || !(sym->flags & SYM_VARIADIC))))
329					{
330						if (p > mp->arg[c] && *(p - 1) == ' ')
331							p--;
332						*p++ = 0;
333						if (!(pp.state & NOEXPAND))
334							pp.state |= NOEXPAND;
335						else
336							mp->arg[c][-1] = 0;
337#if MACKEYARGS
338						if (pp.option & KEYARGS)
339						{
340							pp.token = p + 1;
341							goto keyarg;
342						}
343#endif
344						{
345							if ((pp.state & STRICT) && p == mp->arg[c])
346								error(1, "%s: macro call argument %d is null", sym->name, c + 1);
347							if (c < mac->arity)
348								c++;
349							*p++ = ' ';
350						}
351						pp.toknxt = mp->arg[c] = p;
352					}
353					break;
354				case 0:
355					if (pp.in == old_in)
356						kp = 0;
357					else
358						for (kp = pp.in; kp && kp != old_in; kp = kp->prev);
359					if (!kp)
360					{
361						error(
362#if COMPATIBLE
363							(pp.state & COMPATIBILITY) ? 3 :
364#endif
365							2, "%s: %s in macro argument list", sym->name, pptokchr(0));
366						goto endactuals;
367					}
368					continue;
369				case '\n':
370					pp.state |= HIDDEN;
371					error_info.line++;
372					pp.hidden++;
373					/*FALLTHROUGH*/
374				case ' ':
375					if (p > mp->arg[c] && *(p - 1) != ' ') *p++ = ' ';
376					continue;
377				}
378				p = pp.toknxt;
379				if (error_info.line != last_line)
380				{
381					SETLINE(p, error_info.line);
382					last_line = error_info.line;
383				}
384				if (error_info.file != last_file)
385				{
386					SETFILE(p, error_info.file);
387					last_file = error_info.file;
388				}
389			}
390 endactuals:
391			if (pp.state & NOEXPAND)
392				mp->arg[c][-1] = 0;
393			pp.token = old_token;
394			if (pp.in != old_in)
395			{
396				for (kp = pp.in; kp && kp != old_in; kp = kp->prev);
397				if (kp)
398					error(2, "%s: macro call starts and ends in different files", sym->name);
399			}
400			pp.state &= ~(COLLECTING|FILEPOP|NOEXPAND);
401			sym->flags &= ~SYM_ACTIVE;
402#if MACKEYARGS
403			if (!(pp.option & KEYARGS))
404#endif
405			{
406				if (p > mp->arg[0] && ++m || (sym->flags & SYM_VARIADIC))
407					c++;
408				if (c != mac->arity && !(sym->flags & SYM_EMPTY))
409				{
410					n = mac->arity;
411					if (!(sym->flags & SYM_VARIADIC))
412						error(1, "%s: %d actual argument%s expected", sym->name, n, n == 1 ? "" : "s");
413					else if (c < --n)
414						error(1, "%s: at least %d actual argument%s expected", sym->name, n, n == 1 ? "" : "s");
415#if COMPATIBLE
416					if (!c && (pp.state & (COMPATIBILITY|STRICT)) == (COMPATIBILITY|STRICT))
417						goto disable;
418#endif
419				}
420				if (!c)
421					++c;
422				while (c < mac->arity)
423					mp->arg[c++] = (char*)"\0" + 1;
424			}
425			mp->arg[0][-2] = m;
426			*p++ = 0;
427			nextframe(mp, p);
428			count(function);
429		}
430		if (!tok && (sym->flags & SYM_NOEXPAND))
431		{
432			if (sym->flags & SYM_FUNCTION)
433				popframe(mp);
434			ret = !mac->size;
435		}
436		else if (!(pp.state & HEADER) || (pp.option & HEADEREXPANDALL)  || pp.in->type != IN_COPY)
437		{
438			if (sym->flags & SYM_MULTILINE)
439				PUSH_MULTILINE(sym);
440			else
441				PUSH_MACRO(sym);
442			ret = 1;
443		}
444	}
445 disable:
446	if (ret < 0 && sym->hidden && !(pp.mode & EXPOSE) && !(pp.state & HEADER) && (pp.in->type == IN_FILE || pp.in->type == IN_MACRO || pp.in->type == IN_EXPAND))
447	{
448		struct ppinstk*	inp;
449
450		for (inp = pp.in; inp->type != IN_FILE && inp->prev; inp = inp->prev);
451		sfsprintf(pp.hidebuf, MAXTOKEN, "_%d_%s_hIDe", inp->index, sym->name);
452		PUSH_STRING(pp.hidebuf);
453		ret = 1;
454	}
455	pp.state &= ~NEWLINE;
456	pp.in->flags |= IN_tokens;
457	count(token);
458	return ret;
459}
460