Address expressions are now fully algebraic.

This commit is contained in:
Daniel Beer 2010-04-10 12:25:01 +12:00
parent f8f530f24e
commit aecf8f0175
2 changed files with 256 additions and 64 deletions

View File

@ -296,17 +296,21 @@ renamed.
.SH ADDRESS EXPRESSIONS .SH ADDRESS EXPRESSIONS
Any command which accepts a memory address, length or register value Any command which accepts a memory address, length or register value
as an argument may be given an address expression. An address as an argument may be given an address expression. An address
expression consists of a sequence of one or more address values expression consists of an algebraic combination of values.
separated by the operators \fB+\fR or \fB\-\fR.
An address value may be either a decimal value, a hexadecimal value An address value may be either a decimal value, a hexadecimal value
preceeded by the prefix \fB0x\fR, or a symbol name. preceeded by the prefix \fB0x\fR, or a symbol name.
The operators recognised are the usual algebraic operators: \fB+\fR, \fB-\fR,
\fB*\fR, \fB/\fR, \fB%\fR, \fB(\fR and \fB)\fR. Operator precedence is the
same as in C-like languages, and the \fB-\fR operator may be used as a
unary negation operator.
The following are all valid examples of address expressions: The following are all valid examples of address expressions:
.B 64 .B 2+2
.br .br
.B 0xffe0 .B table_start + (elem_size + elem_pad)*4
.br .br
.B main+0x3f .B main+0x3f
.br .br

308
util.c
View File

