Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * $XConsortium: ifparser.c,v 1.8 95/06/03 00:01:41 gildea Exp $
4 : *
5 : * Copyright 1992 Network Computing Devices, Inc.
6 : *
7 : * Permission to use, copy, modify, and distribute this software and its
8 : * documentation for any purpose and without fee is hereby granted, provided
9 : * that the above copyright notice appear in all copies and that both that
10 : * copyright notice and this permission notice appear in supporting
11 : * documentation, and that the name of Network Computing Devices may not be
12 : * used in advertising or publicity pertaining to distribution of the software
13 : * without specific, written prior permission. Network Computing Devices makes
14 : * no representations about the suitability of this software for any purpose.
15 : * It is provided ``as is'' without express or implied warranty.
16 : *
17 : * NETWORK COMPUTING DEVICES DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
18 : * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
19 : * IN NO EVENT SHALL NETWORK COMPUTING DEVICES BE LIABLE FOR ANY SPECIAL,
20 : * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
21 : * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
22 : * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
23 : * PERFORMANCE OF THIS SOFTWARE.
24 : *
25 : * Author: Jim Fulton
26 : * Network Computing Devices, Inc.
27 : *
28 : * Simple if statement processor
29 : *
30 : * This module can be used to evaluate string representations of C language
31 : * if constructs. It accepts the following grammar:
32 : *
33 : * EXPRESSION := VALUE
34 : * | VALUE BINOP EXPRESSION
35 : *
36 : * VALUE := '(' EXPRESSION ')'
37 : * | '!' VALUE
38 : * | '-' VALUE
39 : * | 'defined' '(' variable ')'
40 : * | 'defined' variable
41 : * | # variable '(' variable-list ')'
42 : * | variable
43 : * | number
44 : *
45 : * BINOP := '*' | '/' | '%'
46 : * | '+' | '-'
47 : * | '<<' | '>>'
48 : * | '<' | '>' | '<=' | '>='
49 : * | '==' | '!='
50 : * | '&' | '|'
51 : * | '&&' | '||'
52 : *
53 : * The normal C order of precidence is supported.
54 : *
55 : *
56 : * External Entry Points:
57 : *
58 : * ParseIfExpression parse a string for #if
59 : */
60 :
61 : #include "ifparser.h"
62 : #include <ctype.h>
63 : #include <stdlib.h>
64 : #include <string.h>
65 :
66 : /****************************************************************************
67 : Internal Macros and Utilities for Parser
68 : ****************************************************************************/
69 :
70 : #define DO(val) if (!(val)) return NULL
71 : #define CALLFUNC(ggg,fff) (*((ggg)->funcs.fff))
72 : #define SKIPSPACE(ccc) while (isspace(*ccc)) ccc++
73 : #define isvarfirstletter(ccc) (isalpha(ccc) || (ccc) == '_')
74 :
75 :
76 : static const char *
77 68 : parse_variable (g, cp, varp)
78 : IfParser *g;
79 : const char *cp;
80 : const char **varp;
81 : {
82 68 : SKIPSPACE (cp);
83 :
84 68 : if (!isvarfirstletter (*cp))
85 0 : return CALLFUNC(g, handle_error) (g, cp, "variable name");
86 :
87 68 : *varp = cp;
88 : /* EMPTY */
89 68 : for (cp++; isalnum(*cp) || *cp == '_'; cp++) ;
90 68 : return cp;
91 : }
92 :
93 :
94 : static const char *
95 23 : parse_number (g, cp, valp)
96 : IfParser *g;
97 : const char *cp;
98 : int *valp;
99 : {
100 23 : SKIPSPACE (cp);
101 :
102 23 : if (!isdigit(*cp))
103 0 : return CALLFUNC(g, handle_error) (g, cp, "number");
104 :
105 : #ifdef WIN32
106 : {
107 : char *cp2;
108 : *valp = strtol(cp, &cp2, 0);
109 : }
110 : #else
111 23 : *valp = atoi (cp);
112 : /* EMPTY */
113 23 : for (cp++; isdigit(*cp); cp++) ;
114 : #endif
115 23 : return cp;
116 : }
117 :
118 :
119 : static const char *
120 104 : parse_value (g, cp, valp)
121 : IfParser *g;
122 : const char *cp;
123 : int *valp;
124 : {
125 : const char *var;
126 :
127 104 : *valp = 0;
128 :
129 104 : SKIPSPACE (cp);
130 104 : if (!*cp)
131 0 : return cp;
132 :
133 104 : switch (*cp) {
134 : case '(':
135 11 : DO (cp = ParseIfExpression (g, cp + 1, valp));
136 11 : SKIPSPACE (cp);
137 11 : if (*cp != ')')
138 3 : return CALLFUNC(g, handle_error) (g, cp, ")");
139 :
140 8 : return cp + 1; /* skip the right paren */
141 :
142 : case '!':
143 2 : DO (cp = parse_value (g, cp + 1, valp));
144 2 : *valp = !(*valp);
145 2 : return cp;
146 :
147 : case '-':
148 0 : DO (cp = parse_value (g, cp + 1, valp));
149 0 : *valp = -(*valp);
150 0 : return cp;
151 :
152 : case '#':
153 0 : DO (cp = parse_variable (g, cp + 1, &var));
154 0 : SKIPSPACE (cp);
155 0 : if (*cp != '(')
156 0 : return CALLFUNC(g, handle_error) (g, cp, "(");
157 : do {
158 0 : DO (cp = parse_variable (g, cp + 1, &var));
159 0 : SKIPSPACE (cp);
160 0 : } while (*cp && *cp != ')');
161 0 : if (*cp != ')')
162 0 : return CALLFUNC(g, handle_error) (g, cp, ")");
163 0 : *valp = 1; /* XXX */
164 0 : return cp + 1;
165 :
166 : case 'd':
167 44 : if (strncmp (cp, "defined", 7) == 0 && !isalnum(cp[7])) {
168 44 : int paren = 0;
169 : int len;
170 :
171 44 : cp += 7;
172 44 : SKIPSPACE (cp);
173 44 : if (*cp == '(') {
174 36 : paren = 1;
175 36 : cp++;
176 : }
177 44 : DO (cp = parse_variable (g, cp, &var));
178 44 : len = (int)(cp - var);
179 44 : SKIPSPACE (cp);
180 44 : if (paren && *cp != ')')
181 0 : return CALLFUNC(g, handle_error) (g, cp, ")");
182 44 : *valp = (*(g->funcs.eval_defined)) (g, var, len);
183 44 : return cp + paren; /* skip the right paren */
184 : }
185 : /* fall out */
186 : }
187 :
188 47 : if (isdigit(*cp)) {
189 23 : DO (cp = parse_number (g, cp, valp));
190 24 : } else if (!isvarfirstletter(*cp))
191 0 : return CALLFUNC(g, handle_error) (g, cp, "variable or number");
192 : else {
193 24 : DO (cp = parse_variable (g, cp, &var));
194 24 : *valp = (*(g->funcs.eval_variable)) (g, var, cp - var);
195 : }
196 :
197 47 : return cp;
198 : }
199 :
200 :
201 :
202 : static const char *
203 102 : parse_product (g, cp, valp)
204 : IfParser *g;
205 : const char *cp;
206 : int *valp;
207 : {
208 : int rightval;
209 :
210 102 : DO (cp = parse_value (g, cp, valp));
211 99 : SKIPSPACE (cp);
212 :
213 99 : switch (*cp) {
214 : case '*':
215 0 : DO (cp = parse_product (g, cp + 1, &rightval));
216 0 : *valp = (*valp * rightval);
217 0 : break;
218 :
219 : case '/':
220 0 : DO (cp = parse_product (g, cp + 1, &rightval));
221 :
222 : /* Do nothing in the divide-by-zero case. */
223 0 : if (rightval) {
224 0 : *valp = (*valp / rightval);
225 : }
226 0 : break;
227 :
228 : case '%':
229 0 : DO (cp = parse_product (g, cp + 1, &rightval));
230 0 : *valp = (*valp % rightval);
231 0 : break;
232 : }
233 99 : return cp;
234 : }
235 :
236 :
237 : static const char *
238 102 : parse_sum (g, cp, valp)
239 : IfParser *g;
240 : const char *cp;
241 : int *valp;
242 : {
243 : int rightval;
244 :
245 102 : DO (cp = parse_product (g, cp, valp));
246 99 : SKIPSPACE (cp);
247 :
248 99 : switch (*cp) {
249 : case '+':
250 0 : DO (cp = parse_sum (g, cp + 1, &rightval));
251 0 : *valp = (*valp + rightval);
252 0 : break;
253 :
254 : case '-':
255 0 : DO (cp = parse_sum (g, cp + 1, &rightval));
256 0 : *valp = (*valp - rightval);
257 0 : break;
258 : }
259 99 : return cp;
260 : }
261 :
262 :
263 : static const char *
264 102 : parse_shift (g, cp, valp)
265 : IfParser *g;
266 : const char *cp;
267 : int *valp;
268 : {
269 : int rightval;
270 :
271 102 : DO (cp = parse_sum (g, cp, valp));
272 99 : SKIPSPACE (cp);
273 :
274 99 : switch (*cp) {
275 : case '<':
276 0 : if (cp[1] == '<') {
277 0 : DO (cp = parse_shift (g, cp + 2, &rightval));
278 0 : *valp = (*valp << rightval);
279 : }
280 0 : break;
281 :
282 : case '>':
283 9 : if (cp[1] == '>') {
284 0 : DO (cp = parse_shift (g, cp + 2, &rightval));
285 0 : *valp = (*valp >> rightval);
286 : }
287 9 : break;
288 : }
289 99 : return cp;
290 : }
291 :
292 :
293 : static const char *
294 102 : parse_inequality (g, cp, valp)
295 : IfParser *g;
296 : const char *cp;
297 : int *valp;
298 : {
299 : int rightval;
300 :
301 102 : DO (cp = parse_shift (g, cp, valp));
302 99 : SKIPSPACE (cp);
303 :
304 99 : switch (*cp) {
305 : case '<':
306 0 : if (cp[1] == '=') {
307 0 : DO (cp = parse_inequality (g, cp + 2, &rightval));
308 0 : *valp = (*valp <= rightval);
309 : } else {
310 0 : DO (cp = parse_inequality (g, cp + 1, &rightval));
311 0 : *valp = (*valp < rightval);
312 : }
313 0 : break;
314 :
315 : case '>':
316 9 : if (cp[1] == '=') {
317 6 : DO (cp = parse_inequality (g, cp + 2, &rightval));
318 6 : *valp = (*valp >= rightval);
319 : } else {
320 3 : DO (cp = parse_inequality (g, cp + 1, &rightval));
321 3 : *valp = (*valp > rightval);
322 : }
323 9 : break;
324 : }
325 99 : return cp;
326 : }
327 :
328 :
329 : static const char *
330 93 : parse_equality (g, cp, valp)
331 : IfParser *g;
332 : const char *cp;
333 : int *valp;
334 : {
335 : int rightval;
336 :
337 93 : DO (cp = parse_inequality (g, cp, valp));
338 90 : SKIPSPACE (cp);
339 :
340 90 : switch (*cp) {
341 : case '=':
342 14 : if (cp[1] == '=')
343 14 : cp++;
344 14 : DO (cp = parse_equality (g, cp + 1, &rightval));
345 14 : *valp = (*valp == rightval);
346 14 : break;
347 :
348 : case '!':
349 0 : if (cp[1] != '=')
350 0 : break;
351 0 : DO (cp = parse_equality (g, cp + 2, &rightval));
352 0 : *valp = (*valp != rightval);
353 0 : break;
354 : }
355 90 : return cp;
356 : }
357 :
358 :
359 : static const char *
360 79 : parse_band (g, cp, valp)
361 : IfParser *g;
362 : const char *cp;
363 : int *valp;
364 : {
365 : int rightval;
366 :
367 79 : DO (cp = parse_equality (g, cp, valp));
368 76 : SKIPSPACE (cp);
369 :
370 76 : switch (*cp) {
371 : case '&':
372 9 : if (cp[1] != '&') {
373 0 : DO (cp = parse_band (g, cp + 1, &rightval));
374 0 : *valp = (*valp & rightval);
375 : }
376 9 : break;
377 : }
378 76 : return cp;
379 : }
380 :
381 :
382 : static const char *
383 79 : parse_bor (g, cp, valp)
384 : IfParser *g;
385 : const char *cp;
386 : int *valp;
387 : {
388 : int rightval;
389 :
390 79 : DO (cp = parse_band (g, cp, valp));
391 76 : SKIPSPACE (cp);
392 :
393 76 : switch (*cp) {
394 : case '|':
395 17 : if (cp[1] != '|') {
396 0 : DO (cp = parse_bor (g, cp + 1, &rightval));
397 0 : *valp = (*valp | rightval);
398 : }
399 17 : break;
400 : }
401 76 : return cp;
402 : }
403 :
404 :
405 : static const char *
406 79 : parse_land (g, cp, valp)
407 : IfParser *g;
408 : const char *cp;
409 : int *valp;
410 : {
411 : int rightval;
412 :
413 79 : DO (cp = parse_bor (g, cp, valp));
414 76 : SKIPSPACE (cp);
415 :
416 76 : switch (*cp) {
417 : case '&':
418 9 : if (cp[1] != '&')
419 0 : return CALLFUNC(g, handle_error) (g, cp, "&&");
420 9 : DO (cp = parse_land (g, cp + 2, &rightval));
421 6 : *valp = (*valp && rightval);
422 6 : break;
423 : }
424 73 : return cp;
425 : }
426 :
427 :
428 : static const char *
429 70 : parse_lor (g, cp, valp)
430 : IfParser *g;
431 : const char *cp;
432 : int *valp;
433 : {
434 : int rightval;
435 :
436 70 : DO (cp = parse_land (g, cp, valp));
437 67 : SKIPSPACE (cp);
438 :
439 67 : switch (*cp) {
440 : case '|':
441 17 : if (cp[1] != '|')
442 0 : return CALLFUNC(g, handle_error) (g, cp, "||");
443 17 : DO (cp = parse_lor (g, cp + 2, &rightval));
444 17 : *valp = (*valp || rightval);
445 17 : break;
446 : }
447 67 : return cp;
448 : }
449 :
450 :
451 : /****************************************************************************
452 : External Entry Points
453 : ****************************************************************************/
454 :
455 : const char *
456 53 : ParseIfExpression (g, cp, valp)
457 : IfParser *g;
458 : const char *cp;
459 : int *valp;
460 : {
461 53 : return parse_lor (g, cp, valp);
462 : }
463 :
464 :
465 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|