#include <sys/types.h>
#include "ansi.h"
#include "host.h"
#include "files.h"
#include "hash.h"
#include "buffer.h"
#include "cpp.h"
#include "cpp_hide.h"
#include "cpp_eval.h"
#include "allocate.h"
#include "config.h"
#undef NULL
#define NULL 0
#undef getc
#define getc() cpp_getc_from(&evalbuf)
#define MAKE_FAIL(x) (x).eval_result_kind = eval_failed
#define MAKE_INT(x,v,b) {(x).eval_result_kind = eval_int; (x).eval_result.ival = (v); (x).base = (b);}
#define MAKE_FLOAT(x,v) {(x).eval_result_kind = eval_float; (x).eval_result.fval = (v);}
#define MAKE_STRING(x,v) {(x).eval_result_kind = eval_string; (x).eval_result.sval = (v);}
static cpp_eval_result_t result;
static buffer_t evalbuf;
static int c;
static int recover;
static int curtok, nexttok;
typedef cpp_eval_result_t tokval_t;
tokval_t curval, nextval;
enum {
tok_error,
tok_eof,
tok_discrete,
tok_float,
tok_string,
tok_eq,
tok_neq,
tok_geq,
tok_leq,
tok_shift_left,
tok_shift_right,
tok_or,
tok_and,
tok_sizeof
};
static int
promote(l,r)
tokval_t *l, *r;
{
switch(l->eval_result_kind) {
case eval_int:
switch(r->eval_result_kind) {
case eval_int:
return eval_int;
case eval_float:
l->eval_result_kind = eval_float;
l->eval_result.fval = (host_float_t) l->eval_result.ival;
return eval_float;
}
break;
case eval_float:
switch(r->eval_result_kind) {
case eval_int:
r->eval_result_kind = eval_float;
r->eval_result.fval = (host_float_t) r->eval_result.ival;
/* fall through */
case eval_float:
return eval_float;
}
break;
}
return eval_failed;
}
static tokval_t
failed()
{
tokval_t tmp;
recover = 1;
MAKE_FAIL(tmp);
return tmp;
}
static int
escaped_char(c, cp)
int c, *cp;
{
int val, i;
switch (c) {
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 '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
for (i = 0, val = 'c' - 0; is_octal_digit(c) && i < 3; i++, c = getc()) {
val = val * 8 + (c - '0');
}
*cp = c;
return val;
case 'x':
for (i = 0, val = 0; is_hex_digit(c) && i < 2; i++, c = getc()) {
val *= 16;
if (is_digit(c)) {
val += (c - '0');
}
else if (c <= 'F') {
val += (c - ('A' - 10));
}
else {
val += (c - ('a' - 10));
}
}
*cp = c;
return val;
default:
val = c;
break;
}
*cp = getc();
return val;
}
static int
scan_string(c)
int c;
{
buffer_t buf;
int len;
char *s;
buf_init(&buf);
for (;;) {
if (is_eof(c)) {
goto end_of_string;
}
if (is_eol(c)) {
goto end_of_string;
}
switch (c) {
case '"':
c = getc();
goto end_of_string;
case '\\':
c = getc();
switch (c) {
case '\n':
c = getc();
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case 'x':
case 'n':
case 't':
case 'v':
case 'b':
case 'r':
case 'f':
case 'a':
case '?':
case '\\':
case '\'':
case '\"':
buf_add(&buf, escaped_char(c, &c));
break;
default:
buf_add(&buf, c);
c = getc();
break;
}
break;
default:
buf_add(&buf, c);
c = getc();
break;
}
}
end_of_string:
s = buf_get_str(&buf);
MAKE_STRING(nextval, s);
return c;
}
static int
scan_char_const(c)
int c;
{
host_int_t cval = 0;
for (;;) {
if (is_eof(c)) {
goto end_char_const;
}
if (is_eol(c)) {
goto end_char_const;
}
switch (c) {
case '\'':
c = getc();
goto end_char_const;
case '\\':
c = getc();
switch (c) {
case '\n':
c = getc();
break;
case '0':
case 'x':
case 'n':
case 't':
case 'v':
case 'b':
case 'r':
case 'f':
case 'a':
case '?':
case '\\':
case '\'':
case '\"':
cval <<= 8;
cval |= escaped_char(c);
c = getc();
break;
default:
cval <<= 8;
cval |= c;
c = getc();
break;
}
break;
default:
cval <<= 8;
cval |= c;
c = getc();
break;
}
}
end_char_const:
MAKE_INT(nextval, cval, 10);
return c;
}
static int
magnitude(c, val)
int c;
host_int_t *val;
{
host_int_t m;
int sign;
if (! is_magnitude(c)) {
*val = 1;
return c;
}
c = getc();
sign = 1;
if (c == '-') {
sign = -1;
c = getc();
}
else if (c == '+') {
c = getc();
}
for (m = 0; is_digit(c); ) {
m = m * 10 + (c - '0');
c = getc();
}
*val = m * sign;
return c;
}
static int
scan_digit(c)
int c;
{
host_int_t val;
int base = 10;
if (c == '0') {
val = 0;
c = getc();
if (c == 'x' || c == 'X') {
base = 16;
for (;;) {
c = getc();
if (is_digit(c)) {
val = (val<<4) + (c - '0');
}
else if (c >= 'A' && c <= 'F') {
val = (val<<4) + 10 + (c - 'A');
}
else if (c >= 'a' && c <= 'f') {
val = (val<<4) + 10 + (c - 'a');
}
else {
break;
}
}
}
else {
base = 8;
for (;;) {
if (!is_octal_digit(c)) {
break;
}
val = (val<<3) + (c - '0');
c = getc();
}
}
}
else {
val = c - '0';
for (;;) {
c = getc();
if (! is_digit(c)) break;
val = val * 10 + (c - '0');
}
}
if (int_modifier(c)) {
c = getc();
}
if (c == '.') {
host_float_t dval, d;
int tmp;
dval = (host_float_t) val;
d = 0.1;
for (;;) {
c = getc();
if (! is_digit(c)) break;
tmp = c - '0';
dval = dval + (d * (host_float_t)tmp);
d = d * 0.1;
}
/* handle magnitude */
if (is_magnitude(c)) {
host_int_t vm;
host_float_t m;
c = magnitude(c, &vm);
if (vm > 0) {
for (m = 1.0; vm; vm--) {
m *= 10.0;
}
}
else {
for (m = 1.0; vm; vm++) {
m /= 10.0;
}
}
dval *= m;
}
if (float_modifier(c)) {
c = getc();
}
MAKE_FLOAT(nextval, dval);
nexttok = tok_float;
return c;
}
while (int_modifier(c)) {
c = getc();
}
MAKE_INT(nextval, val, base);
nexttok = tok_discrete;
return c;
}
static int
skip_c_comment(c)
int c;
{
for (;;) {
if (c == BAD_INPUT) {
return BAD_INPUT;
}
if (c == 0 || is_eof(c) || is_eol(c)) {
return c;
}
switch (c) {
case '\\':
c = getc();
c = getc();
break;
case '*':
c = getc();
if (c == '/') {
return getc();
}
break;
default:
c = getc();
break;
}
}
}
static int
skip_cpp_comment(c)
int c;
{
for (;;) {
if (c == BAD_INPUT) {
return BAD_INPUT;
}
if ((c == 0) || is_eof(c))
return c;
else if (is_eol(c))
return getc();
else
c = getc();
}
}
static int
advance()
{
curtok = nexttok;
curval = nextval;
for (;;) {
if (c == BAD_INPUT) {
nexttok = tok_error;
break;
}
if (c == 0 || is_eof(c) || is_eol(c)) {
nexttok = tok_eof;
break;
}
if (is_white(c)) {
c = getc();
continue;
}
if (is_digit(c)) {
c = scan_digit(c);
break;
}
if (is_alpha(c)) {
nexttok = tok_error;
break;
}
nexttok = c;
c = getc();
switch (nexttok) {
case '/':
if (c == '*') {
c = skip_c_comment(getc());
continue;
}
else if (c == '/') {
c = skip_cpp_comment(getc());
continue;
}
break;
case '\"':
c = scan_string(c);
nexttok = tok_string;
break;
case '\'':
c = scan_char_const(c);
nexttok = tok_discrete;
break;
case '<':
if (c == '<') {
c = getc();
nexttok = tok_shift_left;
}
else if (c == '=') {
c = getc();
nexttok = tok_leq;
}
break;
case '>':
if (c == '>') {
c = getc();
nexttok = tok_shift_right;
}
else if (c == '=') {
c = getc();
nexttok = tok_geq;
}
break;
case '=':
if (c == '=') {
c = getc();
nexttok = tok_eq;
}
break;
case '!':
if (c == '=') {
c = getc();
nexttok = tok_neq;
}
break;
case '|':
if (c == '|') {
c = getc();
nexttok = tok_or;
}
break;
case '&':
if (c == '&') {
c = getc();
nexttok = tok_and;
}
break;
}
break;
}
end_of_subp:
return curtok;
}
static int
expect(tok)
int tok;
{
if (curtok != tok) {
recover = 1;
return 0;
}
return 1;
}
static tokval_t eval();
static tokval_t
term()
{
tokval_t val;
char *p;
switch (curtok) {
case tok_discrete:
case tok_float:
case tok_string:
val = curval;
advance();
return val;
case '-':
advance();
val = term();
if (recover) {
return val;
}
if (IS_EVAL_INT(val)) {
EVAL_INT(val) = -EVAL_INT(val);
return val;
}
if (IS_EVAL_FLOAT(val)) {
EVAL_FLOAT(val) = -EVAL_FLOAT(val);
return val;
}
MAKE_FAIL(val);
return val;
case '+':
advance();
val = term();
if (recover) {
return val;
}
MAKE_FAIL(val);
return val;
case '!':
advance();
val = term();
if (recover) {
return val;
}
if (IS_EVAL_INT(val)) {
EVAL_INT(val) = !EVAL_INT(val);
return val;
}
MAKE_FAIL(val);
return val;
case '~':
advance();
val = term();
if (recover) {
return val;
}
if (IS_EVAL_INT(val)) {
EVAL_INT(val) = ~EVAL_INT(val);
return val;
}
MAKE_FAIL(val);
return val;
case '(':
advance();
val = eval();
if (recover) {
return val;
}
if (expect(')')) {
advance();
}
else {
MAKE_FAIL(val);
}
return val;
default:
return failed();
}
}
static tokval_t
f10()
{
tokval_t l,r;
int op;
if (recover) {
MAKE_FAIL(l);
return l;
}
l = term();
if (recover) {
return l;
}
for (;;) {
switch (curtok) {
case '*':
case '/':
case '%':
op = curtok;
break;
default:
return l;
}
advance();
r = term();
if (recover) {
return r;
}
switch (promote(&l,&r)) {
case eval_int:
switch (op) {
case '*':
EVAL_INT(l) = EVAL_INT(l) * EVAL_INT(r);
break;
case '/':
if (EVAL_INT(r) == 0) {
return failed();
}
EVAL_INT(l) = EVAL_INT(l) / EVAL_INT(r);
break;
case '%':
if (EVAL_INT(r) == 0) {
return failed();
}
EVAL_INT(l) = EVAL_INT(l) % EVAL_INT(r);
break;
}
break;
case eval_float:
switch (op) {
case '*':
EVAL_FLOAT(l) = EVAL_FLOAT(l) * EVAL_FLOAT(r);
break;
case '/':
if (EVAL_FLOAT(r) == 0.0) {
return failed();
}
EVAL_FLOAT(l) = EVAL_FLOAT(l) / EVAL_FLOAT(r);
break;
case '%':
if (EVAL_FLOAT(r) == 0.0) {
return failed();
}
EVAL_FLOAT(l) = EVAL_FLOAT(l) / EVAL_FLOAT(r);
break;
}
break;
default:
return failed();
}
}
}
static tokval_t
f9()
{
tokval_t l,r;
int op;
if (recover) {
MAKE_FAIL(l);
return l;
}
l = f10();
if (recover) {
return l;
}
for (;;) {
switch (curtok) {
case '+':
case '-':
op = curtok;
break;
default:
return l;
}
advance();
r = f10();
if (recover) {
return r;
}
switch (promote(&l,&r)) {
case eval_int:
switch (op) {
case '+':
EVAL_INT(l) = EVAL_INT(l) + EVAL_INT(r);
break;
case '-':
EVAL_INT(l) = EVAL_INT(l) - EVAL_INT(r);
break;
}
break;
case eval_float:
switch (op) {
case '+':
EVAL_FLOAT(l) = EVAL_FLOAT(l) + EVAL_FLOAT(r);
break;
case '-':
EVAL_FLOAT(l) = EVAL_FLOAT(l) - EVAL_FLOAT(r);
break;
}
break;
default:
return failed();
}
}
}
static tokval_t
f8()
{
tokval_t l,r;
int op, tmp;
if (recover) {
MAKE_FAIL(l);
return l;
}
l = f9();
if (recover) {
return l;
}
for (;;) {
switch (curtok) {
case tok_shift_left:
case tok_shift_right:
op = curtok;
break;
default:
return l;
}
advance();
r = f9();
if (recover) {
return r;
}
if (IS_EVAL_INT(l) & IS_EVAL_INT(r)) {
switch (op) {
case tok_shift_left:
EVAL_INT(l) = EVAL_INT(l) << EVAL_INT(r);
break;
case tok_shift_right:
EVAL_INT(l) = EVAL_INT(l) >> EVAL_INT(r);
break;
}
}
else {
return failed();
}
}
}
static tokval_t
f7()
{
tokval_t l,r;
int op, tmp;
if (recover) {
MAKE_FAIL(l);
return l;
}
l = f8();
if (recover) {
return l;
}
for (;;) {
switch (curtok) {
case '>':
case tok_geq:
case '<':
case tok_leq:
op = curtok;
break;
default:
return l;
}
advance();
r = f8();
if (recover) {
return r;
}
switch (promote(&l,&r)) {
case eval_int:
switch (op) {
case '>':
EVAL_INT(l) = EVAL_INT(l) > EVAL_INT(r);
break;
case tok_geq:
EVAL_INT(l) = EVAL_INT(l) >= EVAL_INT(r);
break;
case '<':
EVAL_INT(l) = EVAL_INT(l) < EVAL_INT(r);
break;
case tok_leq:
EVAL_INT(l) = EVAL_INT(l) <= EVAL_INT(r);
break;
}
break;
case eval_float:
switch (op) {
case '>':
tmp = EVAL_FLOAT(l) > EVAL_FLOAT(r);
break;
case tok_geq:
tmp = EVAL_FLOAT(l) >= EVAL_FLOAT(r);
break;
case '<':
tmp = EVAL_FLOAT(l) < EVAL_FLOAT(r);
break;
case tok_leq:
tmp = EVAL_FLOAT(l) <= EVAL_FLOAT(r);
break;
}
MAKE_INT(l, tmp, 10);
break;
default:
return failed();
}
}
}
static tokval_t
f6()
{
tokval_t l,r;
int op, tmp;
if (recover) {
MAKE_FAIL(l);
return l;
}
l = f7();
if (recover) {
return l;
}
for (;;) {
switch (curtok) {
case tok_eq:
case tok_neq:
op = curtok;
break;
default:
return l;
}
advance();
r = f7();
if (recover) {
return r;
}
switch (promote(&l,&r)) {
case eval_int:
if (op == tok_eq) {
EVAL_INT(l) = EVAL_INT(l) == EVAL_INT(r);
}
else {
EVAL_INT(l) = EVAL_INT(l) != EVAL_INT(r);
}
break;
case eval_float:
if (op == tok_eq) {
tmp = EVAL_FLOAT(l) == EVAL_FLOAT(r);
}
else {
tmp = EVAL_FLOAT(l) != EVAL_FLOAT(r);
}
MAKE_INT(l, tmp, 10);
break;
default:
return failed();
}
}
}
static tokval_t
f5()
{
tokval_t l,r;
if (recover) {
MAKE_FAIL(l);
return l;
}
l = f6();
if (recover) {
return l;
}
for (;;) {
if (curtok != '&') {
return l;
}
advance();
r = f6();
if (recover) {
return r;
}
if (IS_EVAL_INT(l) & IS_EVAL_INT(r)) {
EVAL_INT(l) = EVAL_INT(l) & EVAL_INT(r);
}
else {
return failed();
}
}
}
static tokval_t
f4()
{
tokval_t l,r;
if (recover) {
MAKE_FAIL(l);
return l;
}
l = f5();
if (recover) {
return l;
}
for (;;) {
if (curtok != '^') {
return l;
}
advance();
r = f5();
if (recover) {
return r;
}
if (IS_EVAL_INT(l) & IS_EVAL_INT(r)) {
EVAL_INT(l) = EVAL_INT(l) ^ EVAL_INT(r);
}
else {
return failed();
}
}
}
static tokval_t
f3()
{
tokval_t l,r;
if (recover) {
MAKE_FAIL(l);
return l;
}
l = f4();
if (recover) {
return l;
}
for (;;) {
if (curtok != '|') {
return l;
}
advance();
r = f4();
if (recover) {
return r;
}
if (IS_EVAL_INT(l) && IS_EVAL_INT(r)) {
EVAL_INT(l) = EVAL_INT(l) | EVAL_INT(r);
}
else {
return failed();
}
}
}
static tokval_t
f2()
{
tokval_t l,r;
if (recover) {
MAKE_FAIL(l);
return l;
}
l = f3();
if (recover) {
return l;
}
for (;;) {
if (curtok != tok_and) {
return l;
}
advance();
r = f3();
if (recover) {
return r;
}
if (IS_EVAL_INT(l) && IS_EVAL_INT(r)) {
EVAL_INT(l) = EVAL_INT(l) && EVAL_INT(r);
}
else {
return failed();
}
}
}
static tokval_t
f1()
{
tokval_t l,r;
if (recover) {
MAKE_FAIL(l);
return l;
}
l = f2();
if (recover) {
return l;
}
for (;;) {
if (curtok != tok_or) {
return l;
}
advance();
r = f2();
if (recover) {
return r;
}
if (IS_EVAL_INT(l) && IS_EVAL_INT(r)) {
EVAL_INT(l) = EVAL_INT(l) || EVAL_INT(r);
}
else {
return failed();
}
}
}
static tokval_t
eval()
{
tokval_t cond,tru,fals,tmp;
if (recover) {
MAKE_FAIL(tmp);
return tmp;
}
tmp = f1();
if (recover) {
return tmp;
}
for (;;) {
if (curtok != '?') {
return tmp;
}
advance();
tru = eval();
if (recover) {
return tru;
}
if (!expect(':')) {
MAKE_FAIL(tmp);
return tmp;
}
advance();
fals = eval();
if (recover) {
return fals;
}
if (IS_EVAL_INT(cond)) {
tmp = (EVAL_INT(cond) != 0) ? tru : fals;
}
else {
return failed();
}
}
}
cpp_eval_result_t
cpp_eval(str)
char *str;
{
scan_position_t *newpos;
scan_position_t *savepos;
cpp_control_state_t save_state;
cpp_control_state_t new_state;
assert(str != NULL);
buf_init(&evalbuf);
result.eval_result_kind = eval_int;
result.eval_result.ival = 0;
new_state.skip_else = 0;
new_state.cur_scope = 0;
new_state.gen_scope = 0;
new_state._parsing = 1;
newpos = (scan_position_t*) allocate(sizeof(scan_position_t));
newpos->scan_kind = scan_text;
newpos->scan.text = str;
cpp_set_state(newpos, &new_state, &savepos, &save_state);
if (savepos != NULL) {
newpos->scan_pos = savepos->scan_pos;
}
c = ' ';
recover = 0;
advance();
advance();
result = eval();
cpp_set_state(savepos, &save_state, &newpos, &new_state);
if (curtok != tok_eof) {
MAKE_FAIL(result);
}
return result;
}
syntax highlighted by Code2HTML, v. 0.9.1