@ -27,6 +27,7 @@
#include <unistd.h> #include <unistd.h>
#include <errno.h> #include <errno.h>
#include <signal.h> #include <signal.h>
#include <assert.h>
#ifdef USE_READLINE #ifdef USE_READLINE
#include <readline/readline.h> #include <readline/readline.h>
@ -665,82 +666,269 @@ int ctrlc_check(void)
return ctrlc_flag; return ctrlc_flag;
} }
static char token_buf[64];
static int token_len;
static int token_mult;
static int token_sum;
static token_func_t token_func; static token_func_t token_func;
static int token_add(void)
{
int i;
int value;
if (!token_len)
return 0;
token_buf[token_len] = 0;
token_len = 0;
/* Is it a decimal? */
i = 0;
while (token_buf[i] && isdigit(token_buf[i]))
i++;
if (!token_buf[i]) {
token_sum += token_mult * atoi(token_buf);
return 0;
}
/* Is it hex? */
if (token_buf[0] == '0' && tolower(token_buf[1]) == 'x') {
token_sum += token_mult * strtol(token_buf + 2, NULL, 16);
return 0;
}
/* Look up the name in the symbol table */
if (token_func && !token_func(token_buf, &value)) {
token_sum += token_mult * value;
return 0;
}
fprintf(stderr, "unknown token: %s\n", token_buf);
return -1;
}
void set_token_func(token_func_t func) void set_token_func(token_func_t func)
{ {
token_func = func; token_func = func;
} }
int addr_exp(const char *text, int *addr) struct addr_exp_state {
{ int last_operator;
token_len = 0; int data_stack[32];
token_mult = 1; int data_stack_size;
token_sum = 0; int op_stack[32];
int op_stack_size;
};
while (*text) { static int addr_exp_data(struct addr_exp_state *s, const char *text)
if (isalnum(*text) || *text == '_' || *text == '$' || {
*text == '.' || *text == ':') { int value;
if (token_len + 1 < sizeof(token_buf))
token_buf[token_len++] = *text; if (!s->last_operator || s->last_operator == ')') {
} else { fprintf(stderr, "syntax error at token %s\n", text);
if (token_add() < 0)
return -1; return -1;
if (*text == '+')
token_mult = 1;
if (*text == '-')
token_mult = -1;
} }
/* Hex value */
if (*text == '0' && text[1] == 'x')
value = strtoul(text + 2, NULL, 16);
else if (isdigit(*text))
value = atoi(text);
else if (!token_func || token_func(text, &value) < 0) {
fprintf(stderr, "can't parse token: %s\n", text);
return -1;
}
if (s->data_stack_size + 1 > ARRAY_LEN(s->data_stack)) {
fprintf(stderr, "data stack overflow at token %s\n", text);
return -1;
}
s->data_stack[s->data_stack_size++] = value;
s->last_operator = 0;
return 0;
}
static int addr_exp_pop(struct addr_exp_state *s)
{
char op = s->op_stack[--s->op_stack_size];
int data1 = s->data_stack[--s->data_stack_size];
int data2 = 0;
int result = 0;
if (op != 'N')
data2 = s->data_stack[--s->data_stack_size];
assert (s->op_stack_size >= 0);
assert (s->data_stack_size >= 0);
switch (op) {
case '+':
result = data2 + data1;
break;
case '-':
result = data2 - data1;
break;
case '*':
result = data2 * data1;
break;
case '/':
if (!data1)
goto divzero;
result = data2 / data1;
break;
case '%':
if (!data1)
goto divzero;
result = data2 % data1;
break;
case 'N':
result = -data1;
break;
}
s->data_stack[s->data_stack_size++] = result;
return 0;
divzero:
fprintf(stderr, "divide by zero\n");
return -1;
}
static int can_push(struct addr_exp_state *s, char op)
{
char top;
if (!s->op_stack_size || op == '(')
return 1;
top = s->op_stack[s->op_stack_size - 1];
if (top == '(')
return 1;
switch (op) {
case 'N':
return 1;
case '*':
case '%':
case '/':
return top == '+' || top == '-';
default:
break;
}
return 0;
}
static int addr_exp_op(struct addr_exp_state *s, char op)
{
if (op == '(') {
if (!s->last_operator || s->last_operator == ')')
goto syntax_error;
} else if (op == '-') {
if (s->last_operator && s->last_operator != ')')
op = 'N';
} else {
if (s->last_operator && s->last_operator != ')')
goto syntax_error;
}
if (op == ')') {
/* ) collapses the stack to the last matching ( */
while (s->op_stack_size &&
s->op_stack[s->op_stack_size - 1] != '(')
if (addr_exp_pop(s) < 0)
return -1;
if (!s->op_stack_size) {
fprintf(stderr, "parenthesis mismatch: )\n");
return -1;
}
s->op_stack_size--;
} else {
while (!can_push(s, op))
if (addr_exp_pop(s) < 0)
return -1;
if (s->op_stack_size + 1 > ARRAY_LEN(s->op_stack)) {
fprintf(stderr, "operator stack overflow: %c\n", op);
return -1;
}
s->op_stack[s->op_stack_size++] = op;
}
s->last_operator = op;
return 0;
syntax_error:
fprintf(stderr, "syntax error at operator %c\n", op);
return -1;
}
static int addr_exp_finish(struct addr_exp_state *s, int *ret)
{
if (s->last_operator && s->last_operator != ')') {
fprintf(stderr, "syntax error at end of expression\n");
return -1;
}
while (s->op_stack_size) {
if (s->op_stack[s->op_stack_size - 1] == '(') {
fprintf(stderr, "parenthesis mismatch: (\n");
return -1;
}
if (addr_exp_pop(s) < 0)
return -1;
}
if (s->data_stack_size != 1) {
fprintf(stderr, "no data: stack size is %d\n",
s->data_stack_size);
return -1;
}
if (ret)
*ret = s->data_stack[0];
return 0;
}
int addr_exp(const char *text, int *addr)
{
const char *text_save = text;
int last_cc = 1;
char token_buf[64];
int token_len = 0;
struct addr_exp_state s = {0};
s.last_operator = '(';
for (;;) {
int cc;
/* Figure out what class this character is */
if (*text == '+' || *text == '-' ||
*text == '*' || *text == '/' ||
*text == '%' || *text == '(' ||
*text == ')')
cc = 1;
else if (!*text || isspace(*text))
cc = 2;
else if (isalnum(*text) || *text == '.' || *text == '_' ||
*text == '$' || *text == ':')
cc = 3;
else {
fprintf(stderr, "illegal character in expression: %c\n",
*text);
return -1;
}
/* Accumulate and process token text */
if (cc == 3) {
if (token_len + 1 < sizeof(token_buf))
token_buf[token_len++] = *text;
} else if (token_len) {
token_buf[token_len] = 0;
token_len = 0;
if (addr_exp_data(&s, token_buf) < 0)
goto fail;
}
/* Process operators */
if (cc == 1) {
if (addr_exp_op(&s, *text) < 0)
goto fail;
}
if (!*text)
break;
last_cc = cc;
text++; text++;
} }
if (token_add() < 0) if (addr_exp_finish(&s, addr) < 0)
return -1; goto fail;
*addr = token_sum & 0xffff;
return 0; return 0;
fail:
fprintf(stderr, "bad address expression: %s\n", text_save);
return -1;
} }
static int modify_flags; static int modify_flags;