ifparser.c revision d43532a6
1/* 2 * 3 * Copyright 1992 Network Computing Devices, Inc. 4 * 5 * Permission to use, copy, modify, and distribute this software and its 6 * documentation for any purpose and without fee is hereby granted, provided 7 * that the above copyright notice appear in all copies and that both that 8 * copyright notice and this permission notice appear in supporting 9 * documentation, and that the name of Network Computing Devices may not be 10 * used in advertising or publicity pertaining to distribution of the software 11 * without specific, written prior permission. Network Computing Devices makes 12 * no representations about the suitability of this software for any purpose. 13 * It is provided ``as is'' without express or implied warranty. 14 * 15 * NETWORK COMPUTING DEVICES DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS 16 * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, 17 * IN NO EVENT SHALL NETWORK COMPUTING DEVICES BE LIABLE FOR ANY SPECIAL, 18 * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 19 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 20 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 21 * PERFORMANCE OF THIS SOFTWARE. 22 * 23 * Author: Jim Fulton 24 * Network Computing Devices, Inc. 25 * 26 * Simple if statement processor 27 * 28 * This module can be used to evaluate string representations of C language 29 * if constructs. It accepts the following grammar: 30 * 31 * EXPRESSION := VALUE 32 * | VALUE BINOP EXPRESSION 33 * | VALUE '?' EXPRESSION ':' EXPRESSION 34 * 35 * VALUE := '(' EXPRESSION ')' 36 * | '!' VALUE 37 * | '-' VALUE 38 * | '+' VALUE 39 * | '~' VALUE 40 * | 'defined' '(' variable ')' 41 * | 'defined' variable 42 * | # variable '(' variable-list ')' 43 * | variable 44 * | number 45 * 46 * BINOP := '*' | '/' | '%' 47 * | '+' | '-' 48 * | '<<' | '>>' 49 * | '<' | '>' | '<=' | '>=' 50 * | '==' | '!=' 51 * | '&' | '^' | '|' 52 * | '&&' | '||' 53 * 54 * The normal C order of precedence is supported. 55 * 56 * 57 * External Entry Points: 58 * 59 * ParseIfExpression parse a string for #if 60 */ 61 62#include "ifparser.h" 63#include <ctype.h> 64#include <stdlib.h> 65#include <string.h> 66#include <limits.h> 67 68/**************************************************************************** 69 Internal Macros and Utilities for Parser 70 ****************************************************************************/ 71 72#define DO(val) if (!(val)) return NULL 73#define CALLFUNC(ggg,fff) (*((ggg)->funcs.fff)) 74#define SKIPSPACE(ccc) while (isspace(*ccc)) ccc++ 75#define isvarfirstletter(ccc) (isalpha(ccc) || (ccc) == '_') 76 77 78static const char * 79parse_variable (IfParser *g, const char *cp, 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 93static const char * 94parse_number (IfParser *g, const char *cp, long *valp) 95{ 96 long base = 10; 97 SKIPSPACE (cp); 98 99 if (!isdigit(*cp)) 100 return CALLFUNC(g, handle_error) (g, cp, "number"); 101 102 *valp = 0; 103 104 if (*cp == '0') { 105 cp++; 106 if ((*cp == 'x') || (*cp == 'X')) { 107 base = 16; 108 cp++; 109 } else { 110 base = 8; 111 } 112 } 113 114 /* Ignore overflows and assume ASCII, what source is usually written in */ 115 while (1) { 116 int increment = -1; 117 if (base == 8) { 118 if ((*cp >= '0') && (*cp <= '7')) 119 increment = *cp++ - '0'; 120 } else if (base == 16) { 121 if ((*cp >= '0') && (*cp <= '9')) 122 increment = *cp++ - '0'; 123 else if ((*cp >= 'A') && (*cp <= 'F')) 124 increment = *cp++ - ('A' - 10); 125 else if ((*cp >= 'a') && (*cp <= 'f')) 126 increment = *cp++ - ('a' - 10); 127 } else { /* Decimal */ 128 if ((*cp >= '0') && (*cp <= '9')) 129 increment = *cp++ - '0'; 130 } 131 if (increment < 0) 132 break; 133 *valp = (*valp * base) + increment; 134 } 135 136 /* Skip trailing qualifiers */ 137 while (*cp == 'U' || *cp == 'u' || *cp == 'L' || *cp == 'l') cp++; 138 return cp; 139} 140 141static const char * 142parse_character (IfParser *g, const char *cp, long *valp) 143{ 144 char val; 145 146 SKIPSPACE (cp); 147 if (*cp == '\\') 148 switch (cp[1]) { 149 case 'n': val = '\n'; break; 150 case 't': val = '\t'; break; 151 case 'v': val = '\v'; break; 152 case 'b': val = '\b'; break; 153 case 'r': val = '\r'; break; 154 case 'f': val = '\f'; break; 155 case 'a': val = '\a'; break; 156 case '\\': val = '\\'; break; 157 case '?': val = '\?'; break; 158 case '\'': val = '\''; break; 159 case '\"': val = '\"'; break; 160 case 'x': val = (char) strtol (cp + 2, NULL, 16); break; 161 default: val = (char) strtol (cp + 1, NULL, 8); break; 162 } 163 else 164 val = *cp; 165 while (*cp != '\'') cp++; 166 *valp = (long) val; 167 return cp; 168} 169 170static const char * 171parse_value (IfParser *g, const char *cp, long *valp) 172{ 173 const char *var, *varend; 174 175 *valp = 0; 176 177 SKIPSPACE (cp); 178 if (!*cp) 179 return cp; 180 181 switch (*cp) { 182 case '(': 183 DO (cp = ParseIfExpression (g, cp + 1, valp)); 184 SKIPSPACE (cp); 185 if (*cp != ')') 186 return CALLFUNC(g, handle_error) (g, cp, ")"); 187 188 return cp + 1; /* skip the right paren */ 189 190 case '!': 191 DO (cp = parse_value (g, cp + 1, valp)); 192 *valp = !(*valp); 193 return cp; 194 195 case '-': 196 DO (cp = parse_value (g, cp + 1, valp)); 197 *valp = -(*valp); 198 return cp; 199 200 case '+': 201 DO (cp = parse_value (g, cp + 1, valp)); 202 return cp; 203 204 case '~': 205 DO (cp = parse_value (g, cp + 1, valp)); 206 *valp = ~(*valp); 207 return cp; 208 209 case '#': 210 DO (cp = parse_variable (g, cp + 1, &var)); 211 SKIPSPACE (cp); 212 if (*cp != '(') 213 return CALLFUNC(g, handle_error) (g, cp, "("); 214 do { 215 DO (cp = parse_variable (g, cp + 1, &var)); 216 SKIPSPACE (cp); 217 } while (*cp && *cp != ')'); 218 if (*cp != ')') 219 return CALLFUNC(g, handle_error) (g, cp, ")"); 220 *valp = 1; /* XXX */ 221 return cp + 1; 222 223 case '\'': 224 DO (cp = parse_character (g, cp + 1, valp)); 225 if (*cp != '\'') 226 return CALLFUNC(g, handle_error) (g, cp, "'"); 227 return cp + 1; 228 229 case 'd': 230 if (strncmp (cp, "defined", 7) == 0 && !isalnum(cp[7])) { 231 int paren = 0; 232 int len; 233 234 cp += 7; 235 SKIPSPACE (cp); 236 if (*cp == '(') { 237 paren = 1; 238 cp++; 239 } 240 DO (cp = parse_variable (g, cp, &var)); 241 len = cp - var; 242 SKIPSPACE (cp); 243 if (paren && *cp != ')') 244 return CALLFUNC(g, handle_error) (g, cp, ")"); 245 *valp = (*(g->funcs.eval_defined)) (g, var, len); 246 return cp + paren; /* skip the right paren */ 247 } 248 /* fall out */ 249 } 250 251 if (isdigit(*cp)) { 252 DO (cp = parse_number (g, cp, valp)); 253 } else if (!isvarfirstletter(*cp)) 254 return CALLFUNC(g, handle_error) (g, cp, "variable or number"); 255 else { 256 DO (cp = parse_variable (g, cp, &var)); 257 varend = cp; 258 SKIPSPACE(cp); 259 if (*cp != '(') { 260 *valp = (*(g->funcs.eval_variable)) (g, var, varend - var); 261 } else { 262 do { 263 long dummy; 264 DO (cp = ParseIfExpression (g, cp + 1, &dummy)); 265 SKIPSPACE(cp); 266 if (*cp == ')') 267 break; 268 if (*cp != ',') 269 return CALLFUNC(g, handle_error) (g, cp, ","); 270 } while (1); 271 272 *valp = 1; /* XXX */ 273 cp++; 274 } 275 } 276 277 return cp; 278} 279 280 281 282static const char * 283parse_product (IfParser *g, const char *cp, long *valp) 284{ 285 long rightval; 286 287 DO (cp = parse_value (g, cp, valp)); 288 SKIPSPACE (cp); 289 290 switch (*cp) { 291 case '*': 292 DO (cp = parse_product (g, cp + 1, &rightval)); 293 *valp = (*valp * rightval); 294 break; 295 296 case '/': 297 DO (cp = parse_product (g, cp + 1, &rightval)); 298 if (rightval) 299 *valp = (*valp / rightval); 300 else 301 *valp = LONG_MAX; 302 break; 303 304 case '%': 305 DO (cp = parse_product (g, cp + 1, &rightval)); 306 *valp = (*valp % rightval); 307 break; 308 } 309 return cp; 310} 311 312 313static const char * 314parse_sum (IfParser *g, const char *cp, long *valp) 315{ 316 long rightval; 317 318 DO (cp = parse_product (g, cp, valp)); 319 SKIPSPACE (cp); 320 321 switch (*cp) { 322 case '+': 323 DO (cp = parse_sum (g, cp + 1, &rightval)); 324 *valp = (*valp + rightval); 325 break; 326 327 case '-': 328 DO (cp = parse_sum (g, cp + 1, &rightval)); 329 *valp = (*valp - rightval); 330 break; 331 } 332 return cp; 333} 334 335 336static const char * 337parse_shift (IfParser *g, const char *cp, long *valp) 338{ 339 long rightval; 340 341 DO (cp = parse_sum (g, cp, valp)); 342 SKIPSPACE (cp); 343 344 switch (*cp) { 345 case '<': 346 if (cp[1] == '<') { 347 DO (cp = parse_shift (g, cp + 2, &rightval)); 348 *valp = (*valp << rightval); 349 } 350 break; 351 352 case '>': 353 if (cp[1] == '>') { 354 DO (cp = parse_shift (g, cp + 2, &rightval)); 355 *valp = (*valp >> rightval); 356 } 357 break; 358 } 359 return cp; 360} 361 362 363static const char * 364parse_inequality (IfParser *g, const char *cp, long *valp) 365{ 366 long rightval; 367 368 DO (cp = parse_shift (g, cp, valp)); 369 SKIPSPACE (cp); 370 371 switch (*cp) { 372 case '<': 373 if (cp[1] == '=') { 374 DO (cp = parse_inequality (g, cp + 2, &rightval)); 375 *valp = (*valp <= rightval); 376 } else { 377 DO (cp = parse_inequality (g, cp + 1, &rightval)); 378 *valp = (*valp < rightval); 379 } 380 break; 381 382 case '>': 383 if (cp[1] == '=') { 384 DO (cp = parse_inequality (g, cp + 2, &rightval)); 385 *valp = (*valp >= rightval); 386 } else { 387 DO (cp = parse_inequality (g, cp + 1, &rightval)); 388 *valp = (*valp > rightval); 389 } 390 break; 391 } 392 return cp; 393} 394 395 396static const char * 397parse_equality (IfParser *g, const char *cp, long *valp) 398{ 399 long rightval; 400 401 DO (cp = parse_inequality (g, cp, valp)); 402 SKIPSPACE (cp); 403 404 switch (*cp) { 405 case '=': 406 if (cp[1] == '=') 407 cp++; 408 DO (cp = parse_equality (g, cp + 1, &rightval)); 409 *valp = (*valp == rightval); 410 break; 411 412 case '!': 413 if (cp[1] != '=') 414 break; 415 DO (cp = parse_equality (g, cp + 2, &rightval)); 416 *valp = (*valp != rightval); 417 break; 418 } 419 return cp; 420} 421 422 423static const char * 424parse_band (IfParser *g, const char *cp, long *valp) 425{ 426 long rightval; 427 428 DO (cp = parse_equality (g, cp, valp)); 429 SKIPSPACE (cp); 430 431 switch (*cp) { 432 case '&': 433 if (cp[1] != '&') { 434 DO (cp = parse_band (g, cp + 1, &rightval)); 435 *valp = (*valp & rightval); 436 } 437 break; 438 } 439 return cp; 440} 441 442 443static const char * 444parse_bxor (IfParser *g, const char *cp, long *valp) 445{ 446 long rightval; 447 448 DO (cp = parse_band (g, cp, valp)); 449 SKIPSPACE (cp); 450 451 switch (*cp) { 452 case '^': 453 DO (cp = parse_bxor (g, cp + 1, &rightval)); 454 *valp = (*valp ^ rightval); 455 break; 456 } 457 return cp; 458} 459 460 461static const char * 462parse_bor (IfParser *g, const char *cp, long *valp) 463{ 464 long rightval; 465 466 DO (cp = parse_bxor (g, cp, valp)); 467 SKIPSPACE (cp); 468 469 switch (*cp) { 470 case '|': 471 if (cp[1] != '|') { 472 DO (cp = parse_bor (g, cp + 1, &rightval)); 473 *valp = (*valp | rightval); 474 } 475 break; 476 } 477 return cp; 478} 479 480 481static const char * 482parse_land (IfParser *g, const char *cp, long *valp) 483{ 484 long rightval; 485 486 DO (cp = parse_bor (g, cp, valp)); 487 SKIPSPACE (cp); 488 489 switch (*cp) { 490 case '&': 491 if (cp[1] != '&') 492 return CALLFUNC(g, handle_error) (g, cp, "&&"); 493 DO (cp = parse_land (g, cp + 2, &rightval)); 494 *valp = (*valp && rightval); 495 break; 496 } 497 return cp; 498} 499 500 501static const char * 502parse_lor (IfParser *g, const char *cp, long *valp) 503{ 504 long rightval; 505 506 DO (cp = parse_land (g, cp, valp)); 507 SKIPSPACE (cp); 508 509 switch (*cp) { 510 case '|': 511 if (cp[1] != '|') 512 return CALLFUNC(g, handle_error) (g, cp, "||"); 513 DO (cp = parse_lor (g, cp + 2, &rightval)); 514 *valp = (*valp || rightval); 515 break; 516 } 517 return cp; 518} 519 520 521static const char * 522parse_cond(IfParser *g, const char *cp, long *valp) 523{ 524 long trueval, falseval; 525 526 DO (cp = parse_lor (g, cp, valp)); 527 SKIPSPACE (cp); 528 529 switch (*cp) { 530 case '?': 531 DO (cp = parse_cond (g, cp + 1, &trueval)); 532 SKIPSPACE (cp); 533 if (*cp != ':') 534 return CALLFUNC(g, handle_error) (g, cp, ":"); 535 DO (cp = parse_cond (g, cp + 1, &falseval)); 536 *valp = (*valp ? trueval : falseval); 537 break; 538 } 539 return cp; 540} 541 542 543/**************************************************************************** 544 External Entry Points 545 ****************************************************************************/ 546 547const char * 548ParseIfExpression (IfParser *g, const char *cp, long *valp) 549{ 550 return parse_cond (g, cp, valp); 551} 552