Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
att
GitHub Repository: att/ast
Path: blob/master/src/lib/libpp/ppexpr.c
1808 views
1
/***********************************************************************
2
* *
3
* This software is part of the ast package *
4
* Copyright (c) 1986-2011 AT&T Intellectual Property *
5
* and is licensed under the *
6
* Eclipse Public License, Version 1.0 *
7
* by AT&T Intellectual Property *
8
* *
9
* A copy of the License is available at *
10
* http://www.eclipse.org/org/documents/epl-v10.html *
11
* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
12
* *
13
* Information and Software Systems Research *
14
* AT&T Research *
15
* Florham Park NJ *
16
* *
17
* Glenn Fowler <[email protected]> *
18
* *
19
***********************************************************************/
20
#pragma prototyped
21
/*
22
* Glenn Fowler
23
* AT&T Research
24
*
25
* preprocessor expression evaluation support
26
*/
27
28
#include "pplib.h"
29
30
#include <regex.h>
31
32
#define lex(c) ((((c)=peektoken)>=0?(peektoken=(-1)):((c)=pplex())),(c))
33
#define unlex(c) (peektoken=(c))
34
35
static int peektoken; /* expression lookahead token */
36
static char* errmsg; /* subexpr() error message */
37
38
/*
39
* exists predicate evaluation
40
*/
41
42
static int
43
exists(int op, char* pred, register char* args)
44
{
45
register int c;
46
register int type;
47
char* pptoken;
48
long state;
49
char file[MAXTOKEN + 1];
50
51
state = (pp.state & ~DISABLE);
52
PUSH_STRING(args);
53
pptoken = pp.token;
54
pp.token = file;
55
pp.state |= HEADER|PASSEOF;
56
type = pplex();
57
pp.state &= ~HEADER;
58
pp.token = pptoken;
59
switch (type)
60
{
61
case T_STRING:
62
case T_HEADER:
63
break;
64
default:
65
error(1, "%s: \"...\" or <...> argument expected", pred);
66
c = 0;
67
goto done;
68
}
69
if (op == X_EXISTS)
70
{
71
if ((c = pplex()) == ',')
72
{
73
while ((c = pplex()) == T_STRING)
74
{
75
if (pathaccess(pp.token, file, NiL, 0, pp.path, MAXTOKEN + 1))
76
{
77
pathcanon(pp.path, 0, 0);
78
message((-2, "%s: %s found", pred, pp.path));
79
c = 1;
80
goto done;
81
}
82
if ((c = pplex()) != ',') break;
83
}
84
if (c) error(1, "%s: \"...\" arguments expected", pred);
85
strcpy(pp.path, file);
86
message((-2, "%s: %s not found", pred, file));
87
c = 0;
88
}
89
else c = ppsearch(file, type, SEARCH_EXISTS) >= 0;
90
}
91
else
92
{
93
register struct ppfile* fp;
94
95
fp = ppsetfile(file);
96
c = fp->flags || fp->guard == INC_IGNORE;
97
}
98
done:
99
while (pplex());
100
pp.state = state;
101
return c;
102
}
103
104
/*
105
* strcmp/match predicate evaluation
106
*/
107
108
static int
109
compare(char* pred, char* args, int match)
110
{
111
register int c;
112
char* pptoken;
113
long state;
114
regex_t re;
115
char tmp[MAXTOKEN + 1];
116
117
state = (pp.state & ~DISABLE);
118
PUSH_STRING(args);
119
pp.state |= PASSEOF;
120
pptoken = pp.token;
121
pp.token = tmp;
122
if (!pplex())
123
goto bad;
124
pp.token = pptoken;
125
if (pplex() != ',' || !pplex())
126
goto bad;
127
if (!match)
128
c = strcmp(tmp, pp.token);
129
else if ((c = regcomp(&re, pp.token, REG_AUGMENTED|REG_LENIENT|REG_NULL)) || (c = regexec(&re, tmp, NiL, 0, 0)) && c != REG_NOMATCH)
130
regfatal(&re, 4, c);
131
else
132
{
133
c = !c;
134
regfree(&re);
135
}
136
if ((pp.state & PASSEOF) && pplex())
137
goto bad;
138
pp.state = state;
139
return c;
140
bad:
141
pp.token = pptoken;
142
error(2, "%s: 2 arguments expected", pred);
143
while (pplex());
144
pp.state = state;
145
return 0;
146
}
147
148
/*
149
* #if predicate parse and evaluation
150
*/
151
152
static int
153
predicate(int warn)
154
{
155
register char* args;
156
register struct pplist* p;
157
register struct ppsymbol* sym;
158
register int type;
159
int index;
160
161
static char pred[MAXID + 1];
162
163
/*
164
* first gather the args
165
*/
166
167
index = (int)hashref(pp.strtab, pp.token);
168
if (warn && peekchr() != '(') switch (index)
169
{
170
case X_DEFINED:
171
case X_EXISTS:
172
case X_INCLUDED:
173
case X_MATCH:
174
case X_NOTICED:
175
case X_OPTION:
176
case X_SIZEOF:
177
case X_STRCMP:
178
break;
179
default:
180
if (pp.macref) pprefmac(pp.token, REF_IF);
181
return 0;
182
}
183
strcpy(pred, pp.token);
184
pp.state |= DISABLE;
185
type = pppredargs();
186
pp.state &= ~DISABLE;
187
switch (type)
188
{
189
case T_ID:
190
case T_STRING:
191
break;
192
default:
193
unlex(type);
194
/*FALLTHROUGH*/
195
case 0:
196
if (index && !(pp.state & STRICT))
197
error(1, "%s: predicate argument expected", pred);
198
if (pp.macref) pprefmac(pred, REF_IF);
199
return 0;
200
}
201
args = pp.args;
202
203
/*
204
* now evaluate
205
*/
206
207
debug((-6, "pred=%s args=%s", pred, args));
208
if ((pp.state & STRICT) && !(pp.mode & HOSTED)) switch (index)
209
{
210
case X_DEFINED:
211
case X_SIZEOF:
212
break;
213
default:
214
error(1, "%s(%s): non-standard predicate test", pred, args);
215
return 0;
216
}
217
switch (index)
218
{
219
case X_DEFINED:
220
if (type != T_ID) error(1, "%s: identifier argument expected", pred);
221
else if ((sym = pprefmac(args, REF_IF)) && sym->macro) return 1;
222
else if (args[0] == '_' && args[1] == '_' && !strncmp(args, "__STDPP__", 9))
223
{
224
if (pp.hosted == 1 && pp.in->prev->type == IN_FILE)
225
{
226
pp.mode |= HOSTED;
227
pp.flags |= PP_hosted;
228
}
229
return *(args + 9) ? (int)hashref(pp.strtab, args + 9) : 1;
230
}
231
break;
232
case X_EXISTS:
233
case X_INCLUDED:
234
return exists(index, pred, args);
235
case X_MATCH:
236
case X_STRCMP:
237
return compare(pred, args, index == X_MATCH);
238
case X_NOTICED:
239
if (type != T_ID) error(1, "%s: identifier argument expected", pred);
240
else if (((sym = pprefmac(args, REF_IF)) || (sym = ppsymref(pp.symtab, args))) && (sym->flags & SYM_NOTICED)) return 1;
241
break;
242
case X_OPTION:
243
return ppoption(args);
244
case X_SIZEOF:
245
error(2, "%s invalid in #%s expressions", pred, dirname(IF));
246
break;
247
default:
248
if (warn && !(pp.mode & HOSTED) && (sym = ppsymref(pp.symtab, pred)) && (sym->flags & SYM_PREDICATE))
249
error(1, "use #%s(%s) to disambiguate", pred, args);
250
if (p = (struct pplist*)hashget(pp.prdtab, pred))
251
{
252
if (!*args) return 1;
253
while (p)
254
{
255
if (streq(p->value, args)) return 1;
256
p = p->next;
257
}
258
}
259
break;
260
}
261
return 0;
262
}
263
264
/*
265
* evaluate a long integer subexpression with precedence
266
* taken from the library routine streval()
267
* may be called recursively
268
*
269
* NOTE: all operands are evaluated as both the parse
270
* and evaluation are done on the fly
271
*/
272
273
static long
274
subexpr(register int precedence, int* pun)
275
{
276
register int c;
277
register long n;
278
register long x;
279
register int operand = 1;
280
int un = 0;
281
int xn;
282
283
switch (lex(c))
284
{
285
case 0:
286
case '\n':
287
unlex(c);
288
if (!errmsg && !(pp.mode & INACTIVE)) errmsg = "more tokens expected";
289
return 0;
290
case '-':
291
n = -subexpr(13, &un);
292
break;
293
case '+':
294
n = subexpr(13, &un);
295
break;
296
case '!':
297
n = !subexpr(13, &un);
298
break;
299
case '~':
300
n = ~subexpr(13, &un);
301
break;
302
default:
303
unlex(c);
304
n = 0;
305
operand = 0;
306
break;
307
}
308
un <<= 1;
309
for (;;)
310
{
311
switch (lex(c))
312
{
313
case 0:
314
case '\n':
315
goto done;
316
case ')':
317
if (!precedence)
318
{
319
if (!errmsg && !(pp.mode & INACTIVE)) errmsg = "too many )'s";
320
return 0;
321
}
322
goto done;
323
case '(':
324
n = subexpr(1, &un);
325
if (lex(c) != ')')
326
{
327
unlex(c);
328
if (!errmsg && !(pp.mode & INACTIVE)) errmsg = "closing ) expected";
329
return 0;
330
}
331
gotoperand:
332
if (operand)
333
{
334
if (!errmsg && !(pp.mode & INACTIVE)) errmsg = "operator expected";
335
return 0;
336
}
337
operand = 1;
338
un <<= 1;
339
continue;
340
case '?':
341
if (precedence > 1) goto done;
342
un = 0;
343
if (lex(c) == ':')
344
{
345
if (!n) n = subexpr(2, &un);
346
else
347
{
348
x = pp.mode;
349
pp.mode |= INACTIVE;
350
subexpr(2, &xn);
351
pp.mode = x;
352
}
353
}
354
else
355
{
356
unlex(c);
357
x = subexpr(2, &xn);
358
if (lex(c) != ':')
359
{
360
unlex(c);
361
if (!errmsg && !(pp.mode & INACTIVE)) errmsg = ": expected for ? operator";
362
return 0;
363
}
364
if (n)
365
{
366
n = x;
367
un = xn;
368
subexpr(2, &xn);
369
}
370
else n = subexpr(2, &un);
371
}
372
break;
373
case ':':
374
goto done;
375
case T_ANDAND:
376
case T_OROR:
377
xn = (c == T_ANDAND) ? 4 : 3;
378
if (precedence >= xn) goto done;
379
if ((n != 0) == (c == T_ANDAND)) n = subexpr(xn, &un) != 0;
380
else
381
{
382
x = pp.mode;
383
pp.mode |= INACTIVE;
384
subexpr(xn, &un);
385
pp.mode = x;
386
}
387
un = 0;
388
break;
389
case '|':
390
if (precedence > 4) goto done;
391
n |= subexpr(5, &un);
392
break;
393
case '^':
394
if (precedence > 5) goto done;
395
n ^= subexpr(6, &un);
396
break;
397
case '&':
398
if (precedence > 6) goto done;
399
n &= subexpr(7, &un);
400
break;
401
case T_EQ:
402
case T_NE:
403
if (precedence > 7) goto done;
404
n = (n == subexpr(8, &un)) == (c == T_EQ);
405
un = 0;
406
break;
407
case '<':
408
case T_LE:
409
case T_GE:
410
case '>':
411
if (precedence > 8) goto done;
412
x = subexpr(9, &un);
413
switch (c)
414
{
415
case '<':
416
switch (un)
417
{
418
case 01:
419
n = n < (unsigned long)x;
420
break;
421
case 02:
422
n = (unsigned long)n < x;
423
break;
424
case 03:
425
n = (unsigned long)n < (unsigned long)x;
426
break;
427
default:
428
n = n < x;
429
break;
430
}
431
break;
432
case T_LE:
433
switch (un)
434
{
435
case 01:
436
n = n <= (unsigned long)x;
437
break;
438
case 02:
439
n = (unsigned long)n <= x;
440
break;
441
case 03:
442
n = (unsigned long)n <= (unsigned long)x;
443
break;
444
default:
445
n = n <= x;
446
break;
447
}
448
break;
449
case T_GE:
450
switch (un)
451
{
452
case 01:
453
n = n >= (unsigned long)x;
454
break;
455
case 02:
456
n = (unsigned long)n >= x;
457
break;
458
case 03:
459
n = (unsigned long)n >= (unsigned long)x;
460
break;
461
default:
462
n = n >= x;
463
break;
464
}
465
break;
466
case '>':
467
switch (un)
468
{
469
case 01:
470
n = n > (unsigned long)x;
471
break;
472
case 02:
473
n = (unsigned long)n > x;
474
break;
475
case 03:
476
n = (unsigned long)n > (unsigned long)x;
477
break;
478
default:
479
n = n > x;
480
break;
481
}
482
break;
483
}
484
un = 0;
485
break;
486
case T_LSHIFT:
487
case T_RSHIFT:
488
if (precedence > 9) goto done;
489
x = subexpr(10, &un);
490
if (c == T_LSHIFT) n <<= x;
491
else n >>= x;
492
un >>= 1;
493
break;
494
case '+':
495
case '-':
496
if (precedence > 10) goto done;
497
x = subexpr(11, &un);
498
if (c == '+') n += x;
499
else n -= x;
500
break;
501
case '*':
502
case '/':
503
case '%':
504
if (precedence > 11) goto done;
505
x = subexpr(12, &un);
506
if (c == '*') n *= x;
507
else if (x == 0)
508
{
509
if (!errmsg && !(pp.mode & INACTIVE)) errmsg = "divide by zero";
510
return 0;
511
}
512
else if (c == '/') n /= x;
513
else n %= x;
514
break;
515
case '#':
516
pp.state |= DISABLE;
517
c = pplex();
518
pp.state &= ~DISABLE;
519
if (c != T_ID)
520
{
521
if (!errmsg && !(pp.mode & INACTIVE)) errmsg = "# must precede a predicate identifier";
522
return 0;
523
}
524
n = predicate(0);
525
goto gotoperand;
526
case T_ID:
527
n = predicate(1);
528
goto gotoperand;
529
case T_CHARCONST:
530
c = *(pp.toknxt - 1);
531
*(pp.toknxt - 1) = 0;
532
n = chrtoi(pp.token + 1);
533
*(pp.toknxt - 1) = c;
534
if (n & ~((1<<CHAR_BIT)-1))
535
{
536
if (!(pp.mode & HOSTED))
537
error(1, "'%s': multi-character character constants are not portable", pp.token);
538
}
539
#if CHAR_MIN < 0
540
else n = (char)n;
541
#endif
542
goto gotoperand;
543
case T_DECIMAL_U:
544
case T_DECIMAL_UL:
545
case T_OCTAL_U:
546
case T_OCTAL_UL:
547
case T_HEXADECIMAL_U:
548
case T_HEXADECIMAL_UL:
549
un |= 01;
550
/*FALLTHROUGH*/
551
case T_DECIMAL:
552
case T_DECIMAL_L:
553
case T_OCTAL:
554
case T_OCTAL_L:
555
case T_HEXADECIMAL:
556
case T_HEXADECIMAL_L:
557
n = strtoul(pp.token, NiL, 0);
558
if ((unsigned long)n > LONG_MAX) un |= 01;
559
goto gotoperand;
560
case T_WCHARCONST:
561
n = chrtoi(pp.token);
562
goto gotoperand;
563
default:
564
if (!errmsg && !(pp.mode & INACTIVE)) errmsg = "invalid token";
565
return 0;
566
}
567
if (errmsg) return 0;
568
if (!operand) goto nooperand;
569
}
570
done:
571
unlex(c);
572
if (!operand)
573
{
574
nooperand:
575
if (!errmsg && !(pp.mode & INACTIVE)) errmsg = "operand expected";
576
return 0;
577
}
578
if (un) *pun |= 01;
579
return n;
580
}
581
582
/*
583
* preprocessor expression evaluator using modified streval(3)
584
* *pun!=0 if result is unsigned
585
*/
586
587
long
588
ppexpr(int* pun)
589
{
590
long n;
591
int opeektoken;
592
long ppstate;
593
594
ppstate = (pp.state & (CONDITIONAL|DISABLE|NOSPACE|STRIP));
595
pp.state &= ~(DISABLE|STRIP);
596
pp.state |= CONDITIONAL|NOSPACE;
597
opeektoken = peektoken;
598
peektoken = -1;
599
*pun = 0;
600
n = subexpr(0, pun);
601
if (peektoken == ':' && !errmsg && !(pp.mode & INACTIVE)) errmsg = "invalid use of :";
602
if (errmsg)
603
{
604
error(2, "%s in expression", errmsg);
605
errmsg = 0;
606
n = 0;
607
}
608
peektoken = opeektoken;
609
pp.state &= ~(CONDITIONAL|NOSPACE);
610
pp.state |= ppstate;
611
if (*pun) debug((-4, "ppexpr() = %luU", n));
612
else debug((-4, "ppexpr() = %ld", n));
613
return n;
614
}
615
616
/*
617
* return non-zero if option s is set
618
*/
619
620
int
621
ppoption(char* s)
622
{
623
switch ((int)hashget(pp.strtab, s))
624
{
625
case X_ALLMULTIPLE:
626
return pp.mode & ALLMULTIPLE;
627
case X_BUILTIN:
628
return pp.mode & BUILTIN;
629
case X_CATLITERAL:
630
return pp.mode & CATLITERAL;
631
case X_COMPATIBILITY:
632
return pp.state & COMPATIBILITY;
633
case X_DEBUG:
634
return -error_info.trace;
635
case X_ELSEIF:
636
return pp.option & ELSEIF;
637
case X_FINAL:
638
return pp.option & FINAL;
639
case X_HOSTDIR:
640
return pp.mode & HOSTED;
641
case X_HOSTED:
642
return pp.flags & PP_hosted;
643
case X_INITIAL:
644
return pp.option & INITIAL;
645
case X_KEYARGS:
646
return pp.option & KEYARGS;
647
case X_LINEBASE:
648
return pp.flags & PP_linebase;
649
case X_LINEFILE:
650
return pp.flags & PP_linefile;
651
case X_LINETYPE:
652
return pp.flags & PP_linetype;
653
case X_PLUSCOMMENT:
654
return pp.option & PLUSCOMMENT;
655
case X_PLUSPLUS:
656
return pp.option & PLUSPLUS;
657
case X_PLUSSPLICE:
658
return pp.option & PLUSSPLICE;
659
case X_PRAGMAEXPAND:
660
return pp.option & PRAGMAEXPAND;
661
case X_PREDEFINED:
662
return pp.option & PREDEFINED;
663
case X_PREFIX:
664
return pp.option & PREFIX;
665
case X_PROTOTYPED:
666
return pp.option & PROTOTYPED;
667
case X_READONLY:
668
return pp.mode & READONLY;
669
case X_REGUARD:
670
return pp.option & REGUARD;
671
case X_SPACEOUT:
672
return pp.state & SPACEOUT;
673
case X_SPLICECAT:
674
return pp.option & SPLICECAT;
675
case X_SPLICESPACE:
676
return pp.option & SPLICESPACE;
677
case X_STRICT:
678
return pp.state & STRICT;
679
case X_STRINGSPAN:
680
return pp.option & STRINGSPAN;
681
case X_STRINGSPLIT:
682
return pp.option & STRINGSPLIT;
683
case X_TEST:
684
return pp.test;
685
case X_TEXT:
686
return !(pp.state & NOTEXT);
687
case X_TRANSITION:
688
return pp.state & TRANSITION;
689
case X_TRUNCATE:
690
return pp.truncate;
691
case X_WARN:
692
return pp.state & WARN;
693
default:
694
if (pp.state & WARN) error(1, "%s: unknown option name", s);
695
return 0;
696
}
697
}
698
699