Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 1
#include <u.h>
2
#include <libc.h>
3
#include <stdio.h>
4
#include "cpp.h"
5
 
6
/*
7
 * do a macro definition.  tp points to the name being defined in the line
8
 */
9
void
10
dodefine(Tokenrow *trp)
11
{
12
	Token *tp;
13
	Nlist *np;
14
	Tokenrow *def, *args;
15
	int dots;
16
 
17
	dots = 0;
18
	tp = trp->tp+1;
19
	if (tp>=trp->lp || tp->type!=NAME) {
20
		error(ERROR, "#defined token is not a name");
21
		return;
22
	}
23
	np = lookup(tp, 1);
24
	if (np->flag&ISUNCHANGE) {
25
		error(ERROR, "#defined token %t can't be redefined", tp);
26
		return;
27
	}
28
	/* collect arguments */
29
	tp += 1;
30
	args = NULL;
31
	if (tp<trp->lp && tp->type==LP && tp->wslen==0) {
32
		/* macro with args */
33
		int narg = 0;
34
		tp += 1;
35
		args = new(Tokenrow);
36
		maketokenrow(2, args);
37
		if (tp->type!=RP) {
38
			int err = 0;
39
			for (;;) {
40
				Token *atp;
41
				if (tp->type == ELLIPS)
42
					dots++;
43
				else if (tp->type!=NAME) {
44
					err++;
45
					break;
46
				}
47
				if (narg>=args->max)
48
					growtokenrow(args);
49
				for (atp=args->bp; atp<args->lp; atp++)
50
					if (atp->len==tp->len
51
					 && strncmp((char*)atp->t, (char*)tp->t, tp->len)==0)
52
						error(ERROR, "Duplicate macro argument");
53
				*args->lp++ = *tp;
54
				narg++;
55
				tp += 1;
56
				if (tp->type==RP)
57
					break;
58
				if (dots)
59
					error(ERROR, "arguments after '...' in macro");
60
				if (tp->type!=COMMA) {
61
					err++;
62
					break;
63
				}
64
				tp += 1;
65
			}
66
			if (err) {
67
				error(ERROR, "Syntax error in macro parameters");
68
				return;
69
			}
70
		}
71
		tp += 1;
72
	}
73
	trp->tp = tp;
74
	if (((trp->lp)-1)->type==NL)
75
		trp->lp -= 1;
76
	def = normtokenrow(trp);
77
	if (np->flag&ISDEFINED) {
78
		if (comparetokens(def, np->vp)
79
		 || (np->ap==NULL) != (args==NULL)
80
		 || np->ap && comparetokens(args, np->ap))
81
			error(ERROR, "Macro redefinition of %t", trp->bp+2);
82
	}
83
	if (args) {
84
		Tokenrow *tap;
85
		tap = normtokenrow(args);
86
		dofree(args->bp);
87
		args = tap;
88
	}
89
	np->ap = args;
90
	np->vp = def;
91
	np->flag |= ISDEFINED;
92
	if(dots)
93
		np->flag |= ISVARMAC;
94
}
95
 
96
/*
97
 * Definition received via -D or -U
98
 */
99
void
100
doadefine(Tokenrow *trp, int type)
101
{
102
	Nlist *np;
103
	static unsigned char one[] = "1";
104
	static Token onetoken[1] = {{ NUMBER, 0, 0, 0, 1, one }};
105
	static Tokenrow onetr = { onetoken, onetoken, onetoken+1, 1 };
106
 
107
	trp->tp = trp->bp;
108
	if (type=='U') {
109
		if (trp->lp-trp->tp != 2 || trp->tp->type!=NAME)
110
			goto syntax;
111
		if ((np = lookup(trp->tp, 0)) == NULL)
112
			return;
113
		np->flag &= ~ISDEFINED;
114
		return;
115
	}
116
	if (trp->tp >= trp->lp || trp->tp->type!=NAME)
117
		goto syntax;
118
	np = lookup(trp->tp, 1);
119
	np->flag |= ISDEFINED;
120
	trp->tp += 1;
121
	if (trp->tp >= trp->lp || trp->tp->type==END) {
122
		np->vp = &onetr;
123
		return;
124
	}
125
	if (trp->tp->type!=ASGN)
126
		goto syntax;
127
	trp->tp += 1;
128
	if ((trp->lp-1)->type == END)
129
		trp->lp -= 1;
130
	np->vp = normtokenrow(trp);
131
	return;
132
syntax:
133
	error(FATAL, "Illegal -D or -U argument %r", trp);
134
}
135
 
136
/*
137
 * Do macro expansion in a row of tokens.
138
 * Flag is NULL if more input can be gathered.
139
 */
