1244 lines
31 KiB
C
1244 lines
31 KiB
C
/* expr.c - expression evaluator routines */
|
|
|
|
/* SimpleScalar(TM) Tool Suite
|
|
* Copyright (C) 1994-2003 by Todd M. Austin, Ph.D. and SimpleScalar, LLC.
|
|
* All Rights Reserved.
|
|
*
|
|
* THIS IS A LEGAL DOCUMENT, BY USING SIMPLESCALAR,
|
|
* YOU ARE AGREEING TO THESE TERMS AND CONDITIONS.
|
|
*
|
|
* No portion of this work may be used by any commercial entity, or for any
|
|
* commercial purpose, without the prior, written permission of SimpleScalar,
|
|
* LLC (info@simplescalar.com). Nonprofit and noncommercial use is permitted
|
|
* as described below.
|
|
*
|
|
* 1. SimpleScalar is provided AS IS, with no warranty of any kind, express
|
|
* or implied. The user of the program accepts full responsibility for the
|
|
* application of the program and the use of any results.
|
|
*
|
|
* 2. Nonprofit and noncommercial use is encouraged. SimpleScalar may be
|
|
* downloaded, compiled, executed, copied, and modified solely for nonprofit,
|
|
* educational, noncommercial research, and noncommercial scholarship
|
|
* purposes provided that this notice in its entirety accompanies all copies.
|
|
* Copies of the modified software can be delivered to persons who use it
|
|
* solely for nonprofit, educational, noncommercial research, and
|
|
* noncommercial scholarship purposes provided that this notice in its
|
|
* entirety accompanies all copies.
|
|
*
|
|
* 3. ALL COMMERCIAL USE, AND ALL USE BY FOR PROFIT ENTITIES, IS EXPRESSLY
|
|
* PROHIBITED WITHOUT A LICENSE FROM SIMPLESCALAR, LLC (info@simplescalar.com).
|
|
*
|
|
* 4. No nonprofit user may place any restrictions on the use of this software,
|
|
* including as modified by the user, by any other authorized user.
|
|
*
|
|
* 5. Noncommercial and nonprofit users may distribute copies of SimpleScalar
|
|
* in compiled or executable form as set forth in Section 2, provided that
|
|
* either: (A) it is accompanied by the corresponding machine-readable source
|
|
* code, or (B) it is accompanied by a written offer, with no time limit, to
|
|
* give anyone a machine-readable copy of the corresponding source code in
|
|
* return for reimbursement of the cost of distribution. This written offer
|
|
* must permit verbatim duplication by anyone, or (C) it is distributed by
|
|
* someone who received only the executable form, and is accompanied by a
|
|
* copy of the written offer of source code.
|
|
*
|
|
* 6. SimpleScalar was developed by Todd M. Austin, Ph.D. The tool suite is
|
|
* currently maintained by SimpleScalar LLC (info@simplescalar.com). US Mail:
|
|
* 2395 Timbercrest Court, Ann Arbor, MI 48105.
|
|
*
|
|
* Copyright (C) 1994-2003 by Todd M. Austin, Ph.D. and SimpleScalar, LLC.
|
|
*/
|
|
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <ctype.h>
|
|
#include <errno.h>
|
|
|
|
#include "host.h"
|
|
#include "misc.h"
|
|
#include "eval.h"
|
|
|
|
#if defined(sparc) && !defined(__svr4__)
|
|
#define strtoul strtol
|
|
#endif /* sparc */
|
|
|
|
/* expression evaluation error, this must be a global */
|
|
enum eval_err_t eval_error = ERR_NOERR;
|
|
|
|
/* enum eval_err_t -> error description string map */
|
|
char *eval_err_str[ERR_NUM] = {
|
|
/* ERR_NOERR */ "!! no error!!",
|
|
/* ERR_UPAREN */ "unmatched parenthesis",
|
|
/* ERR_NOTERM */ "expression term is missing",
|
|
/* ERR_DIV0 */ "divide by zero",
|
|
/* ERR_BADCONST */ "badly formed constant",
|
|
/* ERR_BADEXPR */ "badly formed expression",
|
|
/* ERR_UNDEFVAR */ "variable is undefined",
|
|
/* ERR_EXTRA */ "extra characters at end of expression"
|
|
};
|
|
|
|
/* *first* token character -> enum eval_token_t map */
|
|
static enum eval_token_t tok_map[256];
|
|
static int tok_map_initialized = FALSE;
|
|
|
|
/* builds the first token map */
|
|
static void
|
|
init_tok_map(void)
|
|
{
|
|
int i;
|
|
|
|
for (i=0; i<256; i++)
|
|
tok_map[i] = tok_invalid;
|
|
|
|
/* identifier characters */
|
|
for (i='a'; i<='z'; i++)
|
|
tok_map[i] = tok_ident;
|
|
for (i='A'; i<='Z'; i++)
|
|
tok_map[i] = tok_ident;
|
|
tok_map[(int)'_'] = tok_ident;
|
|
tok_map[(int)'$'] = tok_ident;
|
|
|
|
/* numeric characters */
|
|
for (i='0'; i<='9'; i++)
|
|
tok_map[i] = tok_const;
|
|
tok_map[(int)'.'] = tok_const;
|
|
|
|
/* operator characters */
|
|
tok_map[(int)'+'] = tok_plus;
|
|
tok_map[(int)'-'] = tok_minus;
|
|
tok_map[(int)'*'] = tok_mult;
|
|
tok_map[(int)'/'] = tok_div;
|
|
tok_map[(int)'('] = tok_oparen;
|
|
tok_map[(int)')'] = tok_cparen;
|
|
|
|
/* whitespace characers */
|
|
tok_map[(int)' '] = tok_whitespace;
|
|
tok_map[(int)'\t'] = tok_whitespace;
|
|
}
|
|
|
|
/* get next token from the expression string */
|
|
static enum eval_token_t /* token parsed */
|
|
get_next_token(struct eval_state_t *es) /* expression evaluator */
|
|
{
|
|
int allow_hex;
|
|
enum eval_token_t tok;
|
|
char *ptok_buf, last_char;
|
|
|
|
/* initialize the token map, if needed */
|
|
if (!tok_map_initialized)
|
|
{
|
|
init_tok_map();
|
|
tok_map_initialized = TRUE;
|
|
}
|
|
|
|
/* use the peek'ed token, if available, tok_buf should still be valid */
|
|
if (es->peek_tok != tok_invalid)
|
|
{
|
|
tok = es->peek_tok;
|
|
es->peek_tok = tok_invalid;
|
|
return tok;
|
|
}
|
|
|
|
/* set up the token string space */
|
|
ptok_buf = es->tok_buf;
|
|
*ptok_buf = '\0';
|
|
|
|
/* skip whitespace */
|
|
while (*es->p && tok_map[(int)*es->p] == tok_whitespace)
|
|
es->p++;
|
|
|
|
/* end of token stream? */
|
|
if (*es->p == '\0')
|
|
return tok_eof;
|
|
|
|
*ptok_buf++ = *es->p;
|
|
tok = tok_map[(int)*es->p++];
|
|
switch (tok)
|
|
{
|
|
case tok_ident:
|
|
/* parse off next identifier */
|
|
while (*es->p
|
|
&& (tok_map[(int)*es->p] == tok_ident
|
|
|| tok_map[(int)*es->p] == tok_const))
|
|
{
|
|
*ptok_buf++ = *es->p++;
|
|
}
|
|
break;
|
|
case tok_const:
|
|
/* parse off next numeric literal */
|
|
last_char = '\0';
|
|
allow_hex = FALSE;
|
|
while (*es->p &&
|
|
(tok_map[(int)*es->p] == tok_const
|
|
|| (*es->p == '-' && last_char == 'e')
|
|
|| (*es->p == '+' && last_char == 'e')
|
|
|| tolower(*es->p) == 'e'
|
|
|| tolower(*es->p) == 'x'
|
|
|| (tolower(*es->p) == 'a' && allow_hex)
|
|
|| (tolower(*es->p) == 'b' && allow_hex)
|
|
|| (tolower(*es->p) == 'c' && allow_hex)
|
|
|| (tolower(*es->p) == 'd' && allow_hex)
|
|
|| (tolower(*es->p) == 'e' && allow_hex)
|
|
|| (tolower(*es->p) == 'f' && allow_hex)))
|
|
{
|
|
last_char = tolower(*es->p);
|
|
if (*es->p == 'x' || *es->p == 'X')
|
|
allow_hex = TRUE;
|
|
*ptok_buf++ = *es->p++;
|
|
}
|
|
break;
|
|
case tok_plus:
|
|
case tok_minus:
|
|
case tok_mult:
|
|
case tok_div:
|
|
case tok_oparen:
|
|
case tok_cparen:
|
|
/* just pass on the token */
|
|
break;
|
|
default:
|
|
tok = tok_invalid;
|
|
break;
|
|
}
|
|
|
|
/* terminate the token string buffer */
|
|
*ptok_buf = '\0';
|
|
|
|
return tok;
|
|
}
|
|
|
|
/* peek ahead at the next token from the expression stream, currently
|
|
only the next token can be peek'ed at */
|
|
static enum eval_token_t /* next token in expression */
|
|
peek_next_token(struct eval_state_t *es) /* expression evalutor */
|
|
{
|
|
/* if there is no peek ahead token, get one */
|
|
if (es->peek_tok == tok_invalid)
|
|
{
|
|
es->lastp = es->p;
|
|
es->peek_tok = get_next_token(es);
|
|
}
|
|
|
|
/* return peek ahead token */
|
|
return es->peek_tok;
|
|
}
|
|
|
|
/* forward declaration */
|
|
static struct eval_value_t expr(struct eval_state_t *es);
|
|
|
|
/* default expression error value, eval_err is also set */
|
|
static struct eval_value_t err_value = { et_int, { 0 } };
|
|
|
|
/* expression type strings */
|
|
char *eval_type_str[et_NUM] = {
|
|
/* et_int */ "int",
|
|
/* et_uint */ "unsigned int",
|
|
/* et_addr */ "md_addr_t",
|
|
#ifdef HOST_HAS_QWORD
|
|
/* et_qword */ "qword_t",
|
|
/* et_sqword */ "sqword_t",
|
|
#endif /* HOST_HAS_QWORD */
|
|
/* et_float */ "float",
|
|
/* et_double */ "double",
|
|
/* et_symbol */ "symbol"
|
|
};
|
|
|
|
/* determine necessary arithmetic conversion on T1 <op> T2 */
|
|
static enum eval_type_t /* type of expression result */
|
|
result_type(enum eval_type_t t1, /* left operand type */
|
|
enum eval_type_t t2) /* right operand type */
|
|
{
|
|
/* sanity check, symbols should not show up in arithmetic exprs */
|
|
if (t1 == et_symbol || t2 == et_symbol)
|
|
panic("symbol used in expression");
|
|
|
|
/* using C rules, i.e., A6.5 */
|
|
if (t1 == et_double || t2 == et_double)
|
|
return et_double;
|
|
else if (t1 == et_float || t2 == et_float)
|
|
return et_float;
|
|
#ifdef HOST_HAS_QWORD
|
|
else if (t1 == et_qword || t2 == et_qword)
|
|
return et_qword;
|
|
else if (t1 == et_sqword || t2 == et_sqword)
|
|
return et_sqword;
|
|
#endif /* HOST_HAS_QWORD */
|
|
else if (t1 == et_addr || t2 == et_addr)
|
|
return et_addr;
|
|
else if (t1 == et_uint || t2 == et_uint)
|
|
return et_uint;
|
|
else
|
|
return et_int;
|
|
}
|
|
|
|
/*
|
|
* expression value arithmetic conversions
|
|
*/
|
|
|
|
/* eval_value_t (any numeric type) -> double */
|
|
double
|
|
eval_as_double(struct eval_value_t val)
|
|
{
|
|
switch (val.type)
|
|
{
|
|
case et_double:
|
|
return val.value.as_double;
|
|
case et_float:
|
|
return (double)val.value.as_float;
|
|
#ifdef HOST_HAS_QWORD
|
|
case et_qword:
|
|
#ifdef _MSC_VER /* FIXME: MSC does not implement qword_t to dbl conversion */
|
|
return (double)(sqword_t)val.value.as_qword;
|
|
#else /* !_MSC_VER */
|
|
return (double)val.value.as_qword;
|
|
#endif /* _MSC_VER */
|
|
case et_sqword:
|
|
return (double)val.value.as_sqword;
|
|
#endif /* HOST_HAS_QWORD */
|
|
case et_addr:
|
|
#if defined(_MSC_VER) && defined(TARGET_ALPHA)
|
|
/* FIXME: MSC does not implement qword_t to double conversion */
|
|
return (double)(sqword_t)val.value.as_addr;
|
|
#else
|
|
return (double)val.value.as_addr;
|
|
#endif
|
|
case et_uint:
|
|
return (double)val.value.as_uint;
|
|
case et_int:
|
|
return (double)val.value.as_int;
|
|
case et_symbol:
|
|
panic("symbol used in expression");
|
|
default:
|
|
panic("illegal arithmetic expression conversion");
|
|
}
|
|
}
|
|
|
|
/* eval_value_t (any numeric type) -> float */
|
|
float
|
|
eval_as_float(struct eval_value_t val)
|
|
{
|
|
switch (val.type)
|
|
{
|
|
case et_double:
|
|
return (float)val.value.as_double;
|
|
case et_float:
|
|
return val.value.as_float;
|
|
#ifdef HOST_HAS_QWORD
|
|
case et_qword:
|
|
#ifdef _MSC_VER /* FIXME: MSC does not implement qword_t to dbl conversion */
|
|
return (float)(sqword_t)val.value.as_qword;
|
|
#else /* !_MSC_VER */
|
|
return (float)val.value.as_qword;
|
|
#endif /* _MSC_VER */
|
|
case et_sqword:
|
|
return (float)val.value.as_sqword;
|
|
#endif /* HOST_HAS_QWORD */
|
|
case et_addr:
|
|
#if defined(_MSC_VER) && defined(TARGET_ALPHA)
|
|
/* FIXME: MSC does not implement qword_t to double conversion */
|
|
return (float)(sqword_t)val.value.as_addr;
|
|
#else
|
|
return (float)val.value.as_addr;
|
|
#endif
|
|
case et_uint:
|
|
return (float)val.value.as_uint;
|
|
case et_int:
|
|
return (float)val.value.as_int;
|
|
case et_symbol:
|
|
panic("symbol used in expression");
|
|
default:
|
|
panic("illegal arithmetic expression conversion");
|
|
}
|
|
}
|
|
|
|
#ifdef HOST_HAS_QWORD
|
|
/* eval_value_t (any numeric type) -> qword_t */
|
|
qword_t
|
|
eval_as_qword(struct eval_value_t val)
|
|
{
|
|
switch (val.type)
|
|
{
|
|
case et_double:
|
|
return (qword_t)val.value.as_double;
|
|
case et_float:
|
|
return (qword_t)val.value.as_float;
|
|
case et_qword:
|
|
return val.value.as_qword;
|
|
case et_sqword:
|
|
return (qword_t)val.value.as_sqword;
|
|
case et_addr:
|
|
return (qword_t)val.value.as_addr;
|
|
case et_uint:
|
|
return (qword_t)val.value.as_uint;
|
|
case et_int:
|
|
return (qword_t)val.value.as_int;
|
|
case et_symbol:
|
|
panic("symbol used in expression");
|
|
default:
|
|
panic("illegal arithmetic expression conversion");
|
|
}
|
|
}
|
|
|
|
/* eval_value_t (any numeric type) -> sqword_t */
|
|
sqword_t
|
|
eval_as_sqword(struct eval_value_t val)
|
|
{
|
|
switch (val.type)
|
|
{
|
|
case et_double:
|
|
return (sqword_t)val.value.as_double;
|
|
case et_float:
|
|
return (sqword_t)val.value.as_float;
|
|
case et_qword:
|
|
return (sqword_t)val.value.as_qword;
|
|
case et_sqword:
|
|
return val.value.as_sqword;
|
|
case et_addr:
|
|
return (sqword_t)val.value.as_addr;
|
|
case et_uint:
|
|
return (sqword_t)val.value.as_uint;
|
|
case et_int:
|
|
return (sqword_t)val.value.as_int;
|
|
case et_symbol:
|
|
panic("symbol used in expression");
|
|
default:
|
|
panic("illegal arithmetic expression conversion");
|
|
}
|
|
}
|
|
#endif /* HOST_HAS_QWORD */
|
|
|
|
/* eval_value_t (any numeric type) -> unsigned int */
|
|
md_addr_t
|
|
eval_as_addr(struct eval_value_t val)
|
|
{
|
|
switch (val.type)
|
|
{
|
|
case et_double:
|
|
return (md_addr_t)val.value.as_double;
|
|
case et_float:
|
|
return (md_addr_t)val.value.as_float;
|
|
#ifdef HOST_HAS_QWORD
|
|
case et_qword:
|
|
return (md_addr_t)val.value.as_qword;
|
|
case et_sqword:
|
|
return (md_addr_t)val.value.as_sqword;
|
|
#endif /* HOST_HAS_QWORD */
|
|
case et_addr:
|
|
return val.value.as_addr;
|
|
case et_uint:
|
|
return (md_addr_t)val.value.as_uint;
|
|
case et_int:
|
|
return (md_addr_t)val.value.as_int;
|
|
case et_symbol:
|
|
panic("symbol used in expression");
|
|
default:
|
|
panic("illegal arithmetic expression conversion");
|
|
}
|
|
}
|
|
|
|
/* eval_value_t (any numeric type) -> unsigned int */
|
|
unsigned int
|
|
eval_as_uint(struct eval_value_t val)
|
|
{
|
|
switch (val.type)
|
|
{
|
|
case et_double:
|
|
return (unsigned int)val.value.as_double;
|
|
case et_float:
|
|
return (unsigned int)val.value.as_float;
|
|
#ifdef HOST_HAS_QWORD
|
|
case et_qword:
|
|
return (unsigned int)val.value.as_qword;
|
|
case et_sqword:
|
|
return (unsigned int)val.value.as_sqword;
|
|
#endif /* HOST_HAS_QWORD */
|
|
case et_addr:
|
|
return (unsigned int)val.value.as_addr;
|
|
case et_uint:
|
|
return val.value.as_uint;
|
|
case et_int:
|
|
return (unsigned int)val.value.as_int;
|
|
case et_symbol:
|
|
panic("symbol used in expression");
|
|
default:
|
|
panic("illegal arithmetic expression conversion");
|
|
}
|
|
}
|
|
|
|
/* eval_value_t (any numeric type) -> int */
|
|
int
|
|
eval_as_int(struct eval_value_t val)
|
|
{
|
|
switch (val.type)
|
|
{
|
|
case et_double:
|
|
return (int)val.value.as_double;
|
|
case et_float:
|
|
return (int)val.value.as_float;
|
|
#ifdef HOST_HAS_QWORD
|
|
case et_qword:
|
|
return (int)val.value.as_qword;
|
|
case et_sqword:
|
|
return (int)val.value.as_sqword;
|
|
#endif /* HOST_HAS_QWORD */
|
|
case et_addr:
|
|
return (int)val.value.as_addr;
|
|
case et_uint:
|
|
return (int)val.value.as_uint;
|
|
case et_int:
|
|
return val.value.as_int;
|
|
case et_symbol:
|
|
panic("symbol used in expression");
|
|
default:
|
|
panic("illegal arithmetic expression conversion");
|
|
}
|
|
}
|
|
|
|
/*
|
|
* arithmetic intrinsics operations, used during expression evaluation
|
|
*/
|
|
|
|
/* compute <val1> + <val2> */
|
|
static struct eval_value_t
|
|
f_add(struct eval_value_t val1, struct eval_value_t val2)
|
|
{
|
|
enum eval_type_t et;
|
|
struct eval_value_t val;
|
|
|
|
/* symbols are not allowed in arithmetic expressions */
|
|
if (val1.type == et_symbol || val2.type == et_symbol)
|
|
{
|
|
eval_error = ERR_BADEXPR;
|
|
return err_value;
|
|
}
|
|
|
|
/* get result type, and perform operation in that type */
|
|
et = result_type(val1.type, val2.type);
|
|
switch (et)
|
|
{
|
|
case et_double:
|
|
val.type = et_double;
|
|
val.value.as_double = eval_as_double(val1) + eval_as_double(val2);
|
|
break;
|
|
case et_float:
|
|
val.type = et_float;
|
|
val.value.as_float = eval_as_float(val1) + eval_as_float(val2);
|
|
break;
|
|
#ifdef HOST_HAS_QWORD
|
|
case et_qword:
|
|
val.type = et_qword;
|
|
val.value.as_qword = eval_as_qword(val1) + eval_as_qword(val2);
|
|
break;
|
|
case et_sqword:
|
|
val.type = et_sqword;
|
|
val.value.as_sqword = eval_as_sqword(val1) + eval_as_sqword(val2);
|
|
break;
|
|
#endif /* HOST_HAS_QWORD */
|
|
case et_addr:
|
|
val.type = et_addr;
|
|
val.value.as_addr = eval_as_addr(val1) + eval_as_addr(val2);
|
|
break;
|
|
case et_uint:
|
|
val.type = et_uint;
|
|
val.value.as_uint = eval_as_uint(val1) + eval_as_uint(val2);
|
|
break;
|
|
case et_int:
|
|
val.type = et_int;
|
|
val.value.as_int = eval_as_int(val1) + eval_as_int(val2);
|
|
break;
|
|
default:
|
|
panic("bogus expression type");
|
|
}
|
|
|
|
return val;
|
|
}
|
|
|
|
/* compute <val1> - <val2> */
|
|
static struct eval_value_t
|
|
f_sub(struct eval_value_t val1, struct eval_value_t val2)
|
|
{
|
|
enum eval_type_t et;
|
|
struct eval_value_t val;
|
|
|
|
/* symbols are not allowed in arithmetic expressions */
|
|
if (val1.type == et_symbol || val2.type == et_symbol)
|
|
{
|
|
eval_error = ERR_BADEXPR;
|
|
return err_value;
|
|
}
|
|
|
|
/* get result type, and perform operation in that type */
|
|
et = result_type(val1.type, val2.type);
|
|
switch (et)
|
|
{
|
|
case et_double:
|
|
val.type = et_double;
|
|
val.value.as_double = eval_as_double(val1) - eval_as_double(val2);
|
|
break;
|
|
case et_float:
|
|
val.type = et_float;
|
|
val.value.as_float = eval_as_float(val1) - eval_as_float(val2);
|
|
break;
|
|
#ifdef HOST_HAS_QWORD
|
|
case et_qword:
|
|
val.type = et_qword;
|
|
val.value.as_qword = eval_as_qword(val1) - eval_as_qword(val2);
|
|
break;
|
|
case et_sqword:
|
|
val.type = et_sqword;
|
|
val.value.as_sqword = eval_as_sqword(val1) - eval_as_sqword(val2);
|
|
break;
|
|
#endif /* HOST_HAS_QWORD */
|
|
case et_addr:
|
|
val.type = et_addr;
|
|
val.value.as_addr = eval_as_addr(val1) - eval_as_addr(val2);
|
|
break;
|
|
case et_uint:
|
|
val.type = et_uint;
|
|
val.value.as_uint = eval_as_uint(val1) - eval_as_uint(val2);
|
|
break;
|
|
case et_int:
|
|
val.type = et_int;
|
|
val.value.as_int = eval_as_int(val1) - eval_as_int(val2);
|
|
break;
|
|
default:
|
|
panic("bogus expression type");
|
|
}
|
|
|
|
return val;
|
|
}
|
|
|
|
/* compute <val1> * <val2> */
|
|
static struct eval_value_t
|
|
f_mult(struct eval_value_t val1, struct eval_value_t val2)
|
|
{
|
|
enum eval_type_t et;
|
|
struct eval_value_t val;
|
|
|
|
/* symbols are not allowed in arithmetic expressions */
|
|
if (val1.type == et_symbol || val2.type == et_symbol)
|
|
{
|
|
eval_error = ERR_BADEXPR;
|
|
return err_value;
|
|
}
|
|
|
|
/* get result type, and perform operation in that type */
|
|
et = result_type(val1.type, val2.type);
|
|
switch (et)
|
|
{
|
|
case et_double:
|
|
val.type = et_double;
|
|
val.value.as_double = eval_as_double(val1) * eval_as_double(val2);
|
|
break;
|
|
case et_float:
|
|
val.type = et_float;
|
|
val.value.as_float = eval_as_float(val1) * eval_as_float(val2);
|
|
break;
|
|
#ifdef HOST_HAS_QWORD
|
|
case et_qword:
|
|
val.type = et_qword;
|
|
val.value.as_qword = eval_as_qword(val1) * eval_as_qword(val2);
|
|
break;
|
|
case et_sqword:
|
|
val.type = et_sqword;
|
|
val.value.as_sqword = eval_as_sqword(val1) * eval_as_sqword(val2);
|
|
break;
|
|
#endif /* HOST_HAS_QWORD */
|
|
case et_addr:
|
|
val.type = et_addr;
|
|
val.value.as_addr = eval_as_addr(val1) * eval_as_addr(val2);
|
|
break;
|
|
case et_uint:
|
|
val.type = et_uint;
|
|
val.value.as_uint = eval_as_uint(val1) * eval_as_uint(val2);
|
|
break;
|
|
case et_int:
|
|
val.type = et_int;
|
|
val.value.as_int = eval_as_int(val1) * eval_as_int(val2);
|
|
break;
|
|
default:
|
|
panic("bogus expression type");
|
|
}
|
|
|
|
return val;
|
|
}
|
|
|
|
/* compute <val1> / <val2> */
|
|
static struct eval_value_t
|
|
f_div(struct eval_value_t val1, struct eval_value_t val2)
|
|
{
|
|
enum eval_type_t et;
|
|
struct eval_value_t val;
|
|
|
|
/* symbols are not allowed in arithmetic expressions */
|
|
if (val1.type == et_symbol || val2.type == et_symbol)
|
|
{
|
|
eval_error = ERR_BADEXPR;
|
|
return err_value;
|
|
}
|
|
|
|
/* get result type, and perform operation in that type */
|
|
et = result_type(val1.type, val2.type);
|
|
switch (et)
|
|
{
|
|
case et_double:
|
|
val.type = et_double;
|
|
val.value.as_double = eval_as_double(val1) / eval_as_double(val2);
|
|
break;
|
|
case et_float:
|
|
val.type = et_float;
|
|
val.value.as_float = eval_as_float(val1) / eval_as_float(val2);
|
|
break;
|
|
#ifdef HOST_HAS_QWORD
|
|
case et_qword:
|
|
val.type = et_qword;
|
|
val.value.as_qword = eval_as_qword(val1) / eval_as_qword(val2);
|
|
break;
|
|
case et_sqword:
|
|
val.type = et_sqword;
|
|
val.value.as_sqword = eval_as_sqword(val1) / eval_as_sqword(val2);
|
|
break;
|
|
#endif /* HOST_HAS_QWORD */
|
|
case et_addr:
|
|
val.type = et_addr;
|
|
val.value.as_addr = eval_as_addr(val1) / eval_as_addr(val2);
|
|
break;
|
|
case et_uint:
|
|
val.type = et_uint;
|
|
val.value.as_uint = eval_as_uint(val1) / eval_as_uint(val2);
|
|
break;
|
|
case et_int:
|
|
val.type = et_int;
|
|
val.value.as_int = eval_as_int(val1) / eval_as_int(val2);
|
|
break;
|
|
default:
|
|
panic("bogus expression type");
|
|
}
|
|
|
|
return val;
|
|
}
|
|
|
|
/* compute - <val1> */
|
|
static struct eval_value_t
|
|
f_neg(struct eval_value_t val1)
|
|
{
|
|
struct eval_value_t val;
|
|
|
|
/* symbols are not allowed in arithmetic expressions */
|
|
if (val1.type == et_symbol)
|
|
{
|
|
eval_error = ERR_BADEXPR;
|
|
return err_value;
|
|
}
|
|
|
|
/* result type is the same as the operand type */
|
|
switch (val1.type)
|
|
{
|
|
case et_double:
|
|
val.type = et_double;
|
|
val.value.as_double = - val1.value.as_double;
|
|
break;
|
|
case et_float:
|
|
val.type = et_float;
|
|
val.value.as_float = - val1.value.as_float;
|
|
break;
|
|
#ifdef HOST_HAS_QWORD
|
|
case et_qword:
|
|
val.type = et_sqword;
|
|
val.value.as_qword = - (sqword_t)val1.value.as_qword;
|
|
break;
|
|
case et_sqword:
|
|
val.type = et_sqword;
|
|
val.value.as_sqword = - val1.value.as_sqword;
|
|
break;
|
|
#endif /* HOST_HAS_QWORD */
|
|
case et_addr:
|
|
val.type = et_addr;
|
|
val.value.as_addr = - val1.value.as_addr;
|
|
break;
|
|
case et_uint:
|
|
if ((unsigned int)val1.value.as_uint > 2147483648U)
|
|
{
|
|
/* promote type */
|
|
#ifdef HOST_HAS_QWORD
|
|
val.type = et_sqword;
|
|
val.value.as_sqword = - ((sqword_t)val1.value.as_uint);
|
|
#else /* !HOST_HAS_QWORD */
|
|
val.type = et_double;
|
|
val.value.as_double = - ((double)val1.value.as_uint);
|
|
#endif /* HOST_HAS_QWORD */
|
|
}
|
|
else
|
|
{
|
|
/* don't promote type */
|
|
val.type = et_int;
|
|
val.value.as_int = - ((int)val1.value.as_uint);
|
|
}
|
|
break;
|
|
case et_int:
|
|
if ((unsigned int)val1.value.as_int == 0x80000000U)
|
|
{
|
|
/* promote type */
|
|
val.type = et_uint;
|
|
val.value.as_uint = 2147483648U;
|
|
}
|
|
else
|
|
{
|
|
/* don't promote type */
|
|
val.type = et_int;
|
|
val.value.as_int = - val1.value.as_int;
|
|
}
|
|
break;
|
|
default:
|
|
panic("bogus expression type");
|
|
}
|
|
|
|
return val;
|
|
}
|
|
|
|
/* compute val1 == 0 */
|
|
static int
|
|
f_eq_zero(struct eval_value_t val1)
|
|
{
|
|
int val;
|
|
|
|
/* symbols are not allowed in arithmetic expressions */
|
|
if (val1.type == et_symbol)
|
|
{
|
|
eval_error = ERR_BADEXPR;
|
|
return FALSE;
|
|
}
|
|
|
|
switch (val1.type)
|
|
{
|
|
case et_double:
|
|
val = val1.value.as_double == 0.0;
|
|
break;
|
|
case et_float:
|
|
val = val1.value.as_float == 0.0;
|
|
break;
|
|
#ifdef HOST_HAS_QWORD
|
|
case et_qword:
|
|
val = val1.value.as_qword == 0;
|
|
break;
|
|
case et_sqword:
|
|
val = val1.value.as_sqword == 0;
|
|
break;
|
|
#endif /* HOST_HAS_QWORD */
|
|
case et_addr:
|
|
val = val1.value.as_addr == 0;
|
|
break;
|
|
case et_uint:
|
|
val = val1.value.as_uint == 0;
|
|
break;
|
|
case et_int:
|
|
val = val1.value.as_int == 0;
|
|
break;
|
|
default:
|
|
panic("bogus expression type");
|
|
}
|
|
|
|
return val;
|
|
}
|
|
|
|
/* evaluate the value of the numeric literal constant in ES->TOK_BUF,
|
|
eval_err is set to a value other than ERR_NOERR if the constant cannot
|
|
be parsed and converted to an expression value */
|
|
static struct eval_value_t /* value of the literal constant */
|
|
constant(struct eval_state_t *es) /* expression evaluator */
|
|
{
|
|
struct eval_value_t val;
|
|
int int_val;
|
|
unsigned int uint_val;
|
|
double double_val;
|
|
char *endp;
|
|
#ifdef HOST_HAS_QWORD
|
|
sqword_t sqword_val;
|
|
qword_t qword_val;
|
|
#endif /* HOST_HAS_QWORD */
|
|
|
|
#if 0 /* no longer needed... */
|
|
#if defined(sparc) && !defined(__svr4__)
|
|
extern long strtol(char *, char **, int);
|
|
extern double strtod(char *, char **);
|
|
#endif /* sparc */
|
|
#endif
|
|
|
|
/*
|
|
* attempt multiple conversions, from least to most precise, using
|
|
* the value returned when the conversion is successful
|
|
*/
|
|
|
|
/* attempt integer conversion */
|
|
errno = 0;
|
|
int_val = strtol(es->tok_buf, &endp, /* parse base */0);
|
|
if (!errno && !*endp)
|
|
{
|
|
/* good conversion */
|
|
val.type = et_int;
|
|
val.value.as_int = int_val;
|
|
return val;
|
|
}
|
|
|
|
/* else, not an integer, attempt unsigned int conversion */
|
|
errno = 0;
|
|
uint_val = strtoul(es->tok_buf, &endp, /* parse base */0);
|
|
if (!errno && !*endp)
|
|
{
|
|
/* good conversion */
|
|
val.type = et_uint;
|
|
val.value.as_uint = uint_val;
|
|
return val;
|
|
}
|
|
|
|
#ifdef HOST_HAS_QWORD
|
|
/* else, not an int/uint, attempt sqword_t conversion */
|
|
errno = 0;
|
|
sqword_val = myatosq(es->tok_buf, &endp, /* parse base */0);
|
|
if (!errno && !*endp)
|
|
{
|
|
/* good conversion */
|
|
val.type = et_sqword;
|
|
val.value.as_sqword = sqword_val;
|
|
return val;
|
|
}
|
|
|
|
/* else, not an sqword_t, attempt qword_t conversion */
|
|
errno = 0;
|
|
qword_val = myatoq(es->tok_buf, &endp, /* parse base */0);
|
|
if (!errno && !*endp)
|
|
{
|
|
/* good conversion */
|
|
val.type = et_qword;
|
|
val.value.as_qword = qword_val;
|
|
return val;
|
|
}
|
|
#endif /* HOST_HAS_QWORD */
|
|
|
|
/* else, not any type of integer, attempt double conversion (NOTE: no
|
|
reliable float conversion is available on all machines) */
|
|
errno = 0;
|
|
double_val = strtod(es->tok_buf, &endp);
|
|
if (!errno && !*endp)
|
|
{
|
|
/* good conversion */
|
|
val.type = et_double;
|
|
val.value.as_double = double_val;
|
|
return val;
|
|
}
|
|
|
|
/* else, not a double value, therefore, could not convert constant,
|
|
declare an error */
|
|
eval_error = ERR_BADCONST;
|
|
return err_value;
|
|
}
|
|
|
|
/* evaluate an expression factor, eval_err will indicate it any
|
|
expression evaluation occurs */
|
|
static struct eval_value_t /* value of factor */
|
|
factor(struct eval_state_t *es) /* expression evaluator */
|
|
{
|
|
enum eval_token_t tok;
|
|
struct eval_value_t val;
|
|
|
|
tok = peek_next_token(es);
|
|
switch (tok)
|
|
{
|
|
case tok_oparen:
|
|
(void)get_next_token(es);
|
|
val = expr(es);
|
|
if (eval_error)
|
|
return err_value;
|
|
|
|
tok = peek_next_token(es);
|
|
if (tok != tok_cparen)
|
|
{
|
|
eval_error = ERR_UPAREN;
|
|
return err_value;
|
|
}
|
|
(void)get_next_token(es);
|
|
break;
|
|
|
|
case tok_minus:
|
|
/* negation operator */
|
|
(void)get_next_token(es);
|
|
val = factor(es);
|
|
if (eval_error)
|
|
return err_value;
|
|
val = f_neg(val);
|
|
break;
|
|
|
|
case tok_ident:
|
|
(void)get_next_token(es);
|
|
/* evaluate the identifier in TOK_BUF */
|
|
val = es->f_eval_ident(es);
|
|
if (eval_error)
|
|
return err_value;
|
|
break;
|
|
|
|
case tok_const:
|
|
(void)get_next_token(es);
|
|
val = constant(es);
|
|
if (eval_error)
|
|
return err_value;
|
|
break;
|
|
|
|
default:
|
|
eval_error = ERR_NOTERM;
|
|
return err_value;
|
|
}
|
|
|
|
return val;
|
|
}
|
|
|
|
/* evaluate an expression term, eval_err will indicate it any
|
|
expression evaluation occurs */
|
|
static struct eval_value_t /* value to expression term */
|
|
term(struct eval_state_t *es) /* expression evaluator */
|
|
{
|
|
enum eval_token_t tok;
|
|
struct eval_value_t val, val1;
|
|
|
|
val = factor(es);
|
|
if (eval_error)
|
|
return err_value;
|
|
|
|
tok = peek_next_token(es);
|
|
switch (tok)
|
|
{
|
|
case tok_mult:
|
|
(void)get_next_token(es);
|
|
val = f_mult(val, term(es));
|
|
if (eval_error)
|
|
return err_value;
|
|
break;
|
|
|
|
case tok_div:
|
|
(void)get_next_token(es);
|
|
val1 = term(es);
|
|
if (eval_error)
|
|
return err_value;
|
|
if (f_eq_zero(val1))
|
|
{
|
|
eval_error = ERR_DIV0;
|
|
return err_value;
|
|
}
|
|
val = f_div(val, val1);
|
|
break;
|
|
|
|
default:;
|
|
}
|
|
|
|
return val;
|
|
}
|
|
|
|
/* evaluate an expression, eval_err will indicate it any expression
|
|
evaluation occurs */
|
|
static struct eval_value_t /* value of the expression */
|
|
expr(struct eval_state_t *es) /* expression evaluator */
|
|
{
|
|
enum eval_token_t tok;
|
|
struct eval_value_t val;
|
|
|
|
val = term(es);
|
|
if (eval_error)
|
|
return err_value;
|
|
|
|
tok = peek_next_token(es);
|
|
switch (tok)
|
|
{
|
|
case tok_plus:
|
|
(void)get_next_token(es);
|
|
val = f_add(val, expr(es));
|
|
if (eval_error)
|
|
return err_value;
|
|
break;
|
|
|
|
case tok_minus:
|
|
(void)get_next_token(es);
|
|
val = f_sub(val, expr(es));
|
|
if (eval_error)
|
|
return err_value;
|
|
break;
|
|
|
|
default:;
|
|
}
|
|
|
|
return val;
|
|
}
|
|
|
|
/* create an evaluator */
|
|
struct eval_state_t * /* expression evaluator */
|
|
eval_new(eval_ident_t f_eval_ident, /* user ident evaluator */
|
|
void *user_ptr) /* user ptr passed to ident fn */
|
|
{
|
|
struct eval_state_t *es;
|
|
|
|
es = calloc(1, sizeof(struct eval_state_t));
|
|
if (!es)
|
|
fatal("out of virtual memory");
|
|
|
|
es->f_eval_ident = f_eval_ident;
|
|
es->user_ptr = user_ptr;
|
|
|
|
return es;
|
|
}
|
|
|
|
/* delete an evaluator */
|
|
void
|
|
eval_delete(struct eval_state_t *es) /* evaluator to delete */
|
|
{
|
|
free(es);
|
|
}
|
|
|
|
/* evaluate an expression, if an error occurs during evaluation, the
|
|
global variable eval_error will be set to a value other than ERR_NOERR */
|
|
struct eval_value_t /* value of the expression */
|
|
eval_expr(struct eval_state_t *es, /* expression evaluator */
|
|
char *p, /* ptr to expression string */
|
|
char **endp) /* returns ptr to 1st unused char */
|
|
{
|
|
struct eval_value_t val;
|
|
|
|
/* initialize the evaluator state */
|
|
eval_error = ERR_NOERR;
|
|
es->p = p;
|
|
*es->tok_buf = '\0';
|
|
es->peek_tok = tok_invalid;
|
|
|
|
/* evaluate the expression */
|
|
val = expr(es);
|
|
|
|
/* return a pointer to the first character not used in the expression */
|
|
if (endp)
|
|
{
|
|
if (es->peek_tok != tok_invalid)
|
|
{
|
|
/* did not consume peek'ed token, so return last p */
|
|
*endp = es->lastp;
|
|
}
|
|
else
|
|
*endp = es->p;
|
|
}
|
|
|
|
return val;
|
|
}
|
|
|
|
/* print an expression value */
|
|
void
|
|
eval_print(FILE *stream, /* output stream */
|
|
struct eval_value_t val) /* expression value to print */
|
|
{
|
|
switch (val.type)
|
|
{
|
|
case et_double:
|
|
fprintf(stream, "%f [double]", val.value.as_double);
|
|
break;
|
|
case et_float:
|
|
fprintf(stream, "%f [float]", (double)val.value.as_float);
|
|
break;
|
|
#ifdef HOST_HAS_QWORD
|
|
case et_qword:
|
|
myfprintf(stream, "%lu [qword_t]", val.value.as_qword);
|
|
break;
|
|
case et_sqword:
|
|
myfprintf(stream, "%ld [sqword_t]", val.value.as_sqword);
|
|
break;
|
|
#endif /* HOST_HAS_QWORD */
|
|
case et_addr:
|
|
myfprintf(stream, "0x%p [md_addr_t]", val.value.as_addr);
|
|
break;
|
|
case et_uint:
|
|
fprintf(stream, "%u [uint]", val.value.as_uint);
|
|
break;
|
|
case et_int:
|
|
fprintf(stream, "%d [int]", val.value.as_int);
|
|
break;
|
|
case et_symbol:
|
|
fprintf(stream, "\"%s\" [symbol]", val.value.as_symbol);
|
|
break;
|
|
default:
|
|
panic("bogus expression type");
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef TEST
|
|
|
|
static struct eval_value_t an_int;
|
|
static struct eval_value_t a_uint;
|
|
static struct eval_value_t a_float;
|
|
static struct eval_value_t a_double;
|
|
static struct eval_value_t a_symbol;
|
|
|
|
struct sym_map_t {
|
|
char *symbol;
|
|
struct eval_value_t *value;
|
|
};
|
|
|
|
static struct sym_map_t sym_map[] = {
|
|
{ "an_int", &an_int },
|
|
{ "a_uint", &a_uint },
|
|
{ "a_float", &a_float },
|
|
{ "a_double", &a_double },
|
|
{ "a_symbol", &a_symbol },
|
|
{ NULL, NULL },
|
|
};
|
|
|
|
struct eval_value_t
|
|
my_eval_ident(struct eval_state_t *es)
|
|
{
|
|
struct sym_map_t *sym;
|
|
|
|
for (sym=sym_map; sym->symbol != NULL; sym++)
|
|
{
|
|
if (!strcmp(sym->symbol, es->tok_buf))
|
|
return *sym->value;
|
|
}
|
|
|
|
eval_error = ERR_UNDEFVAR;
|
|
return err_value;
|
|
}
|
|
|
|
void
|
|
main(void)
|
|
{
|
|
struct eval_state_t *es;
|
|
|
|
/* set up test variables */
|
|
an_int.type = et_int; an_int.value.as_int = 1;
|
|
a_uint.type = et_uint; a_uint.value.as_uint = 2;
|
|
a_float.type = et_float; a_float.value.as_float = 3.0f;
|
|
a_double.type = et_double; a_double.value.as_double = 4.0;
|
|
a_symbol.type = et_symbol; a_symbol.value.as_symbol = "testing 1 2 3...";
|
|
|
|
/* instantiate an evaluator */
|
|
es = eval_new(my_eval_ident, NULL);
|
|
|
|
while (1)
|
|
{
|
|
struct eval_value_t val;
|
|
char expr_buf[1024];
|
|
|
|
fgets(expr_buf, 1024, stdin);
|
|
|
|
/* chop */
|
|
if (expr_buf[strlen(expr_buf)-1] == '\n')
|
|
expr_buf[strlen(expr_buf)-1] = '\0';
|
|
|
|
if (expr_buf[0] == '\0')
|
|
exit(0);
|
|
|
|
val = eval_expr(es, expr_buf, NULL);
|
|
if (eval_error)
|
|
fprintf(stdout, "eval error: %s\n", eval_err_str[eval_error]);
|
|
else
|
|
{
|
|
fprintf(stdout, "%s == ", expr_buf);
|
|
eval_print(stdout, val);
|
|
fprintf(stdout, "\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif /* TEST */
|