|
|
1.1 ! root 1: /* Parse C expressions for CCCP. ! 2: Copyright (C) 1987 Free Software Foundation. ! 3: ! 4: NO WARRANTY ! 5: ! 6: BECAUSE THIS PROGRAM IS LICENSED FREE OF CHARGE, WE PROVIDE ABSOLUTELY ! 7: NO WARRANTY, TO THE EXTENT PERMITTED BY APPLICABLE STATE LAW. EXCEPT ! 8: WHEN OTHERWISE STATED IN WRITING, FREE SOFTWARE FOUNDATION, INC, ! 9: RICHARD M. STALLMAN AND/OR OTHER PARTIES PROVIDE THIS PROGRAM "AS IS" ! 10: WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, ! 11: BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND ! 12: FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY ! 13: AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE ! 14: DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR ! 15: CORRECTION. ! 16: ! 17: IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW WILL RICHARD M. ! 18: STALLMAN, THE FREE SOFTWARE FOUNDATION, INC., AND/OR ANY OTHER PARTY ! 19: WHO MAY MODIFY AND REDISTRIBUTE THIS PROGRAM AS PERMITTED BELOW, BE ! 20: LIABLE TO YOU FOR DAMAGES, INCLUDING ANY LOST PROFITS, LOST MONIES, OR ! 21: OTHER SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE ! 22: USE OR INABILITY TO USE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR ! 23: DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY THIRD PARTIES OR ! 24: A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS) THIS ! 25: PROGRAM, EVEN IF YOU HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH ! 26: DAMAGES, OR FOR ANY CLAIM BY ANY OTHER PARTY. ! 27: ! 28: GENERAL PUBLIC LICENSE TO COPY ! 29: ! 30: 1. You may copy and distribute verbatim copies of this source file ! 31: as you receive it, in any medium, provided that you conspicuously and ! 32: appropriately publish on each copy a valid copyright notice "Copyright ! 33: (C) 1987 Free Software Foundation"; and include following the ! 34: copyright notice a verbatim copy of the above disclaimer of warranty ! 35: and of this License. You may charge a distribution fee for the ! 36: physical act of transferring a copy. ! 37: ! 38: 2. You may modify your copy or copies of this source file or ! 39: any portion of it, and copy and distribute such modifications under ! 40: the terms of Paragraph 1 above, provided that you also do the following: ! 41: ! 42: a) cause the modified files to carry prominent notices stating ! 43: that you changed the files and the date of any change; and ! 44: ! 45: b) cause the whole of any work that you distribute or publish, ! 46: that in whole or in part contains or is a derivative of this ! 47: program or any part thereof, to be licensed at no charge to all ! 48: third parties on terms identical to those contained in this ! 49: License Agreement (except that you may choose to grant more extensive ! 50: warranty protection to some or all third parties, at your option). ! 51: ! 52: c) You may charge a distribution fee for the physical act of ! 53: transferring a copy, and you may at your option offer warranty ! 54: protection in exchange for a fee. ! 55: ! 56: Mere aggregation of another unrelated program with this program (or its ! 57: derivative) on a volume of a storage or distribution medium does not bring ! 58: the other program under the scope of these terms. ! 59: ! 60: 3. You may copy and distribute this program (or a portion or derivative ! 61: of it, under Paragraph 2) in object code or executable form under the terms ! 62: of Paragraphs 1 and 2 above provided that you also do one of the following: ! 63: ! 64: a) accompany it with the complete corresponding machine-readable ! 65: source code, which must be distributed under the terms of ! 66: Paragraphs 1 and 2 above; or, ! 67: ! 68: b) accompany it with a written offer, valid for at least three ! 69: years, to give any third party free (except for a nominal ! 70: shipping charge) a complete machine-readable copy of the ! 71: corresponding source code, to be distributed under the terms of ! 72: Paragraphs 1 and 2 above; or, ! 73: ! 74: c) accompany it with the information you received as to where the ! 75: corresponding source code may be obtained. (This alternative is ! 76: allowed only for noncommercial distribution and only if you ! 77: received the program in object code or executable form alone.) ! 78: ! 79: For an executable file, complete source code means all the source code for ! 80: all modules it contains; but, as a special exception, it need not include ! 81: source code for modules which are standard libraries that accompany the ! 82: operating system on which the executable file runs. ! 83: ! 84: 4. You may not copy, sublicense, distribute or transfer this program ! 85: except as expressly provided under this License Agreement. Any attempt ! 86: otherwise to copy, sublicense, distribute or transfer this program is void and ! 87: your rights to use the program under this License agreement shall be ! 88: automatically terminated. However, parties who have received computer ! 89: software programs from you with this License Agreement will not have ! 90: their licenses terminated so long as such parties remain in full compliance. ! 91: ! 92: 5. If you wish to incorporate parts of this program into other free ! 93: programs whose distribution conditions are different, write to the Free ! 94: Software Foundation at 675 Mass Ave, Cambridge, MA 02139. We have not yet ! 95: worked out a simple rule that can be stated here, but we will often permit ! 96: this. We will be guided by the two goals of preserving the free status of ! 97: all derivatives of our free software and of promoting the sharing and reuse of ! 98: software. ! 99: ! 100: ! 101: In other words, you are welcome to use, share and improve this program. ! 102: You are forbidden to forbid anyone else to use, share and improve ! 103: what you give them. Help stamp out software-hoarding! ! 104: ! 105: Adapted from expread.y of GDB by Paul Rubin, July 1986. ! 106: ! 107: /* Parse a C expression from text in a string */ ! 108: ! 109: %{ ! 110: #include "config.h" ! 111: #include <setjmp.h> ! 112: /* #define YYDEBUG 1 */ ! 113: ! 114: static int yylex (); ! 115: static yyerror (); ! 116: int expression_value; ! 117: ! 118: static jmp_buf parse_return_error; ! 119: ! 120: /* some external tables of character types */ ! 121: extern unsigned char is_idstart[], is_idchar[]; ! 122: ! 123: %} ! 124: ! 125: %union { ! 126: long lval; ! 127: int voidval; ! 128: char *sval; ! 129: } ! 130: ! 131: %type <lval> exp exp1 start ! 132: %token <lval> INT CHAR ! 133: %token <sval> NAME ! 134: %token <lval> ERROR ! 135: ! 136: %right '?' ':' ! 137: %left ',' ! 138: %left OR ! 139: %left AND ! 140: %left '|' ! 141: %left '^' ! 142: %left '&' ! 143: %left EQUAL NOTEQUAL ! 144: %left '<' '>' LEQ GEQ ! 145: %left LSH RSH ! 146: %left '+' '-' ! 147: %left '*' '/' '%' ! 148: %right UNARY ! 149: ! 150: /* %expect 40 */ ! 151: ! 152: %% ! 153: ! 154: start : exp1 ! 155: { expression_value = $1; } ! 156: ; ! 157: ! 158: /* Expressions, including the comma operator. */ ! 159: exp1 : exp ! 160: | exp1 ',' exp ! 161: { $$ = $3; } ! 162: ; ! 163: ! 164: /* Expressions, not including the comma operator. */ ! 165: exp : '-' exp %prec UNARY ! 166: { $$ = - $2; } ! 167: | '!' exp %prec UNARY ! 168: { $$ = ! $2; } ! 169: | '~' exp %prec UNARY ! 170: { $$ = ~ $2; } ! 171: | '(' exp1 ')' ! 172: { $$ = $2; } ! 173: ; ! 174: ! 175: /* Binary operators in order of decreasing precedence. */ ! 176: exp : exp '*' exp ! 177: { $$ = $1 * $3; } ! 178: | exp '/' exp ! 179: { $$ = $1 / $3; } ! 180: | exp '%' exp ! 181: { $$ = $1 % $3; } ! 182: | exp '+' exp ! 183: { $$ = $1 + $3; } ! 184: | exp '-' exp ! 185: { $$ = $1 - $3; } ! 186: | exp LSH exp ! 187: { $$ = $1 << $3; } ! 188: | exp RSH exp ! 189: { $$ = $1 >> $3; } ! 190: | exp EQUAL exp ! 191: { $$ = ($1 == $3); } ! 192: | exp NOTEQUAL exp ! 193: { $$ = ($1 != $3); } ! 194: | exp LEQ exp ! 195: { $$ = ($1 <= $3); } ! 196: | exp GEQ exp ! 197: { $$ = ($1 >= $3); } ! 198: | exp '<' exp ! 199: { $$ = ($1 < $3); } ! 200: | exp '>' exp ! 201: { $$ = ($1 > $3); } ! 202: | exp '&' exp ! 203: { $$ = ($1 & $3); } ! 204: | exp '^' exp ! 205: { $$ = ($1 ^ $3); } ! 206: | exp '|' exp ! 207: { $$ = ($1 | $3); } ! 208: | exp AND exp ! 209: { $$ = ($1 && $3); } ! 210: | exp OR exp ! 211: { $$ = ($1 || $3); } ! 212: | exp '?' exp ':' exp ! 213: { $$ = $1 ? $3 : $5; } ! 214: | INT ! 215: { $$ = yylval.lval; } ! 216: | CHAR ! 217: { $$ = yylval.lval; } ! 218: | NAME ! 219: { $$ = 0; } ! 220: ; ! 221: %% ! 222: ! 223: /* During parsing of a C expression, the pointer to the next character ! 224: is in this variable. */ ! 225: ! 226: static char *lexptr; ! 227: ! 228: /* Take care of parsing a number (anything that starts with a digit). ! 229: Set yylval and return the token type; update lexptr. ! 230: LEN is the number of characters in it. */ ! 231: ! 232: /* maybe needs to actually deal with floating point numbers */ ! 233: ! 234: static int ! 235: parse_number (olen) ! 236: int olen; ! 237: { ! 238: register char *p = lexptr; ! 239: register long n = 0; ! 240: register int c; ! 241: register int base = 10; ! 242: register len = olen; ! 243: char *err_copy; ! 244: ! 245: extern double atof (); ! 246: ! 247: for (c = 0; c < len; c++) ! 248: if (p[c] == '.') { ! 249: /* It's a float since it contains a point. */ ! 250: yyerror ("floating point numbers not allowed in #if expressions"); ! 251: return ERROR; ! 252: ! 253: /* **************** ! 254: yylval.dval = atof (p); ! 255: lexptr += len; ! 256: return FLOAT; ! 257: **************** */ ! 258: } ! 259: ! 260: if (len >= 3 && (!strncmp (p, "0x", 2) || !strncmp (p, "0X", 2))) { ! 261: p += 2; ! 262: base = 16; ! 263: len -= 2; ! 264: } ! 265: else if (*p == '0') ! 266: base = 8; ! 267: ! 268: while (len-- > 0) { ! 269: c = *p++; ! 270: n *= base; ! 271: if (c >= '0' && c <= '9') ! 272: n += c - '0'; ! 273: else { ! 274: if (c >= 'A' && c <= 'Z') c += 'a' - 'A'; ! 275: if (base == 16 && c >= 'a' && c <= 'f') ! 276: n += c - 'a' + 10; ! 277: else if (len == 0 && c == 'l') ! 278: ; ! 279: else { ! 280: yyerror ("Invalid number in #if expression"); ! 281: return ERROR; ! 282: } ! 283: } ! 284: } ! 285: ! 286: lexptr = p; ! 287: yylval.lval = n; ! 288: return INT; ! 289: } ! 290: ! 291: struct token { ! 292: char *operator; ! 293: int token; ! 294: }; ! 295: ! 296: #define NULL 0 ! 297: ! 298: static struct token tokentab2[] = { ! 299: {"&&", AND}, ! 300: {"||", OR}, ! 301: {"<<", LSH}, ! 302: {">>", RSH}, ! 303: {"==", EQUAL}, ! 304: {"!=", NOTEQUAL}, ! 305: {"<=", LEQ}, ! 306: {">=", GEQ}, ! 307: {NULL, ERROR} ! 308: }; ! 309: ! 310: /* Read one token, getting characters through lexptr. */ ! 311: ! 312: static int ! 313: yylex () ! 314: { ! 315: register int c; ! 316: register int namelen; ! 317: register char *tokstart; ! 318: register struct token *toktab; ! 319: ! 320: retry: ! 321: ! 322: tokstart = lexptr; ! 323: c = *tokstart; ! 324: /* See if it is a special token of length 2. */ ! 325: for (toktab = tokentab2; toktab->operator != NULL; toktab++) ! 326: if (c == *toktab->operator && tokstart[1] == toktab->operator[1]) { ! 327: lexptr += 2; ! 328: return toktab->token; ! 329: } ! 330: ! 331: switch (c) { ! 332: case 0: ! 333: return 0; ! 334: ! 335: case ' ': ! 336: case '\t': ! 337: case '\n': ! 338: lexptr++; ! 339: goto retry; ! 340: ! 341: case '\'': ! 342: lexptr++; ! 343: c = *lexptr++; ! 344: if (c == '\\') ! 345: c = parse_escape (&lexptr); ! 346: yylval.lval = c; ! 347: c = *lexptr++; ! 348: if (c != '\'') { ! 349: yyerror ("Invalid character constant in #if"); ! 350: return ERROR; ! 351: } ! 352: ! 353: return CHAR; ! 354: ! 355: /* some of these chars are invalid in constant expressions; ! 356: maybe do something about them later */ ! 357: case '/': ! 358: case '+': ! 359: case '-': ! 360: case '*': ! 361: case '%': ! 362: case '|': ! 363: case '&': ! 364: case '^': ! 365: case '~': ! 366: case '!': ! 367: case '@': ! 368: case '<': ! 369: case '>': ! 370: case '(': ! 371: case ')': ! 372: case '[': ! 373: case ']': ! 374: case '.': ! 375: case '?': ! 376: case ':': ! 377: case '=': ! 378: case '{': ! 379: case '}': ! 380: case ',': ! 381: lexptr++; ! 382: return c; ! 383: ! 384: case '"': ! 385: yyerror ("double quoted strings not allowed in #if expressions"); ! 386: return ERROR; ! 387: } ! 388: if (c >= '0' && c <= '9') { ! 389: /* It's a number */ ! 390: for (namelen = 0; ! 391: c = tokstart[namelen], is_idchar[c] || c == '.'; ! 392: namelen++) ! 393: ; ! 394: return parse_number (namelen); ! 395: } ! 396: ! 397: if (!is_idstart[c]) { ! 398: yyerror ("Invalid token in expression"); ! 399: return ERROR; ! 400: } ! 401: ! 402: /* It is a name. See how long it is. */ ! 403: ! 404: for (namelen = 0; is_idchar[tokstart[namelen]]; namelen++) ! 405: ; ! 406: ! 407: lexptr += namelen; ! 408: return NAME; ! 409: } ! 410: ! 411: ! 412: /* Parse a C escape sequence. STRING_PTR points to a variable ! 413: containing a pointer to the string to parse. That pointer ! 414: is updated past the characters we use. The value of the ! 415: escape sequence is returned. ! 416: ! 417: A negative value means the sequence \ newline was seen, ! 418: which is supposed to be equivalent to nothing at all. ! 419: ! 420: If \ is followed by a null character, we return a negative ! 421: value and leave the string pointer pointing at the null character. ! 422: ! 423: If \ is followed by 000, we return 0 and leave the string pointer ! 424: after the zeros. A value of 0 does not mean end of string. */ ! 425: ! 426: static int ! 427: parse_escape (string_ptr) ! 428: char **string_ptr; ! 429: { ! 430: register int c = *(*string_ptr)++; ! 431: switch (c) ! 432: { ! 433: case 'a': ! 434: return '\a'; ! 435: case 'b': ! 436: return '\b'; ! 437: case 'e': ! 438: return 033; ! 439: case 'f': ! 440: return '\f'; ! 441: case 'n': ! 442: return '\n'; ! 443: case 'r': ! 444: return '\r'; ! 445: case 't': ! 446: return '\t'; ! 447: case 'v': ! 448: return '\v'; ! 449: case '\n': ! 450: return -2; ! 451: case 0: ! 452: (*string_ptr)--; ! 453: return 0; ! 454: case '^': ! 455: c = *(*string_ptr)++; ! 456: if (c == '\\') ! 457: c = parse_escape (string_ptr); ! 458: if (c == '?') ! 459: return 0177; ! 460: return (c & 0200) | (c & 037); ! 461: ! 462: case '0': ! 463: case '1': ! 464: case '2': ! 465: case '3': ! 466: case '4': ! 467: case '5': ! 468: case '6': ! 469: case '7': ! 470: { ! 471: register int i = c - '0'; ! 472: register int count = 0; ! 473: while (++count < 3) ! 474: { ! 475: if ((c = *(*string_ptr)++) >= '0' && c <= '7') ! 476: { ! 477: i *= 8; ! 478: i += c - '0'; ! 479: } ! 480: else ! 481: { ! 482: (*string_ptr)--; ! 483: break; ! 484: } ! 485: } ! 486: return i; ! 487: } ! 488: default: ! 489: return c; ! 490: } ! 491: } ! 492: ! 493: static ! 494: yyerror (s) ! 495: char *s; ! 496: { ! 497: error (s); ! 498: longjmp (parse_return_error, 1); ! 499: } ! 500: ! 501: /* This page contains the entry point to this file. */ ! 502: ! 503: /* Parse STRING as an expression, and complain if this fails ! 504: to use up all of the contents of STRING. */ ! 505: /* We do not support C comments. They should be removed before ! 506: this function is called. */ ! 507: ! 508: int ! 509: parse_c_expression (string) ! 510: char *string; ! 511: { ! 512: lexptr = string; ! 513: ! 514: if (lexptr == 0 || *lexptr == 0) { ! 515: error ("empty #if expression"); ! 516: return 0; /* don't include the #if group */ ! 517: } ! 518: ! 519: /* if there is some sort of scanning error, just return 0 and assume ! 520: the parsing routine has printed an error message somewhere. ! 521: there is surely a better thing to do than this. */ ! 522: if (setjmp(parse_return_error)) ! 523: return 0; ! 524: ! 525: if (yyparse ()) ! 526: return 0; /* actually this is never reached ! 527: the way things stand. */ ! 528: if (*lexptr) ! 529: error ("Junk after end of expression."); ! 530: ! 531: return expression_value; /* set by yyparse() */ ! 532: } ! 533: ! 534: #ifdef TEST_EXP_READER ! 535: /* main program, for testing purposes. */ ! 536: main() ! 537: { ! 538: int n; ! 539: char buf[1024]; ! 540: extern int yydebug; ! 541: /* ! 542: yydebug = 1; ! 543: */ ! 544: initialize_random_junk (); ! 545: ! 546: for (;;) { ! 547: printf("enter expression: "); ! 548: n = 0; ! 549: while ((buf[n] = getchar()) != '\n') ! 550: n++; ! 551: buf[n] = '\0'; ! 552: printf("parser returned %d\n", parse_c_expression(buf)); ! 553: } ! 554: } ! 555: ! 556: /* table to tell if char can be part of a C identifier. */ ! 557: char is_idchar[256]; ! 558: /* table to tell if char can be first char of a c identifier. */ ! 559: char is_idstart[256]; ! 560: /* table to tell if c is horizontal space. isspace() thinks that ! 561: newline is space; this is not a good idea for this program. */ ! 562: char is_hor_space[256]; ! 563: ! 564: /* ! 565: * initialize random junk in the hash table and maybe other places ! 566: */ ! 567: initialize_random_junk() ! 568: { ! 569: register int i; ! 570: ! 571: /* ! 572: * Set up is_idchar and is_idstart tables. These should be ! 573: * faster than saying (is_alpha(c) || c == '_'), etc. ! 574: * Must do set up these things before calling any routines tthat ! 575: * refer to them. ! 576: */ ! 577: for (i = 'a'; i <= 'z'; i++) { ! 578: ++is_idchar[i - 'a' + 'A']; ! 579: ++is_idchar[i]; ! 580: ++is_idstart[i - 'a' + 'A']; ! 581: ++is_idstart[i]; ! 582: } ! 583: for (i = '0'; i <= '9'; i++) ! 584: ++is_idchar[i]; ! 585: ++is_idchar['_']; ! 586: ++is_idstart['_']; ! 587: #ifdef DOLLARS_IN_IDENTIFIERS ! 588: ++is_idchar['$']; ! 589: ++is_idstart['$']; ! 590: #endif ! 591: ! 592: /* horizontal space table */ ! 593: ++is_hor_space[' ']; ! 594: ++is_hor_space['\t']; ! 595: } ! 596: ! 597: error (msg) ! 598: { ! 599: printf("error: %s\n", msg); ! 600: } ! 601: #endif
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.