diff options
Diffstat (limited to 'cpp/cexp.y')
-rw-r--r-- | cpp/cexp.y | 591 |
1 files changed, 591 insertions, 0 deletions
diff --git a/cpp/cexp.y b/cpp/cexp.y new file mode 100644 index 00000000000..c41aa25af14 --- /dev/null +++ b/cpp/cexp.y @@ -0,0 +1,591 @@ +/* Parse C expressions for CCCP. + Copyright (C) 1986 Free Software Foundation. + + NO WARRANTY + + BECAUSE THIS PROGRAM IS LICENSED FREE OF CHARGE, WE PROVIDE ABSOLUTELY +NO WARRANTY, TO THE EXTENT PERMITTED BY APPLICABLE STATE LAW. EXCEPT +WHEN OTHERWISE STATED IN WRITING, FREE SOFTWARE FOUNDATION, INC, +RICHARD M. STALLMAN AND/OR OTHER PARTIES PROVIDE THIS PROGRAM "AS IS" +WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY +AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE +DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR +CORRECTION. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW WILL RICHARD M. +STALLMAN, THE FREE SOFTWARE FOUNDATION, INC., AND/OR ANY OTHER PARTY +WHO MAY MODIFY AND REDISTRIBUTE THIS PROGRAM AS PERMITTED BELOW, BE +LIABLE TO YOU FOR DAMAGES, INCLUDING ANY LOST PROFITS, LOST MONIES, OR +OTHER SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR +DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY THIRD PARTIES OR +A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS) THIS +PROGRAM, EVEN IF YOU HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES, OR FOR ANY CLAIM BY ANY OTHER PARTY. + + GENERAL PUBLIC LICENSE TO COPY + + 1. You may copy and distribute verbatim copies of this source file +as you receive it, in any medium, provided that you conspicuously +and appropriately publish on each copy a valid copyright notice +"Copyright (C) 1986 Free Software Foundation"; and include +following the copyright notice a verbatim copy of the above disclaimer +of warranty and of this License. + + 2. You may modify your copy or copies of this source file or +any portion of it, and copy and distribute such modifications under +the terms of Paragraph 1 above, provided that you also do the following: + + a) cause the modified files to carry prominent notices stating + that you changed the files and the date of any change; and + + b) cause the whole of any work that you distribute or publish, + that in whole or in part contains or is a derivative of this + program or any part thereof, to be licensed at no charge to all + third parties on terms identical to those contained in this + License Agreement (except that you may choose to grant more extensive + warranty protection to some or all third parties, at your option). + + c) You may charge a distribution fee for the physical act of + transferring a copy, and you may at your option offer warranty + protection in exchange for a fee. + +Mere aggregation of another unrelated program with this program (or its +derivative) on a volume of a storage or distribution medium does not bring +the other program under the scope of these terms. + + 3. You may copy and distribute this program (or a portion or derivative +of it, under Paragraph 2) in object code or executable form under the terms +of Paragraphs 1 and 2 above provided that you also do one of the following: + + a) accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of + Paragraphs 1 and 2 above; or, + + b) accompany it with a written offer, valid for at least three + years, to give any third party free (except for a nominal + shipping charge) a complete machine-readable copy of the + corresponding source code, to be distributed under the terms of + Paragraphs 1 and 2 above; or, + + c) accompany it with the information you received as to where the + corresponding source code may be obtained. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form alone.) + +For an executable file, complete source code means all the source code for +all modules it contains; but, as a special exception, it need not include +source code for modules which are standard libraries that accompany the +operating system on which the executable file runs. + + 4. You may not copy, sublicense, distribute or transfer this program +except as expressly provided under this License Agreement. Any attempt +otherwise to copy, sublicense, distribute or transfer this program is void and +your rights to use the program under this License agreement shall be +automatically terminated. However, parties who have received computer +software programs from you with this License Agreement will not have +their licenses terminated so long as such parties remain in full compliance. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! + + Adapted from expread.y of GDB by Paul Rubin, July 1986. + +/* Parse a C expression from text in a string */ + +%{ +#include <setjmp.h> +/* #define YYDEBUG 1 */ + + static int yylex (); + static yyerror (); + int expression_value; + + static jmp_buf parse_return_error; + + /* some external tables of character types */ + extern unsigned char is_idstart[], is_idchar[]; + +%} + +%union { + long lval; + int voidval; + char *sval; +} + +%type <lval> exp exp1 start +%token <lval> INT CHAR +%token <sval> NAME +%token <lval> ERROR + +%left ',' +%left OR +%left AND +%left '|' +%left '^' +%left '&' +%left EQUAL NOTEQUAL +%left '<' '>' LEQ GEQ +%left LSH RSH +%left '+' '-' +%left '*' '/' '%' +%right UNARY + +%% + +start : exp1 + { expression_value = $1; } + ; + +/* Expressions, including the comma operator. */ +exp1 : exp + | exp1 ',' exp + { $$ = $3; } + ; + +/* Expressions, not including the comma operator. */ +exp : '-' exp %prec UNARY + { $$ = - $2; } + | '!' exp %prec UNARY + { $$ = ! $2; } + | '~' exp %prec UNARY + { $$ = ~ $2; } + | '(' exp1 ')' + { $$ = $2; } + ; + +/* Binary operators in order of decreasing precedence. */ +exp : exp '*' exp + { $$ = $1 * $3; } + | exp '/' exp + { $$ = $1 / $3; } + | exp '%' exp + { $$ = $1 % $3; } + | exp '+' exp + { $$ = $1 + $3; } + | exp '-' exp + { $$ = $1 - $3; } + | exp LSH exp + { $$ = $1 << $3; } + | exp RSH exp + { $$ = $1 >> $3; } + | exp EQUAL exp + { $$ = ($1 == $3); } + | exp NOTEQUAL exp + { $$ = ($1 != $3); } + | exp LEQ exp + { $$ = ($1 <= $3); } + | exp GEQ exp + { $$ = ($1 >= $3); } + | exp '<' exp + { $$ = ($1 < $3); } + | exp '>' exp + { $$ = ($1 > $3); } + | exp '&' exp + { $$ = ($1 & $3); } + | exp '^' exp + { $$ = ($1 ^ $3); } + | exp '|' exp + { $$ = ($1 | $3); } + | exp AND exp + { $$ = ($1 && $3); } + | exp OR exp + { $$ = ($1 || $3); } + | exp '?' exp ':' exp + { $$ = $1 ? $3 : $5; } + | INT + { $$ = yylval.lval; } + | CHAR + { $$ = yylval.lval; } + | NAME + { $$ = 0; } + ; +%% + +/* During parsing of a C expression, the pointer to the next character + is in this variable. */ + +static char *lexptr; + +/* Take care of parsing a number (anything that starts with a digit). + Set yylval and return the token type; update lexptr. + LEN is the number of characters in it. */ + +/* maybe needs to actually deal with floating point numbers */ + +static int +parse_number (olen) + int olen; +{ + register char *p = lexptr; + register long n = 0; + register int c; + register int base = 10; + register len = olen; + char *err_copy; + + extern double atof (); + + for (c = 0; c < len; c++) + if (p[c] == '.') { + /* It's a float since it contains a point. */ + yyerror ("floating point numbers not allowed in #if expressions"); + return ERROR; + +/* **************** + yylval.dval = atof (p); + lexptr += len; + return FLOAT; + **************** */ + } + + if (len >= 3 && (!strncmp (p, "0x", 2) || !strncmp (p, "0X", 2))) { + p += 2; + base = 16; + len -= 2; + } + else if (*p == '0') + base = 8; + + while (len-- > 0) { + c = *p++; + n *= base; + if (c >= '0' && c <= '9') + n += c - '0'; + else { + if (c >= 'A' && c <= 'Z') c += 'a' - 'A'; + if (base == 16 && c >= 'a' && c <= 'f') + n += c - 'a' + 10; + else if (len == 0 && c == 'l') + ; + else { + yyerror ("Invalid number in #if expression"); + return ERROR; + } + } + } + + lexptr = p; + yylval.lval = n; + return INT; +} + +struct token { + char *operator; + int token; +}; + +#define NULL 0 + +static struct token tokentab2[] = { + {"&&", AND}, + {"||", OR}, + {"<<", LSH}, + {">>", RSH}, + {"==", EQUAL}, + {"!=", NOTEQUAL}, + {"<=", LEQ}, + {">=", GEQ}, + {NULL, ERROR} +}; + +/* Read one token, getting characters through lexptr. */ + +static int +yylex () +{ + register int c; + register int namelen; + register char *tokstart; + register struct token *toktab; + + retry: + + tokstart = lexptr; + c = *tokstart; + /* See if it is a special token of length 2. */ + for (toktab = tokentab2; toktab->operator != NULL; toktab++) + if (c == *toktab->operator && tokstart[1] == toktab->operator[1]) { + lexptr += 2; + return toktab->token; + } + + switch (c) { + case 0: + return 0; + + case ' ': + case '\t': + case '\n': + lexptr++; + goto retry; + + case '\'': + lexptr++; + c = *lexptr++; + if (c == '\\') + c = parse_escape (&lexptr); + yylval.lval = c; + c = *lexptr++; + if (c != '\'') { + yyerror ("Invalid character constant in #if"); + return ERROR; + } + + return CHAR; + + case '/': /* possible comment */ + if (*lexptr != '*') + return c; + for (;;) { + while (*lexptr != '\0') { + if (*lexptr++ == '*' && *lexptr == '/') { + lexptr++; + goto retry; + } + } + } + + /* some of these chars are invalid in constant expressions; + maybe do something about them later */ + case '+': + case '-': + case '*': + case '%': + case '|': + case '&': + case '^': + case '~': + case '!': + case '@': + case '<': + case '>': + case '(': + case ')': + case '[': + case ']': + case '.': + case '?': + case ':': + case '=': + case '{': + case '}': + case ',': + lexptr++; + return c; + + case '"': + yyerror ("double quoted strings not allowed in #if expressions"); + return ERROR; + } + if (c >= '0' && c <= '9') { + /* It's a number */ + for (namelen = 0; + c = tokstart[namelen], is_idchar[c] || c == '.'; + namelen++) + ; + return parse_number (namelen); + } + + if (!is_idstart[c]) { + yyerror ("Invalid token in expression"); + return ERROR; + } + + /* It is a name. See how long it is. */ + + for (namelen = 0; is_idchar[tokstart[namelen]]; namelen++) + ; + + lexptr += namelen; + return NAME; +} + + +/* Parse a C escape sequence. STRING_PTR points to a variable + containing a pointer to the string to parse. That pointer + is updated past the characters we use. The value of the + escape sequence is returned. + + A negative value means the sequence \ newline was seen, + which is supposed to be equivalent to nothing at all. + + If \ is followed by a null character, we return a negative + value and leave the string pointer pointing at the null character. + + If \ is followed by 000, we return 0 and leave the string pointer + after the zeros. A value of 0 does not mean end of string. */ + +static int +parse_escape (string_ptr) + char **string_ptr; +{ + register int c = *(*string_ptr)++; + switch (c) + { + case 'a': + return '\a'; + case 'b': + return '\b'; + case 'e': + return 033; + case 'f': + return '\f'; + case 'n': + return '\n'; + case 'r': + return '\r'; + case 't': + return '\t'; + case 'v': + return '\v'; + case '\n': + return -2; + case 0: + (*string_ptr)--; + return 0; + case '^': + c = *(*string_ptr)++; + if (c == '\\') + c = parse_escape (string_ptr); + if (c == '?') + return 0177; + return (c & 0200) | (c & 037); + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + { + register int i = c - '0'; + register int count = 0; + while (++count < 3) + { + if ((c = *(*string_ptr)++) >= '0' && c <= '7') + { + i *= 8; + i += c - '0'; + } + else + { + (*string_ptr)--; + break; + } + } + return i; + } + default: + return c; + } +} + +static +yyerror (s) + char *s; +{ + error (s); + longjmp (parse_return_error, 1); +} + +/* This page contains the entry point to this file. */ + +/* Parse STRING as an expression, and complain if this fails + to use up all of the contents of STRING. */ +int +parse_c_expression (string) + char *string; +{ + lexptr = string; + + if (lexptr == 0 || *lexptr == 0) { + error ("empty #if expression"); + return 0; /* don't include the #if group */ + } + + /* if there is some sort of scanning error, just return 0 and assume + the parsing routine has printed an error message somewhere. + there is surely a better thing to do than this. */ + if (setjmp(parse_return_error)) + return 0; + + if (yyparse ()) + return 0; /* actually this is never reached + the way things stand. */ + if (*lexptr) + error ("Junk after end of expression."); + + return expression_value; /* set by yyparse() */ +} + +#ifdef TEST_EXP_READER +/* main program, for testing purposes. */ +main() +{ + int n; + char buf[1024]; + extern int yydebug; +/* + yydebug = 1; +*/ + initialize_random_junk (); + + for (;;) { + printf("enter expression: "); + n = 0; + while ((buf[n] = getchar()) != '\n') + n++; + buf[n] = '\0'; + printf("parser returned %d\n", parse_c_expression(buf)); + } +} + +/* table to tell if char can be part of a C identifier. */ +char is_idchar[256]; +/* table to tell if char can be first char of a c identifier. */ +char is_idstart[256]; +/* table to tell if c is horizontal space. isspace() thinks that + newline is space; this is not a good idea for this program. */ +char is_hor_space[256]; + +/* + * initialize random junk in the hash table and maybe other places + */ +initialize_random_junk() +{ + register int i; + + /* + * Set up is_idchar and is_idstart tables. These should be + * faster than saying (is_alpha(c) || c == '_'), etc. + * Must do set up these things before calling any routines tthat + * refer to them. + */ + for (i = 'a'; i <= 'z'; i++) { + ++is_idchar[i - 'a' + 'A']; + ++is_idchar[i]; + ++is_idstart[i - 'a' + 'A']; + ++is_idstart[i]; + } + for (i = '0'; i <= '9'; i++) + ++is_idchar[i]; + ++is_idchar['_']; + ++is_idstart['_']; + + /* horizontal space table */ + ++is_hor_space[' ']; + ++is_hor_space['\t']; +} + +error (msg) +{ + printf("error: %s\n", msg); +} +#endif |