140
void
141
expandrow(Tokenrow *trp, char *flag, int inmacro)
142
{
143
	Token *tp;
144
	Nlist *np;
145
 
146
	if (flag)
147
		setsource(flag, -1, "");
148
	for (tp = trp->tp; tp<trp->lp; ) {
149
		if (tp->type!=NAME
150
		 || quicklook(tp->t[0], tp->len>1?tp->t[1]:0)==0
151
		 || (np = lookup(tp, 0))==NULL
152
		 || (np->flag&(ISDEFINED|ISMAC))==0
153
		 || tp->hideset && checkhideset(tp->hideset, np)) {
154
			tp++;
155
			continue;
156
		}
157
		trp->tp = tp;
158
		if (np->val==KDEFINED) {
159
			tp->type = DEFINED;
160
			if ((tp+1)<trp->lp && (tp+1)->type==NAME)
161
				(tp+1)->type = NAME1;
162
			else if ((tp+3)<trp->lp && (tp+1)->type==LP
163
			 && (tp+2)->type==NAME && (tp+3)->type==RP)
164
				(tp+2)->type = NAME1;
165
			else
166
				error(ERROR, "Incorrect syntax for `defined'");
167
			tp++;
168
			continue;
169
		}
170
		if (np->flag&ISMAC)
171
			builtin(trp, np->val);
172
		else {
173
			expand(trp, np, inmacro);
174
		}
175
		tp = trp->tp;
176
	}
177
	if (flag)
178
		unsetsource();
179
}
180
 
181
/*
182
 * Expand the macro whose name is np, at token trp->tp, in the tokenrow.
183
 * Return trp->tp at the first token next to be expanded
184
 * (ordinarily the beginning of the expansion)
185
 */
186
void
187
expand(Tokenrow *trp, Nlist *np, int inmacro)
188
{
189
	Tokenrow ntr;
190
	int ntokc, narg, i;
191
	Token *tp;
192
	Tokenrow *atr[NARG+1];
193
	int hs;
194
 
195
	copytokenrow(&ntr, np->vp);		/* copy macro value */
196
	if (np->ap==NULL)			/* parameterless */
197
		ntokc = 1;
198
	else {
199
		ntokc = gatherargs(trp, atr, (np->flag&ISVARMAC) ? rowlen(np->ap) : 0, &narg);
200
		if (narg<0) {			/* not actually a call (no '(') */
201
/* error(WARNING, "%d %r\n", narg, trp); */
202
			/* gatherargs has already pushed trp->tr to the next token */
203
			return;
204
		}
205
		if (narg != rowlen(np->ap)) {
206
			error(ERROR, "Disagreement in number of macro arguments");
207
			trp->tp->hideset = newhideset(trp->tp->hideset, np);
208
			trp->tp += ntokc;
209
			return;
210
		}
211
		substargs(np, &ntr, atr);	/* put args into replacement */
212
		for (i=0; i<narg; i++) {
213
			dofree(atr[i]->bp);
214
			dofree(atr[i]);
215
		}
216
	}
217
	if(!inmacro)
218
		doconcat(&ntr);				/* execute ## operators */
219
	hs = newhideset(trp->tp->hideset, np);
220
	for (tp=ntr.bp; tp<ntr.lp; tp++) {	/* distribute hidesets */
221
		if (tp->type==NAME) {
222
			if (tp->hideset==0)
223
				tp->hideset = hs;
224
			else
225
				tp->hideset = unionhideset(tp->hideset, hs);
226
		}
227
	}
228
	ntr.tp = ntr.bp;
229
	insertrow(trp, ntokc, &ntr);
230
	trp->tp -= rowlen(&ntr);
231
	dofree(ntr.bp);
232
	return;
233
}	
234
 
235
/*
236
 * Gather an arglist, starting in trp with tp pointing at the macro name.
237
 * Return total number of tokens passed, stash number of args found.
238
 * trp->tp is not changed relative to the tokenrow.
239
 */
