ifparser.c revision 0eb10989
1/*
2 * $Xorg: ifparser.c,v 1.3 2000/08/17 19:41:50 cpqbld Exp $
3 *
4 * Copyright 1992 Network Computing Devices, Inc.
5 *
6 * Permission to use, copy, modify, and distribute this software and its
7 * documentation for any purpose and without fee is hereby granted, provided
8 * that the above copyright notice appear in all copies and that both that
9 * copyright notice and this permission notice appear in supporting
10 * documentation, and that the name of Network Computing Devices may not be
11 * used in advertising or publicity pertaining to distribution of the software
12 * without specific, written prior permission.  Network Computing Devices makes
13 * no representations about the suitability of this software for any purpose.
14 * It is provided ``as is'' without express or implied warranty.
15 *
16 * NETWORK COMPUTING DEVICES DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
17 * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
18 * IN NO EVENT SHALL NETWORK COMPUTING DEVICES BE LIABLE FOR ANY SPECIAL,
19 * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
20 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
21 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
22 * PERFORMANCE OF THIS SOFTWARE.
23 *
24 * Author:  Jim Fulton
25 *          Network Computing Devices, Inc.
26 *
27 * Simple if statement processor
28 *
29 * This module can be used to evaluate string representations of C language
30 * if constructs.  It accepts the following grammar:
31 *
32 *     EXPRESSION	:=	VALUE
33 * 			 |	VALUE  BINOP	EXPRESSION
34 *			 |	VALUE	'?'	EXPRESSION ':'	EXPRESSION
35 *
36 *     VALUE		:=	'('  EXPRESSION  ')'
37 * 			 |	'!'  VALUE
38 * 			 |	'-'  VALUE
39 * 			 |	'+'  VALUE
40 *			 |	'~'  VALUE
41 * 			 |	'defined'  '('  variable  ')'
42 * 			 |	'defined'  variable
43 *			 |	# variable '(' variable-list ')'
44 * 			 |	variable
45 * 			 |	number
46 *
47 *     BINOP		:=	'*'	|  '/'	|  '%'
48 * 			 |	'+'	|  '-'
49 * 			 |	'<<'	|  '>>'
50 * 			 |	'<'	|  '>'	|  '<='  |  '>='
51 * 			 |	'=='	|  '!='
52 * 			 |	'&'	|  '^'  |  '|'
53 * 			 |	'&&'	|  '||'
54 *
55 * The normal C order of precedence is supported.
56 *
57 *
58 * External Entry Points:
59 *
60 *     ParseIfExpression		parse a string for #if
61 */
62/* $XFree86: xc/config/makedepend/ifparser.c,v 3.10tsi Exp $ */
63
64#include "ifparser.h"
65#include <ctype.h>
66#include <stdlib.h>
67#include <string.h>
68#include <limits.h>
69
70/****************************************************************************
71		   Internal Macros and Utilities for Parser
72 ****************************************************************************/
73
74#define DO(val) if (!(val)) return NULL
75#define CALLFUNC(ggg,fff) (*((ggg)->funcs.fff))
76#define SKIPSPACE(ccc) while (isspace(*ccc)) ccc++
77#define isvarfirstletter(ccc) (isalpha(ccc) || (ccc) == '_')
78
79
80static const char *
81parse_variable (IfParser *g, const char *cp, const char **varp)
82{
83    SKIPSPACE (cp);
84
85    if (!isvarfirstletter (*cp))
86	return CALLFUNC(g, handle_error) (g, cp, "variable name");
87
88    *varp = cp;
89    /* EMPTY */
90    for (cp++; isalnum(*cp) || *cp == '_'; cp++) ;
91    return cp;
92}
93
94
95static const char *
96parse_number (IfParser *g, const char *cp, long *valp)
97{
98    long base = 10;
99    SKIPSPACE (cp);
100
101    if (!isdigit(*cp))
102	return CALLFUNC(g, handle_error) (g, cp, "number");
103
104    *valp = 0;
105
106    if (*cp == '0') {
107	cp++;
108	if ((*cp == 'x') || (*cp == 'X')) {
109	    base = 16;
110	    cp++;
111	} else {
112	    base = 8;
113	}
114    }
115
116    /* Ignore overflows and assume ASCII, what source is usually written in */
117    while (1) {
118	int increment = -1;
119	if (base == 8) {
120	    if ((*cp >= '0') && (*cp <= '7'))
121		increment = *cp++ - '0';
122	} else if (base == 16) {
123	    if ((*cp >= '0') && (*cp <= '9'))
124		increment = *cp++ - '0';
125	    else if ((*cp >= 'A') &&  (*cp <= 'F'))
126		increment = *cp++ - ('A' - 10);
127	    else if ((*cp >= 'a') && (*cp <= 'f'))
128		increment = *cp++ - ('a' - 10);
129	} else {	/* Decimal */
130	    if ((*cp >= '0') && (*cp <= '9'))
131		increment = *cp++ - '0';
132	}
133	if (increment < 0)
134	    break;
135	*valp = (*valp * base) + increment;
136    }
137
138    /* Skip trailing qualifiers */
139    while (*cp == 'U' || *cp == 'u' || *cp == 'L' || *cp == 'l') cp++;
140    return cp;
141}
142
143static const char *
144parse_character (IfParser *g, const char *cp, long *valp)
145{
146    char val;
147
148    SKIPSPACE (cp);
149    if (*cp == '\\')
150	switch (cp[1]) {
151	case 'n': val = '\n'; break;
152	case 't': val = '\t'; break;
153	case 'v': val = '\v'; break;
154	case 'b': val = '\b'; break;
155	case 'r': val = '\r'; break;
156	case 'f': val = '\f'; break;
157	case 'a': val = '\a'; break;
158	case '\\': val = '\\'; break;
159	case '?': val = '\?'; break;
160	case '\'': val = '\''; break;
161	case '\"': val = '\"'; break;
162	case 'x': val = (char) strtol (cp + 2, NULL, 16); break;
163	default: val = (char) strtol (cp + 1, NULL, 8); break;
164	}
165    else
166	val = *cp;
167    while (*cp != '\'') cp++;
168    *valp = (long) val;
169    return cp;
170}
171
172static const char *
173parse_value (IfParser *g, const char *cp, long *valp)
174{
175    const char *var, *varend;
176
177    *valp = 0;
178
179    SKIPSPACE (cp);
180    if (!*cp)
181	return cp;
182
183    switch (*cp) {
184      case '(':
185	DO (cp = ParseIfExpression (g, cp + 1, valp));
186	SKIPSPACE (cp);
187	if (*cp != ')')
188	    return CALLFUNC(g, handle_error) (g, cp, ")");
189
190	return cp + 1;			/* skip the right paren */
191
192      case '!':
193	DO (cp = parse_value (g, cp + 1, valp));
194	*valp = !(*valp);
195	return cp;
196
197      case '-':
198	DO (cp = parse_value (g, cp + 1, valp));
199	*valp = -(*valp);
200	return cp;
201
202      case '+':
203	DO (cp = parse_value (g, cp + 1, valp));
204	return cp;
205
206      case '~':
207	DO (cp = parse_value (g, cp + 1, valp));
208	*valp = ~(*valp);
209	return cp;
210
211      case '#':
212	DO (cp = parse_variable (g, cp + 1, &var));
213	SKIPSPACE (cp);
214	if (*cp != '(')
215	    return CALLFUNC(g, handle_error) (g, cp, "(");
216	do {
217	    DO (cp = parse_variable (g, cp + 1, &var));
218	    SKIPSPACE (cp);
219	} while (*cp && *cp != ')');
220	if (*cp != ')')
221	    return CALLFUNC(g, handle_error) (g, cp, ")");
222	*valp = 1; /* XXX */
223	return cp + 1;
224
225      case '\'':
226	DO (cp = parse_character (g, cp + 1, valp));
227	if (*cp != '\'')
228	    return CALLFUNC(g, handle_error) (g, cp, "'");
229	return cp + 1;
230
231      case 'd':
232	if (strncmp (cp, "defined", 7) == 0 && !isalnum(cp[7])) {
233	    int paren = 0;
234	    int len;
235
236	    cp += 7;
237	    SKIPSPACE (cp);
238	    if (*cp == '(') {
239		paren = 1;
240		cp++;
241	    }
242	    DO (cp = parse_variable (g, cp, &var));
243	    len = cp - var;
244	    SKIPSPACE (cp);
245	    if (paren && *cp != ')')
246		return CALLFUNC(g, handle_error) (g, cp, ")");
247	    *valp = (*(g->funcs.eval_defined)) (g, var, len);
248	    return cp + paren;		/* skip the right paren */
249	}
250	/* fall out */
251    }
252
253    if (isdigit(*cp)) {
254	DO (cp = parse_number (g, cp, valp));
255    } else if (!isvarfirstletter(*cp))
256	return CALLFUNC(g, handle_error) (g, cp, "variable or number");
257    else {
258	DO (cp = parse_variable (g, cp, &var));
259	varend = cp;
260	SKIPSPACE(cp);
261	if (*cp != '(') {
262	    *valp = (*(g->funcs.eval_variable)) (g, var, varend - var);
263	} else {
264	    do {
265		long dummy;
266		DO (cp = ParseIfExpression (g, cp + 1, &dummy));
267		SKIPSPACE(cp);
268		if (*cp == ')')
269		    break;
270		if (*cp != ',')
271		    return CALLFUNC(g, handle_error) (g, cp, ",");
272	    } while (1);
273
274	    *valp = 1;	/* XXX */
275	    cp++;
276	}
277    }
278
279    return cp;
280}
281
282
283
284static const char *
285parse_product (IfParser *g, const char *cp, long *valp)
286{
287    long rightval;
288
289    DO (cp = parse_value (g, cp, valp));
290    SKIPSPACE (cp);
291
292    switch (*cp) {
293      case '*':
294	DO (cp = parse_product (g, cp + 1, &rightval));
295	*valp = (*valp * rightval);
296	break;
297
298      case '/':
299	DO (cp = parse_product (g, cp + 1, &rightval));
300    if (rightval)
301	    *valp = (*valp / rightval);
302    else
303        *valp = LONG_MAX;
304	break;
305
306      case '%':
307	DO (cp = parse_product (g, cp + 1, &rightval));
308	*valp = (*valp % rightval);
309	break;
310    }
311    return cp;
312}
313
314
315static const char *
316parse_sum (IfParser *g, const char *cp, long *valp)
317{
318    long rightval;
319
320    DO (cp = parse_product (g, cp, valp));
321    SKIPSPACE (cp);
322
323    switch (*cp) {
324      case '+':
325	DO (cp = parse_sum (g, cp + 1, &rightval));
326	*valp = (*valp + rightval);
327	break;
328
329      case '-':
330	DO (cp = parse_sum (g, cp + 1, &rightval));
331	*valp = (*valp - rightval);
332	break;
333    }
334    return cp;
335}
336
337
338static const char *
339parse_shift (IfParser *g, const char *cp, long *valp)
340{
341    long rightval;
342
343    DO (cp = parse_sum (g, cp, valp));
344    SKIPSPACE (cp);
345
346    switch (*cp) {
347      case '<':
348	if (cp[1] == '<') {
349	    DO (cp = parse_shift (g, cp + 2, &rightval));
350	    *valp = (*valp << rightval);
351	}
352	break;
353
354      case '>':
355	if (cp[1] == '>') {
356	    DO (cp = parse_shift (g, cp + 2, &rightval));
357	    *valp = (*valp >> rightval);
358	}
359	break;
360    }
361    return cp;
362}
363
364
365static const char *
366parse_inequality (IfParser *g, const char *cp, long *valp)
367{
368    long rightval;
369
370    DO (cp = parse_shift (g, cp, valp));
371    SKIPSPACE (cp);
372
373    switch (*cp) {
374      case '<':
375	if (cp[1] == '=') {
376	    DO (cp = parse_inequality (g, cp + 2, &rightval));
377	    *valp = (*valp <= rightval);
378	} else {
379	    DO (cp = parse_inequality (g, cp + 1, &rightval));
380	    *valp = (*valp < rightval);
381	}
382	break;
383
384      case '>':
385	if (cp[1] == '=') {
386	    DO (cp = parse_inequality (g, cp + 2, &rightval));
387	    *valp = (*valp >= rightval);
388	} else {
389	    DO (cp = parse_inequality (g, cp + 1, &rightval));
390	    *valp = (*valp > rightval);
391	}
392	break;
393    }
394    return cp;
395}
396
397
398static const char *
399parse_equality (IfParser *g, const char *cp, long *valp)
400{
401    long rightval;
402
403    DO (cp = parse_inequality (g, cp, valp));
404    SKIPSPACE (cp);
405
406    switch (*cp) {
407      case '=':
408	if (cp[1] == '=')
409	    cp++;
410	DO (cp = parse_equality (g, cp + 1, &rightval));
411	*valp = (*valp == rightval);
412	break;
413
414      case '!':
415	if (cp[1] != '=')
416	    break;
417	DO (cp = parse_equality (g, cp + 2, &rightval));
418	*valp = (*valp != rightval);
419	break;
420    }
421    return cp;
422}
423
424
425static const char *
426parse_band (IfParser *g, const char *cp, long *valp)
427{
428    long rightval;
429
430    DO (cp = parse_equality (g, cp, valp));
431    SKIPSPACE (cp);
432
433    switch (*cp) {
434      case '&':
435	if (cp[1] != '&') {
436	    DO (cp = parse_band (g, cp + 1, &rightval));
437	    *valp = (*valp & rightval);
438	}
439	break;
440    }
441    return cp;
442}
443
444
445static const char *
446parse_bxor (IfParser *g, const char *cp, long *valp)
447{
448    long rightval;
449
450    DO (cp = parse_band (g, cp, valp));
451    SKIPSPACE (cp);
452
453    switch (*cp) {
454      case '^':
455	DO (cp = parse_bxor (g, cp + 1, &rightval));
456	*valp = (*valp ^ rightval);
457	break;
458    }
459    return cp;
460}
461
462
463static const char *
464parse_bor (IfParser *g, const char *cp, long *valp)
465{
466    long rightval;
467
468    DO (cp = parse_bxor (g, cp, valp));
469    SKIPSPACE (cp);
470
471    switch (*cp) {
472      case '|':
473	if (cp[1] != '|') {
474	    DO (cp = parse_bor (g, cp + 1, &rightval));
475	    *valp = (*valp | rightval);
476	}
477	break;
478    }
479    return cp;
480}
481
482
483static const char *
484parse_land (IfParser *g, const char *cp, long *valp)
485{
486    long rightval;
487
488    DO (cp = parse_bor (g, cp, valp));
489    SKIPSPACE (cp);
490
491    switch (*cp) {
492      case '&':
493	if (cp[1] != '&')
494	    return CALLFUNC(g, handle_error) (g, cp, "&&");
495	DO (cp = parse_land (g, cp + 2, &rightval));
496	*valp = (*valp && rightval);
497	break;
498    }
499    return cp;
500}
501
502
503static const char *
504parse_lor (IfParser *g, const char *cp, long *valp)
505{
506    long rightval;
507
508    DO (cp = parse_land (g, cp, valp));
509    SKIPSPACE (cp);
510
511    switch (*cp) {
512      case '|':
513	if (cp[1] != '|')
514	    return CALLFUNC(g, handle_error) (g, cp, "||");
515	DO (cp = parse_lor (g, cp + 2, &rightval));
516	*valp = (*valp || rightval);
517	break;
518    }
519    return cp;
520}
521
522
523static const char *
524parse_cond(IfParser *g, const char *cp, long *valp)
525{
526    long trueval, falseval;
527
528    DO (cp = parse_lor (g, cp, valp));
529    SKIPSPACE (cp);
530
531    switch (*cp) {
532      case '?':
533	DO (cp = parse_cond (g, cp + 1, &trueval));
534	SKIPSPACE (cp);
535	if (*cp != ':')
536	    return CALLFUNC(g, handle_error) (g, cp, ":");
537	DO (cp = parse_cond (g, cp + 1, &falseval));
538	*valp = (*valp ? trueval : falseval);
539	break;
540    }
541    return cp;
542}
543
544
545/****************************************************************************
546			     External Entry Points
547 ****************************************************************************/
548
549const char *
550ParseIfExpression (IfParser *g, const char *cp, long *valp)
551{
552    return parse_cond (g, cp, valp);
553}
554