1 /*
2 * $XConsortium: ifparser.c,v 1.8 95/06/03 00:01:41 gildea 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 *
35 * VALUE := '(' EXPRESSION ')'
36 * | '!' VALUE
37 * | '-' VALUE
38 * | 'defined' '(' variable ')'
39 * | 'defined' variable
40 * | # variable '(' variable-list ')'
41 * | variable
42 * | number
43 *
44 * BINOP := '*' | '/' | '%'
45 * | '+' | '-'
46 * | '<<' | '>>'
47 * | '<' | '>' | '<=' | '>='
48 * | '==' | '!='
49 * | '&' | '|'
50 * | '&&' | '||'
51 *
52 * The normal C order of precidence is supported.
53 *
54 *
55 * External Entry Points:
56 *
57 * ParseIfExpression parse a string for #if
58 */
59
60 #include "ifparser.h"
61 #include <ctype.h>
62 #include <stdlib.h>
63 #include <string.h>
64
65 /****************************************************************************
66 Internal Macros and Utilities for Parser
67 ****************************************************************************/
68
69 #define DO(val) if (!(val)) return NULL
70 #define CALLFUNC(ggg,fff) (*((ggg)->funcs.fff))
71 #define SKIPSPACE(ccc) while (isspace(*ccc)) ccc++
72 #define isvarfirstletter(ccc) (isalpha(ccc) || (ccc) == '_')
73
74
75 static const char *
parse_variable(g,cp,varp)76 parse_variable (g, cp, varp)
77 IfParser *g;
78 const char *cp;
79 const char **varp;
80 {
81 SKIPSPACE (cp);
82
83 if (!isvarfirstletter (*cp))
84 return CALLFUNC(g, handle_error) (g, cp, "variable name");
85
86 *varp = cp;
87 /* EMPTY */
88 for (cp++; isalnum(*cp) || *cp == '_'; cp++) ;
89 return cp;
90 }
91
92
93 static const char *
parse_number(g,cp,valp)94 parse_number (g, cp, valp)
95 IfParser *g;
96 const char *cp;
97 int *valp;
98 {
99 SKIPSPACE (cp);
100
101 if (!isdigit(*cp))
102 return CALLFUNC(g, handle_error) (g, cp, "number");
103
104 #ifdef WIN32
105 *valp = strtol(cp, &cp, 0);
106 #else
107 *valp = atoi (cp);
108 /* EMPTY */
109 for (cp++; isdigit(*cp); cp++) ;
110 #endif
111 return cp;
112 }
113
114
115 static const char *
parse_value(g,cp,valp)116 parse_value (g, cp, valp)
117 IfParser *g;
118 const char *cp;
119 int *valp;
120 {
121 const char *var;
122
123 *valp = 0;
124
125 SKIPSPACE (cp);
126 if (!*cp)
127 return cp;
128
129 switch (*cp) {
130 case '(':
131 DO (cp = ParseIfExpression (g, cp + 1, valp));
132 SKIPSPACE (cp);
133 if (*cp != ')')
134 return CALLFUNC(g, handle_error) (g, cp, ")");
135
136 return cp + 1; /* skip the right paren */
137
138 case '!':
139 DO (cp = parse_value (g, cp + 1, valp));
140 *valp = !(*valp);
141 return cp;
142
143 case '-':
144 DO (cp = parse_value (g, cp + 1, valp));
145 *valp = -(*valp);
146 return cp;
147
148 case '#':
149 DO (cp = parse_variable (g, cp + 1, &var));
150 SKIPSPACE (cp);
151 if (*cp != '(')
152 return CALLFUNC(g, handle_error) (g, cp, "(");
153 do {
154 DO (cp = parse_variable (g, cp + 1, &var));
155 SKIPSPACE (cp);
156 } while (*cp && *cp != ')');
157 if (*cp != ')')
158 return CALLFUNC(g, handle_error) (g, cp, ")");
159 *valp = 1; /* XXX */
160 return cp + 1;
161
162 case 'd':
163 if (strncmp (cp, "defined", 7) == 0 && !isalnum(cp[7])) {
164 int paren = 0;
165 int len;
166
167 cp += 7;
168 SKIPSPACE (cp);
169 if (*cp == '(') {
170 paren = 1;
171 cp++;
172 }
173 DO (cp = parse_variable (g, cp, &var));
174 len = cp - var;
175 SKIPSPACE (cp);
176 if (paren && *cp != ')')
177 return CALLFUNC(g, handle_error) (g, cp, ")");
178 *valp = (*(g->funcs.eval_defined)) (g, var, len);
179 return cp + paren; /* skip the right paren */
180 }
181 /* fall out */
182 }
183
184 if (isdigit(*cp)) {
185 DO (cp = parse_number (g, cp, valp));
186 } else if (!isvarfirstletter(*cp))
187 return CALLFUNC(g, handle_error) (g, cp, "variable or number");
188 else {
189 DO (cp = parse_variable (g, cp, &var));
190 *valp = (*(g->funcs.eval_variable)) (g, var, cp - var);
191 }
192
193 return cp;
194 }
195
196
197
198 static const char *
parse_product(g,cp,valp)199 parse_product (g, cp, valp)
200 IfParser *g;
201 const char *cp;
202 int *valp;
203 {
204 int rightval;
205
206 DO (cp = parse_value (g, cp, valp));
207 SKIPSPACE (cp);
208
209 switch (*cp) {
210 case '*':
211 DO (cp = parse_product (g, cp + 1, &rightval));
212 *valp = (*valp * rightval);
213 break;
214
215 case '/':
216 DO (cp = parse_product (g, cp + 1, &rightval));
217
218 /* Do nothing in the divide-by-zero case. */
219 if (rightval) {
220 *valp = (*valp / rightval);
221 }
222 break;
223
224 case '%':
225 DO (cp = parse_product (g, cp + 1, &rightval));
226 *valp = (*valp % rightval);
227 break;
228 }
229 return cp;
230 }
231
232
233 static const char *
parse_sum(g,cp,valp)234 parse_sum (g, cp, valp)
235 IfParser *g;
236 const char *cp;
237 int *valp;
238 {
239 int rightval;
240
241 DO (cp = parse_product (g, cp, valp));
242 SKIPSPACE (cp);
243
244 switch (*cp) {
245 case '+':
246 DO (cp = parse_sum (g, cp + 1, &rightval));
247 *valp = (*valp + rightval);
248 break;
249
250 case '-':
251 DO (cp = parse_sum (g, cp + 1, &rightval));
252 *valp = (*valp - rightval);
253 break;
254 }
255 return cp;
256 }
257
258
259 static const char *
parse_shift(g,cp,valp)260 parse_shift (g, cp, valp)
261 IfParser *g;
262 const char *cp;
263 int *valp;
264 {
265 int rightval;
266
267 DO (cp = parse_sum (g, cp, valp));
268 SKIPSPACE (cp);
269
270 switch (*cp) {
271 case '<':
272 if (cp[1] == '<') {
273 DO (cp = parse_shift (g, cp + 2, &rightval));
274 *valp = (*valp << rightval);
275 }
276 break;
277
278 case '>':
279 if (cp[1] == '>') {
280 DO (cp = parse_shift (g, cp + 2, &rightval));
281 *valp = (*valp >> rightval);
282 }
283 break;
284 }
285 return cp;
286 }
287
288
289 static const char *
parse_inequality(g,cp,valp)290 parse_inequality (g, cp, valp)
291 IfParser *g;
292 const char *cp;
293 int *valp;
294 {
295 int rightval;
296
297 DO (cp = parse_shift (g, cp, valp));
298 SKIPSPACE (cp);
299
300 switch (*cp) {
301 case '<':
302 if (cp[1] == '=') {
303 DO (cp = parse_inequality (g, cp + 2, &rightval));
304 *valp = (*valp <= rightval);
305 } else {
306 DO (cp = parse_inequality (g, cp + 1, &rightval));
307 *valp = (*valp < rightval);
308 }
309 break;
310
311 case '>':
312 if (cp[1] == '=') {
313 DO (cp = parse_inequality (g, cp + 2, &rightval));
314 *valp = (*valp >= rightval);
315 } else {
316 DO (cp = parse_inequality (g, cp + 1, &rightval));
317 *valp = (*valp > rightval);
318 }
319 break;
320 }
321 return cp;
322 }
323
324
325 static const char *
parse_equality(g,cp,valp)326 parse_equality (g, cp, valp)
327 IfParser *g;
328 const char *cp;
329 int *valp;
330 {
331 int rightval;
332
333 DO (cp = parse_inequality (g, cp, valp));
334 SKIPSPACE (cp);
335
336 switch (*cp) {
337 case '=':
338 if (cp[1] == '=')
339 cp++;
340 DO (cp = parse_equality (g, cp + 1, &rightval));
341 *valp = (*valp == rightval);
342 break;
343
344 case '!':
345 if (cp[1] != '=')
346 break;
347 DO (cp = parse_equality (g, cp + 2, &rightval));
348 *valp = (*valp != rightval);
349 break;
350 }
351 return cp;
352 }
353
354
355 static const char *
parse_band(g,cp,valp)356 parse_band (g, cp, valp)
357 IfParser *g;
358 const char *cp;
359 int *valp;
360 {
361 int rightval;
362
363 DO (cp = parse_equality (g, cp, valp));
364 SKIPSPACE (cp);
365
366 switch (*cp) {
367 case '&':
368 if (cp[1] != '&') {
369 DO (cp = parse_band (g, cp + 1, &rightval));
370 *valp = (*valp & rightval);
371 }
372 break;
373 }
374 return cp;
375 }
376
377
378 static const char *
parse_bor(g,cp,valp)379 parse_bor (g, cp, valp)
380 IfParser *g;
381 const char *cp;
382 int *valp;
383 {
384 int rightval;
385
386 DO (cp = parse_band (g, cp, valp));
387 SKIPSPACE (cp);
388
389 switch (*cp) {
390 case '|':
391 if (cp[1] != '|') {
392 DO (cp = parse_bor (g, cp + 1, &rightval));
393 *valp = (*valp | rightval);
394 }
395 break;
396 }
397 return cp;
398 }
399
400
401 static const char *
parse_land(g,cp,valp)402 parse_land (g, cp, valp)
403 IfParser *g;
404 const char *cp;
405 int *valp;
406 {
407 int rightval;
408
409 DO (cp = parse_bor (g, cp, valp));
410 SKIPSPACE (cp);
411
412 switch (*cp) {
413 case '&':
414 if (cp[1] != '&')
415 return CALLFUNC(g, handle_error) (g, cp, "&&");
416 DO (cp = parse_land (g, cp + 2, &rightval));
417 *valp = (*valp && rightval);
418 break;
419 }
420 return cp;
421 }
422
423
424 static const char *
parse_lor(g,cp,valp)425 parse_lor (g, cp, valp)
426 IfParser *g;
427 const char *cp;
428 int *valp;
429 {
430 int rightval;
431
432 DO (cp = parse_land (g, cp, valp));
433 SKIPSPACE (cp);
434
435 switch (*cp) {
436 case '|':
437 if (cp[1] != '|')
438 return CALLFUNC(g, handle_error) (g, cp, "||");
439 DO (cp = parse_lor (g, cp + 2, &rightval));
440 *valp = (*valp || rightval);
441 break;
442 }
443 return cp;
444 }
445
446
447 /****************************************************************************
448 External Entry Points
449 ****************************************************************************/
450
451 const char *
ParseIfExpression(g,cp,valp)452 ParseIfExpression (g, cp, valp)
453 IfParser *g;
454 const char *cp;
455 int *valp;
456 {
457 return parse_lor (g, cp, valp);
458 }
459
460
461