240
int
241
gatherargs(Tokenrow *trp, Tokenrow **atr, int dots, int *narg)
242
{
243
	int parens = 1;
244
	int ntok = 0;
245
	Token *bp, *lp;
246
	Tokenrow ttr;
247
	int ntokp;
248
	int needspace;
249
 
250
	*narg = -1;			/* means that there is no macro call */
251
	/* look for the ( */
252
	for (;;) {
253
		trp->tp++;
254
		ntok++;
255
		if (trp->tp >= trp->lp) {
256
			gettokens(trp, 0);
257
			if ((trp->lp-1)->type==END) {
258
/* error(WARNING, "reach END\n"); */
259
				trp->lp -= 1;
260
				if (*narg>=0)
261
					trp->tp -= ntok;
262
				return ntok;
263
			}
264
		}
265
		if (trp->tp->type==LP)
266
			break;
267
		if (trp->tp->type!=NL)
268
			return ntok;
269
	}
270
	*narg = 0;
271
	ntok++;
272
	ntokp = ntok;
273
	trp->tp++;
274
	/* search for the terminating ), possibly extending the row */
275
	needspace = 0;
276
	while (parens>0) {
277
		if (trp->tp >= trp->lp)
278
			gettokens(trp, 0);
279
		if (needspace) {
280
			needspace = 0;
281
			makespace(trp);
282
		}
283
		if (trp->tp->type==END) {
284
			trp->lp -= 1;
285
			trp->tp -= ntok;
286
			error(ERROR, "EOF in macro arglist");
287
			return ntok;
288
		}
289
		if (trp->tp->type==NL) {
290
			trp->tp += 1;
291
			adjustrow(trp, -1);
292
			trp->tp -= 1;
293
			makespace(trp);
294
			needspace = 1;
295
			continue;
296
		}
297
		if (trp->tp->type==LP)
298
			parens++;
299
		else if (trp->tp->type==RP)
300
			parens--;
301
		trp->tp++;
302
		ntok++;
303
	}
304
	trp->tp -= ntok;
305
	/* Now trp->tp won't move underneath us */
306
	lp = bp = trp->tp+ntokp;
307
	for (; parens>=0; lp++) {
308
		if (lp->type == LP) {
309
			parens++;
310
			continue;
311
		}
312
		if (lp->type==RP)
313
			parens--;
314
		if (lp->type==DSHARP)
315
			lp->type = DSHARP1;	/* ## not special in arg */
316
		if ((lp->type==COMMA && parens==0) || (parens<0 && (lp-1)->type!=LP)) {
317
			if (lp->type == COMMA && dots && *narg == dots-1)
318
				continue;
319
			if (*narg>=NARG-1)
320
				error(FATAL, "Sorry, too many macro arguments");
321
			ttr.bp = ttr.tp = bp;
322
			ttr.lp = lp;
323
			atr[(*narg)++] = normtokenrow(&ttr);
324
			bp = lp+1;
325
		}
326
	}
327
	return ntok;
328
}
329
 
330
/*
331
 * substitute the argument list into the replacement string
332
 *  This would be simple except for ## and #
333
 */
334
void
335
substargs(Nlist *np, Tokenrow *rtr, Tokenrow **atr)
336
{
337
	Tokenrow tatr;
338
	Token *tp;
339
	int ntok, argno;
340
 
341
	for (rtr->tp=rtr->bp; rtr->tp<rtr->lp; ) {
342
		if (rtr->tp->type==SHARP) {	/* string operator */
343
			tp = rtr->tp;
344
			rtr->tp += 1;
345
			if ((argno = lookuparg(np, rtr->tp))<0) {
346
				error(ERROR, "# not followed by macro parameter");
347
				continue;
348
			}
349
			ntok = 1 + (rtr->tp - tp);
350
			rtr->tp = tp;
351
			insertrow(rtr, ntok, stringify(atr[argno]));
352
			continue;
353
		}
354
		if (rtr->tp->type==NAME
355
		 && (argno = lookuparg(np, rtr->tp)) >= 0) {
356
			if (rtr->tp < rtr->bp)
357
				error(ERROR, "access out of bounds");
358
			if ((rtr->tp+1)->type==DSHARP
359
			 || rtr->tp!=rtr->bp && (rtr->tp-1)->type==DSHARP)
360
				insertrow(rtr, 1, atr[argno]);
361
			else {
362
				copytokenrow(&tatr, atr[argno]);
363
				expandrow(&tatr, "<macro>", Inmacro);
364
				insertrow(rtr, 1, &tatr);
365
				dofree(tatr.bp);
366
			}
367
			continue;
368
		}
369
		rtr->tp++;
370
	}
371
}
372
 
373
/*
374
 * Evaluate the ## operators in a tokenrow
375
 */
