|
|
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
50: extensive warranty protection to 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: 3. You may copy and distribute this program or any portion of it in
57: compiled, executable or object code form under the terms of Paragraphs
58: 1 and 2 above provided that you do the following:
59:
60: a) cause each such copy to be accompanied by the
61: corresponding machine-readable source code, which must
62: be distributed under the terms of Paragraphs 1 and 2 above; or,
63:
64: b) cause each such copy to be accompanied by a
65: written offer, with no time limit, to give any third party
66: free (except for a nominal shipping charge) a machine readable
67: copy of the corresponding source code, to be distributed
68: under the terms of Paragraphs 1 and 2 above; or,
69:
70: c) in the case of a recipient of this program in compiled, executable
71: or object code form (without the corresponding source code) you
72: shall cause copies you distribute to be accompanied by a copy
73: of the written offer of source code which you received along
74: with the copy you received.
75:
76: 4. You may not copy, sublicense, distribute or transfer this program
77: except as expressly provided under this License Agreement. Any attempt
78: otherwise to copy, sublicense, distribute or transfer this program is void and
79: your rights to use the program under this License agreement shall be
80: automatically terminated. However, parties who have received computer
81: software programs from you with this License Agreement will not have
82: their licenses terminated so long as such parties remain in full compliance.
83:
84: 5. If you wish to incorporate parts of this program into other free
85: programs whose distribution conditions are different, write to the Free
86: Software Foundation at 1000 Mass Ave, Cambridge, MA 02138. We have not yet
87: worked out a simple rule that can be stated here, but we will often permit
88: this. We will be guided by the two goals of preserving the free status of
89: all derivatives our free software and of promoting the sharing and reuse of
90: software.
91:
92:
93: In other words, you are welcome to use, share and improve this program.
94: You are forbidden to forbid anyone else to use, share and improve
95: what you give them. Help stamp out software-hoarding!
96:
97: Adapted from expread.y of GDB by Paul Rubin, July 1986.
98:
99: /* Parse a C expression from text in a string */
100:
101: %{
102: #include <setjmp.h>
103: /* #define YYDEBUG 1 */
104:
105: static int yylex ();
106: static yyerror ();
107: int expression_value;
108:
109: static jmp_buf parse_return_error;
110:
111: /* some external tables of character types */
112: extern unsigned char is_idstart[], is_idchar[];
113:
114: %}
115:
116: %union {
117: long lval;
118: int voidval;
119: char *sval;
120: }
121:
122: %type <lval> exp exp1 start
123: %token <lval> INT CHAR
124: %token <sval> NAME
125: %token <lval> ERROR
126:
127: %left ','
128: %left OR
129: %left AND
130: %left '|'
131: %left '^'
132: %left '&'
133: %left EQUAL NOTEQUAL
134: %left '<' '>' LEQ GEQ
135: %left LSH RSH
136: %left '+' '-'
137: %left '*' '/' '%'
138: %right UNARY
139:
140: %%
141:
142: start : exp1
143: { expression_value = $1; }
144: ;
145:
146: /* Expressions, including the comma operator. */
147: exp1 : exp
148: | exp1 ',' exp
149: { $$ = $3; }
150: ;
151:
152: /* Expressions, not including the comma operator. */
153: exp : '-' exp %prec UNARY
154: { $$ = - $2; }
155: | '!' exp %prec UNARY
156: { $$ = ! $2; }
157: | '~' exp %prec UNARY
158: { $$ = ~ $2; }
159: | '(' exp1 ')'
160: { $$ = $2; }
161: ;
162:
163: /* Binary operators in order of decreasing precedence. */
164: exp : exp '*' exp
165: { $$ = $1 * $3; }
166: | exp '/' exp
167: { $$ = $1 / $3; }
168: | exp '%' exp
169: { $$ = $1 % $3; }
170: | exp '+' exp
171: { $$ = $1 + $3; }
172: | exp '-' exp
173: { $$ = $1 - $3; }
174: | exp LSH exp
175: { $$ = $1 << $3; }
176: | exp RSH exp
177: { $$ = $1 >> $3; }
178: | exp EQUAL exp
179: { $$ = ($1 == $3); }
180: | exp NOTEQUAL exp
181: { $$ = ($1 != $3); }
182: | exp LEQ exp
183: { $$ = ($1 <= $3); }
184: | exp GEQ exp
185: { $$ = ($1 >= $3); }
186: | exp '<' exp
187: { $$ = ($1 < $3); }
188: | exp '>' exp
189: { $$ = ($1 > $3); }
190: | exp '&' exp
191: { $$ = ($1 & $3); }
192: | exp '^' exp
193: { $$ = ($1 ^ $3); }
194: | exp '|' exp
195: { $$ = ($1 | $3); }
196: | exp AND exp
197: { $$ = ($1 && $3); }
198: | exp OR exp
199: { $$ = ($1 || $3); }
200: | exp '?' exp ':' exp
201: { $$ = $1 ? $3 : $5; }
202: | INT
203: { $$ = yylval.lval; }
204: | CHAR
205: { $$ = yylval.lval; }
206: | NAME
207: { $$ = 0; }
208: ;
209: %%
210:
211: /* During parsing of a C expression, the pointer to the next character
212: is in this variable. */
213:
214: static char *lexptr;
215:
216: /* Take care of parsing a number (anything that starts with a digit).
217: Set yylval and return the token type; update lexptr.
218: LEN is the number of characters in it. */
219:
220: /* maybe needs to actually deal with floating point numbers */
221:
222: static int
223: parse_number (olen)
224: int olen;
225: {
226: register char *p = lexptr;
227: register long n = 0;
228: register int c;
229: register int base = 10;
230: register len = olen;
231: char *err_copy;
232:
233: extern double atof ();
234:
235: for (c = 0; c < len; c++)
236: if (p[c] == '.') {
237: /* It's a float since it contains a point. */
238: yyerror ("floating point numbers not allowed in #if expressions");
239: return ERROR;
240:
241: /* ****************
242: yylval.dval = atof (p);
243: lexptr += len;
244: return FLOAT;
245: **************** */
246: }
247:
248: if (len >= 3 && (!strncmp (p, "0x", 2) || !strncmp (p, "0X", 2))) {
249: p += 2;
250: base = 16;
251: len -= 2;
252: }
253: else if (*p == '0')
254: base = 8;
255:
256: while (len-- > 0) {
257: c = *p++;
258: n *= base;
259: if (c >= '0' && c <= '9')
260: n += c - '0';
261: else {
262: if (c >= 'A' && c <= 'Z') c += 'a' - 'A';
263: if (base == 16 && c >= 'a' && c <= 'f')
264: n += c - 'a' + 10;
265: else if (len == 0 && c == 'l')
266: ;
267: else {
268: yyerror ("Invalid number in #if expression");
269: return ERROR;
270: }
271: }
272: }
273:
274: lexptr = p;
275: yylval.lval = n;
276: return INT;
277: }
278:
279: struct token {
280: char *operator;
281: int token;
282: };
283:
284: #define NULL 0
285:
286: static struct token tokentab2[] = {
287: {"&&", AND},
288: {"||", OR},
289: {"<<", LSH},
290: {">>", RSH},
291: {"==", EQUAL},
292: {"!=", NOTEQUAL},
293: {"<=", LEQ},
294: {">=", GEQ},
295: {NULL, ERROR}
296: };
297:
298: /* Read one token, getting characters through lexptr. */
299:
300: static int
301: yylex ()
302: {
303: register int c;
304: register int namelen;
305: register char *tokstart;
306: register struct token *toktab;
307:
308: retry:
309:
310: tokstart = lexptr;
311: c = *tokstart;
312: /* See if it is a special token of length 2. */
313: for (toktab = tokentab2; toktab->operator != NULL; toktab++)
314: if (c == *toktab->operator && tokstart[1] == toktab->operator[1]) {
315: lexptr += 2;
316: return toktab->token;
317: }
318:
319: switch (c) {
320: case 0:
321: return 0;
322:
323: case ' ':
324: case '\t':
325: case '\n':
326: lexptr++;
327: goto retry;
328:
329: case '\'':
330: lexptr++;
331: c = *lexptr++;
332: if (c == '\\')
333: c = parse_escape (&lexptr);
334: yylval.lval = c;
335: c = *lexptr++;
336: if (c != '\'') {
337: yyerror ("Invalid character constant in #if");
338: return ERROR;
339: }
340:
341: return CHAR;
342:
343: case '/': /* possible comment */
344: if (*lexptr != '*')
345: return c;
346: for (;;) {
347: while (*lexptr != '\0') {
348: if (*lexptr++ == '*' && *lexptr == '/') {
349: lexptr++;
350: goto retry;
351: }
352: }
353: }
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: lexptr++;
381: return c;
382:
383: case '"':
384: yyerror ("double quoted strings not allowed in #if expressions");
385: return ERROR;
386: }
387: if (c >= '0' && c <= '9') {
388: /* It's a number */
389: for (namelen = 0;
390: c = tokstart[namelen], is_idchar[c] || c == '.';
391: namelen++)
392: ;
393: return parse_number (namelen);
394: }
395:
396: if (!is_idstart[c]) {
397: yyerror ("Invalid token in expression");
398: return ERROR;
399: }
400:
401: /* It is a name. See how long it is. */
402:
403: for (namelen = 0; is_idchar[tokstart[namelen]]; namelen++)
404: ;
405:
406: lexptr += namelen;
407: return NAME;
408: }
409:
410:
411: /* Parse a C escape sequence. STRING_PTR points to a variable
412: containing a pointer to the string to parse. That pointer
413: is updated past the characters we use. The value of the
414: escape sequence is returned.
415:
416: A negative value means the sequence \ newline was seen,
417: which is supposed to be equivalent to nothing at all.
418:
419: If \ is followed by a null character, we return a negative
420: value and leave the string pointer pointing at the null character.
421:
422: If \ is followed by 000, we return 0 and leave the string pointer
423: after the zeros. A value of 0 does not mean end of string. */
424:
425: static int
426: parse_escape (string_ptr)
427: char **string_ptr;
428: {
429: register int c = *(*string_ptr)++;
430: switch (c)
431: {
432: case 'a':
433: return '\a';
434: case 'b':
435: return '\b';
436: case 'e':
437: return 033;
438: case 'f':
439: return '\f';
440: case 'n':
441: return '\n';
442: case 'r':
443: return '\r';
444: case 't':
445: return '\t';
446: case 'v':
447: return '\v';
448: case '\n':
449: return -2;
450: case 0:
451: (*string_ptr)--;
452: return 0;
453: case '^':
454: c = *(*string_ptr)++;
455: if (c == '\\')
456: c = parse_escape (string_ptr);
457: if (c == '?')
458: return 0177;
459: return (c & 0200) | (c & 037);
460:
461: case '0':
462: case '1':
463: case '2':
464: case '3':
465: case '4':
466: case '5':
467: case '6':
468: case '7':
469: {
470: register int i = c - '0';
471: register int count = 0;
472: while (++count < 3)
473: {
474: if ((c = *(*string_ptr)++) >= '0' && c <= '7')
475: {
476: i *= 8;
477: i += c - '0';
478: }
479: else
480: {
481: (*string_ptr)--;
482: break;
483: }
484: }
485: return i;
486: }
487: default:
488: return c;
489: }
490: }
491:
492: static
493: yyerror (s)
494: char *s;
495: {
496: error (s);
497: longjmp (parse_return_error, 1);
498: }
499:
500: /* This page contains the entry point to this file. */
501:
502: /* Parse STRING as an expression, and complain if this fails
503: to use up all of the contents of STRING. */
504: int
505: parse_c_expression (string)
506: char *string;
507: {
508: lexptr = string;
509:
510: if (lexptr == 0 || *lexptr == 0) {
511: error ("empty #if expression");
512: return 0; /* don't include the #if group */
513: }
514:
515: /* if there is some sort of scanning error, just return 0 and assume
516: the parsing routine has printed an error message somewhere.
517: there is surely a better thing to do than this. */
518: if (setjmp(parse_return_error))
519: return 0;
520:
521: if (yyparse ())
522: return 0; /* actually this is never reached
523: the way things stand. */
524: if (*lexptr)
525: error ("Junk after end of expression.");
526:
527: return expression_value; /* set by yyparse() */
528: }
529:
530: #ifdef TEST_EXP_READER
531: /* main program, for testing purposes. */
532: main()
533: {
534: int n;
535: char buf[1024];
536: extern int yydebug;
537: /*
538: yydebug = 1;
539: */
540: initialize_random_junk ();
541:
542: for (;;) {
543: printf("enter expression: ");
544: n = 0;
545: while ((buf[n] = getchar()) != '\n')
546: n++;
547: buf[n] = '\0';
548: printf("parser returned %d\n", parse_c_expression(buf));
549: }
550: }
551:
552: /* table to tell if char can be part of a C identifier. */
553: char is_idchar[256];
554: /* table to tell if char can be first char of a c identifier. */
555: char is_idstart[256];
556: /* table to tell if c is horizontal space. isspace() thinks that
557: newline is space; this is not a good idea for this program. */
558: char is_hor_space[256];
559:
560: /*
561: * initialize random junk in the hash table and maybe other places
562: */
563: initialize_random_junk()
564: {
565: register int i;
566:
567: /*
568: * Set up is_idchar and is_idstart tables. These should be
569: * faster than saying (is_alpha(c) || c == '_'), etc.
570: * Must do set up these things before calling any routines tthat
571: * refer to them.
572: */
573: for (i = 'a'; i <= 'z'; i++) {
574: ++is_idchar[i - 'a' + 'A'];
575: ++is_idchar[i];
576: ++is_idstart[i - 'a' + 'A'];
577: ++is_idstart[i];
578: }
579: for (i = '0'; i <= '9'; i++)
580: ++is_idchar[i];
581: ++is_idchar['_'];
582: ++is_idstart['_'];
583:
584: /* horizontal space table */
585: ++is_hor_space[' '];
586: ++is_hor_space['\t'];
587: }
588:
589: error (msg)
590: {
591: printf("error: %s\n", msg);
592: }
593: #endif
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.