| /* |
| * $Xorg: ifparser.c,v 1.3 2000/08/17 19:41:50 cpqbld Exp $ |
| * |
| * Copyright 1992 Network Computing Devices, Inc. |
| * |
| * Permission to use, copy, modify, and distribute this software and its |
| * documentation for any purpose and without fee is hereby granted, provided |
| * that the above copyright notice appear in all copies and that both that |
| * copyright notice and this permission notice appear in supporting |
| * documentation, and that the name of Network Computing Devices may not be |
| * used in advertising or publicity pertaining to distribution of the software |
| * without specific, written prior permission. Network Computing Devices makes |
| * no representations about the suitability of this software for any purpose. |
| * It is provided ``as is'' without express or implied warranty. |
| * |
| * NETWORK COMPUTING DEVICES DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS |
| * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, |
| * IN NO EVENT SHALL NETWORK COMPUTING DEVICES BE LIABLE FOR ANY SPECIAL, |
| * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM |
| * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE |
| * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR |
| * PERFORMANCE OF THIS SOFTWARE. |
| * |
| * Author: Jim Fulton |
| * Network Computing Devices, Inc. |
| * |
| * Simple if statement processor |
| * |
| * This module can be used to evaluate string representations of C language |
| * if constructs. It accepts the following grammar: |
| * |
| * EXPRESSION := VALUE |
| * | VALUE BINOP EXPRESSION |
| * | VALUE '?' EXPRESSION ':' EXPRESSION |
| * |
| * VALUE := '(' EXPRESSION ')' |
| * | '!' VALUE |
| * | '-' VALUE |
| * | '+' VALUE |
| * | '~' VALUE |
| * | 'defined' '(' variable ')' |
| * | 'defined' variable |
| * | # variable '(' variable-list ')' |
| * | variable |
| * | number |
| * |
| * BINOP := '*' | '/' | '%' |
| * | '+' | '-' |
| * | '<<' | '>>' |
| * | '<' | '>' | '<=' | '>=' |
| * | '==' | '!=' |
| * | '&' | '^' | '|' |
| * | '&&' | '||' |
| * |
| * The normal C order of precedence is supported. |
| * |
| * |
| * External Entry Points: |
| * |
| * ParseIfExpression parse a string for #if |
| */ |
| /* $XFree86: xc/config/makedepend/ifparser.c,v 3.11 2002/09/23 01:48:08 tsi Exp $ */ |
| |
| #include "ifparser.h" |
| #include <ctype.h> |
| #include <stdlib.h> |
| #include <string.h> |
| |
| /**************************************************************************** |
| Internal Macros and Utilities for Parser |
| ****************************************************************************/ |
| |
| #define DO(val) if (!(val)) return NULL |
| #define CALLFUNC(ggg,fff) (*((ggg)->funcs.fff)) |
| #define SKIPSPACE(ccc) while (isspace(*ccc)) ccc++ |
| #define isvarfirstletter(ccc) (isalpha(ccc) || (ccc) == '_') |
| |
| |
| static const char * |
| parse_variable (IfParser *g, const char *cp, const char **varp) |
| { |
| SKIPSPACE (cp); |
| |
| if (!isvarfirstletter (*cp)) |
| return CALLFUNC(g, handle_error) (g, cp, "variable name"); |
| |
| *varp = cp; |
| /* EMPTY */ |
| for (cp++; isalnum(*cp) || *cp == '_'; cp++) ; |
| return cp; |
| } |
| |
| |
| static const char * |
| parse_number (IfParser *g, const char *cp, long *valp) |
| { |
| long base = 10; |
| SKIPSPACE (cp); |
| |
| if (!isdigit(*cp)) |
| return CALLFUNC(g, handle_error) (g, cp, "number"); |
| |
| *valp = 0; |
| |
| if (*cp == '0') { |
| cp++; |
| if ((*cp == 'x') || (*cp == 'X')) { |
| base = 16; |
| cp++; |
| } else { |
| base = 8; |
| } |
| } |
| |
| /* Ignore overflows and assume ASCII, what source is usually written in */ |
| while (1) { |
| int increment = -1; |
| if (base == 8) { |
| if ((*cp >= '0') && (*cp <= '7')) |
| increment = *cp++ - '0'; |
| } else if (base == 16) { |
| if ((*cp >= '0') && (*cp <= '9')) |
| increment = *cp++ - '0'; |
| else if ((*cp >= 'A') && (*cp <= 'F')) |
| increment = *cp++ - ('A' - 10); |
| else if ((*cp >= 'a') && (*cp <= 'f')) |
| increment = *cp++ - ('a' - 10); |
| } else { /* Decimal */ |
| if ((*cp >= '0') && (*cp <= '9')) |
| increment = *cp++ - '0'; |
| } |
| if (increment < 0) |
| break; |
| *valp = (*valp * base) + increment; |
| } |
| |
| /* Skip trailing qualifiers */ |
| while (*cp == 'U' || *cp == 'u' || *cp == 'L' || *cp == 'l') cp++; |
| return cp; |
| } |
| |
| static const char * |
| parse_character (IfParser *g, const char *cp, long *valp) |
| { |
| char val; |
| |
| SKIPSPACE (cp); |
| if (*cp == '\\') |
| switch (cp[1]) { |
| case 'n': val = '\n'; break; |
| case 't': val = '\t'; break; |
| case 'v': val = '\v'; break; |
| case 'b': val = '\b'; break; |
| case 'r': val = '\r'; break; |
| case 'f': val = '\f'; break; |
| case 'a': val = '\a'; break; |
| case '\\': val = '\\'; break; |
| case '?': val = '\?'; break; |
| case '\'': val = '\''; break; |
| case '\"': val = '\"'; break; |
| case 'x': val = (char) strtol (cp + 2, NULL, 16); break; |
| default: val = (char) strtol (cp + 1, NULL, 8); break; |
| } |
| else |
| val = *cp; |
| while (*cp != '\'') cp++; |
| *valp = (long) val; |
| return cp; |
| } |
| |
| static const char * |
| parse_value (IfParser *g, const char *cp, long *valp) |
| { |
| const char *var, *varend; |
| |
| *valp = 0; |
| |
| SKIPSPACE (cp); |
| if (!*cp) |
| return cp; |
| |
| switch (*cp) { |
| case '(': |
| DO (cp = ParseIfExpression (g, cp + 1, valp)); |
| SKIPSPACE (cp); |
| if (*cp != ')') |
| return CALLFUNC(g, handle_error) (g, cp, ")"); |
| |
| return cp + 1; /* skip the right paren */ |
| |
| case '!': |
| DO (cp = parse_value (g, cp + 1, valp)); |
| *valp = !(*valp); |
| return cp; |
| |
| case '-': |
| DO (cp = parse_value (g, cp + 1, valp)); |
| *valp = -(*valp); |
| return cp; |
| |
| case '+': |
| DO (cp = parse_value (g, cp + 1, valp)); |
| return cp; |
| |
| case '~': |
| DO (cp = parse_value (g, cp + 1, valp)); |
| *valp = ~(*valp); |
| return cp; |
| |
| case '#': |
| DO (cp = parse_variable (g, cp + 1, &var)); |
| SKIPSPACE (cp); |
| if (*cp != '(') |
| return CALLFUNC(g, handle_error) (g, cp, "("); |
| do { |
| DO (cp = parse_variable (g, cp + 1, &var)); |
| SKIPSPACE (cp); |
| } while (*cp && *cp != ')'); |
| if (*cp != ')') |
| return CALLFUNC(g, handle_error) (g, cp, ")"); |
| *valp = 1; /* XXX */ |
| return cp + 1; |
| |
| case '\'': |
| DO (cp = parse_character (g, cp + 1, valp)); |
| if (*cp != '\'') |
| return CALLFUNC(g, handle_error) (g, cp, "'"); |
| return cp + 1; |
| |
| case 'd': |
| if (strncmp (cp, "defined", 7) == 0 && !isalnum(cp[7])) { |
| int paren = 0; |
| int len; |
| |
| cp += 7; |
| SKIPSPACE (cp); |
| if (*cp == '(') { |
| paren = 1; |
| cp++; |
| } |
| DO (cp = parse_variable (g, cp, &var)); |
| len = cp - var; |
| SKIPSPACE (cp); |
| if (paren && *cp != ')') |
| return CALLFUNC(g, handle_error) (g, cp, ")"); |
| *valp = (*(g->funcs.eval_defined)) (g, var, len); |
| return cp + paren; /* skip the right paren */ |
| } |
| /* fall out */ |
| } |
| |
| if (isdigit(*cp)) { |
| DO (cp = parse_number (g, cp, valp)); |
| } else if (!isvarfirstletter(*cp)) |
| return CALLFUNC(g, handle_error) (g, cp, "variable or number"); |
| else { |
| DO (cp = parse_variable (g, cp, &var)); |
| varend = cp; |
| SKIPSPACE(cp); |
| if (*cp != '(') { |
| *valp = (*(g->funcs.eval_variable)) (g, var, varend - var); |
| } else { |
| do { |
| long dummy; |
| DO (cp = ParseIfExpression (g, cp + 1, &dummy)); |
| SKIPSPACE(cp); |
| if (*cp == ')') |
| break; |
| if (*cp != ',') |
| return CALLFUNC(g, handle_error) (g, cp, ","); |
| } while (1); |
| |
| *valp = 1; /* XXX */ |
| cp++; |
| } |
| } |
| |
| return cp; |
| } |
| |
| |
| |
| static const char * |
| parse_product (IfParser *g, const char *cp, long *valp) |
| { |
| long rightval; |
| |
| DO (cp = parse_value (g, cp, valp)); |
| SKIPSPACE (cp); |
| |
| switch (*cp) { |
| case '*': |
| DO (cp = parse_product (g, cp + 1, &rightval)); |
| *valp = (*valp * rightval); |
| break; |
| |
| case '/': |
| DO (cp = parse_product (g, cp + 1, &rightval)); |
| if (rightval == 0) |
| return CALLFUNC(g, handle_error) (g, cp, "0"); |
| *valp = (*valp / rightval); |
| break; |
| |
| case '%': |
| DO (cp = parse_product (g, cp + 1, &rightval)); |
| *valp = (*valp % rightval); |
| break; |
| } |
| return cp; |
| } |
| |
| |
| static const char * |
| parse_sum (IfParser *g, const char *cp, long *valp) |
| { |
| long rightval; |
| |
| DO (cp = parse_product (g, cp, valp)); |
| SKIPSPACE (cp); |
| |
| switch (*cp) { |
| case '+': |
| DO (cp = parse_sum (g, cp + 1, &rightval)); |
| *valp = (*valp + rightval); |
| break; |
| |
| case '-': |
| DO (cp = parse_sum (g, cp + 1, &rightval)); |
| *valp = (*valp - rightval); |
| break; |
| } |
| return cp; |
| } |
| |
| |
| static const char * |
| parse_shift (IfParser *g, const char *cp, long *valp) |
| { |
| long rightval; |
| |
| DO (cp = parse_sum (g, cp, valp)); |
| SKIPSPACE (cp); |
| |
| switch (*cp) { |
| case '<': |
| if (cp[1] == '<') { |
| DO (cp = parse_shift (g, cp + 2, &rightval)); |
| *valp = (*valp << rightval); |
| } |
| break; |
| |
| case '>': |
| if (cp[1] == '>') { |
| DO (cp = parse_shift (g, cp + 2, &rightval)); |
| *valp = (*valp >> rightval); |
| } |
| break; |
| } |
| return cp; |
| } |
| |
| |
| static const char * |
| parse_inequality (IfParser *g, const char *cp, long *valp) |
| { |
| long rightval; |
| |
| DO (cp = parse_shift (g, cp, valp)); |
| SKIPSPACE (cp); |
| |
| switch (*cp) { |
| case '<': |
| if (cp[1] == '=') { |
| DO (cp = parse_inequality (g, cp + 2, &rightval)); |
| *valp = (*valp <= rightval); |
| } else { |
| DO (cp = parse_inequality (g, cp + 1, &rightval)); |
| *valp = (*valp < rightval); |
| } |
| break; |
| |
| case '>': |
| if (cp[1] == '=') { |
| DO (cp = parse_inequality (g, cp + 2, &rightval)); |
| *valp = (*valp >= rightval); |
| } else { |
| DO (cp = parse_inequality (g, cp + 1, &rightval)); |
| *valp = (*valp > rightval); |
| } |
| break; |
| } |
| return cp; |
| } |
| |
| |
| static const char * |
| parse_equality (IfParser *g, const char *cp, long *valp) |
| { |
| long rightval; |
| |
| DO (cp = parse_inequality (g, cp, valp)); |
| SKIPSPACE (cp); |
| |
| switch (*cp) { |
| case '=': |
| if (cp[1] == '=') |
| cp++; |
| DO (cp = parse_equality (g, cp + 1, &rightval)); |
| *valp = (*valp == rightval); |
| break; |
| |
| case '!': |
| if (cp[1] != '=') |
| break; |
| DO (cp = parse_equality (g, cp + 2, &rightval)); |
| *valp = (*valp != rightval); |
| break; |
| } |
| return cp; |
| } |
| |
| |
| static const char * |
| parse_band (IfParser *g, const char *cp, long *valp) |
| { |
| long rightval; |
| |
| DO (cp = parse_equality (g, cp, valp)); |
| SKIPSPACE (cp); |
| |
| switch (*cp) { |
| case '&': |
| if (cp[1] != '&') { |
| DO (cp = parse_band (g, cp + 1, &rightval)); |
| *valp = (*valp & rightval); |
| } |
| break; |
| } |
| return cp; |
| } |
| |
| |
| static const char * |
| parse_bxor (IfParser *g, const char *cp, long *valp) |
| { |
| long rightval; |
| |
| DO (cp = parse_band (g, cp, valp)); |
| SKIPSPACE (cp); |
| |
| switch (*cp) { |
| case '^': |
| DO (cp = parse_bxor (g, cp + 1, &rightval)); |
| *valp = (*valp ^ rightval); |
| break; |
| } |
| return cp; |
| } |
| |
| |
| static const char * |
| parse_bor (IfParser *g, const char *cp, long *valp) |
| { |
| long rightval; |
| |
| DO (cp = parse_bxor (g, cp, valp)); |
| SKIPSPACE (cp); |
| |
| switch (*cp) { |
| case '|': |
| if (cp[1] != '|') { |
| DO (cp = parse_bor (g, cp + 1, &rightval)); |
| *valp = (*valp | rightval); |
| } |
| break; |
| } |
| return cp; |
| } |
| |
| |
| static const char * |
| parse_land (IfParser *g, const char *cp, long *valp) |
| { |
| long rightval; |
| |
| DO (cp = parse_bor (g, cp, valp)); |
| SKIPSPACE (cp); |
| |
| switch (*cp) { |
| case '&': |
| if (cp[1] != '&') |
| return CALLFUNC(g, handle_error) (g, cp, "&&"); |
| DO (cp = parse_land (g, cp + 2, &rightval)); |
| *valp = (*valp && rightval); |
| break; |
| } |
| return cp; |
| } |
| |
| |
| static const char * |
| parse_lor (IfParser *g, const char *cp, long *valp) |
| { |
| long rightval; |
| |
| DO (cp = parse_land (g, cp, valp)); |
| SKIPSPACE (cp); |
| |
| switch (*cp) { |
| case '|': |
| if (cp[1] != '|') |
| return CALLFUNC(g, handle_error) (g, cp, "||"); |
| DO (cp = parse_lor (g, cp + 2, &rightval)); |
| *valp = (*valp || rightval); |
| break; |
| } |
| return cp; |
| } |
| |
| |
| static const char * |
| parse_cond(IfParser *g, const char *cp, long *valp) |
| { |
| long trueval, falseval; |
| |
| DO (cp = parse_lor (g, cp, valp)); |
| SKIPSPACE (cp); |
| |
| switch (*cp) { |
| case '?': |
| DO (cp = parse_cond (g, cp + 1, &trueval)); |
| SKIPSPACE (cp); |
| if (*cp != ':') |
| return CALLFUNC(g, handle_error) (g, cp, ":"); |
| DO (cp = parse_cond (g, cp + 1, &falseval)); |
| *valp = (*valp ? trueval : falseval); |
| break; |
| } |
| return cp; |
| } |
| |
| |
| /**************************************************************************** |
| External Entry Points |
| ****************************************************************************/ |
| |
| const char * |
| ParseIfExpression (IfParser *g, const char *cp, long *valp) |
| { |
| return parse_cond (g, cp, valp); |
| } |