376
void
377
doconcat(Tokenrow *trp)
378
{
379
	Token *ltp, *ntp;
380
	Tokenrow ntr;
381
	int len;
382
 
383
	for (trp->tp=trp->bp; trp->tp<trp->lp; trp->tp++) {
384
		if (trp->tp->type==DSHARP1)
385
			trp->tp->type = DSHARP;
386
		else if (trp->tp->type==DSHARP) {
387
			char tt[128];
388
			ltp = trp->tp-1;
389
			ntp = trp->tp+1;
390
			if (ltp<trp->bp || ntp>=trp->lp) {
391
				error(ERROR, "## occurs at border of replacement");
392
				continue;
393
			}
394
			len = ltp->len + ntp->len;
395
			strncpy((char*)tt, (char*)ltp->t, ltp->len);
396
			strncpy((char*)tt+ltp->len, (char*)ntp->t, ntp->len);
397
			tt[len] = '\0';
398
			setsource("<##>", -1, tt);
399
			maketokenrow(3, &ntr);
400
			gettokens(&ntr, 1);
401
			unsetsource();
402
			if (ntr.lp-ntr.bp!=2 || ntr.bp->type==UNCLASS)
403
				error(WARNING, "Bad token %r produced by ##", &ntr);
404
			ntr.lp = ntr.bp+1;
405
			trp->tp = ltp;
406
			makespace(&ntr);
407
			insertrow(trp, (ntp-ltp)+1, &ntr);
408
			dofree(ntr.bp);
409
			trp->tp--;
410
		}
411
	}
412
}
413
 
414
/*
415
 * tp is a potential parameter name of macro mac;
416
 * look it up in mac's arglist, and if found, return the
417
 * corresponding index in the argname array.  Return -1 if not found.
418
 */
419
int
420
lookuparg(Nlist *mac, Token *tp)
421
{
422
	Token *ap;
423
 
424
	if (tp->type!=NAME || mac->ap==NULL)
425
		return -1;
426
	if((mac->flag & ISVARMAC) && strcmp((char*)tp->t, "__VA_ARGS__") == 0)
427
		return rowlen(mac->ap) - 1;
428
	for (ap=mac->ap->bp; ap<mac->ap->lp; ap++) {
429
		if (ap->len==tp->len && strncmp((char*)ap->t,(char*)tp->t,ap->len)==0)
430
			return ap - mac->ap->bp;
431
	}
432
	return -1;
433
}
434
 
435
/*
436
 * Return a quoted version of the tokenrow (from # arg)
437
 */
438
#define	STRLEN	512
439
Tokenrow *
440
stringify(Tokenrow *vp)
441
{
442
	static Token t = { STRING };
443
	static Tokenrow tr = { &t, &t, &t+1, 1 };
444
	Token *tp;
445
	uchar s[STRLEN];
446
	uchar *sp = s, *cp;
447
	int i, instring;
448
 
449
	*sp++ = '"';
450
	for (tp = vp->bp; tp < vp->lp; tp++) {
451
		instring = tp->type==STRING || tp->type==CCON;
452
		if (sp+2*tp->len >= &s[STRLEN-10]) {
453
			error(ERROR, "Stringified macro arg is too long");
454
			break;
455
		}
456
		if (tp->wslen /* && (tp->flag&XPWS)==0 */)
457
			*sp++ = ' ';
458
		for (i=0, cp=tp->t; i<tp->len; i++) {	
459
			if (instring && (*cp=='"' || *cp=='\\'))
460
				*sp++ = '\\';
461
			*sp++ = *cp++;
462
		}
463
	}
464
	*sp++ = '"';
465
	*sp = '\0';
466
	sp = s;
467
	t.len = strlen((char*)sp);
468
	t.t = newstring(sp, t.len, 0);
469
	return &tr;
470
}
471
 
472
/*
473
 * expand a builtin name
474
 */
475
void
476
builtin(Tokenrow *trp, int biname)
477
{
478
	char *op;
479
	Token *tp;
480
	Source *s;
481
 
482
	tp = trp->tp;
483
	trp->tp++;
484
	/* need to find the real source */
485
	s = cursource;
486
	while (s && s->fd==-1)
487
		s = s->next;
488
	if (s==NULL)
489
		s = cursource;
490
	/* most are strings */
491
	tp->type = STRING;
492
	if (tp->wslen) {
493
		*outp++ = ' ';
494
		tp->wslen = 1;
495
	}
496
	op = outp;
497
	*op++ = '"';
498
	switch (biname) {
499
 
500
	case KLINENO:
501
		tp->type = NUMBER;
502
		op = outnum(op-1, s->line);
503
		break;
504
 
505
	case KFILE:
506
		strcpy(op, s->filename);
507
		op += strlen(s->filename);
508
		break;
509
 
510
	case KDATE:
511
		strncpy(op, curtime+4, 7);
512
		strncpy(op+7, curtime+24, 4); /* Plan 9 asctime disobeys standard */
513
		op += 11;
514
		break;
515
 
516
	case KTIME:
517
		strncpy(op, curtime+11, 8);
518
		op += 8;
519
		break;
520
 
521
	default:
522
		error(ERROR, "cpp botch: unknown internal macro");
523
		return;
524
	}
525
	if (tp->type==STRING)
526
		*op++ = '"';
527
	tp->t = (uchar*)outp;
528
	tp->len = op - outp;
529
	outp = op;
530
}