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 (IfParser *g, const char *cp, const char **varp)
78 : {
79 0 : SKIPSPACE (cp);
80 :
81 0 : if (!isvarfirstletter (*cp))
82 0 : return CALLFUNC(g, handle_error) (g, cp, "variable name");
83 :
84 0 : *varp = cp;
85 : /* EMPTY */
86 0 : for (cp++; isalnum(*cp) || *cp == '_'; cp++) ;
87 0 : return cp;
88 : }
89 :
90 :
91 : static const char *
92 0 : parse_number (IfParser *g, const char *cp, int *valp)
93 : {
94 0 : SKIPSPACE (cp);
95 :
96 0 : if (!isdigit(*cp))
97 0 : return CALLFUNC(g, handle_error) (g, cp, "number");
98 :
99 : #ifdef WIN32
100 : {
101 : char *cp2;
102 : *valp = strtol(cp, &cp2, 0);
103 : }
104 : #else
105 0 : *valp = atoi (cp);
106 : /* EMPTY */
107 0 : for (cp++; isdigit(*cp); cp++) ;
108 : #endif
109 0 : return cp;
110 : }
111 :
112 :
113 : static const char *
114 0 : parse_value (IfParser *g, const char *cp, int *valp)
115 : {
116 : const char *var;
117 :
118 0 : *valp = 0;
119 :
120 0 : SKIPSPACE (cp);
121 0 : if (!*cp)
122 0 : return cp;
123 :
124 0 : switch (*cp) {
125 : case '(':
126 0 : DO (cp = ParseIfExpression (g, cp + 1, valp));
127 0 : SKIPSPACE (cp);
128 0 : if (*cp != ')')
129 0 : return CALLFUNC(g, handle_error) (g, cp, ")");
130 :
131 0 : return cp + 1; /* skip the right paren */
132 :
133 : case '!':
134 0 : DO (cp = parse_value (g, cp + 1, valp));
135 0 : *valp = !(*valp);
136 0 : return cp;
137 :
138 : case '-':
139 0 : DO (cp = parse_value (g, cp + 1, valp));
140 0 : *valp = -(*valp);
141 0 : return cp;
142 :
143 : case '#':
144 0 : DO (cp = parse_variable (g, cp + 1, &var));
145 0 : SKIPSPACE (cp);
146 0 : if (*cp != '(')
147 0 : return CALLFUNC(g, handle_error) (g, cp, "(");
148 : do {
149 0 : DO (cp = parse_variable (g, cp + 1, &var));
150 0 : SKIPSPACE (cp);
151 0 : } while (*cp && *cp != ')');
152 0 : if (*cp != ')')
153 0 : return CALLFUNC(g, handle_error) (g, cp, ")");
154 0 : *valp = 1; /* XXX */
155 0 : return cp + 1;
156 :
157 : case 'd':
158 0 : if (strncmp (cp, "defined", 7) == 0 && !isalnum(cp[7])) {
159 0 : int paren = 0;
160 : size_t len;
161 :
162 0 : cp += 7;
163 0 : SKIPSPACE (cp);
164 0 : if (*cp == '(') {
165 0 : paren = 1;
166 0 : cp++;
167 : }
168 0 : DO (cp = parse_variable (g, cp, &var));
169 0 : len = (size_t)(cp - var);
170 0 : SKIPSPACE (cp);
171 0 : if (paren && *cp != ')')
172 0 : return CALLFUNC(g, handle_error) (g, cp, ")");
173 0 : *valp = (*(g->funcs.eval_defined)) (g, var, len);
174 0 : return cp + paren; /* skip the right paren */
175 : }
176 : /* fall out */
177 : }
178 :
179 0 : if (isdigit(*cp)) {
180 0 : DO (cp = parse_number (g, cp, valp));
181 0 : } else if (!isvarfirstletter(*cp))
182 0 : return CALLFUNC(g, handle_error) (g, cp, "variable or number");
183 : else {
184 0 : DO (cp = parse_variable (g, cp, &var));
185 0 : *valp = (*(g->funcs.eval_variable)) (g, var, (size_t)(cp - var));
186 : }
187 :
188 0 : return cp;
189 : }
190 :
191 :
192 :
193 : static const char *
194 0 : parse_product (IfParser *g, const char *cp, int *valp)
195 : {
196 : int rightval;
197 :
198 0 : DO (cp = parse_value (g, cp, valp));
199 0 : SKIPSPACE (cp);
200 :
201 0 : switch (*cp) {
202 : case '*':
203 0 : DO (cp = parse_product (g, cp + 1, &rightval));
204 0 : *valp = (*valp * rightval);
205 0 : break;
206 :
207 : case '/':
208 0 : DO (cp = parse_product (g, cp + 1, &rightval));
209 :
210 : /* Do nothing in the divide-by-zero case. */
211 0 : if (rightval) {
212 0 : *valp = (*valp / rightval);
213 : }
214 0 : break;
215 :
216 : case '%':
217 0 : DO (cp = parse_product (g, cp + 1, &rightval));
218 0 : *valp = (*valp % rightval);
219 0 : break;
220 : }
221 0 : return cp;
222 : }
223 :
224 :
225 : static const char *
226 0 : parse_sum (IfParser *g, const char *cp, int *valp)
227 : {
228 : int rightval;
229 :
230 0 : DO (cp = parse_product (g, cp, valp));
231 0 : SKIPSPACE (cp);
232 :
233 0 : switch (*cp) {
234 : case '+':
235 0 : DO (cp = parse_sum (g, cp + 1, &rightval));
236 0 : *valp = (*valp + rightval);
237 0 : break;
238 :
239 : case '-':
240 0 : DO (cp = parse_sum (g, cp + 1, &rightval));
241 0 : *valp = (*valp - rightval);
242 0 : break;
243 : }
244 0 : return cp;
245 : }
246 :
247 :
248 : static const char *
249 0 : parse_shift (IfParser *g, const char *cp, int *valp)
250 : {
251 : int rightval;
252 :
253 0 : DO (cp = parse_sum (g, cp, valp));
254 0 : SKIPSPACE (cp);
255 :
256 0 : switch (*cp) {
257 : case '<':
258 0 : if (cp[1] == '<') {
259 0 : DO (cp = parse_shift (g, cp + 2, &rightval));
260 0 : *valp = (*valp << rightval);
261 : }
262 0 : break;
263 :
264 : case '>':
265 0 : if (cp[1] == '>') {
266 0 : DO (cp = parse_shift (g, cp + 2, &rightval));
267 0 : *valp = (*valp >> rightval);
268 : }
269 0 : break;
270 : }
271 0 : return cp;
272 : }
273 :
274 :
275 : static const char *
276 0 : parse_inequality (IfParser *g, const char *cp, int *valp)
277 : {
278 : int rightval;
279 :
280 0 : DO (cp = parse_shift (g, cp, valp));
281 0 : SKIPSPACE (cp);
282 :
283 0 : switch (*cp) {
284 : case '<':
285 0 : if (cp[1] == '=') {
286 0 : DO (cp = parse_inequality (g, cp + 2, &rightval));
287 0 : *valp = (*valp <= rightval);
288 : } else {
289 0 : DO (cp = parse_inequality (g, cp + 1, &rightval));
290 0 : *valp = (*valp < rightval);
291 : }
292 0 : break;
293 :
294 : case '>':
295 0 : if (cp[1] == '=') {
296 0 : DO (cp = parse_inequality (g, cp + 2, &rightval));
297 0 : *valp = (*valp >= rightval);
298 : } else {
299 0 : DO (cp = parse_inequality (g, cp + 1, &rightval));
300 0 : *valp = (*valp > rightval);
301 : }
302 0 : break;
303 : }
304 0 : return cp;
305 : }
306 :
307 :
308 : static const char *
309 0 : parse_equality (IfParser *g, const char *cp, int *valp)
310 : {
311 : int rightval;
312 :
313 0 : DO (cp = parse_inequality (g, cp, valp));
314 0 : SKIPSPACE (cp);
315 :
316 0 : switch (*cp) {
317 : case '=':
318 0 : if (cp[1] == '=')
319 0 : cp++;
320 0 : DO (cp = parse_equality (g, cp + 1, &rightval));
321 0 : *valp = (*valp == rightval);
322 0 : break;
323 :
324 : case '!':
325 0 : if (cp[1] != '=')
326 0 : break;
327 0 : DO (cp = parse_equality (g, cp + 2, &rightval));
328 0 : *valp = (*valp != rightval);
329 0 : break;
330 : }
331 0 : return cp;
332 : }
333 :
334 :
335 : static const char *
336 0 : parse_band (IfParser *g, const char *cp, int *valp)
337 : {
338 : int rightval;
339 :
340 0 : DO (cp = parse_equality (g, cp, valp));
341 0 : SKIPSPACE (cp);
342 :
343 0 : switch (*cp) {
344 : case '&':
345 0 : if (cp[1] != '&') {
346 0 : DO (cp = parse_band (g, cp + 1, &rightval));
347 0 : *valp = (*valp & rightval);
348 : }
349 0 : break;
350 : }
351 0 : return cp;
352 : }
353 :
354 :
355 : static const char *
356 0 : parse_bor (IfParser *g, const char *cp, int *valp)
357 : {
358 : int rightval;
359 :
360 0 : DO (cp = parse_band (g, cp, valp));
361 0 : SKIPSPACE (cp);
362 :
363 0 : switch (*cp) {
364 : case '|':
365 0 : if (cp[1] != '|') {
366 0 : DO (cp = parse_bor (g, cp + 1, &rightval));
367 0 : *valp = (*valp | rightval);
368 : }
369 0 : break;
370 : }
371 0 : return cp;
372 : }
373 :
374 :
375 : static const char *
376 0 : parse_land (IfParser *g, const char *cp, int *valp)
377 : {
378 : int rightval;
379 :
380 0 : DO (cp = parse_bor (g, cp, valp));
381 0 : SKIPSPACE (cp);
382 :
383 0 : switch (*cp) {
384 : case '&':
385 0 : if (cp[1] != '&')
386 0 : return CALLFUNC(g, handle_error) (g, cp, "&&");
387 0 : DO (cp = parse_land (g, cp + 2, &rightval));
388 0 : *valp = (*valp && rightval);
389 0 : break;
390 : }
391 0 : return cp;
392 : }
393 :
394 :
395 : static const char *
396 0 : parse_lor (IfParser *g, const char *cp, int *valp)
397 : {
398 : int rightval;
399 :
400 0 : DO (cp = parse_land (g, cp, valp));
401 0 : SKIPSPACE (cp);
402 :
403 0 : switch (*cp) {
404 : case '|':
405 0 : if (cp[1] != '|')
406 0 : return CALLFUNC(g, handle_error) (g, cp, "||");
407 0 : DO (cp = parse_lor (g, cp + 2, &rightval));
408 0 : *valp = (*valp || rightval);
409 0 : break;
410 : }
411 0 : return cp;
412 : }
413 :
414 :
415 : /****************************************************************************
416 : External Entry Points
417 : ****************************************************************************/
418 :
419 : const char *
420 0 : ParseIfExpression (IfParser *g, const char *cp, int *valp)
421 : {
422 0 : return parse_lor (g, cp, valp);
423 : }
424 :
425 :
426 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|