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 0 : parse_variable (g, cp, varp)
78 : IfParser *g;
79 : const char *cp;
80 : const char **varp;
81 : {
82 0 : SKIPSPACE (cp);
83 :
84 0 : if (!isvarfirstletter (*cp))
85 0 : return CALLFUNC(g, handle_error) (g, cp, "variable name");
86 :
87 0 : *varp = cp;
88 : /* EMPTY */
89 0 : for (cp++; isalnum(*cp) || *cp == '_'; cp++) ;
90 0 : return cp;
91 : }
92 :
93 :
94 : static const char *
95 0 : parse_number (g, cp, valp)
96 : IfParser *g;
97 : const char *cp;
98 : int *valp;
99 : {
100 0 : SKIPSPACE (cp);
101 :
102 0 : 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 0 : *valp = atoi (cp);
112 : /* EMPTY */
113 0 : for (cp++; isdigit(*cp); cp++) ;
114 : #endif
115 0 : return cp;
116 : }
117 :
118 :
119 : static const char *
120 0 : parse_value (g, cp, valp)
121 : IfParser *g;
122 : const char *cp;
123 : int *valp;
124 : {
125 : const char *var;
126 :
127 0 : *valp = 0;
128 :
129 0 : SKIPSPACE (cp);
130 0 : if (!*cp)
131 0 : return cp;
132 :
133 0 : switch (*cp) {
134 : case '(':
135 0 : DO (cp = ParseIfExpression (g, cp + 1, valp));
136 0 : SKIPSPACE (cp);
137 0 : if (*cp != ')')
138 0 : return CALLFUNC(g, handle_error) (g, cp, ")");
139 :
140 0 : return cp + 1; /* skip the right paren */
141 :
142 : case '!':
143 0 : DO (cp = parse_value (g, cp + 1, valp));
144 0 : *valp = !(*valp);
145 0 : 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 0 : if (strncmp (cp, "defined", 7) == 0 && !isalnum(cp[7])) {
168 0 : int paren = 0;
169 : int len;
170 :
171 0 : cp += 7;
172 0 : SKIPSPACE (cp);
173 0 : if (*cp == '(') {
174 0 : paren = 1;
175 0 : cp++;
176 : }
177 0 : DO (cp = parse_variable (g, cp, &var));
178 0 : len = (int)(cp - var);
179 0 : SKIPSPACE (cp);
180 0 : if (paren && *cp != ')')
181 0 : return CALLFUNC(g, handle_error) (g, cp, ")");
182 0 : *valp = (*(g->funcs.eval_defined)) (g, var, len);
183 0 : return cp + paren; /* skip the right paren */
184 : }
185 : /* fall out */
186 : }
187 :
188 0 : if (isdigit(*cp)) {
189 0 : DO (cp = parse_number (g, cp, valp));
190 0 : } else if (!isvarfirstletter(*cp))
191 0 : return CALLFUNC(g, handle_error) (g, cp, "variable or number");
192 : else {
193 0 : DO (cp = parse_variable (g, cp, &var));
194 0 : *valp = (*(g->funcs.eval_variable)) (g, var, cp - var);
195 : }
196 :
197 0 : return cp;
198 : }
199 :
200 :
201 :
202 : static const char *
203 0 : parse_product (g, cp, valp)
204 : IfParser *g;
205 : const char *cp;
206 : int *valp;
207 : {
208 : int rightval;
209 :
210 0 : DO (cp = parse_value (g, cp, valp));
211 0 : SKIPSPACE (cp);
212 :
213 0 : 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 0 : return cp;
234 : }
235 :
236 :
237 : static const char *
238 0 : parse_sum (g, cp, valp)
239 : IfParser *g;
240 : const char *cp;
241 : int *valp;
242 : {
243 : int rightval;
244 :
245 0 : DO (cp = parse_product (g, cp, valp));
246 0 : SKIPSPACE (cp);
247 :
248 0 : 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 0 : return cp;
260 : }
261 :
262 :
263 : static const char *
264 0 : parse_shift (g, cp, valp)
265 : IfParser *g;
266 : const char *cp;
267 : int *valp;
268 : {
269 : int rightval;
270 :
271 0 : DO (cp = parse_sum (g, cp, valp));
272 0 : SKIPSPACE (cp);
273 :
274 0 : 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 0 : if (cp[1] == '>') {
284 0 : DO (cp = parse_shift (g, cp + 2, &rightval));
285 0 : *valp = (*valp >> rightval);
286 : }
287 0 : break;
288 : }
289 0 : return cp;
290 : }
291 :
292 :
293 : static const char *
294 0 : parse_inequality (g, cp, valp)
295 : IfParser *g;
296 : const char *cp;
297 : int *valp;
298 : {
299 : int rightval;
300 :
301 0 : DO (cp = parse_shift (g, cp, valp));
302 0 : SKIPSPACE (cp);
303 :
304 0 : 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 0 : if (cp[1] == '=') {
317 0 : DO (cp = parse_inequality (g, cp + 2, &rightval));
318 0 : *valp = (*valp >= rightval);
319 : } else {
320 0 : DO (cp = parse_inequality (g, cp + 1, &rightval));
321 0 : *valp = (*valp > rightval);
322 : }
323 0 : break;
324 : }
325 0 : return cp;
326 : }
327 :
328 :
329 : static const char *
330 0 : parse_equality (g, cp, valp)
331 : IfParser *g;
332 : const char *cp;
333 : int *valp;
334 : {
335 : int rightval;
336 :
337 0 : DO (cp = parse_inequality (g, cp, valp));
338 0 : SKIPSPACE (cp);
339 :
340 0 : switch (*cp) {
341 : case '=':
342 0 : if (cp[1] == '=')
343 0 : cp++;
344 0 : DO (cp = parse_equality (g, cp + 1, &rightval));
345 0 : *valp = (*valp == rightval);
346 0 : 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 0 : return cp;
356 : }
357 :
358 :
359 : static const char *
360 0 : parse_band (g, cp, valp)
361 : IfParser *g;
362 : const char *cp;
363 : int *valp;
364 : {
365 : int rightval;
366 :
367 0 : DO (cp = parse_equality (g, cp, valp));
368 0 : SKIPSPACE (cp);
369 :
370 0 : switch (*cp) {
371 : case '&':
372 0 : if (cp[1] != '&') {
373 0 : DO (cp = parse_band (g, cp + 1, &rightval));
374 0 : *valp = (*valp & rightval);
375 : }
376 0 : break;
377 : }
378 0 : return cp;
379 : }
380 :
381 :
382 : static const char *
383 0 : parse_bor (g, cp, valp)
384 : IfParser *g;
385 : const char *cp;
386 : int *valp;
387 : {
388 : int rightval;
389 :
390 0 : DO (cp = parse_band (g, cp, valp));
391 0 : SKIPSPACE (cp);
392 :
393 0 : switch (*cp) {
394 : case '|':
395 0 : if (cp[1] != '|') {
396 0 : DO (cp = parse_bor (g, cp + 1, &rightval));
397 0 : *valp = (*valp | rightval);
398 : }
399 0 : break;
400 : }
401 0 : return cp;
402 : }
403 :
404 :
405 : static const char *
406 0 : parse_land (g, cp, valp)
407 : IfParser *g;
408 : const char *cp;
409 : int *valp;
410 : {
411 : int rightval;
412 :
413 0 : DO (cp = parse_bor (g, cp, valp));
414 0 : SKIPSPACE (cp);
415 :
416 0 : switch (*cp) {
417 : case '&':
418 0 : if (cp[1] != '&')
419 0 : return CALLFUNC(g, handle_error) (g, cp, "&&");
420 0 : DO (cp = parse_land (g, cp + 2, &rightval));
421 0 : *valp = (*valp && rightval);
422 0 : break;
423 : }
424 0 : return cp;
425 : }
426 :
427 :
428 : static const char *
429 0 : parse_lor (g, cp, valp)
430 : IfParser *g;
431 : const char *cp;
432 : int *valp;
433 : {
434 : int rightval;
435 :
436 0 : DO (cp = parse_land (g, cp, valp));
437 0 : SKIPSPACE (cp);
438 :
439 0 : switch (*cp) {
440 : case '|':
441 0 : if (cp[1] != '|')
442 0 : return CALLFUNC(g, handle_error) (g, cp, "||");
443 0 : DO (cp = parse_lor (g, cp + 2, &rightval));
444 0 : *valp = (*valp || rightval);
445 0 : break;
446 : }
447 0 : return cp;
448 : }
449 :
450 :
451 : /****************************************************************************
452 : External Entry Points
453 : ****************************************************************************/
454 :
455 : const char *
456 0 : ParseIfExpression (g, cp, valp)
457 : IfParser *g;
458 : const char *cp;
459 : int *valp;
460 : {
461 0 : return parse_lor (g, cp, valp);
462 : }
463 :
464 :
465 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|