552 lines
12 KiB
C
552 lines
12 KiB
C
|
/*
|
||
|
*
|
||
|
* 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
|
||
|
*/
|
||
|
|
||
|
#include "ifparser.h"
|
||
|
#include <ctype.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
#include <limits.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)
|
||
|
*valp = (*valp / rightval);
|
||
|
else
|
||
|
*valp = LONG_MAX;
|
||
|
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);
|
||
|
}
|