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