home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ftp.cs.arizona.edu
/
ftp.cs.arizona.edu.tar
/
ftp.cs.arizona.edu
/
icon
/
historic
/
v92.tgz
/
v92.tar
/
v92
/
src
/
preproc
/
evaluate.c
< prev
next >
Wrap
C/C++ Source or Header
|
1996-03-22
|
15KB
|
560 lines
/*
* This file contains functions to evaluate constant expressions for
* conditional inclusion. These functions are organized as a recursive
* decent parser based on the C grammar presented in the ANSI C Standard
* document. The function eval() is called from the outside.
*/
#include "::preproc:preproc.h"
#include "::preproc:ptoken.h"
/*
* Prototypes for static functions.
*/
hidden long primary Params((struct token **tp, struct token *trigger));
hidden long unary Params((struct token **tp, struct token *trigger));
hidden long multiplicative Params((struct token **tp, struct token *trigger));
hidden long additive Params((struct token **tp, struct token *trigger));
hidden long shift Params((struct token **tp, struct token *trigger));
hidden long relation Params((struct token **tp, struct token *trigger));
hidden long equality Params((struct token **tp, struct token *trigger));
hidden long and Params((struct token **tp, struct token *trigger));
hidden long excl_or Params((struct token **tp, struct token *trigger));
hidden long incl_or Params((struct token **tp, struct token *trigger));
hidden long log_and Params((struct token **tp, struct token *trigger));
hidden long log_or Params((struct token **tp, struct token *trigger));
#include "::preproc:pproto.h"
/*
* <primary> ::= <identifier>
* defined <identifier>
* defined '(' <identifier> ')'
* <number>
* <character-constant>
* '(' <conditional> ')'
*/
static long primary(tp, trigger)
struct token **tp;
struct token *trigger;
{
struct token *t = NULL;
struct token *id = NULL;
long e1;
int i;
int is_hex_char;
char *s;
switch ((*tp)->tok_id) {
case Identifier:
/*
* Check for "defined", it is the only reserved word in this expression
* evaluation (See ANSI C Standard).
*/
if (strcmp((*tp)->image, "defined") == 0) {
nxt_non_wh(&t);
if (t->tok_id == '(') {
nxt_non_wh(&id);
nxt_non_wh(&t);
if (t == NULL || t->tok_id != ')')
errt1(id, "')' missing in 'defined' expression");
free_t(t);
}
else
id = t;
if (id->tok_id != Identifier)
errt1(id, "'defined' must be followed by an identifier");
advance_tok(tp);
if (m_lookup(id) == NULL)
e1 = 0L;
else
e1 = 1L;
free_t(id);
}
else {
advance_tok(tp);
e1 = 0L; /* undefined: all macros have been expanded */
}
return e1;
case PpNumber:
s = (*tp)->image;
e1 = 0L;
if (*s == '0') {
++s;
if ((*s == 'x') || (*s == 'X')) {
/*
* Hex constant
*/
++s;
if (*s == '\0' || *s == 'u' || *s == 'U' || *s == 'l' ||
*s == 'L')
errt2(*tp, "invalid hex constant in condition of #",
trigger->image);
while (*s != '\0' && *s != 'u' && *s != 'U' && *s != 'l' &&
*s != 'L') {
e1 <<= 4;
if (*s >= '0' && *s <= '9')
e1 |= *s - '0';
else
switch (*s) {
case 'a': case 'A': e1 |= 10; break;
case 'b': case 'B': e1 |= 11; break;
case 'c': case 'C': e1 |= 12; break;
case 'd': case 'D': e1 |= 13; break;
case 'e': case 'E': e1 |= 14; break;
case 'f': case 'F': e1 |= 15; break;
default:
errt2(*tp, "invalid hex constant in condition of #",
trigger->image);
}
++s;
}
}
else {
/*
* Octal constant
*/
while (*s != '\0' && *s != 'u' && *s != 'U' && *s != 'l' &&
*s != 'L') {
if (*s >= '0' && *s <= '7')
e1 = (e1 << 3) | (*s - '0');
else
errt2(*tp, "invalid octal constant in condition of #",
trigger->image);
++s;
}
}
}
else {
/*
* Decimal constant
*/
while (*s != '\0' && *s != 'u' && *s != 'U' && *s != 'l' &&
*s != 'L') {
if (*s >= '0' && *s <= '9')
e1 = e1 * 10 + (*s - '0');
else
errt2(*tp, "invalid decimal constant in condition of #",
trigger->image);
++s;
}
}
advance_tok(tp);
/*
* Check integer suffix for validity
*/
if (*s == '\0')
return e1;
else if (*s == 'u' || *s == 'U') {
++s;
if (*s == '\0')
return e1;
else if ((*s == 'l' || *s == 'L') && *++s == '\0')
return e1;
}
else if (*s == 'l' || *s == 'L') {
++s;
if (*s == '\0')
return e1;
else if ((*s == 'u' || *s == 'U') && *++s == '\0')
return e1;
}
errt2(*tp, "invalid integer constant in condition of #",
trigger->image);
case CharConst:
case LCharConst:
/*
* Wide characters are treated the same as characters. Only the
* first byte of a multi-byte character is used.
*/
s = (*tp)->image;
if (*s != '\\')
e1 = (long)*s;
else {
/*
* Escape sequence.
*/
e1 = 0L;
++s;
if (*s >= '0' && *s <= '7') {
for (i = 1; i <= 3 && *s >= '0' && *s <= '7'; ++i, ++s)
e1 = (e1 << 3) | (*s - '0');
if (e1 != (long)(unsigned char)e1)
errt1(*tp, "octal escape sequece larger than a character");
e1 = (long)(char)e1;
}
else switch (*s) {
case '\'': e1 = (long) '\''; break;
case '"': e1 = (long) '"'; break;
case '?': e1 = (long) '?'; break;
case '\\': e1 = (long) '\\'; break;
case 'a': e1 = (long) Bell; break;
case 'b': e1 = (long) '\b'; break;
case 'f': e1 = (long) '\f'; break;
case 'n': e1 = (long) '\n'; break;
case 'r': e1 = (long) '\r'; break;
case 't': e1 = (long) '\t'; break;
case 'v': e1 = (long) '\v'; break;
case 'x':
++s;
is_hex_char = 1;
while (is_hex_char) {
if (*s >= '0' && *s <= '9')
e1 = (e1 << 4) | (*s - '0');
else switch (*s) {
case 'a': case 'A': e1 = (e1 << 4) | 10; break;
case 'b': case 'B': e1 = (e1 << 4) | 11; break;
case 'c': case 'C': e1 = (e1 << 4) | 12; break;
case 'd': case 'D': e1 = (e1 << 4) | 13; break;
case 'e': case 'E': e1 = (e1 << 4) | 14; break;
case 'f': case 'F': e1 = (e1 << 4) | 15; break;
default: is_hex_char = 0;
}
if (is_hex_char)
++s;
if (e1 != (long)(unsigned char)e1)
errt1(*tp,"hex escape sequece larger than a character");
}
e1 = (long)(char)e1;
break;
default:
e1 = (long) *s;
}
}
advance_tok(tp);
return e1;
case '(':
advance_tok(tp);
e1 = conditional(tp, trigger);
if ((*tp)->tok_id != ')')
errt2(*tp, "expected ')' in conditional of #", trigger->image);
advance_tok(tp);
return e1;
default:
errt2(*tp, "syntax error in condition of #", trigger->image);
}
}
/*
* <unary> ::= <primary> |
* '+' <unary> |
* '-' <unary> |
* '~' <unary> |
* '!' <unary>
*/
static long unary(tp, trigger)
struct token **tp;
struct token *trigger;
{
switch ((*tp)->tok_id) {
case '+':
advance_tok(tp);
return unary(tp, trigger);
case '-':
advance_tok(tp);
return -unary(tp, trigger);
case '~':
advance_tok(tp);
return ~unary(tp, trigger);
case '!':
advance_tok(tp);
return !unary(tp, trigger);
default:
return primary(tp, trigger);
}
}
/*
* <multiplicative> ::= <unary> |
* <multiplicative> '*' <unary> |
* <multiplicative> '/' <unary> |
* <multiplicative> '%' <unary>
*/
static long multiplicative(tp, trigger)
struct token **tp;
struct token *trigger;
{
long e1, e2;
int tok_id;
e1 = unary(tp, trigger);
tok_id = (*tp)->tok_id;
while (tok_id == '*' || tok_id == '/' || tok_id == '%') {
advance_tok(tp);
e2 = unary(tp, trigger);
switch (tok_id) {
case '*':
e1 = (e1 * e2);
break;
case '/':
e1 = (e1 / e2);
break;
case '%':
e1 = (e1 % e2);
break;
}
tok_id = (*tp)->tok_id;
}
return e1;
}
/*
* <additive> ::= <multiplicative> |
* <additive> '+' <multiplicative> |
* <additive> '-' <multiplicative>
*/
static long additive(tp, trigger)
struct token **tp;
struct token *trigger;
{
long e1, e2;
int tok_id;
e1 = multiplicative(tp, trigger);
tok_id = (*tp)->tok_id;
while (tok_id == '+' || tok_id == '-') {
advance_tok(tp);
e2 = multiplicative(tp, trigger);
if (tok_id == '+')
e1 = (e1 + e2);
else
e1 = (e1 - e2);
tok_id = (*tp)->tok_id;
}
return e1;
}
/*
* <shift> ::= <additive> |
* <shift> '<<' <additive> |
* <shift> '>>' <additive>
*/
static long shift(tp, trigger)
struct token **tp;
struct token *trigger;
{
long e1, e2;
int tok_id;
e1 = additive(tp, trigger);
tok_id = (*tp)->tok_id;
while (tok_id == LShft || tok_id == RShft) {
advance_tok(tp);
e2 = additive(tp, trigger);
if (tok_id == LShft)
e1 = (e1 << e2);
else
e1 = (e1 >> e2);
tok_id = (*tp)->tok_id;
}
return e1;
}
/*
* <relation> ::= <shift> |
* <relation> '<' <shift> |
* <relation> '<=' <shift> |
* <relation> '>' <shift> |
* <relation> '>=' <shift>
*/
static long relation(tp, trigger)
struct token **tp;
struct token *trigger;
{
long e1, e2;
int tok_id;
e1 = shift(tp, trigger);
tok_id = (*tp)->tok_id;
while (tok_id == '<' || tok_id == Leq || tok_id == '>' || tok_id == Geq) {
advance_tok(tp);
e2 = shift(tp, trigger);
switch (tok_id) {
case '<':
e1 = (e1 < e2);
break;
case Leq:
e1 = (e1 <= e2);
break;
case '>':
e1 = (e1 > e2);
break;
case Geq:
e1 = (e1 >= e2);
break;
}
tok_id = (*tp)->tok_id;
}
return e1;
}
/*
* <equality> ::= <relation> |
* <equality> '==' <relation> |
* <equality> '!=' <relation>
*/
static long equality(tp, trigger)
struct token **tp;
struct token *trigger;
{
long e1, e2;
int tok_id;
e1 = relation(tp, trigger);
tok_id = (*tp)->tok_id;
while (tok_id == Equal || tok_id == Neq) {
advance_tok(tp);
e2 = relation(tp, trigger);
if (tok_id == Equal)
e1 = (e1 == e2);
else
e1 = (e1 != e2);
tok_id = (*tp)->tok_id;
}
return e1;
}
/*
* <and> ::= <equality> |
* <and> '&' <equality>
*/
static long and(tp, trigger)
struct token **tp;
struct token *trigger;
{
long e1, e2;
e1 = equality(tp, trigger);
while ((*tp)->tok_id == '&') {
advance_tok(tp);
e2 = equality(tp, trigger);
e1 = (e1 & e2);
}
return e1;
}
/*
* <excl_or> ::= <and> |
* <excl_or> '^' <and>
*/
static long excl_or(tp, trigger)
struct token **tp;
struct token *trigger;
{
long e1, e2;
e1 = and(tp, trigger);
while ((*tp)->tok_id == '^') {
advance_tok(tp);
e2 = and(tp, trigger);
e1 = (e1 ^ e2);
}
return e1;
}
/*
* <incl_or> ::= <excl_or> |
* <incl_or> '|' <excl_or>
*/
static long incl_or(tp, trigger)
struct token **tp;
struct token *trigger;
{
long e1, e2;
e1 = excl_or(tp, trigger);
while ((*tp)->tok_id == '|') {
advance_tok(tp);
e2 = excl_or(tp, trigger);
e1 = (e1 | e2);
}
return e1;
}
/*
* <log_and> ::= <incl_or> |
* <log_and> '&&' <incl_or>
*/
static long log_and(tp, trigger)
struct token **tp;
struct token *trigger;
{
long e1, e2;
e1 = incl_or(tp, trigger);
while ((*tp)->tok_id == And) {
advance_tok(tp);
e2 = incl_or(tp, trigger);
e1 = (e1 && e2);
}
return e1;
}
/*
* <log_or> ::= <log_and> |
* <log_or> '||' <log_and>
*/
static long log_or(tp, trigger)
struct token **tp;
struct token *trigger;
{
long e1, e2;
e1 = log_and(tp, trigger);
while ((*tp)->tok_id == Or) {
advance_tok(tp);
e2 = log_and(tp, trigger);
e1 = (e1 || e2);
}
return e1;
}
/*
* <conditional> ::= <log_or> |
* <log_or> '?' <conditional> ':' <conditional>
*/
long conditional(tp, trigger)
struct token **tp;
struct token *trigger;
{
long e1, e2, e3;
e1 = log_or(tp, trigger);
if ((*tp)->tok_id == '?') {
advance_tok(tp);
e2 = conditional(tp, trigger);
if ((*tp)->tok_id != ':')
errt2(*tp, "expected ':' in conditional of #", trigger->image);
advance_tok(tp);
e3 = conditional(tp, trigger);
return e1 ? e2 : e3;
}
else
return e1;
}
/*
* eval - get the tokens for a conditional and evaluate it to 0 or 1.
* trigger is the preprocessing directive that triggered the evaluation;
* it is used for error messages.
*/
int eval(trigger)
struct token *trigger;
{
struct token *t = NULL;
int result;
advance_tok(&t);
result = (conditional(&t, trigger) != 0L);
if (t->tok_id != PpDirEnd)
errt2(t, "expected end of condition of #", trigger->image);
free_t(t);
return result;
}