|
|
1.1 root 1: /* Parse C expressions for CCCP.
2: Copyright (C) 1987 Free Software Foundation.
3:
1.1.1.6 ! root 4: This program is free software; you can redistribute it and/or modify it
! 5: under the terms of the GNU General Public License as published by the
! 6: Free Software Foundation; either version 1, or (at your option) any
! 7: later version.
! 8:
! 9: This program is distributed in the hope that it will be useful,
! 10: but WITHOUT ANY WARRANTY; without even the implied warranty of
! 11: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
! 12: GNU General Public License for more details.
! 13:
! 14: You should have received a copy of the GNU General Public License
! 15: along with this program; if not, write to the Free Software
! 16: Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
1.1 root 17:
18: In other words, you are welcome to use, share and improve this program.
19: You are forbidden to forbid anyone else to use, share and improve
20: what you give them. Help stamp out software-hoarding!
21:
22: Adapted from expread.y of GDB by Paul Rubin, July 1986.
23:
24: /* Parse a C expression from text in a string */
25:
26: %{
1.1.1.3 root 27: #include "config.h"
1.1 root 28: #include <setjmp.h>
29: /* #define YYDEBUG 1 */
30:
1.1.1.4 root 31: int yylex ();
32: void yyerror ();
1.1 root 33: int expression_value;
34:
35: static jmp_buf parse_return_error;
36:
37: /* some external tables of character types */
38: extern unsigned char is_idstart[], is_idchar[];
39:
40: %}
41:
42: %union {
43: long lval;
44: int voidval;
45: char *sval;
46: }
47:
48: %type <lval> exp exp1 start
49: %token <lval> INT CHAR
50: %token <sval> NAME
51: %token <lval> ERROR
52:
1.1.1.2 root 53: %right '?' ':'
1.1 root 54: %left ','
55: %left OR
56: %left AND
57: %left '|'
58: %left '^'
59: %left '&'
60: %left EQUAL NOTEQUAL
61: %left '<' '>' LEQ GEQ
62: %left LSH RSH
63: %left '+' '-'
64: %left '*' '/' '%'
65: %right UNARY
1.1.1.2 root 66:
67: /* %expect 40 */
1.1 root 68:
69: %%
70:
71: start : exp1
72: { expression_value = $1; }
73: ;
74:
75: /* Expressions, including the comma operator. */
76: exp1 : exp
77: | exp1 ',' exp
78: { $$ = $3; }
79: ;
80:
81: /* Expressions, not including the comma operator. */
82: exp : '-' exp %prec UNARY
83: { $$ = - $2; }
84: | '!' exp %prec UNARY
85: { $$ = ! $2; }
86: | '~' exp %prec UNARY
87: { $$ = ~ $2; }
88: | '(' exp1 ')'
89: { $$ = $2; }
90: ;
91:
92: /* Binary operators in order of decreasing precedence. */
93: exp : exp '*' exp
94: { $$ = $1 * $3; }
95: | exp '/' exp
1.1.1.5 root 96: { if ($3 == 0)
97: {
98: error ("division by zero in #if");
99: $3 = 1;
100: }
101: $$ = $1 / $3; }
1.1 root 102: | exp '%' exp
1.1.1.5 root 103: { if ($3 == 0)
104: {
105: error ("division by zero in #if");
106: $3 = 1;
107: }
108: $$ = $1 % $3; }
1.1 root 109: | exp '+' exp
110: { $$ = $1 + $3; }
111: | exp '-' exp
112: { $$ = $1 - $3; }
113: | exp LSH exp
114: { $$ = $1 << $3; }
115: | exp RSH exp
116: { $$ = $1 >> $3; }
117: | exp EQUAL exp
118: { $$ = ($1 == $3); }
119: | exp NOTEQUAL exp
120: { $$ = ($1 != $3); }
121: | exp LEQ exp
122: { $$ = ($1 <= $3); }
123: | exp GEQ exp
124: { $$ = ($1 >= $3); }
125: | exp '<' exp
126: { $$ = ($1 < $3); }
127: | exp '>' exp
128: { $$ = ($1 > $3); }
129: | exp '&' exp
130: { $$ = ($1 & $3); }
131: | exp '^' exp
132: { $$ = ($1 ^ $3); }
133: | exp '|' exp
134: { $$ = ($1 | $3); }
135: | exp AND exp
136: { $$ = ($1 && $3); }
137: | exp OR exp
138: { $$ = ($1 || $3); }
139: | exp '?' exp ':' exp
140: { $$ = $1 ? $3 : $5; }
141: | INT
142: { $$ = yylval.lval; }
143: | CHAR
144: { $$ = yylval.lval; }
145: | NAME
146: { $$ = 0; }
147: ;
148: %%
149:
150: /* During parsing of a C expression, the pointer to the next character
151: is in this variable. */
152:
153: static char *lexptr;
154:
155: /* Take care of parsing a number (anything that starts with a digit).
156: Set yylval and return the token type; update lexptr.
157: LEN is the number of characters in it. */
158:
159: /* maybe needs to actually deal with floating point numbers */
160:
1.1.1.4 root 161: int
1.1 root 162: parse_number (olen)
163: int olen;
164: {
165: register char *p = lexptr;
166: register long n = 0;
167: register int c;
168: register int base = 10;
169: register len = olen;
170:
171: extern double atof ();
172:
173: for (c = 0; c < len; c++)
174: if (p[c] == '.') {
175: /* It's a float since it contains a point. */
176: yyerror ("floating point numbers not allowed in #if expressions");
177: return ERROR;
178:
179: /* ****************
180: yylval.dval = atof (p);
181: lexptr += len;
182: return FLOAT;
183: **************** */
184: }
185:
186: if (len >= 3 && (!strncmp (p, "0x", 2) || !strncmp (p, "0X", 2))) {
187: p += 2;
188: base = 16;
189: len -= 2;
190: }
191: else if (*p == '0')
192: base = 8;
193:
194: while (len-- > 0) {
195: c = *p++;
196: n *= base;
197: if (c >= '0' && c <= '9')
198: n += c - '0';
199: else {
200: if (c >= 'A' && c <= 'Z') c += 'a' - 'A';
201: if (base == 16 && c >= 'a' && c <= 'f')
202: n += c - 'a' + 10;
203: else if (len == 0 && c == 'l')
204: ;
205: else {
206: yyerror ("Invalid number in #if expression");
207: return ERROR;
208: }
209: }
210: }
211:
212: lexptr = p;
213: yylval.lval = n;
214: return INT;
215: }
216:
217: struct token {
218: char *operator;
219: int token;
220: };
221:
222: #define NULL 0
223:
224: static struct token tokentab2[] = {
225: {"&&", AND},
226: {"||", OR},
227: {"<<", LSH},
228: {">>", RSH},
229: {"==", EQUAL},
230: {"!=", NOTEQUAL},
231: {"<=", LEQ},
232: {">=", GEQ},
233: {NULL, ERROR}
234: };
235:
236: /* Read one token, getting characters through lexptr. */
237:
1.1.1.4 root 238: int
1.1 root 239: yylex ()
240: {
241: register int c;
242: register int namelen;
243: register char *tokstart;
244: register struct token *toktab;
245:
246: retry:
247:
248: tokstart = lexptr;
249: c = *tokstart;
250: /* See if it is a special token of length 2. */
251: for (toktab = tokentab2; toktab->operator != NULL; toktab++)
252: if (c == *toktab->operator && tokstart[1] == toktab->operator[1]) {
253: lexptr += 2;
254: return toktab->token;
255: }
256:
257: switch (c) {
258: case 0:
259: return 0;
260:
261: case ' ':
262: case '\t':
263: case '\n':
264: lexptr++;
265: goto retry;
266:
267: case '\'':
268: lexptr++;
269: c = *lexptr++;
270: if (c == '\\')
271: c = parse_escape (&lexptr);
272: yylval.lval = c;
273: c = *lexptr++;
274: if (c != '\'') {
275: yyerror ("Invalid character constant in #if");
276: return ERROR;
277: }
278:
279: return CHAR;
280:
281: /* some of these chars are invalid in constant expressions;
282: maybe do something about them later */
1.1.1.2 root 283: case '/':
1.1 root 284: case '+':
285: case '-':
286: case '*':
287: case '%':
288: case '|':
289: case '&':
290: case '^':
291: case '~':
292: case '!':
293: case '@':
294: case '<':
295: case '>':
296: case '(':
297: case ')':
298: case '[':
299: case ']':
300: case '.':
301: case '?':
302: case ':':
303: case '=':
304: case '{':
305: case '}':
306: case ',':
307: lexptr++;
308: return c;
309:
310: case '"':
311: yyerror ("double quoted strings not allowed in #if expressions");
312: return ERROR;
313: }
314: if (c >= '0' && c <= '9') {
315: /* It's a number */
316: for (namelen = 0;
317: c = tokstart[namelen], is_idchar[c] || c == '.';
318: namelen++)
319: ;
320: return parse_number (namelen);
321: }
322:
323: if (!is_idstart[c]) {
324: yyerror ("Invalid token in expression");
325: return ERROR;
326: }
327:
328: /* It is a name. See how long it is. */
329:
330: for (namelen = 0; is_idchar[tokstart[namelen]]; namelen++)
331: ;
332:
333: lexptr += namelen;
334: return NAME;
335: }
336:
337:
338: /* Parse a C escape sequence. STRING_PTR points to a variable
339: containing a pointer to the string to parse. That pointer
340: is updated past the characters we use. The value of the
341: escape sequence is returned.
342:
343: A negative value means the sequence \ newline was seen,
344: which is supposed to be equivalent to nothing at all.
345:
346: If \ is followed by a null character, we return a negative
347: value and leave the string pointer pointing at the null character.
348:
349: If \ is followed by 000, we return 0 and leave the string pointer
350: after the zeros. A value of 0 does not mean end of string. */
351:
1.1.1.4 root 352: int
1.1 root 353: parse_escape (string_ptr)
354: char **string_ptr;
355: {
356: register int c = *(*string_ptr)++;
357: switch (c)
358: {
359: case 'a':
360: return '\a';
361: case 'b':
362: return '\b';
363: case 'e':
364: return 033;
365: case 'f':
366: return '\f';
367: case 'n':
368: return '\n';
369: case 'r':
370: return '\r';
371: case 't':
372: return '\t';
373: case 'v':
374: return '\v';
375: case '\n':
376: return -2;
377: case 0:
378: (*string_ptr)--;
379: return 0;
380: case '^':
381: c = *(*string_ptr)++;
382: if (c == '\\')
383: c = parse_escape (string_ptr);
384: if (c == '?')
385: return 0177;
386: return (c & 0200) | (c & 037);
387:
388: case '0':
389: case '1':
390: case '2':
391: case '3':
392: case '4':
393: case '5':
394: case '6':
395: case '7':
396: {
397: register int i = c - '0';
398: register int count = 0;
399: while (++count < 3)
400: {
401: if ((c = *(*string_ptr)++) >= '0' && c <= '7')
402: {
403: i *= 8;
404: i += c - '0';
405: }
406: else
407: {
408: (*string_ptr)--;
409: break;
410: }
411: }
412: return i;
413: }
414: default:
415: return c;
416: }
417: }
418:
1.1.1.4 root 419: void
1.1 root 420: yyerror (s)
421: char *s;
422: {
423: error (s);
424: longjmp (parse_return_error, 1);
425: }
426:
427: /* This page contains the entry point to this file. */
428:
429: /* Parse STRING as an expression, and complain if this fails
430: to use up all of the contents of STRING. */
1.1.1.2 root 431: /* We do not support C comments. They should be removed before
432: this function is called. */
433:
1.1 root 434: int
435: parse_c_expression (string)
436: char *string;
437: {
438: lexptr = string;
439:
440: if (lexptr == 0 || *lexptr == 0) {
441: error ("empty #if expression");
442: return 0; /* don't include the #if group */
443: }
444:
445: /* if there is some sort of scanning error, just return 0 and assume
446: the parsing routine has printed an error message somewhere.
447: there is surely a better thing to do than this. */
448: if (setjmp(parse_return_error))
449: return 0;
450:
451: if (yyparse ())
452: return 0; /* actually this is never reached
453: the way things stand. */
454: if (*lexptr)
455: error ("Junk after end of expression.");
456:
457: return expression_value; /* set by yyparse() */
458: }
459:
460: #ifdef TEST_EXP_READER
461: /* main program, for testing purposes. */
462: main()
463: {
464: int n;
465: char buf[1024];
466: extern int yydebug;
467: /*
468: yydebug = 1;
469: */
470: initialize_random_junk ();
471:
472: for (;;) {
473: printf("enter expression: ");
474: n = 0;
475: while ((buf[n] = getchar()) != '\n')
476: n++;
477: buf[n] = '\0';
478: printf("parser returned %d\n", parse_c_expression(buf));
479: }
480: }
481:
482: /* table to tell if char can be part of a C identifier. */
483: char is_idchar[256];
484: /* table to tell if char can be first char of a c identifier. */
485: char is_idstart[256];
486: /* table to tell if c is horizontal space. isspace() thinks that
487: newline is space; this is not a good idea for this program. */
488: char is_hor_space[256];
489:
490: /*
491: * initialize random junk in the hash table and maybe other places
492: */
493: initialize_random_junk()
494: {
495: register int i;
496:
497: /*
498: * Set up is_idchar and is_idstart tables. These should be
499: * faster than saying (is_alpha(c) || c == '_'), etc.
500: * Must do set up these things before calling any routines tthat
501: * refer to them.
502: */
503: for (i = 'a'; i <= 'z'; i++) {
504: ++is_idchar[i - 'a' + 'A'];
505: ++is_idchar[i];
506: ++is_idstart[i - 'a' + 'A'];
507: ++is_idstart[i];
508: }
509: for (i = '0'; i <= '9'; i++)
510: ++is_idchar[i];
511: ++is_idchar['_'];
512: ++is_idstart['_'];
1.1.1.2 root 513: #ifdef DOLLARS_IN_IDENTIFIERS
514: ++is_idchar['$'];
515: ++is_idstart['$'];
516: #endif
1.1 root 517:
518: /* horizontal space table */
519: ++is_hor_space[' '];
520: ++is_hor_space['\t'];
521: }
522:
523: error (msg)
524: {
525: printf("error: %s\n", msg);
526: }
527: #endif
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.